Skip to main content

计划任务

在看银狐(Silver Fox / ValleyRAT / Winos)样本时,很多人会注意到一条很显眼的进程命令行:

C:\Windows\System32\svchost.exe -k netsvcs -p -s Schedule

这条命令很容易让人误解成:

银狐把自己的 EXE 直接塞进了 svchost 的参数里执行

但从 Windows 机制上讲,更准确的理解应该是:

这条命令表示“任务计划服务(Schedule)正在启动并运行”

而不是“svchost.exe 直接把某个恶意 EXE 当命令行参数执行”。

真正发生的事情通常是:

攻击者想办法把恶意载荷注册成一个会被任务计划器触发的对象

Task Scheduler 服务负责读取任务定义

到达触发条件后,由 Schedule 服务代表系统调度执行相应动作

恶意程序被拉起,或者由中间加载器再去拉起后续模块

所以在银狐语境里,看到 svchost.exe -k netsvcs -p -s Schedule,更应该联想到:

恶意样本正在滥用 Windows 任务计划机制做持久化或恢复执行

而不是把注意力只放在 svchost.exe 这个文件名上。

机制

Windows 的很多系统服务都会跑在 svchost.exe 里,命令行中的:

-k netsvcs -p -s Schedule

本质是在说明:

  • 这个 svchost.exe 承载的是 netsvcs 这一组服务。
  • -s Schedule 指明当前实例关注的是 Task Scheduler 服务。
  • 之后由任务计划子系统决定该在什么时机启动什么动作。

因此如果分析结论写成:

svchost.exe -k netsvcs -p -s Schedule 执行了银狐

更准确的展开应该是:

银狐通过任务计划相关机制完成注册或留存,随后由 Schedule 服务调度出恶意任务动作

这个区别很重要,因为它决定了检测和溯源时到底该去看哪里。

路径

这里不写可直接复现的恶意配置步骤,只从检测和分析角度说明常见路径。

先区分两个容易混淆的概念:

创建入口
=
调用方以什么方式向任务计划服务提出“注册任务”的请求

持久化落点
=
任务注册完成后,系统中留下的任务文件、TaskCache 映射和事件日志

schtasks、COM、PowerShell 和直接 RPC 的差别,主要在前者;它们成功注册后,最终通常都会收敛到同一套任务定义与调度服务。反过来,直接改任务文件或注册表属于绕过正常登记流程的篡改方式,不能把它和受支持的创建接口混为一谈。

schtasks

schtasks.exe 是 Windows 自带的任务计划命令行客户端,也是最直观的一种创建入口。它接收命令行参数,再把任务的名称、触发条件、运行身份和动作提交给 Task Scheduler 服务。

从行为链看,它更接近:

调用进程

schtasks.exe

Task Scheduler 接口

Schedule 服务

任务文件 + TaskCache + 调度信息

这条路径的特点是可见性较高:

  • 进程创建遥测通常能直接看到 schtasks.exe、父进程和完整命令行。
  • 防守方容易还原任务名称、动作和触发器;远程管理场景还可能出现网络认证与远程 RPC 线索。
  • 它既可能是正常的软件部署、运维脚本,也可能是入侵后的持久化,因此不能只凭工具名定性。

对木马而言,使用它的门槛低;对检测而言,它也是最容易被传统规则覆盖的一条路径。

COM 注册

Task Scheduler 2.0 暴露了正式的 COM 自动化接口。程序可通过诸如 ITaskServiceITaskFolderITaskDefinitionIRegisteredTask 一类对象,构造任务定义并请求服务注册或更新任务。

从防守视角,可以把它理解成:

不是“命令行创建任务”
而是“程序化调用任务计划 API / COM 完成同样的事”

典型的内部关系是:

调用进程

Task Scheduler COM 对象

COM 代理 / RPC 运行时

Schedule 服务

这样做的好处通常在于:

  • 进程命令行上不一定出现明显的 schtasks.exe 痕迹。
  • 整体行为更像“软件自己在调用系统组件”。
  • 可以更细粒度地设置触发器、运行账户、隐藏属性和动作内容。

但它并不会凭空消失,仍然会留下:

  • 任务注册后的文件与注册表落点。
  • COM 接口调用前后的任务计划服务交互痕迹。
  • 任务创建和更新事件。
  • 随后由计划任务触发出的进程树。

因此,COM 路径改变的是“注册方式”,不是“落地结果”。

PowerShell 与管理封装

