[笔记]Windows安全之《四》自启动技术

505 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第3天

参考: 《windows黑客编程》

前言

即使成功植入模块并启动攻击模块,依然不能解决永久驻留的问题。

解决永久驻留的第一步便是如何实现伴随系统启动而启动的问题,即开机自启动。这样,即使用户关机重启,病毒木马也能随着系统的启动,而由系统加载到内存中运行,从而窃取用户数据和隐私。因此,开机自启动技术是病毒木马至关重要的技术,也是杀软重点监测的技术。对于杀软来说,只要把守住自启动的入口,就可以扣病毒木马栀杀在摇篮之中。

本章主要介绍4种开机自启动技术,它包括:

  • 注册表
  • 快速启动目录
  • 计划任务
  • 系统服务。

注册表

函数介绍

RegOpenKeyEx函数

打开一个指定的注册表键

LSTATUS RegOpenKeyEx( 
    [in] HKEY hKey, 
    [in, optional] LPCSTR lpSubKey, 
    [in] DWORD ulOptions, 
    [in] REGSAM samDesired, 
    [out] PHKEY phkResult 
);

RegSetValueEx函数

在注册表项下设置指定值的数据和类型。

LSTATUS RegSetValueEx(
    [in] HKEY hKey, 
    [in, optional] LPCSTR lpValueName, 
    DWORD Reserved, 
    [in] DWORD dwType, 
    [in] const BYTE *lpData, 
    [in] DWORD cbData 
);

原理

Windows每次开机都会从一些注册表键下获取键值中的程序路径,并创建进程启动程序。所以想实现注册表开机自启动,只需要在这个注册表键下添加自己想要设置的程序路径即可。

自启动注册表路径包括:

  • 当前用户登录自启(注:用户权限即可):
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
  • 当前机器开机自启(注:需要管理员权限才能修改):
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run

代码实现

