Collapser的文章

  • 创建计划任务实现开机自启动

    背景想必实现程序开机自启动,是很常见的功能了。无论是恶意程序,还是正常的应用软件,都会提供这个功能,方便用户的使用。程序开机自启动,顾名思义,就是计算机开机后,不用人为地去运行程序,程序就可以自己运行起来。对于这个功能的,一直都是杀软重点监测的地方。因为,对于病毒来说,重要的不是如何被破坏,而是如何启动。
    在过去写的大大小小的程序中,我也实现过程序自启动的功能。现在,我把这些自启动功能的实现方式进行下总结。常见的方式有:修改开机自启动注册表、开机自启动目录、创建开机自启计划任务、创建开机自启系统服务等方式。现在对这些技术一一进行分析,并形成文档分享给大家。本文介绍的是创建自启动任务计划实现开机自启动的方式,其它的实现方式可以搜索我写的相关系列的文档。
    实现原理我是使用Windows Shell编程实现创建任务计划,所以会涉及COM组件相关知识。
    现在,为方便大家的理解,我把整个程序的逻辑概括为 3 各部分,分别是:初始化操作、创建任务计划以及删除任务计划。现在,我们一一对每一个部分进行解析。
    初始化操作初始化操作的目的就是获取 ITaskService 对象以及 ITaskFolder 对象,我们创建任务计划,主要是对这两个指针进行操作。具体流程是:

    首先初始化COM接口环境,因为我们接下来会使用到COM组件
    然后,创建 ITaskService 对象,并连接到任务服务上
    接着,从 ITaskService 对象中获取 ITaskFolder 对象

    这样,初始化操作就完成了,接下来就是直接使用 ITaskService 对象以及 ITaskFolder 对象进行操作了。
    创建任务计划现在,解析下任务计划的具体创建过程:

    首先,从 ITaskService 对象中创建一个任务定义对象 ITaskDefinition,用来来创建任务
    接着,就是开始对任务定义对象 ITaskDefinition进行设置:

    设置注册信息,包括设置作者的信息
    设置主体信息,包括登陆类型、运行权限
    设置设置信息,包括设置在使用电池运行时是否停止、在使用电池是是否允许运行、是否允许手动运行、是否设置多个实例
    设置操作信息,包括启动程序,并设置运行程序的路径和参数
    设置触发器信息,包括用户登录时触发

    最后,使用 ITaskFolder 对象根据任务定义对象 ITaskDefinition的设置,注册任务计划

    这样,任务计划创建的操作就完成了,只要满足设置的触发条件,那么就会启动指定程序。
    删除任务计划 ITaskFolder 对象存储着已经注册成功的任务计划的信息,我们只需要将任务计划的名称传入其中,调用DeleteTask接口函数,就可以删除指定的任务计划了。
    编码实现创建任务计划的初始化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;}
    程序测试在 main 函数中调用上述封装好的函数,进行测试。main 函数为:
    int _tmain(int argc, _TCHAR* argv[]){ CMyTaskSchedule task; BOOL bRet = FALSE; // 创建 任务计划 bRet = task.NewTask("520", "C:\\Users\\DemonGan\\Desktop\\520.exe", "", ""); if (FALSE == bRet) { printf("Create Task Schedule Error!\n"); } // 暂停 printf("Create Task Schedule OK!\n"); system("pause"); // 卸载 任务计划 bRet = task.Delete("520"); if (FALSE == bRet) { printf("Delete Task Schedule Error!\n"); } printf("Delete Task Schedule OK!\n"); system("pause"); return 0;}
    测试结果:
    “以管理员身份运行程序”方式打开程序,提示任务计划创建成功。

    打开任务计划列表进行查看,发现“520”任务计划成功创建。



    然后,删除创建的任务计划,程序提示删除成功。

    查看任务计划列表,发现“520”任务计划已经成功删除。

    所以,测试成功。
    总结这个功能的实现涉及到COM组件的相关知识,所以初学者对此可能感到陌生,这是很正常的。其实,对于这方面的理解我也没有什么好的建议,只有多动手练几遍,加深程序逻辑和印象吧。
    注意的地方就是,创建任务计划,要求程序必须要有管机员权限才行。所以,测试的时候,不要忘记以管理员身份运行程序。
    参考参考自《Windows黑客编程技术详解》一书
    1  留言 2018-11-20 14:39:47
eject