有些样本、安装器或运维脚本会通过 PowerShell 的 ScheduledTasks 模块、.NET 封装或管理框架操作任务计划。它们常把“定义触发器、动作、运行条件”和“提交注册”拆成多个对象或命令,因此从脚本表面看,不一定出现 schtasks.exe

它的本质仍然是对任务计划组件的上层封装:

PowerShell / 管理脚本

模块、.NET 或管理提供程序

任务计划 COM / 服务接口

Schedule 服务

这类路径的排查重点应放在脚本来源、脚本块日志(若已启用)、PowerShell 启动参数,以及任务注册前后的落地结果。不要因为没有 schtasks.exe 就排除计划任务持久化。

直接 RPC 调用

任务计划服务的 RPC 协议由 Microsoft 的 [MS-TSCH] 规范描述,其中的服务接口称为 ITaskSchedulerService。任务计划 GUI、schtasks.exe 和跨进程 COM 在与 Schedule 服务交互时也可能使用 RPC;部分样本则跳过 schtasks.exe 和 COM Automation,直接构造该协议的 RPC 请求。

其调用链可以简化为:

调用进程

RPC 运行时

Task Scheduler RPC 接口

Schedule 服务执行权限检查、XML 校验和登记

这里有三个关键点:

  • RPC 是 Windows 服务间的正常通信机制,不是漏洞;服务仍会依据调用者令牌检查权限。
  • SchRpcRegisterTaskSchRpcRunSchRpcDelete 等是 [MS-TSCH] 中定义的协议操作。微软虽公开了协议规范,但常规软件开发一般仍使用 Task Scheduler COM API 或官方管理工具,而非自行实现 RPC 客户端。
  • 直接 RPC 往往不会产生 schtasks.exe 或典型 COM 激活痕迹,所以只依赖进程名和命令行的规则容易漏报。

木马执行链

银狐等样本走直接 RPC 时,可以从“任务怎样被系统真正创建、运行和清理”来理解,而不是只把它看成少了一个 schtasks.exe。完整链路通常如下:

木马进程

建立到 Task Scheduler RPC 服务的本地绑定

提交任务路径与 XML 定义

SchRpcRegisterTask

Schedule 服务校验并登记任务

可选:SchRpcRun 立即运行

可选:SchRpcDelete 删除一次性任务

因此,恶意进程通常是 RPC 客户端;真正执行任务登记、维护任务状态、在触发时派生动作的主体是 Schedule 服务。这个区别会直接影响进程树、文件写入归因和检测规则的设计。

连接服务

样本会先利用 RPC 运行时创建指向任务计划服务的绑定,设置所需的认证与调用参数,然后向 ITaskSchedulerService 发起请求。实现细节在不同样本、系统版本和逆向工具中会有所不同,但防守视角不必执着于某个包装函数名,关键是:

不常见的客户端进程

本地 RPC

Task Scheduler 服务

这一步通常不会生成 schtasks.exe 子进程,也未必出现由 COM 激活带来的显眼进程或命令行线索;但它不意味着绕过权限控制。服务会在接收请求时基于调用者令牌进行授权判断。

交付任务定义

创建请求的核心内容是任务路径和任务 XML。XML 中描述了触发器、动作、运行账户、运行条件与其他设置;从语义上说,它和导入 XML、或由 schtasks / COM 生成的任务定义属于同一类格式。

任务路径
例如:\Microsoft\Windows\<伪装名称>

任务 XML
├── Triggers:何时触发
├── Actions:触发后做什么
├── Principals:以哪个主体运行
└── Settings:运行条件与行为

对检测来说,最重要的通常不是 RPC 调用本身,而是 XML 最终指定的动作:它是直接启动载荷,还是先经过脚本解释器、加载器、DLL 启动器或其他中转层。

注册与落地

随后,样本通过 SchRpcRegisterTask 请求注册任务。请求中可带有任务路径、XML、创建或更新语义、登录类型与安全描述符等信息;服务端再判断当前调用者是否有权完成相应操作。

这里的“注册”需要按结果区分:

  • 对一个新任务使用创建语义并成功提交时,通常会新增一套标准任务状态。
  • 对同一路径使用更新语义时,原有任务文件和 TaskCache 会被修改,而不是再出现一个新任务。
  • 若请求只是验证、被权限或 XML 校验拒绝,或在写入前失败,则不会得到完整的持久化落点。