BOOL Reg_CurrentUser(char *lpszFileName, char *lpszValueName)
{
	// 默认权限
	HKEY hKey;
	// 打开注册表键
	if (ERROR_SUCCESS != ::RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run", 0, KEY_WRITE, &hKey))
	{
		ShowError("RegOpenKeyEx");
		return FALSE;
	}
	// 修改注册表值,实现开机自启
	if (ERROR_SUCCESS != ::RegSetValueEx(hKey, lpszValueName, 0, REG_SZ, (BYTE *)lpszFileName, (1 + ::lstrlen(lpszFileName))))
	{
		::RegCloseKey(hKey);
		ShowError("RegSetValueEx");
		return FALSE;
	}
	// 关闭注册表键
	::RegCloseKey(hKey);

	return TRUE;
}

可以通过regedit.exe查看(或win+R 输入regedit查看对应注册表路径是否有对应键值程序路径的添加添加成功)

小结

截图_20220606112404.png

快速启动目录

不用修改任何系统数据,并且实现起来最为简单的开机自启动方法

函数介绍

SHGetSpecialFolderPath函数

BOOL SHGetSpecialFolderPath( 
    HWND hwndOwner, 
    LPTSTR lpszPath, 
    int nFolder,
    BOOL fCreate );

原理

Windows系统有自带的快速启动文件夹,它是最为简单的自启动方式。只要把程序放入到这个快速启动文件夹中,系统在启动时就会自动地加载并运行相应的程序,实现开机自启动功能。

快速启动目录并不是一个固定目录,每台计算机的快速启动目录都不相同。但是程序可以使用SHGetSpecialFolderPath函数获取Windows系统中快速启动目录的路径,快速启动目录的CSIDL标识值为CSIDL STARTUP。

然后,使用CopyFile函数,将想要自启动的程序复制到快速启动目录下即可。当然,为程序创建快捷方式,并把快捷方式放入到快速启动目录中,也同样可以达到开机自启动的效果。

代码实现

BOOL AutoRun_Startup(char *lpszSrcFilePath, char *lpszDestFileName)
{
	BOOL bRet = FALSE;
	char szStartupPath[MAX_PATH] = {0};
	char szDestFilePath[MAX_PATH] = {0};
	// 获取 快速启动目录 路径
	bRet = ::SHGetSpecialFolderPath(NULL, szStartupPath, CSIDL_STARTUP, TRUE);
	printf("szStartupPath=%s\n", szStartupPath);
	if (FALSE == bRet)
	{
		return FALSE;
	}
	// 构造拷贝的 目的文件路径
	::wsprintf(szDestFilePath, "%s\\%s", szStartupPath, lpszDestFileName);
	// 拷贝文件到快速启动目录下
	bRet = ::CopyFile(lpszSrcFilePath, szDestFilePath, FALSE);
	if (FALSE == bRet)
	{
		return FALSE;
	}

	return TRUE;
}

之后可打开文件路径查看是否有副本exe

%appdata%\Microsoft\Windows\Start Menu\Programs\Startup

小结

截图_20220606113108.png

计划任务

Windows系统可以设置计划任务来执行一些定时任务。而在本书中,计划任务的触发条件为在用户登录时触发,执行启动指定路径程序的操作,从而实现开机自启动。

对于用户来说,手动创建计划任务并不复杂,也就是点几下鼠标的事情。但是,编程实现添加计划任务还是略微复杂的。

函数介绍

原理

使用管理员权限,通过COM组件接口,添加计划任务。 主要步骤:

  • 初始化COM组件
  • 创建计划任务操作实现
  • 删除计划任务操作实现

代码实现

com组件初始化

CMyTaskSchedule::CMyTaskSchedule(void)
{
	m_lpITS = NULL;
	m_lpRootFolder = NULL;
	// 初始化COM
	HRESULT hr = ::CoInitialize(NULL);
	if(FAILED(hr))
	{
		ShowError("CoInitialize", hr);
	}
	// 创建一个任务服务(Task Service)实例
	hr = ::CoCreateInstance(CLSID_TaskScheduler,
		NULL,
		CLSCTX_INPROC_SERVER,
		IID_ITaskService,
		(LPVOID *)(&m_lpITS));
	if(FAILED(hr))
	{
		ShowError("CoCreateInstance", hr);
	}
	// 连接到任务服务(Task Service)
	hr = m_lpITS->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
	if(FAILED(hr))
	{
		ShowError("ITaskService::Connect", hr);
	}
	// 获取Root Task Folder的指针,这个指针指向的是新注册的任务
	hr = m_lpITS->GetFolder(_bstr_t("\\"), &m_lpRootFolder);
	if(FAILED(hr))
	{
		ShowError("ITaskService::GetFolder", hr);
	}
}

创建计划任务

BOOL CMyTaskSchedule::NewTask(char *lpszTaskName, char *lpszProgramPath, char *lpszParameters, char *lpszAuthor)
{
	if(NULL == m_lpRootFolder)
	{
		return FALSE;
	}
	// 如果存在相同的计划任务,则删除
	Delete(lpszTaskName);
	// 创建任务定义对象来创建任务
	ITaskDefinition *pTaskDefinition = NULL;
	HRESULT hr = m_lpITS->NewTask(0, &pTaskDefinition);
	if(FAILED(hr))
	{
		ShowError("ITaskService::NewTask", hr);
		return FALSE;
	}

	/* 设置注册信息 */
	IRegistrationInfo *pRegInfo = NULL;
	CComVariant variantAuthor(NULL);
	variantAuthor = lpszAuthor;
	hr = pTaskDefinition->get_RegistrationInfo(&pRegInfo);
	if(FAILED(hr))
	{
		ShowError("pTaskDefinition::get_RegistrationInfo", hr);
		return FALSE;
	}
	// 设置作者信息
	hr = pRegInfo->put_Author(variantAuthor.bstrVal);
	pRegInfo->Release();

	/* 设置登录类型和运行权限 */
	IPrincipal *pPrincipal = NULL;
	hr = pTaskDefinition->get_Principal(&pPrincipal);
	if(FAILED(hr))
	{
		ShowError("pTaskDefinition::get_Principal", hr);
		return FALSE;
	}
	// 设置登录类型
	hr = pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
	// 设置运行权限
	// 最高权限
	hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_HIGHEST);  
	pPrincipal->Release();

	/* 设置其他信息 */
	ITaskSettings *pSettting = NULL;
	hr = pTaskDefinition->get_Settings(&pSettting);
	if(FAILED(hr))
	{
		ShowError("pTaskDefinition::get_Settings", hr);
		return FALSE;
	}
	// 设置其他信息
	hr = pSettting->put_StopIfGoingOnBatteries(VARIANT_FALSE);
	hr = pSettting->put_DisallowStartIfOnBatteries(VARIANT_FALSE);
	hr = pSettting->put_AllowDemandStart(VARIANT_TRUE);
	hr = pSettting->put_StartWhenAvailable(VARIANT_FALSE);
	hr = pSettting->put_MultipleInstances(TASK_INSTANCES_PARALLEL);
	pSettting->Release();

	/* 创建执行动作 */
	IActionCollection *pActionCollect = NULL;
	hr = pTaskDefinition->get_Actions(&pActionCollect);
	if(FAILED(hr))
	{
		ShowError("pTaskDefinition::get_Actions", hr);
		return FALSE;
	}
	IAction *pAction = NULL;
	// 创建执行操作
	hr = pActionCollect->Create(TASK_ACTION_EXEC, &pAction);
	pActionCollect->Release();

	/* 设置执行程序路径和参数 */
	CComVariant variantProgramPath(NULL);
	CComVariant variantParameters(NULL);
	IExecAction *pExecAction = NULL;
	hr = pAction->QueryInterface(IID_IExecAction, (PVOID *)(&pExecAction));
	if(FAILED(hr))
	{
		pAction->Release();
		ShowError("IAction::QueryInterface", hr);
		return FALSE;
	}
	pAction->Release();
	// 设置程序路径和参数
	variantProgramPath = lpszProgramPath;
	variantParameters = lpszParameters;
	pExecAction->put_Path(variantProgramPath.bstrVal);
	pExecAction->put_Arguments(variantParameters.bstrVal);
	pExecAction->Release();

	/* 创建触发器,实现用户登陆自启动 */
	ITriggerCollection *pTriggers = NULL;
	hr = pTaskDefinition->get_Triggers(&pTriggers);
	if (FAILED(hr))
	{
		ShowError("pTaskDefinition::get_Triggers", hr);
		return FALSE;
	}
	// 创建触发器
	ITrigger *pTrigger = NULL;
	hr = pTriggers->Create(TASK_TRIGGER_LOGON, &pTrigger);
	if (FAILED(hr))
	{
		ShowError("ITriggerCollection::Create", hr);
		return FALSE;
	}

	/* 注册任务计划  */
	IRegisteredTask *pRegisteredTask = NULL;
	CComVariant variantTaskName(NULL);
	variantTaskName = lpszTaskName;
	hr = m_lpRootFolder->RegisterTaskDefinition(variantTaskName.bstrVal,
		pTaskDefinition,
		TASK_CREATE_OR_UPDATE,
		_variant_t(),
		_variant_t(),
		TASK_LOGON_INTERACTIVE_TOKEN,
		_variant_t(""),
		&pRegisteredTask);
	if(FAILED(hr))
	{
		pTaskDefinition->Release();
		ShowError("ITaskFolder::RegisterTaskDefinition", hr);
		return FALSE;
	}
	pTaskDefinition->Release();
	pRegisteredTask->Release();

	return TRUE;
}

