任务计划程序服务(Task Scheduler service)是Windows操作系统中的一个核心服务,它负责管理和执行计划任务。任务计划程序服务(Task Scheduler service)在后台运行,并由 svchost.exe 进程来托管。任务计划程序服务允许用户创建、编辑和删除计划任务。用户可以通过图形用户界面(Task Scheduler GUI)、命令行工具(如schtasks)或编程接口来管理计划任务,用于在预定的时间或特定事件发生时自动执行一系列任务。
通过Windows计划任务,用户可以:
Windows计划任务提供了一个用户友好的界面,使用户能够轻松创建、编辑和管理这些计划任务。用户可以设置任务的触发条件、执行操作、设置任务的运行时期、安全选项等,可以通过Win + R键,打开运行对话框,输入"taskschd.msc",然后点击"确定",即可打开"任务计划程序",在该应用程序中,可以创建、编辑和管理计划任务。
在Windows计划任务中,任务是通过一种层级结构进行组织和管理的。这个层级结构允许你将任务分组,使任务管理更加有条理和灵活。计划任务的层级结构包含以下几个层级:
任务的结构在Windows计划任务中通常包含以下主要部分:
可以通过任务计划程序(如图所示)查看Windows计划任务的结构,在左侧面板,你会看到任务树,其中列出了计划任务的层级结构,在右侧面板,你会看到任务列表,其中显示了所选文件夹或计划任务的详细信息。这包括任务的名称、描述、状态以及下一次触发的时间等信息,同时在右边可以新建计划任务等等操作,这里不作讨论。
“taskschd.dll”和“taskcomp.dll”都是Windows 任务计划程序服务使用的动态链接库(DLL)。
taskschd.dll 是任务计划程序的主要 DLL,它提供了在 Windows 中管理计划任务的核心功能和接口。它包含各种函数和接口,应用程序可以使用这些函数和接口来编程与任务计划程序服务进行交互。以下是一些 taskschd.dll 支持的主要 API:
taskcomp.dll 是计划任务组件的 DLL 文件,它是 taskschd.dll 的辅助 DLL,用于支持任务计划程序的某些功能。taskcomp.dll 主要包含以下 API:
taskcomp.dll 主要用于支持计划任务处理程序的执行和交互,使开发者能够为计划任务定义自定义的执行逻辑,并与处理程序进行交互。
总结一下,taskschd.dll 负责提供任务计划程序的核心功能,而 taskcomp.dll 则通过 COM 接口暴露任务计划程序服务,使应用程序可以通过基于 COM 的编程与任务计划程序进行交互。
在进程中我们可以很明显定位到任务计划程序服务(Task Scheduler service)的主进程;
获取对应的PID使用Process Moniter进行监视运行,可以看到新建的计划任务会读取注册表,计算机\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache 是Windows操作系统中存储计划任务信息的一部分注册表路径。在这个路径下,Windows保存了计划任务的一些配置信息,但并非所有计划任务的完整设置,而是一些缓存和元数据信息。
该注册表路径下的主要子项如下:
往下可以看到新建了一个文件夹,并新建了一个与我们同名的文件,打开为一个XML文件,可以知道计划任务的详细配置信息是保存在C:\Windows\System32\Tasks\[计划任务名]XML文件中的,而不是直接保存在注册表中。
在这个文件夹中,每个计划任务都有一个对应的XML文件,保存了包含计划任务的触发器、操作、条件、设置等信息。任务的完整配置。
可以使用下面Powershell脚本来解析计划任务的 XML 配置文件
# 指定XML文件路径
$xmlFilePath = "C:\Path\to\your\Task.xml"
# 创建XmlDocument对象并加载XML文件
$xmlDocument = New-Object System.Xml.XmlDocument
$xmlDocument.Load($xmlFilePath)
# 创建XML命名空间管理器
$nsManager = New-Object System.Xml.XmlNamespaceManager($xmlDocument.NameTable)
$nsManager.AddNamespace("ns", $xmlDocument.DocumentElement.NamespaceURI)
# 获取根节点
$rootNode = $xmlDocument.DocumentElement
# 获取子节点的值
$taskVersion = $rootNode.GetAttribute("version")
$namespace = $rootNode.NamespaceURI
# 获取RegistrationInfo节点的子节点值
$registrationInfoNode = $rootNode.SelectSingleNode("//ns:RegistrationInfo", $nsManager)
if ($registrationInfoNode -ne $null) {
$date = $registrationInfoNode.SelectSingleNode("ns:Date", $nsManager).InnerText
$author = $registrationInfoNode.SelectSingleNode("ns:Author", $nsManager).InnerText
}
# 获取Triggers节点下的TimeTrigger子节点值
$triggersNode = $rootNode.SelectSingleNode("//ns:Triggers", $nsManager)
if ($triggersNode -ne $null) {
$timeTriggerNode = $triggersNode.SelectSingleNode("//ns:TimeTrigger", $nsManager)
if ($timeTriggerNode -ne $null) {
$startBoundary = $timeTriggerNode.SelectSingleNode("ns:StartBoundary", $nsManager).InnerText
$isEnabled = $timeTriggerNode.SelectSingleNode("ns:Enabled", $nsManager).InnerText
}
}
# 获取Actions节点下的Exec子节点值
$actionsNode = $rootNode.SelectSingleNode("//ns:Actions", $nsManager)
if ($actionsNode -ne $null) {
$execNode = $actionsNode.SelectSingleNode("//ns:Exec", $nsManager)
if ($execNode -ne $null) {
$command = $execNode.SelectSingleNode("ns:Command", $nsManager).InnerText
}
}
# 输出解析的结果
Write-Host "任务版本:$taskVersion"
Write-Host "XML命名空间:$namespace"
Write-Host "注册日期:$date"
Write-Host "作者:$author"
Write-Host "启动边界:$startBoundary"
Write-Host "触发器是否启用:$isEnabled"
Write-Host "执行命令:$command"
在CMD中可以使用schtasks /query 命令获取计划任务的信息,schtasks /query /xml 指定 /xml 参数可以以XML格式列出计划任务的配置信息,包括任务的详细设置和触发器。
在Powershell中可以通过Get-ScheduledTask cmdlet 来获取计划任务详细信息,
新建计划任务的方法有多种,包括使用图形用户界面 (GUI) 工具、使用 PowerShell 命令以及使用系统自带的命令行工具。但是我们想要做的的是怎么样绕过终端安全进行写计划任务进行权限维持。
方法一:使用 schtasks.exe 命令来创建计划任务。例如:
schtasks /Create /TN "MyTask" /TR "C:\MyScript.bat" /SC DAILY /ST 09:00
但是如果是在存在签名的Chrome进行写计划任务360是没有拦截的,基本可以判断360对使用schtasks 添加计划任务的拦截是判断添加计划任务的主体是否可信任。
在管理员权限下使用Powershell Register-ScheduledTask cmdlet来创建计划任务。例如
Register-ScheduledTask -TaskName "MyTask" -Trigger (New-ScheduledTaskTrigger -AtLogon) -Action (New-ScheduledTaskAction -Execute "C:\MyScript.ps1")
360是没有任何反应的。
使用 New-ScheduledTask cmdlet 来创建计划任务。在高权限下同样没有拦截,例如:
# 创建计划任务的触发器
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Minutes 3) -RepetitionDuration (New-TimeSpan -Days 1)
# 重复持续时间设置为 1 天
# 创建计划任务的操作
$action = New-ScheduledTaskAction -Execute "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -Argument "-file C:\Users\shi001admin\Desktop\test.ps1"
# 创建计划任务对象
$task = New-ScheduledTask -Action $action -Trigger $trigger
# 注册计划任务并指定任务名称
Register-ScheduledTask -TaskName "MyTask" -InputObject $task
但是Register-ScheduledTask和New-ScheduledTask创建计划任务都需要Administrator权限,通用性不太好,那么我们可以通过WMI和COM进行创建计划任务,在 Windows 系统中,用于创建计划任务的 COM 接口主要有两个:
例如使用 "Task Scheduler Scripting Object Model" 接口:
# 创建计划任务的操作
$action = New-Object -ComObject "Schedule.Service" -Property @{
"ActionType" = 0 # 0 表示运行程序,其他类型可参考相关文档
"Id" = 1 # 操作的 ID,可以设置为 1
"Path" = "C:\MyScript.ps1" # 要执行的脚本路径
}
# 创建计划任务的触发器
$trigger = New-Object -ComObject "Schedule.Service" -Property @{
"TriggerType" = 1 # 1 表示登录时触发,其他类型可参考相关文档
}
# 获取计划任务服务
$service = New-Object -ComObject "Schedule.Service"
$service.Connect()
# 创建计划任务对象
$rootFolder = $service.GetFolder("\")
$taskDefinition = $service.NewTask(0)
$taskDefinition.RegistrationInfo.Description = "My Task Description"
$taskDefinition.Settings.Enabled = $true
$taskDefinition.Settings.Hidden = $false
$trigger = $taskDefinition.Triggers.Create(1) # 1 表示登录时触发,其他类型可参考相关文档
$trigger.Id = "Trigger1"
$trigger.StartBoundary = (Get-Date).AddMinutes(1).ToString("yyyy-MM-ddTHH:mm:ss")
$trigger.Enabled = $true
$action = $taskDefinition.Actions.Create(0) # 0 表示运行程序,其他类型可参考相关文档
$action.Id = "Action1"
$action.Path = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
$action.Arguments = "-ExecutionPolicy Bypass -File C:\MyScript.ps1"
$rootFolder.RegisterTaskDefinition("MyTask", $taskDefinition, 6, $null, $null, 3) # 6 表示 "TaskScheduler" 设置
# 运行计划任务
$task = $rootFolder.GetTask("MyTask")
$task.Run("")
其他可以用来创建计划任务的接口还有:
Microsoft.Win32.TaskScheduler 命名空间是 .NET Framework 中提供的用于操作计划任务的命名空间。它位于 TaskScheduler.dll 程序集中,可以用于创建、修改、删除以及管理计划任务。
以下是一些常用的类和接口在 Microsoft.Win32.TaskScheduler 命名空间中:
例如可以使用使用 TaskScheduler 类库来创建和管理计划任务:
1.引用 Microsoft.Win32.TaskScheduler 程序集,在 Visual Studio 中通过 NuGet 包管理器安装此程序集,或者手动将它添加到项目的引用中。
2.使用Microsoft.Win32.TaskScheduler程序集中的TaskService类和TaskDefinition类来创建计划任务。
using System;
using Microsoft.Win32.TaskScheduler;
class Program
{
static void Main()
{
string taskName = "MyTask";
string command = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe";
string arguments = "-ExecutionPolicy Bypass -File C:\\MyScript.ps1";
DateTime startTime = DateTime.Now.AddMinutes(1);
bool runRepeatedly = true;
using (TaskService taskService = new TaskService())
{
TaskDefinition taskDefinition = taskService.NewTask();
// 设置计划任务的基本属性
taskDefinition.RegistrationInfo.Description = "My Task Description";
taskDefinition.Settings.AllowDemandStart = true;
taskDefinition.Settings.DisallowStartIfOnBatteries = false;
taskDefinition.Settings.StopIfGoingOnBatteries = false;
// 设置触发器,这里使用时间触发器,表示在 startTime 后重复执行
TimeTrigger trigger = new TimeTrigger(startTime);
trigger.Repetition.Interval = TimeSpan.FromMinutes(3); // 每3分钟重复执行
// 设置重复执行的持续时间,例如设置为2天
trigger.Repetition.Duration = TimeSpan.FromDays(2);
taskDefinition.Triggers.Add(trigger);
// 设置执行操作,这里是调用 PowerShell 脚本
ExecAction action = new ExecAction(command, arguments);
taskDefinition.Actions.Add(action);
// 注册计划任务
taskService.RootFolder.RegisterTaskDefinition(taskName, taskDefinition);
Console.WriteLine("Task created successfully.");
}
}
}
使用 WMI (Windows Management Instrumentation) 来创建计划任务需要调用 Win32_ScheduledJob 类。但是,请注意在 Windows 10 和较新的 Windows 版本中,Win32_ScheduledJob 类已被弃用,并且不再推荐使用,这里不再讨论。
using System;
using System.Management;
class Program
{
static void Main()
{
string taskName = "MyTask";
string command = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe";
string arguments = "-ExecutionPolicy Bypass -File C:\\MyScript.ps1";
DateTime startTime = DateTime.Now.AddMinutes(1);
bool runRepeatedly = true;
string xml = $@"
<Task xmlns='http://schemas.microsoft.com/windows/2004/02/mit/task'>
<RegistrationInfo>
<Description>My Task Description</Description>
</RegistrationInfo>
<Triggers>
<TimeTrigger>
<StartBoundary>{startTime:s}</StartBoundary>
<Repetition>
<Interval>PT3M</Interval>
<Duration>PT1H</Duration>
</Repetition>
</TimeTrigger>
</Triggers>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
</Settings>
<Actions>
<Exec>
<Command>{command}</Command>
<Arguments>{arguments}</Arguments>
</Exec>
</Actions>
</Task>
";
using (ManagementClass taskClass = new ManagementClass(@"root\cimv2", "Win32_ScheduledJob", null))
{
ManagementBaseObject inParams = taskClass.GetMethodParameters("Create");
inParams["JobData"] = xml;
ManagementBaseObject outParams = taskClass.InvokeMethod("Create", inParams, null);
int returnValue = Convert.ToInt32(outParams["ReturnValue"]);
if (returnValue == 0)
{
Console.WriteLine("Task created successfully.");
}
else
{
Console.WriteLine("Failed to create task. Error code: " + returnValue);
}
}
}
}
直接使用Windows API来创建计划任务,而无需依赖外部库或COM接口,这里的方法是调用TaskCreate“taskschd.dll”库中的方法来创建一个新任务。
using System;
using System.Runtime.InteropServices;
class Program
{
private const int ERROR_SUCCESS = 0;
private const string TaskFolder = "\\MyTasks\\"; // The folder in which the task will be created
[DllImport("taskschd.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int TaskCreate(
string pwszTaskName,
ref Guid rclsid,
ref Guid riid,
[MarshalAs(UnmanagedType.Interface)] out ITask pUnk);
[ComImport]
[Guid("148BD520-A2AB-11CE-B11F-00AA00530503")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface ITask
{
void SetApplicationName([MarshalAs(UnmanagedType.LPWStr)] string pwszApplicationName);
void SetParameters([MarshalAs(UnmanagedType.LPWStr)] string pwszParameters);
void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pwszWorkingDirectory);
void SetFlags(uint dwFlags);
void SetAccountInformation([MarshalAs(UnmanagedType.LPWStr)] string pwszAccountName, IntPtr pwszPassword);
void SetCreator([MarshalAs(UnmanagedType.LPWStr)] string pwszCreatorName);
void SetComment([MarshalAs(UnmanagedType.LPWStr)] string pwszComment);
void SetTrigger(IntPtr pTrigger);
void SetErrorRetryInterval(uint dwErrorRetryInterval);
void SetErrorRetryCount(uint dwErrorRetryCount);
}
static void Main()
{
string taskName = "MyTask";
string command = "C:\\Windows\\System32\\calc.exe";
string arguments = "";
Guid clsidTaskScheduler = new Guid("{148BD524-A2AB-11CE-B11F-00AA00530503}");
Guid iidITaskScheduler = new Guid("{148BD520-A2AB-11CE-B11F-00AA00530503}");
ITask task;
int result = TaskCreate(taskName, ref clsidTaskScheduler, ref iidITaskScheduler, out task);
if (result == ERROR_SUCCESS)
{
// Set the task properties
task.SetApplicationName(command);
task.SetParameters(arguments);
task.SetWorkingDirectory("C:\\Windows\\System32");
task.SetFlags(0); // 0 means run with default settings
task.SetAccountInformation(null, IntPtr.Zero); // Run with the user's credentials
task.SetCreator("MyTaskCreator");
task.SetComment("My Task Description");
// Set the trigger (e.g., run every day at a specific time)
// ...
// Save the task
// ...
Console.WriteLine("Task created successfully.");
}
else
{
Console.WriteLine("Failed to create task. Error code: " + result);
}
}
}
通过任何方法添加计划任务时,实际上是将任务的配置信息添加到操作系统的计划任务服务中。这些配置信息包括任务的名称、触发器(例如时间触发器或事件触发器)、执行操作(例如运行程序或执行脚本)、重复规则等。