当请求成功后,Schedule 服务负责维护任务状态,通常会:

  • 创建或更新 %SystemRoot%\System32\Tasks\ 下的任务定义文件。
  • 更新 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache 中任务路径与 GUID 等映射。常见的 Tree 子项将任务路径关联到 GUID,Tasks\{GUID} 则保存路径、触发器、动态信息和安全相关数据等任务状态。
  • 校验并登记触发器,使任务在登录、开机、定时或其他条件满足时运行。

所以文件、注册表与事件日志上的结果,通常应归因于任务计划服务代表调用者完成登记,而不是简单理解成木马进程自行写入了所有落点。

立即运行与删除

任务注册不一定等到触发器发生。部分样本会紧接着调用 SchRpcRun,要求服务立即运行指定任务;如果任务只用于一次性拉起、权限边界转换或中转执行,随后还可能调用 SchRpcDelete 删除它。

SchRpcRegisterTask

SchRpcRun

恶意动作被 Schedule 服务调度

SchRpcDelete(可选)

这种“创建 → 运行 → 删除”的短生命周期任务,未必是持久化:它也可以是一次性执行或规避痕迹的手段。因此排查时必须把短时间窗口内的 RPC 调用、任务文件变化、任务计划事件和后续进程串联起来,不能只依赖一次静态任务盘点。

逆向命名

逆向报告中出现的 s_TaskSchedulerCreateSchedules_TaskSchedulerExecuteSchedule 等名称,通常是分析者或样本作者为封装函数赋予的可读标签,不是 Windows SDK 中的官方 API 名。它们往往分别包装了注册和运行相关 RPC 调用。

因此应区分:

s_TaskSchedulerCreateSchedule
=
逆向语境中的包装函数名

SchRpcRegisterTask
=
[MS-TSCH] 定义的任务注册 RPC 操作

从检测角度,重点不是“看到 RPC 就告警”,而是确认异常进程是否在同一时间窗内请求任务计划服务、注册了什么 XML,以及该任务随后执行了什么。有关 RPC 本身的分层关系可参阅 RPC

WP TaskScheduler 私有 RPC(ShadowScheduler)

除了 [MS-TSCH] 的标准任务计划接口,还存在由 Windows 内部组件使用、但未作为常规开发接口文档化的 WP TaskScheduler 子系统。开源项目 miunasu/ShadowScheduler 演示了通过该私有 RPC 接口建立登录触发任务的 PoC;项目将底层创建操作命名为 WptsCreateSchedule

它不是前文 SchRpcRegisterTask 的另一种封装,而是不同的 RPC 接口和不同的存储路径。根据该项目的逆向结果:

标准 MS-TSCH 路径
客户端

ITaskSchedulerService / SchRpcRegisterTask

Schedule 服务

Tasks 文件 + TaskCache

ShadowScheduler 演示路径
客户端

WP TaskScheduler 私有 RPC(ncalrpc)

Schedule 服务内的 WPTaskScheduler.dll

Schedule\WP\TaskScheduler\Schedules

该项目给出的接口标识为 33D84484-3626-47EE-8C6F-E7E98B113BE1,版本 2.0,并使用本地 ncalrpc 传输。它直接传递逆向得到的结构化触发器与动作参数,而非 [MS-TSCH]SchRpcRegisterTask 所接收的标准任务 XML;因此不能把它归类为“标准任务 XML 换了一条 RPC 通道”。

warning

这是根据第三方 PoC 的逆向结论整理的未文档化实现,不是 Microsoft 承诺的稳定接口或持久化行为。接口、存储位置、权限要求和遥测表现都可能随 Windows 版本、补丁与安全产品发生变化;应在隔离测试环境先验证,不能仅凭该项目 README 推断所有主机都完全相同。

与标准 RPC 的落点差异

标准 [MS-TSCH] 注册成功后,通常会生成任务 XML 并更新标准 TaskCache。ShadowScheduler 项目则声称任务状态只位于:

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\WP\TaskScheduler\Schedules\

相应地,项目在其测试环境中观察到:

  • %SystemRoot%\System32\Tasks\ 下没有对应任务 XML。
  • TaskCache\TreeTaskCache\Tasks 及其常见分类中没有对应的标准任务映射。
  • schtasks /queryGet-ScheduledTask 和 Task Scheduler MMC 无法通过标准枚举路径发现该任务。

这并不表示“没有注册表痕迹”,而是痕迹从常规 TaskCache 转移到了 WP 专用注册表子树。因此,依赖 TaskCache、任务 XML 或标准任务枚举结果的资产盘点,对这类实现可能存在盲区。

