Windows RPC 计划任务(MS-TSCH协议)
Windows RPC 计划任务(MS-TSCH协议)
参考链接
https://github.com/Rvn0xsy/SchtaskCreator
实现目标
上一篇文章实现了自己调用自己编写的rpc接口,达到了远程调用的效果。
本篇目标尝试调用windows系统自身的rpc接口,实现在本地机器和远程机器创建计划任务
环境和文件
开发环境
windows10
visual studio 2019
需要建立的文件
SchRpc.idl文件(下载链接:https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/96c9b399-c373-4490-b7f5-78ec3849444e)
第一步 编译SchRpc.idl文件
第二步 创建一个工程,使用上一步生成的文件
#include <stdio.h>
#include <taskschd.h>
#include "SchRpc.h"
#pragma comment(lib, "rpcrt4.lib")
#define MAX_LOADSTRING 2048
WCHAR szDefaultTaskXML[MAX_LOADSTRING];
void __RPC_FAR* __RPC_USER midl_user_allocate(size_t cBytes)
{
return((void __RPC_FAR*) malloc(cBytes));
}
void __RPC_USER midl_user_free(void __RPC_FAR* p)
{
free(p);
}
BOOL RegisterNewTask(PWCHAR username, PWCHAR password, PWCHAR domain, PWCHAR task_xml, PWCHAR ip_address, BOOL is_local, BOOL just_run) {
RPC_STATUS status;
RPC_WSTR StringBinding = NULL;
RPC_BINDING_HANDLE Binding = NULL;
PWCHAR ConnectProtocol = NULL;
WCHAR ConnectRemoteProtocol[] = L"ncacn_ip_tcp";
WCHAR ConnectLocalProtocol[] = L"ncalrpc";
RPC_WSTR Hostname = NULL;
SEC_WINNT_AUTH_IDENTITY_W Auth;
if (lstrlenW(domain) == 0) {
Auth.Domain = NULL;
Auth.DomainLength = 0;
}
else {
Auth.Domain = (RPC_WSTR)domain;
Auth.DomainLength = lstrlenW(domain);
}
//判断是不是本地执行,组装相应的参数
if (is_local) {
ConnectProtocol = ConnectLocalProtocol;
}
else {
ConnectProtocol = ConnectRemoteProtocol;
Auth.User = (RPC_WSTR)username; // username
Auth.Password = (RPC_WSTR)password; // password
Auth.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
Auth.UserLength = lstrlenW(username);
Auth.PasswordLength = lstrlenW(password);
Hostname = (RPC_WSTR)ip_address;
}
//1.RpcStringBindingCompose 函数创建字符串绑定句柄
status = RpcStringBindingCompose(
NULL, // Interface's GUID, will be handled by NdrClientCall
(RPC_WSTR)ConnectProtocol, // Protocol sequence
Hostname, // Network address
NULL, // Endpoint
NULL, // No options here
&StringBinding // Output string binding
);
if (status != RPC_S_OK) {
// printf("RpcStringBindingCompose failed - %x\n", status);
return FALSE;
}
//2.RpcBindingFromStringBinding 函数从绑定句柄的字符串表示形式返回绑定句柄
// 记得绑定成功的话,最后要释放绑定的句柄
status = RpcBindingFromStringBinding(
StringBinding, // Previously created string binding 指向绑定句柄的字符串表示形式的指针
&Binding // Output binding handle 返回指向服务器绑定句柄的指针
);
if (status != RPC_S_OK) {
// printf("RpcBindingFromStringBindingA failed - %x\n", status);
return FALSE;
}
// 3.如果不是本地认证,则采用网络认证获取成功的认证信息,为后续远程调用打下基础
if (!is_local) {
RpcTryExcept{
//RpcBindingSetAuthInfo 函数设置绑定句柄的身份验证和授权信息(认证成功的凭证应该在Binding中)
status = RpcBindingSetAuthInfo(
Binding,
Hostname,
RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_AUTHN_DEFAULT,
&Auth,
NULL
);
if (status != RPC_S_OK)
{
// printf("RpcBindingSetAuthInfo failed - %d\n", status);
return FALSE;
}
}
RpcExcept(EXCEPTION_EXECUTE_HANDLER);
{
return FALSE;
}
RpcEndExcept
}
status = RpcStringFree(&StringBinding); //释放句柄
// 4.开始调用 计划任务
RpcTryExcept
{
/*
SchRpcCreateFolder 创建一个新文件夹
HRESULT SchRpcCreateFolder(
[in, string] const wchar_t* path,
[in, string, unique] const wchar_t* sddl,
[in] DWORD flags
);
*/
static const WCHAR Folder[] = L"\\Hello";
SchRpcCreateFolder(Binding, Folder, NULL, 0);
/*
SchRpcRegisterTask 方法必须向服务器注册任务
HRESULT SchRpcRegisterTask(
[in, string, unique] const wchar_t* path, 句柄
[in, string] const wchar_t* xml, xml文件的计划任务
[in] DWORD flags,
[in, string, unique] const wchar_t* sddl,
[in] DWORD logonType,
[in] DWORD cCreds,
[in, size_is(cCreds), unique] const TASK_USER_CRED* pCreds,
[out, string] wchar_t** pActualPath,
[out] PTASK_XML_ERROR_INFO* pErrorInfo
);
*/
static const WCHAR TaskPath[] = L"\\Hello\\TaskName";
WCHAR* path = NULL;
TASK_XML_ERROR_INFO* info = NULL;
status = SchRpcRegisterTask(
Binding, //rpc调用多一个参数,手册中没有这个参数
TaskPath,
task_xml,
TASK_CREATE | TASK_UPDATE,
NULL,
TASK_LOGON_NONE,
0,
NULL,
&path,
&info);
if (status == RPC_S_OK && just_run) {
GUID pGuid;
//立即运行
status = SchRpcRun(Binding, TaskPath, NULL, NULL, TASK_RUN_IGNORE_CONSTRAINTS, NULL, NULL, &pGuid);
}
}
RpcExcept(EXCEPTION_EXECUTE_HANDLER);
{
return FALSE;
}
RpcEndExcept
{
}
if (status != RPC_S_OK) {
return FALSE;
}
//释放绑定的句柄
status = RpcBindingFree(&Binding);
return TRUE;
}
int main()
{
WCHAR szUsername[100] = L"administrator";
WCHAR szPassword[100] = L"123456";
WCHAR szIPAddress[100] = L"192.168.139.122";
WCHAR szTaskXML[MAX_LOADSTRING] = L"<Task xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\"><RegistrationInfo><Date>2005-10-11T13:21:17-08:00</Date><Author>AuthorName</Author><Version>1.0.0</Version><Description>Notepad starts every day.</Description></RegistrationInfo><Triggers><CalendarTrigger><StartBoundary>2005-10-11T13:21:17-08:00</StartBoundary><EndBoundary>2006-01-01T00:00:00-08:00</EndBoundary><Repetition><Interval>PT1M</Interval><Duration>PT4M</Duration></Repetition><ScheduleByDay><DaysInterval>1</DaysInterval></ScheduleByDay></CalendarTrigger></Triggers><Principals><Principal id=\"LocalSystem\"><UserId>S-1-5-18</UserId><RunLevel>HighestAvailable</RunLevel></Principal></Principals><Settings><Enabled>true</Enabled><AllowStartOnDemand>true</AllowStartOnDemand><AllowHardTerminate>true</AllowHardTerminate></Settings><Actions><Exec><Command>notepad.exe</Command></Exec></Actions></Task>";
WCHAR szDomain[100] = { 0 };
// 开始本地执行(需要管理员权限)
#if 0
if (RegisterNewTask(NULL, NULL, NULL, szTaskXML, NULL, TRUE, TRUE)) {
printf("[*] 本地执行计划任务 ==> 立即执行成功");
}
#endif // 0
#if 1
//远程执行
if (RegisterNewTask(szUsername, szPassword, szDomain, szTaskXML, szIPAddress, FALSE, TRUE)) {
printf("[*] 远程执行计划任务 ==> 立即执行成功");
}
#endif // 0
return 0;
}