自启动技术详解
字数 1313 2025-08-09 13:33:47
Windows自启动技术详解
一、注册表自启动
1.1 注册表路径
Windows系统通过注册表实现程序自启动,主要路径包括:
用户级路径:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunHKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce
管理员权限路径:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunHKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceHKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run
注意:在64位系统中,HKEY_LOCAL_MACHINE路径可能需要使用HKEY_LOCAL_MACHINE\Software\WOW6432Node\Microsoft\Windows\CurrentVersion\Run
1.2 实现代码
DWORD REUserRegedit(LPWSTR lpszValueName, LPWSTR lpszFileName) {
HKEY hKey;
// 尝试打开注册表键
if (ERROR_SUCCESS != ::RegOpenKeyEx(HKEY_CURRENT_USER,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
0, KEY_WRITE, &hKey)) {
printf("[!] RegOpenKeyEx not found, try create keyvalue");
// 如果不存在则创建
if (ERROR_SUCCESS != ::RegCreateKeyW(HKEY_CURRENT_USER,
L"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
&hKey)) {
printf("[!] RegCreateKeyW failed, error is : %d\n\n", GetLastError());
return FALSE;
} else {
printf("[*] RegCreateKeyW successfully!\n\n");
}
} else {
printf("[*] RegOpenKeyEx successfully!\n\n");
}
// 设置注册表值
if (ERROR_SUCCESS != ::RegSetValueEx(hKey, lpszValueName, 0, REG_SZ,
(BYTE*)lpszFileName, (::lstrlenW(lpszFileName) + 16))) {
::RegCloseKey(hKey);
printf("[!] RegSetValueEx failed, error is : %d\n\n", GetLastError());
return FALSE;
} else {
printf("[*] RegSetValueEx successfully!\n\n");
}
::RegCloseKey(hKey);
return TRUE;
}
1.3 Run与RunOnce区别
Run目录下的注册表会一直存在,每次开机都会执行RunOnce目录下的注册表在执行一次后会自动删除
二、快速启动目录
2.1 快速启动路径
默认路径为:
C:\Users\用户名\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
2.2 实现代码
DWORD AutoSetup(char *lpszSrcFilePath, char *lpszDestFileName) {
CHAR szStartupPath[MAX_PATH] = {0};
CHAR szDestFilePath[MAX_PATH] = {0};
// 获取快速启动目录
if (FALSE == ::SHGetSpecialFolderPathA(NULL, szStartupPath, CSIDL_STARTUP, TRUE)) {
printf("[!] Get szStartupPath failed, error is : %d\n\n", GetLastError());
return FALSE;
} else {
printf("[*] The szStartupPath is : %s\n\n", szStartupPath);
}
// 拼接目标路径
::wsprintfA(szDestFilePath, "%s\\%s", szStartupPath, lpszDestFileName);
// 复制文件到启动目录
if (FALSE == ::CopyFileA(lpszSrcFilePath, szDestFilePath, FALSE)) {
printf("[!] CopyFile failed, error is : %d\n\n", GetLastError());
return FALSE;
} else {
printf("[*] CopyFile successfully!\n\n");
}
return TRUE;
}
三、计划任务
3.1 实现流程
- 初始化COM接口环境
- 创建任务服务实例并连接
- 获取根任务文件夹指针
- 创建任务定义对象
- 设置注册信息、作者信息
- 设置主题信息(登录类型与运行权限)
- 设置其他任务设置
- 创建执行动作(设置程序路径和参数)
- 创建触发器(如登录触发器)
- 注册计划任务
3.2 关键代码
// 初始化COM组件
HRESULT hr = ::CoInitialize(NULL);
if (FAILED(hr)) {
printf("[!] CoInitialize failed\n\n", hr);
return FALSE;
}
// 创建任务服务实例
hr = ::CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER,
IID_ITaskService, (LPVOID*)(&m_lpITS));
// 连接到任务服务
hr = m_lpITS->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
// 获取根任务文件夹指针
hr = m_lpITS->GetFolder(_bstr_t("\\"), &m_lpRootFolder);
// 创建任务定义
ITaskDefinition *pTaskDefinition = NULL;
HRESULT hr = m_lpITS->NewTask(0, &pTaskDefinition);
// 设置注册信息
IRegistrationInfo *pRegInfo = NULL;
CComVariant variantAuthor(NULL);
variantAuthor = lpszAuthor;
hr = pTaskDefinition->get_RegistrationInfo(&pRegInfo);
hr = pRegInfo->put_Author(variantAuthor.bstrVal);
pRegInfo->Release();
// 设置执行动作
IActionCollection *pActionCollect = NULL;
hr = pTaskDefinition->get_Actions(&pActionCollect);
IAction *pAction = NULL;
hr = pActionCollect->Create(TASK_ACTION_EXEC, &pAction);
pActionCollect->Release();
// 设置程序路径和参数
IExecAction *pExecAction = NULL;
hr = pAction->QueryInterface(IID_IExecAction, (PVOID *)(&pExecAction));
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);
ITrigger *pTrigger = NULL;
hr = pTriggers->Create(TASK_TRIGGER_LOGON, &pTrigger);
// 注册计划任务
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);
3.3 删除计划任务
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;
}
四、系统服务
4.1 系统服务特点
- 运行在session 0
- 不同会话间相互独立,不能直接通信
- 不能显示程序界面
4.2 服务管理操作
// 打开服务控制管理器
SC_HANDLE shOSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
// 创建服务
SC_HANDLE shCS = ::CreateServiceA(shOSCM, szName, szName,
SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
SERVICE_AUTO_START, // 或 SERVICE_DEMAND_START 手动启动
SERVICE_ERROR_NORMAL,
lpszDriverPath, NULL, NULL, NULL, NULL, NULL);
// 启动服务
::StartService(shCS, 0, NULL);
// 停止服务
SERVICE_STATUS ss;
::ControlService(shCS, SERVICE_CONTROL_STOP, &ss);
// 删除服务
::DeleteService(shCS);
// 关闭句柄
::CloseServiceHandle(shCS);
::CloseServiceHandle(shOSCM);
4.3 服务程序结构
// 服务入口函数表
SERVICE_TABLE_ENTRY stDispatchTable[] = {
{g_szServiceName, (LPSERVICE_MAIN_FUNCTION)ServiceMain},
{NULL, NULL}
};
// 连接服务控制管理器
::StartServiceCtrlDispatcher(stDispatchTable);
// 服务主函数
void __stdcall ServiceMain(DWORD dwArgc, char *lpszArgv) {
g_ServiceStatus.dwServiceType = SERVICE_WIN32;
g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING;
g_ServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwServiceSpecificExitCode = 0;
g_ServiceStatus.dwCheckPoint = 0;
g_ServiceStatus.dwWaitHint = 0;
// 注册控制处理程序
g_ServiceStatusHandle = ::RegisterServiceCtrlHandler(g_szServiceName, ServiceCtrlHandle);
// 设置运行状态
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
g_ServiceStatus.dwCheckPoint = 0;
g_ServiceStatus.dwWaitHint = 0;
::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
// 服务主循环
while(TRUE) {
Sleep(5000);
DoTask();
}
}
// 服务控制处理函数
void __stdcall ServiceCtrlHandle(DWORD dwOperateCode) {
switch(dwOperateCode) {
case SERVICE_CONTROL_PAUSE: // 暂停
g_ServiceStatus.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE: // 继续
g_ServiceStatus.dwCurrentState = SERVICE_RUNNING;
break;
case SERVICE_CONTROL_STOP: // 停止
g_ServiceStatus.dwWin32ExitCode = 0;
g_ServiceStatus.dwCurrentState = SERVICE_STOPPED;
g_ServiceStatus.dwCheckPoint = 0;
g_ServiceStatus.dwWaitHint = 0;
::SetServiceStatus(g_ServiceStatusHandle, &g_ServiceStatus);
break;
case SERVICE_CONTROL_INTERROGATE: // 询问
break;
default:
break;
}
}
五、总结对比
| 自启动方式 | 权限要求 | 持久性 | 实现复杂度 | 适用场景 |
|---|---|---|---|---|
| 注册表(Run) | 用户/管理员 | 持久 | 简单 | 常规程序自启动 |
| 注册表(RunOnce) | 用户/管理员 | 单次 | 简单 | 安装程序、一次性任务 |
| 快速启动目录 | 用户 | 持久 | 最简单 | 用户级程序自启动 |
| 计划任务 | 管理员 | 持久 | 复杂 | 需要触发条件的自启动 |
| 系统服务 | 管理员 | 持久 | 最复杂 | 后台服务、守护进程 |
选择自启动方式时应根据程序的实际需求和运行环境综合考虑。