与标准 RPC 的对比

维度标准 [MS-TSCH] 直接 RPCWP TaskScheduler 私有 RPC(ShadowScheduler PoC)
面向接口Microsoft 公开协议中的 ITaskSchedulerService逆向得到的未文档化 WP TaskScheduler 接口
创建操作SchRpcRegisterTask项目称为 WptsCreateSchedule 的私有调用
参数形态任务路径、任务 XML、创建标志、登录类型、安全描述符等逆向结构体中的任务名、触发器、动作等字段,不是标准任务 XML
RPC 传输由任务计划服务端点承载,可被 COM / 工具间接使用项目使用本地 ncalrpc 绑定
任务文件通常写入 %SystemRoot%\System32\Tasks\项目测试中无对应 XML 文件
标准注册表通常更新 ...\Schedule\TaskCache\TreeTasks项目测试中不写标准 TaskCache
专用注册表不适用项目称写入 ...\Schedule\WP\TaskScheduler\Schedules
标准管理工具schtasks、PowerShell、MMC 可枚举项目测试中不可通过这些标准路径枚举
常规持久化排查检查 XML、TaskCache、任务计划日志与进程链额外检查 WP 子树、私有 RPC 接口活动及实际触发的动作

ETW、事件日志与审计

“不出现在任务计划 UI 或 TaskCache 中”不等于“ETW 完全没有记录”。ShadowScheduler 项目的 README 和源码没有给出 Task Scheduler Operational 或 ETW 抓包结果,因此仅凭该项目无法证明私有 RPC 创建一定不会产生任何事件。

更准确的判断应是:

证据面标准 [MS-TSCH] RPCWP TaskScheduler 私有 RPC
Task Scheduler Operational标准注册、更新、运行、删除流程通常是优先检查对象是否产生等价的标准注册事件不能仅据 PoC 断言;需按具体 Windows 版本实测
RPC ETW / EDR RPC 遥测若产品采集接口与方法,可能关联到 ITaskSchedulerServiceSchRpc*若采集本地 RPC 接口 UUID,可关注 33D84484-3626-47EE-8C6F-E7E98B113BE1ncalrpc 和异常客户端;能否解析私有方法取决于产品
注册表 ETW / Sysmon / EDR可看到服务侧更新 TaskCache,前提是采集相应注册表操作应将 ...\Schedule\WP\TaskScheduler\Schedules 纳入监控;写入者预期仍是任务计划服务侧,而非客户端直接写入
文件遥测通常有 System32\Tasks 文件创建或修改项目测试中没有对应文件落点,因此不能依赖文件创建发现
进程创建遥测任务触发后可关联任务动作与后续载荷同样适用;最终动作执行、网络连接、注入等后续行为仍是重要证据

也就是说,标准 Operational 事件是否缺失只是一个待验证的“可能盲点”,不能当作已证实的规避效果;而私有 RPC、WP 注册表子树和最终载荷执行,即使在标准任务枚举失效时,仍可能为 EDR、ETW 或注册表审计提供检测入口。

检测思路

对这类私有接口,更合适的检测链是:

不可信进程 / 被注入宿主

本地 RPC(ncalrpc)访问 WP TaskScheduler 接口 UUID

Schedule 服务侧修改 ...\Schedule\WP\TaskScheduler\Schedules\

登录或其他触发条件满足

非预期的任务动作、载荷启动、联网或注入

防守侧应额外基线化该 WP 注册表子树:正常系统是否存在条目、哪些 Windows 组件会写入、写入时间与用户登录或软件安装是否一致。只有在确认正常基线后,才能把来自 Office、下载目录、用户可写目录程序或异常宿主的私有 RPC 调用提升为高置信度告警。

任务文件与注册表篡改

注册成功后,任务计划通常会在两个位置维护状态:

  • %SystemRoot%\System32\Tasks\:任务定义文件,内容为 XML,但通常没有 .xml 扩展名。
  • HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache:任务路径、GUID、树结构等注册表映射与缓存信息。

因此,具有足够权限的攻击者也可能尝试直接写任务文件、篡改已有任务文件,或修改 TaskCache 项,以伪造、隐藏或破坏任务状态。它和前面的“通过服务注册”不同:

受支持的接口

Schedule 服务统一写入并保持文件、注册表和内部状态一致

直接篡改

绕开部分服务逻辑,试图改动持久化落点

