首页
社区
课程
招聘
[原创]开机自启动技术
2021-10-7 10:22 15602

[原创]开机自启动技术

2021-10-7 10:22
15602

一.快速启动文件夹

    在Windows系统中有一个快速启动文件夹,只要把我们的程序放到这个文件夹下,就可以实现开启启动。但由于每台计算机的快速启动文件夹都不一样,所以我们需要使用SHGetSpecialFolderPath函数来获取快速启动文件夹。它在文档中的定义如下

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

参数hwndOwner代表了窗口所有者的句柄,这里传NULL就好

参数lpszPath是我们指定的缓冲区,用来保存返回快速启动文件夹路径。缓冲区的大小最少为MAX_PATH。

参数nFloader是系统路径的CSIDL标识,这里传入CSIDL_STARTUP代表快速启动文件夹。

参数fCreate指定文件夹不存在时是否要创建。TRUE表示创建,FALSE为不创建。

    既然得到了快速启动文件夹的路径,只要拼接上我们的文件名,并将文件拷贝过去就可以实现开机启动。具体实现代码如下

void StartProgram()
{
	BOOL bRet = FALSE;
	char szDestFilePath[MAX_PATH] = { 0 };
	char szStartupPath[MAX_PATH] = { 0 };
	char destFileName[] = { "demo.exe" };
	char orgFilePath[] = { "C:\\Documents and Settings\\1900\\桌面\\demo.exe" };

	bRet = SHGetSpecialFolderPath(NULL, szStartupPath, CSIDL_STARTUP, TRUE);	//获取开机启动项的目录

	if (!bRet)
	{
		printf("SHGetSpecialFolderPath error %d\n", GetLastError());
		return;
	}

	wsprintf(szDestFilePath, "%s\\%s", szStartupPath, destFileName);	//拼接起来组成完整文件路径

	bRet = CopyFile(orgFilePath, szDestFilePath, FALSE);	//将文件复制到文件夹下
	if (!bRet)
	{
		printf("CopyFile error %d\n", GetLastError());
		return;
	}
}

    运行程序以后可以在以下文件夹下面看到程序被复制进去,此时重启电脑,程序就会自动运行。

二.注册表

    Windows每次开机启动后都会在两个专门的注册表键下遍历键值,得到相应的程序路径并且将它们启动。

    这两个注册表键在32位下分别是

  1. HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows\\CurrentVersion\\Run

  2. HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run

     这两个注册表键有两个区别。

  1. 对HKEY_CURRENT_USER主键的修改只需要用户默认的权限就可以,而对HKEY_LOCAL_MACHINE注册表键的修改却需要管理员权限。

  2. 第一个注册表键在64位中会被重定位成HKEY_LOCAL_MACHINE\\Software\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Run

    要实现注册表的操作,首先就要使用RegOpenKeyEx获取相应的注册表键的句柄,它文档中的定义如下

LONG WINAPI RegOpenKeyEx(
  __in        HKEY hKey,
  __in_opt    LPCTSTR lpSubKey,
  __reserved  DWORD ulOptions,
  __in        REGSAM samDesired,
  __out       PHKEY phkResult);

参数hKey指定要打开的主键名。分别有

  • HKEY_CLASSES_ROOT

  • HKEY_CURRENT_USER

  • HKEY_LOCAL_MACHINE

  • HKEY_USERS

参数lpSubKey指向一个字符串用来表明我们要打开的键的名称。

参数ulOptions被保留起来,必须设为0。

参数samDesired指定得到的键值的权限。其中KEY_ALL_ACCESS代表获取全部权限。

参数phkResult是一个用来保存打开注册表键句柄的指针。

如果函数指向成功,返回值就是ERROR_SUCCESS。

    其次是使用RegSetValueEx来设置指定值的数据和类型。它在文档中的定义如下

LONG WINAPI RegSetValueEx(
  __in        HKEY hKey,
  __in_opt    LPCTSTR lpValueName,
  __reserved  DWORD Reserved,
  __in        DWORD dwType,
  __in_opt    const BYTE* lpData,
  __in        DWORD cbData);

参数hKey就是上面打开的注册表的键值句柄。

参数lpValueName指向了包含预设值名称的字符串。

参数Reserved被保留起来,必须设为0。

参数dwType指定将存储的数据类型。其中REG_SZ表示类型是一个以0结尾的字符串。

参数lpData指向了一个缓冲区,缓冲区中包含了要存储的数据。

参数cbData指定了lpData的大小。