删除计划任务

BOOL CMyTaskSchedule::Delete(char *lpszTaskName)
{
	if(NULL == m_lpRootFolder)
	{
		return FALSE;
	}
	CComVariant variantTaskName(NULL);
	variantTaskName = lpszTaskName;
	HRESULT hr = m_lpRootFolder->DeleteTask(variantTaskName.bstrVal, 0);
	if(FAILED(hr))
	{
		return FALSE;
	}

	return TRUE;
}	

小结

截图_20220606113411.png

系统服务

打开计算机上的任务管理器,可以发现有许多系统服务进程在后台运行,而且大多数的系统服务进程都是随着系统启动而启动的,如svchost..exe进程。

系统服务运行在SESSION 0,由于系统服务的SESSION0隔离,阻断了系统服务和用户桌面进程之间进行交互和通信的桥梁。各个会话之间是相互独立的,在不同会话中运行的实体,相互之间不能发送Windows消息、共享UI元素,或者是在没有指定有权限访问全局名字空间(且提供正确的访间控制设置)的情况下共享核心对象。这也就是为什么在系统服务中不能显示程序界面的原因,也不能用常规的方式创建有界面的进程。

系统进程自启动是通过创建系统服务并设置服务启动类型为自动启动来实现的,接下来就介绍创建系统服务进程的原理和实现。

函数介绍

OpenSCManager函数

建立一个到服务控制管理器的连接,并打开指定的数据库。

CreateService函数

创建一个服务对象,并将其添加到指定的服务控制管理器数据库中。

OpenService函数

打开一个已经存在的服务。

StartService函数

启动服务。

StartServiceCtrlDispatcher函数

将服务进程的主线程连接到服务控制管理器,该线程将作为调用过程的服务控制分派器线程。

原理

创建自启动系统服务进程分成两部分:

  • 创建启动系统服务
  • 系统服务程序的编写

步骤:

  • 以管理员身份运行系统服务创建启动程序AutoRun Service Test.exe,并为系统服务程序ServiceTest.exe创建系统服务,创建完成后,打开服务管理器查看服务,发现“ServiceTest.exe'服务成功创建。
  • 关机重启计算机,然后,打开任务管理器查看系统服务进程,发现“ServiceTest.exe”系统服务进程成功实现开机自启动。

代码实现

参看

小结

截图_20220606114922.png

总结