这类方式并不可靠,也不能把“写一个注册表值”理解成能够稳定创建计划任务:任务文件、TaskCache 和服务的内部状态需要一致;系统 ACL、XML 校验和后续服务刷新都可能使不完整的篡改失效或暴露异常。

不过,对防守方而言,它仍是高价值排查面:

  • 任务文件或 TaskCache 变化,却没有合理的 schtasks、COM、PowerShell 或安装器来源。
  • 文件时间、注册表最后写入时间与任务计划事件彼此对不上。
  • 已有任务名称正常,但动作、主体、触发器或安全描述符突然改变。

新建、更新与劫持

无论入口是 schtasks、COM、PowerShell 还是 RPC,攻击者最后一般会落在以下三种结果之一:

新建任务

留下新的任务名和新的执行链

更新任务

保留任务名,但替换动作、触发器或运行条件

劫持任务

利用已有任务或其依赖文件,使下一次触发落到恶意内容

新建任务是最常见、也最容易理解的一类。高层逻辑通常是:

落地或注入后的木马

向任务计划器注册一个新任务

设置触发条件(开机、登录、定时、空闲等)

把动作指向恶意载荷、加载器或中转脚本

后续由 Schedule 服务在合适时机拉起

在主机上,这类行为常见会留下几类痕迹:

  • %SystemRoot%\System32\Tasks\ 下出现新的任务文件。
  • 注册表 TaskCache 相关项发生新增或修改。
  • 任务计划日志出现注册、更新、启用、触发等事件。
  • 很短时间后出现由 taskeng.exetaskhostw.exesvchost.exe -s Schedule 关联出的子进程。

对于银狐这类样本来说,任务动作未必直接指向最终后门本体,也可能先指向一个安装器、加载器、旁加载链路或脚本中转层。

有些攻击者不愿意留下一个全新的陌生命名任务,因为那在资产盘点和规则扫描里比较显眼。

于是更隐蔽的一种思路是:

找到系统里已有的任务

修改它的动作、触发器、运行条件或关联文件

让它在下次触发时带出恶意载荷

这种方式的风险在于,它可能让“任务名看起来正常”,但内容已经变了。

对防守方来说,排查重点不应只盯:

有没有新增任务

还要看:

  • 原本正常的任务 XML 是否被改过。
  • 任务动作路径是否从系统目录变成了用户目录、临时目录或伪装路径。
  • 任务运行账户、触发器和安全描述符是否发生异常变化。
  • 同名任务的最后修改时间是否与可疑入侵时段重合。

恢复执行

银狐这类家族经常不是只靠单一计划任务长期存活。

更常见的理解方式是:

计划任务
=
持久化链路中的一个恢复节点

例如它可能与下列机制组合出现:

  • 守护逻辑(watchdog)负责短周期恢复。
  • 计划任务负责开机、登录或定时兜底恢复。
  • 注入到常见宿主进程的模块负责继续通信与二次拉起。

这样即便某个进程被手工结束,只要任务仍在,系统后续仍可能把它带回来。

链路转接

你提到的“劫持已有服务”更适合从高层机制理解,而不是把它当作单一步骤。

从防守角度,攻击者的目标往往不是“改成某个服务直接运行恶意 EXE”,而是想办法让一条可信的系统执行链最终回到恶意载荷。

在这个思路下,常见现象可能包括:

  • 先通过服务、注入或高权限宿主拿到足够权限。
  • 再由高权限上下文去注册、修改或触发计划任务。
  • 或者篡改与任务动作相关的文件、脚本、DLL、工作目录,使原本正常任务在触发时跑出恶意内容。

所以“服务劫持”和“计划任务持久化”在真实样本里经常不是互斥关系,而是前后衔接关系。

对比

下面的对比针对的是“任务如何被登记”,不是对任务本身恶意性的判断。无论走哪条受支持路径,真正需要分析的仍然是调用主体、任务定义和后续执行链。