如果函数执行成功,返回值就是ERROR_SUCCESS。

    据此可以写出下列代码来实现开机启动。

void StartProgram()
{
	BOOL bRet = FALSE;
	HKEY hKey = NULL;
	char lpValueName[] = { "C:\\Documents and Settings\\1900\\桌面\\demo.exe" };
	char lpKeyName[] = { "demo" };

	if (RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Run",
				0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
	{
		printf("RegOpenKeyEx error %d\n", GetLastError());
		return;
	}


	if (RegSetValueEx(hKey, lpKeyName, 0, REG_SZ, (const BYTE*)lpValueName,
										strlen(lpValueName) + 1) != ERROR_SUCCESS)
	{
		printf("RegSetValueEx error %d\n", GetLastError());
		return;
	}

	if (hKey) RegCloseKey(hKey);
}

    程序运行之后在注册表中可以看到如下的键值

   此时重启系统,程序就会在重启后运行。

三.驱动服务自启动

    这种自启动方式主要是通过设置我们编写的驱动以随系统自启动的方式启动我们的驱动来实现的。要实现这个功能需要以下的步骤

    首先我们需要使用OpenSCManager建立一个到服务控制管理器的连接,并打开指定的数据库,文档中定义如下

SC_HANDLE WINAPI OpenSCManager(
  __in_opt  LPCTSTR lpMachineName,
  __in_opt  LPCTSTR lpDatabaseName,
  __in      DWORD dwDesiredAccess);

参数1指向目标计算机名的字符串,如果为NULL则连接到本地的服务控制管理器

参数2指向要打开的服务控制管理数据库的名称。此参数应该设置为SERVICES_ACTIVE_DATABASE,如果设置为NULL,就是默认打开它。

参数3指定服务访问控制管理器的权限。

函数成功调用则返回服务控制管理器数据库的句柄,否则为NULL。

    接下来需要使用CreateService函数来创建一个服务对象并添加到指定的服务控制管理器数据库中。该函数在文档中定义如下

SC_HANDLE WINAPI CreateService(
  __in       SC_HANDLE hSCManager,
  __in       LPCTSTR lpServiceName,
  __in_opt   LPCTSTR lpDisplayName,
  __in       DWORD dwDesiredAccess,
  __in       DWORD dwServiceType,
  __in       DWORD dwStartType,
  __in       DWORD dwErrorControl,
  __in_opt   LPCTSTR lpBinaryPathName,
  __in_opt   LPCTSTR lpLoadOrderGroup,
  __out_opt  LPDWORD lpdwTagId,
  __in_opt   LPCTSTR lpDependencies,
  __in_opt   LPCTSTR lpServiceStartName,
  __in_opt   LPCTSTR lpPassword);

参数1指向的就是上面那个函数得到的句柄。

参数2指向要安装服务器的名称。

参数3指向用户界面识别服务的显示名称。

参数4指定对服务的访问。

参数5指定服务的类型。主要有以下几个类型

  • SERVICE_FILE_SYSTEM_DRIVER 文件系统驱动服务程序

  • SERVICE_KERNEL_DRIVER 驱动服务程序

  • SERVICE_WIN32_OWN_PROCESS 运行在独立进程中的服务程序

  • SERVICE_WIN32_SHARE_PROCESS 由多个进程共享的服务程序


参数6指定服务的启动选项。有以下几个类型

  • SERVICE_AUTO_START 系统启动时由服务控制管理器自启动该服务程序

  • SERVICE_BOOT_START 用在由系统加载器创建的设备驱动程序中,它只能用于驱动服务程序

  • SERVICE_DEMAND_START 由服务控制管理器启动的服务

  • SERVICE_DISABLED 表示该服务不可启动

  • SERVICE_SYSTEM_START 用于由IoInitSystem函数创建的设备驱动程序

参数7指定当启动服务失败时,产生的严重程度以及应采取的保护措施。

参数8指定了你要加载的服务的程序路径。

剩下的五个参数指定为NULL就好。

该函数调用成功就会返回服务的句柄,否则为NULL。

    创建好服务以后就可以用OpenService来打开服务,该函数在文档中的定义如下

SC_HANDLE WINAPI OpenService(
  __in  SC_HANDLE hSCManager,
  __in  LPCTSTR lpServiceName,
  __in  DWORD dwDesiredAccess);
Parameters

参数1指向的就是SCM数据库的句柄。

参数2指向要打开的服务器的名称,和上一个函数的第二个参数一样。

参数3指定服务的权限。

函数成功调用返回服务句柄,否则返回NULL。

    接下来就需要使用StartService来启动服务,该函数在文档中的定义如下

BOOL WINAPI StartService(
  __in      SC_HANDLE hService,
  __in      DWORD dwNumServiceArgs,
  __in_opt  LPCTSTR* lpServiceArgVectors);

参数1指向服务的句柄。

参数2指定参数3的字符串个数,如果参数3为空,则它为0。

参数3传递给服务的参数,如果没有则为NULL。

    如果要停止服务,则需要使用到的是ControlService函数,该函数在文档中的定义如下

BOOL WINAPI ControlService(
  __in   SC_HANDLE hService,
  __in   DWORD dwControl,
  __out  LPSERVICE_STATUS lpServiceStatus);

参数1指向了服务的句柄。

参数2指定控制码。这里主要使用以下两种

  • SERVICE_CONTROL_PAUSE 暂停服务

  • SERVICE_CONTROL_STOP 停止服务

参数3指向SERVICE_STATUS的指针。里面包含了执行函数后服务的状态,在文档中的定义如下

typedef struct _SERVICE_STATUS {
  DWORD dwServiceType;
  DWORD dwCurrentState;
  DWORD dwControlsAccepted;
  DWORD dwWin32ExitCode;
  DWORD dwServiceSpecificExitCode;
  DWORD dwCheckPoint;
  DWORD dwWaitHint;
} SERVICE_STATUS, *LPSERVICE_STATUS;

    要删除服务,则需要使用到DeleteService,该函数在文档中的定义如下

BOOL WINAPI DeleteService(
  __in  SC_HANDLE hService);

参数指向要删除的服务的句柄。

    有了上面的基础就可以写出下面的代码来实现自启动

void StartProgram()
{
	SC_HANDLE hSCMHandle = NULL, hService = NULL;

	hSCMHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	if (hSCMHandle == NULL)
	{
		printf("OpenSCManager error %d\n", GetLastError());
		goto exit;
	}

	hService = CreateService(hSCMHandle,
				  "HelloDriver", 
				  "HelloDriver", 
		          SERVICE_ALL_ACCESS, 
		          SERVICE_KERNEL_DRIVER, 
		          SERVICE_AUTO_START,
				  SERVICE_ERROR_NORMAL,
			      "C:\\Documents and Settings\\1900\\桌面\\HelloDriver.sys", 
				  NULL, NULL, NULL, NULL, NULL);
	if (!hService)
	{
		printf("CreateService error %d\n", GetLastError());
		goto exit;
	}

	if (!StartService(hService, 0, NULL))
	{
		printf("StartService error %d\n", GetLastError());
		goto exit;
	}

	printf("install service success\n");
exit:
	if (hSCMHandle)	CloseServiceHandle(hSCMHandle);
	if (hService) CloseServiceHandle(hService);
}

    程序运行以后可以在监控程序中看到驱动被成功启动

    并在注册表中可以看到相应的键值被添加进去


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

最后于 2021-11-12 15:26 被1900编辑 ,原因:
收藏
点赞3
打赏
分享
最新回复 (10)
雪    币: 2927
活跃值: (2530)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
caocaofff 2021-10-7 11:18
2
0
可惜现在驱动也要签名了
雪    币: 22397
活跃值: (25277)
能力值: ( LV15,RANK:910 )
在线值:
发帖
回帖
粉丝
1900 6 2021-10-7 12:49
3
0
caocaofff 可惜现在驱动也要签名了
害 做个记录罢了
雪    币: 293
活跃值: (232)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
瀚海云烟 1 2021-10-20 17:14
4
0
explorer 插件启动,任务计划,再加Native 启动,登录之前。
雪    币: 94
活跃值: (400)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dico 2021-10-25 11:10
5
0
安装系统服务也行啊
雪    币: 201
活跃值: (543)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
椰子比尔 2021-11-9 17:16
6
1
autoruns里面涵盖了所有启动方式
雪    币: 2418
活跃值: (2282)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dearfuture 2021-11-10 09:49
7
0
还有定时任务之类的
雪    币: 38
活跃值: (33)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
DarkFirer 2021-11-10 15:20
8
0
感谢分享
雪    币: 145
活跃值: (60)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mb_ahswxwbo 2022-1-29 11:08
9
0
感谢分享
雪    币: 238
活跃值: (163)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
alan113 2022-1-29 11:56
10
0
椰子比尔 autoruns里面涵盖了所有启动方式
不是所有,是绝大部分
游客
登录 | 注册 方可回帖
返回