在 rpcrt4!NdrClientCall2 函数中 - 它如何知道使用哪个管道将数据传输到另一个进程?

逆向工程 风袋 反调试
2021-06-13 10:07:34

嘿,我有一个非常耗时的问题,我想我可能会在这里找到比我经验更好的人来帮助我。

我正在对一个应用程序进行逆向工程,该应用程序在某些时候使用NdrClientCall2 api 来使用其他服务的远程过程(我不知道是哪个服务)

现在摆在我听到没有尝试任何我自己的意见有一些真正好的应用程序来完成我想要什么样NtTracestrace的和大致oSpy可以达到同样的效果藏汉最终。但是我的应用程序有一些非常困难的反调试技术,这迫使我手动完成所有操作。

最终我想要实现的是知道正在调用什么程序以及在什么服务\进程上。

这是 MSDN 的 NdrClientCall2 Decleration

CLIENT_CALL_RETURN RPC_VAR_ENTRY NdrClientCall2(
  __in          PMIDL_STUB_DESC pStubDescriptor,
  __in          PFORMAT_STRING pFormat,
  __in_out       ...
);

所以它使用PMIDL_STUB_DESC结构,其定义如下:

typedef struct _MIDL_STUB_DESC {
  void                                 *RpcInterfaceInformation;
  void*                                (__RPC_API *pfnAllocate)(size_t);
  void                                 (__RPC_API *pfnFree)(void*);
  union {
    handle_t              *pAutoHandle;
    handle_t              *pPrimitiveHandle;
    PGENERIC_BINDING_INFO pGenericBindingInfo;
  } IMPLICIT_HANDLE_INFO;
  const NDR_RUNDOWN                    *apfnNdrRundownRoutines;
  const GENERIC_BINDING_ROUTINE_PAIR   *aGenericBindingRoutinePairs;
  const EXPR_EVAL                      *apfnExprEval;
  const XMIT_ROUTINE_QUINTUPLE         *aXmitQuintuple;
  const unsigned char                  *pFormatTypes;
  int                                  fCheckBounds;
  unsigned long                        Version;
  MALLOC_FREE_STRUCT                   *pMallocFreeStruct;
  long                                 MIDLVersion;
  const COMM_FAULT_OFFSETS             *CommFaultOffsets;
  const USER_MARSHAL_ROUTINE_QUADRUPLE *aUserMarshalQuadruple;
  const NDR_NOTIFY_ROUTINE             *NotifyRoutineTable;
  ULONG_PTR                            mFlags;
  const NDR_CS_ROUTINES                *CsRoutineTables;
  void                                 *Reserved4;
  ULONG_PTR                            Reserved5;
} MIDL_STUB_DESC, *PMIDL_STUB_DESC;

这是在windbg中的样子,当我在NdrClientCall2函数中放置一个断点时