手法本质常见可见性与正常任务服务的关系排查重点
schtasks.exe命令行客户端提交任务高:进程创建与命令行通常明确通过任务计划接口提交父进程、命令行、任务动作与触发器
COM通过 Task Scheduler COM 对象登记中:未必有明显命令行工具COM 跨进程调用通常再由 RPC 到服务调用进程、COM/服务交互、落地任务
PowerShell / 管理封装用模块、.NET 或脚本包装任务操作中:可从脚本、模块和启动参数追溯最终调用 COM 或任务计划服务接口脚本来源、日志、任务创建前后文件与注册表变化
直接 RPC直接请求任务计划 RPC 方法较低:没有 schtasks,可能也无典型 COM 激活直接与 Schedule 服务通信,仍受权限检查RPC 上下文、请求内容、任务 XML 与执行结果
WP TaskScheduler 私有 RPC通过未文档化 WP 调度接口登记任务较低:标准任务枚举、XML 与 TaskCache 在项目测试中均不可见同样在任务计划服务侧处理,但走不同接口与 WP 专用状态私有 RPC UUID、WP 专用注册表子树、最终动作与版本化基线
直接写任务文件 / TaskCache篡改任务定义或缓存映射取决于文件、注册表与审计覆盖绕开标准登记路径,可能造成状态不一致文件—注册表—事件日志是否一致、是否劫持既有任务

可以把它压缩成一句话:

schtasks、COM、PowerShell、标准 RPC 是同一类任务计划的不同“提交入口”;
WP 私有 RPC 则可能使用另一套服务内部状态;
任务文件与 TaskCache 是标准任务最常见的“状态落点”。

前四者改变的是创建时的可见性,
最后一种更多是在篡改落点,风险更高且可靠性更差。

对于成功新建的标准 [MS-TSCH] 任务,可以把最核心的服务侧落点概括为:

...\Schedule\TaskCache
+
%SystemRoot%\System32\Tasks\<任务路径>
+
TaskScheduler Operational 生命周期事件(日志启用且未被清理时)

因此,检测策略不应只写成:

发现 schtasks.exe 就告警

更稳妥的思路是同时覆盖:

  • 谁请求或改动了任务计划服务。
  • 任务文件、TaskCache 和任务计划事件是否新增、更新或不一致。
  • 任务动作是否指向可疑路径、解释器或异常账户。
  • 该任务触发后是否把可疑载荷带入后续注入、通信或自恢复链。

价值

因为对长期驻留木马来说,任务计划机制具备几个很现实的优势:

  • 它是 Windows 原生机制,出现本身不反常。
  • 可以选择多种触发方式,不只限于开机启动。
  • 能以较稳定的方式在系统重启、用户登录、时间到达后再次拉起。
  • 在进程树中容易和正常系统调度行为混在一起。

如果银狐前面已经拿到管理员甚至 SYSTEM,上任务计划就会让它的驻留能力更稳定。

检测

EDR 真正有效的地方,不是只看某一个 API,而是把“注册任务”和“任务实际执行”串成一条行为链。

任务变更

这类检测关注的是持久化动作本身。

常见信号包括:

  • 任务计划相关注册表路径新增或修改,尤其是 TaskCache\TreeTaskCache\Tasks
  • %SystemRoot%\System32\Tasks\ 下任务 XML 新建、覆盖或属性异常变化。
  • Microsoft-Windows-TaskScheduler/Operational 日志中出现任务注册、更新、启用、运行事件。
  • 与任务管理相关的系统调用或 COM 调用,出现在不常见的进程上下文中。

这类规则往往适合做:

“谁在改任务”

的告警。

RPC 行为链

针对直接 RPC 的样本,单看 schtasks.exe 已经不够。若遥测能够识别 Task Scheduler RPC 活动,应优先关联下面这条链:

未知、未签名或来源异常的进程

请求 ITaskSchedulerService

SchRpcRegisterTask

Schedule 服务创建或更新任务

SchRpcRun(短时间内,可选)

SchRpcDelete(短时间内,可选)

其中“创建后迅速运行、删除”的组合尤其值得关注:它可能表示一次性执行,而不是传统意义上的长期持久化。应继续检查任务动作是否指向 ProgramDataUsers\PublicAppData 等用户可写目录,以及被调度进程后续是否出现联网、注入或自恢复行为。

RPC 调用留下的痕迹

直接 RPC 不等于无痕。它改变的主要是注册动作的调用链和写入主体:恶意进程请求服务,而 Schedule 服务代表调用者完成任务的校验、登记与后续调度。

传统命令行路径
可疑进程

schtasks.exe

任务计划服务

直接 RPC 路径
可疑进程

本地 RPC

svchost.exe(Schedule)

写入任务状态 / 调度任务动作

因此,它绕过的是“schtasks.exe 作为明显中间进程”的检测习惯,不是把计划任务产生的状态和执行结果一起消掉。

不会出现的痕迹

任务计划 RPC 使用的是已存在的 Schedule 服务。注册任务本身通常不会新建 Windows 服务,也不会在服务注册表树下新增一个服务项。

