由于 Windows 不开源,而 Windows 的某一项服务可能受多个配置项影响,所以很多研究员通过逆向的方式,分析服务调用过程,推测执行流程,例如
https://mp.weixin.qq.com/s/ktGug1VbSpmzh9CEGKbbdw
https://mp.weixin.qq.com/s/aS5MRwnYR5pqE1PmKiH24w
这里不搞这么复杂,我们通过查询资料得知,计划任务的配置既存在于计划任务文件之中,又存在于注册表之中
接下来我们通过简单的实验,确定一下到底是计划任务文件还是注册表在决定着计划任务的执行结果,还是相互同步修改的?
测试环境: Windows Server 2016
不同操作系统的情况可能不同
整体思路如下:
创建两个计划任务,一个修改文件,一个修改注册表,之后观察两个计划任务的执行情况
计划任务文件地址
C:\Windows\System32\Tasks
注册表位置
注册表相关的在此位置
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Schedule 计划任务的 id、index、SD 在此位置
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree
计划任务的具体配置在此位置
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tasks\{id}
taskschd.msc
打开任务计划程序,这名字有点绕口,后续称为计划任务程序
添加一个操作: 执行 cmd
将触发器设置为每 3
分钟执行一次
稍作等待
成功执行计划任务
计划任务文件地址
C:\Windows\System32\Tasks
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2024-01-04T01:53:37.7714703</Date>
<Author>WIN-2MTJ8IQ5VEA\Administrator</Author>
<URI>\test1</URI>
</RegistrationInfo>
<Triggers>
<CalendarTrigger>
<Repetition>
<Interval>PT3M</Interval>
<Duration>P1D</Duration>
<StopAtDurationEnd>false</StopAtDurationEnd>
</Repetition>
<StartBoundary>2024-01-04T01:51:41</StartBoundary>
<Enabled>true</Enabled>
<ScheduleByDay>
<DaysInterval>1</DaysInterval>
</ScheduleByDay>
</CalendarTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<RunLevel>LeastPrivilege</RunLevel>
<UserId>WIN-2MTJ8IQ5VEA\Administrator</UserId>
<LogonType>InteractiveToken</LogonType>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>P3D</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>C:\Windows\System32\calc.exe</Command>
</Exec>
<Exec>
<Command>C:\Windows\System32\cmd.exe</Command>
</Exec>
</Actions>
</Task>
先获取该计划任务的 id
{E44EFFC6-29A1-470C-9553-52531D9962B5}
在 Tasks
上点击编辑 -> 查找
其中 Actions
就是计划任务执行的操作,是一个二进制值
删除掉执行 cmd
的操作,即删除
<Exec>
<Command>C:\Windows\System32\cmd.exe</Command>
</Exec>
计划任务程序并没有发生改变,等待下次计划任务执行
多次执行结果都是 计算机和 cmd
都执行了
此时查看注册表
并没有发生变化
当然,也可以尝试重启计划任务服务,虚拟机,重启服务器方便很多
依旧是两个操作都执行了
注册表没有被修改
看来计划任务文件不是决定计划任务执行结果的主因
删除掉 test1
,创建一个一摸一样的 test2
,这次两分钟执行一次
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2024-01-04T02:17:38.3850798</Date>
<Author>WIN-2MTJ8IQ5VEA\Administrator</Author>
<URI>\test2</URI>
</RegistrationInfo>
<Triggers>
<CalendarTrigger>
<Repetition>
<Interval>PT2M</Interval>
<Duration>P1D</Duration>
<StopAtDurationEnd>false</StopAtDurationEnd>
</Repetition>
<StartBoundary>2024-01-04T02:17:29</StartBoundary>
<Enabled>true</Enabled>
<ScheduleByDay>
<DaysInterval>1</DaysInterval>
</ScheduleByDay>
</CalendarTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<RunLevel>LeastPrivilege</RunLevel>
<UserId>WIN-2MTJ8IQ5VEA\Administrator</UserId>
<LogonType>InteractiveToken</LogonType>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>P3D</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>C:\Windows\System32\calc.exe</Command>
</Exec>
<Exec>
<Command>C:\Windows\System32\cmd.exe</Command>
</Exec>
</Actions>
</Task>
修改计划任务注册表需要 SYSTEM
权限,通过 SysinternalsSuite
套件中的 psexec64.exe
以 SYSTEM
权限启动注册表编辑器,就可以编辑了
https://learn.microsoft.com/zh-cn/sysinternals/downloads/sysinternals-suite
PsExec64.exe -i -s regedit
尝试删除掉 C:\Windows\System32\cmd.exe
刷新计划任务程序
原本的计划任务不见了
查看计划任务文件
计划任务文件没有被修改
我们设置的计划任务是 2
分钟执行一次,不急,让子弹飞一会儿
修改后虽然看不见了,依旧可以执行,观察多次执行结果都是如此
在总的计划任务状态里还是能看见的
重启后,该计划任务不再运行,计划任务文件没有被更改
注册表对计划任务的影响很大,但是修改后,重启服务后导致不再计划任务运行,可能是修改后 HASH
校验过不去?
计划任务程序依旧显示为空
注册表对计划任务影响很大,修改后不会立即生效,会在计划任务服务重启后生效
具体修改后,重启计划任务服务后执行直白,可能是因为 HASH
校验吧,也可能是因为我们修改二进制值格式不对,接下来我们来探究
既然二进制值修改有问题,我修改字符串试试
创建计划任务 test3
将创建时间中的 2:44:58
修改为 2:40:58
这次刷新计划任务程序,非但没有小时,创建时间还被更改成功了,看来计划任务程序的内容是从注册表中拿的
目前能够成功执行,根据之前的测试结果,计划任务服务此时并不会加载注册表的修改
计划任务文件并没有被修改
重启后,不仅创建时间被修改了没变回来,计划任务可以正常执行
计划任务文件并没有被修改
Actions
如果我将 test3
的 Actions
用给 test2
,会不会把 test2
救活呢?
获取 test3
的 Actions
找到 test2
,替换
test2
回来了,删除 test3
,看看 test2
会不会立即生效
等了一会儿,没有执行
重启服务器后成功执行
也就是说刚才我们修改二进制数据修改的不对,只要字符格式正确,应该就可以显示
Actions
值直接用 test2
就好
可以考虑从结尾一个字符一个字符删除,之后每次去刷新计划任务程序,查看是否显示
但是稍加观察,也可以发现,每个操作的程序路径结尾有九个00
,由于我们知道计划任务中操作的实际内容,那直接尝试删除到九个00
处
刷新后,计划任务程序中 test2
还在,操作处果然只剩下一个操作了
计划任务文件并没有被更改
这下可以等一等接下来的计划任务执行了
之后的多次执行结果都是计算器和cmd
均执行,计划任务文件没有被更改
通过注册表对计划任务的修改开始生效,只执行了计算器
计划任务文件没有被更改,内容如下
任务管理器中直接重启是不行的,需要通过 SYSTEM
权限打开任务管理器
PsExec64.exe -i -s taskmgr /v
这回启动后, pid
就变了
计划任务文件依旧没有改变
这里说的并不是指修改 SD
那种,就是单纯的将 Actions
去掉一个 00
新建一个 test4
去掉一个 00
计划任务程序处已经消失了,但是还在执行计算器,这是因为注册表修改的计划任务会在计划任务服务重启后生效
计划任务没有再次执行,计划任务文件没有被更改
schtasks /query /tn "\test4"