0:006> .echo "Arguments:"; dds esp+4 L5
Arguments:
06d9ece4  74cc2158 SspiCli!sspirpc_StubDesc
06d9ece8  74cc2322 SspiCli!sspirpc__MIDL_ProcFormatString+0x17a
06d9ecec  06d9ed00
06d9ecf0  91640000
06d9ecf4  91640000
0:006> .echo "PMIDL_STUB_DESC:"; dds poi(esp+4) L20
PMIDL_STUB_DESC:
74cc2158  74cc2690 SspiCli!sspirpc_ServerInfo+0x24
74cc215c  74cca1cd SspiCli!MIDL_user_allocate
74cc2160  74cca1e6 SspiCli!MIDL_user_free
74cc2164  74ce0590 SspiCli!SecpCheckSignatureRoutineRefCount+0x4
74cc2168  00000000
74cc216c  00000000
74cc2170  00000000
74cc2174  00000000
74cc2178  74cc1c52 SspiCli!sspirpc__MIDL_TypeFormatString+0x2
74cc217c  00000001
74cc2180  00060001
74cc2184  00000000
74cc2188  0700022b
74cc218c  00000000
74cc2190  00000000
74cc2194  00000000
74cc2198  00000001
74cc219c  00000000
74cc21a0  00000000
74cc21a4  00000000
74cc21a8  48000000
74cc21ac  00000000
74cc21b0  001c0000
74cc21b4  00000032
74cc21b8  00780008
74cc21bc  41080646
74cc21c0  00000000
74cc21c4  000b0000
74cc21c8  00020004
74cc21cc  00080048
74cc21d0  21500008
74cc21d4  0008000c
0:006> .echo "PFORMAT_STRING:"; db poi(esp+8)
PFORMAT_STRING:
74cc2322  00 48 00 00 00 00 06 00-4c 00 30 40 00 00 00 00  .H......L.0@....
74cc2332  ec 00 bc 00 47 13 08 47-01 00 01 00 00 00 08 00  ....G..G........
74cc2342  00 00 14 01 0a 01 04 00-6e 00 58 01 08 00 08 00  ........n.X.....
74cc2352  0b 00 0c 00 20 01 0a 01-10 00 f6 00 0a 01 14 00  .... ...........
74cc2362  f6 00 48 00 18 00 08 00-48 00 1c 00 08 00 0b 00  ..H.....H.......
74cc2372  20 00 2c 01 0b 01 24 00-a2 01 0b 00 28 00 b8 01   .,...$.....(...
74cc2382  13 41 2c 00 a2 01 13 20-30 00 f8 01 13 41 34 00  .A,.... 0....A4.
74cc2392  60 01 12 41 38 00 f6 00-50 21 3c 00 08 00 12 21  `..A8...P!<....!

那么我究竟如何确定它要与之通信的远程进程是什么,或者它使用什么管道进行通信?

据我从 MSDN 了解到,它应该调用远程过程。如果我理解正确,这意味着它应该调用远程函数,就好像它是导出的 dll 函数一样。如何在那里设置断点?

PS:

提出这个函数的主要原因是 NdrClientCall2 看起来非常庞大。

1个回答

那么我究竟如何确定它要与之通信的远程进程是什么,或者它使用什么管道进行通信?

第一步是找到RPC客户端接口。这可以通过NdrClientCall2()名为的第一个参数找到pStubDescriptor在你的问题中,pStubDescriptor指出SspiCli!sspirpc_StubDesc

这是在windbg中的样子,当我在NdrClientCall2函数中放置一个断点时

0:006> .echo "Arguments:"; dds esp+4 L5
Arguments:
06d9ece4  74cc2158 SspiCli!sspirpc_StubDesc

SspiCli!sspirpc_StubDesc是 a MIDL_STUB_DESC,在我的电脑上,这是它的关联值(通过 IDA Pro):

struct _MIDL_STUB_DESC const sspirpc_StubDesc MIDL_STUB_DESC
<
    offset dword_22229B8,
    offset SecClientAllocate(x),
    offset MIDL_user_free(x),
    <offset unk_22383F4>,
    0,
    0,
    0,
    0,
    offset word_22224B2,
    1,
    60001h,
    0,
    700022Bh,
    0,
    0,
    0,
    1,
    0,
    0,
    0
>

如 MSDN 上所述,上述结构中的第一个字段“指向 RPC 客户端接口结构”。因此,我们可以将该地址处的数据解析为一个RPC_CLIENT_INTERFACE结构体:

stru_22229B8    dd 44h                  ; Length
                dd 4F32ADC8h            ; InterfaceId.SyntaxGUID.Data1
                dw 6052h                ; InterfaceId.SyntaxGUID.Data2
                dw 4A04h                ; InterfaceId.SyntaxGUID.Data3
                db 87h, 1, 29h, 3Ch, 0CFh, 20h, 96h, 0F0h; InterfaceId.SyntaxGUID.Data4
                dw 1                    ; InterfaceId.SyntaxVersion.MajorVersion
                dw 0                    ; InterfaceId.SyntaxVersion.MinorVersion
                dd 8A885D04h            ; TransferSyntax.SyntaxGUID.Data1
                dw 1CEBh                ; TransferSyntax.SyntaxGUID.Data2
                dw 11C9h                ; TransferSyntax.SyntaxGUID.Data3
                db 9Fh, 0E8h, 8, 0, 2Bh, 10h, 48h, 60h; TransferSyntax.SyntaxGUID.Data4
                dw 2                    ; TransferSyntax.SyntaxVersion.MajorVersion
                dw 0                    ; TransferSyntax.SyntaxVersion.MinorVersion
                dd offset RPC_DISPATCH_TABLE const sspirpc_DispatchTable; DispatchTable
                dd 0                    ; RpcProtseqEndpointCount
                dd 0                    ; RpcProtseqEndpoint
                dd 0                    ; Reserved
                dd offset _MIDL_SERVER_INFO_ const sspirpc_ServerInfo; InterpreterInfo
                dd 4000000h             ; Flags

RPC_CLIENT_INTERFACE上面结构中,我们可以提取InterfaceIdGUID:4F32ADC8-6052-4A04-8701-293CCF2096F0

我们现在可以使用RpcView查找该接口 GUID以找到关联的 DLL、正在运行的进程和端点:

接口 过程 端点

要找出 LSASS 过程中 SSPI RPC 服务器正在使用哪个特定端点,我们可以对sspisrv.dll. 在导出的函数中SspiSrvInitialize(),我们看到以下调用:

RpcServerUseProtseqEpW(L"ncalrpc", 0xAu, L"lsasspirpc", 0);

要确定正在调用哪个特定函数sspisrv.dll,我们需要查看pFormat传递给数据NdrClientCall2在上面的示例代码中,pFormat数据是:

00 48 00 00 00 00 06 00-4c 00 30 40 00 00 00 00 ...

如果我们将pFormat数据解析NDR_PROC_HEADER_RPC结构,我们会得到:

handle_type = 0x00
Oi_flags    = 0x48
rpc_flags   = 0x00000000
proc_num    = 0x0006
stack_size  = 0x004C

proc_num,我们可以看到这个 RPC 调用正在调用sspisrv.dll. 我们可以再次使用 RpcView 来获取第 6 个 RPC 函数的地址:

程序

使用 IDA Pro,我们可以在sspisrv.dllat address 中看到函数0x7573159D

.text:7573159D __stdcall SspirProcessSecurityContext(x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x, x) proc near

RpcView 还向我们展示了该函数原型的反编译:

反编译

(请注意,在您的计算机上,第 6 个函数可能不在 virtual address 0x7573159D,而且,第 6 个函数可能不在SspirProcessSecurityContext(),但这是您将使用的方法。)

因此,我们现在可以说以下内容:

  • NdrClientCall2()调用的 RPC 服务器代码位于sspisrv.dll
  • NdrClientCall2()呼叫的 RPC 服务器正在 LSASS 的进程中运行
  • NdrClientCall2()呼叫的端点名为lsasspirpc
  • NdrClientCall2()调用的RPC服务器函数sspisrv.dllSspirProcessSecurityContext()