不会因为注册任务而出现

CreateService / OpenSCManager
sc.exe create
HKLM\SYSTEM\CurrentControlSet\Services\<新服务>

同样,SchRpcRegisterTask 的职责是提交和登记任务定义,而不是立刻启动任务动作。若样本仅完成注册、等待登录或定时触发,那么注册阶段通常也没有新的载荷进程;不能把“发现任务文件创建”直接等同于“载荷已经执行”。

服务侧的任务状态

一旦 Schedule 服务接受注册请求,典型可见结果包括:

  • %SystemRoot%\System32\Tasks\ 下新增或更新任务定义文件。
  • HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\TasksTaskCache\Tree 中的映射或缓存变化。
  • 任务计划 Operational 日志中的注册、更新、启用或删除记录。

这些文件和注册表项通常由 svchost.exe -s Schedule 所承载的服务写入,而不是 RPC 客户端进程直接写入。因此,若规则只寻找“可疑 EXE 直接创建 Tasks 文件”或“可疑 EXE 直接改 TaskCache”,直接 RPC 路径会遗漏;规则应改为关联服务侧写入前的异常 RPC 客户端。

对一次成功的新建标准任务而言,TaskCache 和任务定义文件是最稳定的两类落点;但“必然新建”应限定为新任务创建成功。若是更新已有任务,应观察到对应对象的修改;若 RPC 请求失败或仅做验证,则未必有任何持久化对象变化。

异常 RPC 客户端

Schedule 服务
├── 创建 / 更新 Tasks 文件
├── 更新 TaskCache
└── 写入任务计划事件

运行阶段的进程痕迹

只有任务真正被触发,或样本另行调用 SchRpcRun 请求立即运行时,才会进入动作执行阶段。此时任务计划器会启动任务 XML 定义的动作;在不同 Windows 版本、任务类型、运行账户和会话条件下,遥测中可能看到 svchost.exe (Schedule)taskeng.exetaskhostw.exe 等任务计划相关组件参与调度。

更稳妥的判断方式不是写死某一个父进程名,而是关联:

任务注册或立即运行事件

任务计划相关服务 / 宿主

任务 XML 中定义的动作

payload.exe、脚本解释器或加载器

如果随后调用 SchRpcDelete,任务文件、TaskCache 项和删除事件可能很快消失或被覆盖;但时间线中的文件、注册表、任务计划日志和进程创建遥测仍可能保留证据,保留时长取决于审计配置与日志轮转。

日志与遥测边界

可用日志会因系统审计和安全产品配置而不同,不能假定每台主机都能直接看到 RPC 方法名。常见证据源可按优先级理解为:

证据源通常可回答的问题局限
EDR / ETW 的 RPC 遥测哪个进程请求了 Task Scheduler RPC 接口,部分产品还能看到方法是否采集接口 UUID、方法或请求内容取决于产品配置
Microsoft-Windows-TaskScheduler/Operational任务何时注册、启动、完成、删除不一定直接给出原始 RPC 客户端或完整 XML
文件与注册表审计 / EDR哪个服务进程写入任务文件和 TaskCache通常看到的是 Schedule 服务,而不是最初的 RPC 客户端
Security 审计(如已启用相应审核)与计划任务创建、修改、删除、运行相关的安全事件依赖高级审核策略,主机间覆盖并不一致
进程创建遥测任务最终启动了什么、后续做了什么如果任务只注册未运行,或日志已轮转,就无法独立证明创建来源
NTFS USN Journal(如可获取)任务文件的创建、重命名、删除等文件系统变更时间线只说明卷上的文件变化,不能单独还原 RPC 客户端;旧记录可能被覆盖

所以最有价值的证据不是孤立的一条“RPC 事件”或一条文件写入,而是按时间把客户端、服务侧落点、任务定义和执行结果串起来。

对标准任务计划的 Operational 日志,常见可关注的事件包括:

Event ID常见含义
106任务已注册
140任务已更新
141任务已删除
100任务已启动
102任务已完成
200 / 201任务动作启动 / 完成

这些事件是否存在及字段细节仍取决于启用状态、Windows 版本和日志保留策略;它们通常能证明任务生命周期,却不一定直接给出最初 RPC 客户端或完整的原始 XML。

来源上下文

不是所有创建任务的行为都可疑,但父子进程和来源上下文经常很有价值。

例如下面这些组合通常值得提高优先级:

  • Office、压缩包解压目录、下载目录中的可执行文件随后创建或修改任务。
  • 被注入宿主进程、脚本解释器、临时目录程序去碰任务计划接口。
  • 没有运维背景的普通用户程序突然大量访问任务计划组件。

如果 EDR 能关联:

钓鱼附件 / Loader

提权或注入

任务注册

计划任务触发恶意进程

那条链的置信度通常会很高。

任务内容

很多高价值检测并不在“有没有任务”,而在“任务执行什么”。

EDR 或检测规则通常会重点关注:

  • 任务动作指向用户目录、临时目录、ProgramData、回收站伪装路径等非常见位置。
  • 动作调用 cmd.exepowershell.exewscript.exemshta.exerundll32.exe 等中转解释器。
  • 任务动作路径与文件签名、文件创建时间、父进程来源明显不匹配。
  • 任务名伪装成系统组件,但落地路径和作者信息不可信。
  • 任务 XML 中隐藏属性、异常运行账户、异常触发频率等特征。

这类检测的核心是:

任务看起来像系统任务,但执行内容不像系统任务

执行结果

因为很多恶意样本最终都会体现在执行链上,所以 EDR 很常做下面这类关联:

  • svchost.exe -k netsvcs -p -s Schedule 之后触发出异常子进程。
  • taskeng.exetaskhostw.exesvchost.exe 触发的进程路径落在用户可写目录。
  • 被拉起进程随后又表现出联网、注入、释放文件、禁用安全软件等高危动作。
  • 同一任务被反复短周期触发,像“自恢复”而不是正常计划作业。

这类检测关注的是:

“任务计划服务帮谁把谁拉起来了”

而不是孤立地看 svchost.exe 本身。

关联分析

银狐样本的危险之处,往往不是“它会创建计划任务”这么简单,而是它会把多种能力接起来。

因此更成熟的 EDR 会做跨阶段关联,例如:

  • 先出现 UAC 绕过或管理员权限获取。
  • 紧接着启用 SeDebugPrivilege 或访问高权限进程。
  • 随后由高权限上下文创建计划任务。
  • 后续计划任务拉起的进程又进行注入、通信或对抗安全产品。

当这些动作出现在同一主机、同一时间窗、同一进程家族里时,单点噪音就会变成一条很强的入侵链证据。

排查

如果你已经在某台机器上看到了可疑的 Schedule 相关行为,实战排查通常可以先看:

  • C:\Windows\System32\Tasks\ 中最近新增或修改的任务文件。
  • HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree
  • HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks
  • Microsoft-Windows-TaskScheduler/Operational 日志。
  • taskeng.exetaskhostw.exesvchost.exe -s Schedule 触发的后续进程树。
  • 任务动作指向的 EXE、DLL、脚本、快捷方式、中转命令是否近期落地。

如果排查对象是银狐,还要特别结合:

  • 是否存在注入到 explorer.exesvchost.exeVSSVC.exe 一类宿主的迹象。
  • 是否同时存在 watchdog、提权、SeDebugPrivilege、网络回连等链路证据。
  • 计划任务是不是只是“恢复执行”的一环,而非唯一落点。

案例

你给的奇安信文章《【天穹】新瓶旧酒——银狐钓鱼再现江湖》很适合作为这一点的案例。

从文中公开分析可以提炼出下面这条链:

初始样本运行

内存加载 PE 模块

检查权限并尝试提升权限

启动并注入 VSS 服务进程(失败时回退 explorer.exe)

释放额外文件

通过任务计划相关 COM 接口写入计划任务

在后续阶段运行 Schedule 服务对应链路

创建 / 拉起新的 svchost 宿主并注入后门模块

后门常驻并连接远端 C2

这里有两个很值得记住的点。

第一,文章里的重点并不是:

svchost 直接带参数执行了银狐 EXE

而是:

银狐先通过高权限进程和任务计划机制建立起可持续调度链路

第二,文章说明了任务计划并不是孤立动作,而是夹在:

提权 / 注入

后门常驻 / 通信

之间的中间环节。

这也是为什么在主机取证时,不能只看到一个任务就结束分析,而要继续往前追它是谁创建的,往后追它又拉起了什么。

简化理解

如果想把这篇笔记压缩成一句话,可以记成:

银狐不是“把自己写进 svchost 参数里”,而是“把自己挂到任务计划这条系统调度链上”,再借 Schedule 服务把恶意动作稳定带出来。

参考链接