Hey i have a very time consuming problem, and i thought i might find someone here with better experience than mine that could help me out.

I am reverse-engineering an application which at some point uses the NdrClientCall2 api to use a remote procedure of some other service (which i dont know which one that is)

Now before i hear comments about not trying anything my self There are some really good applications to accomplish what i want like NtTrace, Strace and roughly oSpy can achieve the same result aswell eventually. But my application has some really hard anti-debugging techniques which force me to do everything manually.

What eventually i want to achieve is know what procedure is being called and on what service \ process.

Here is the NdrClientCall2 Decleration by MSDN

  __in          PMIDL_STUB_DESC pStubDescriptor,
  __in          PFORMAT_STRING pFormat,
  __in_out       ...

so it uses the PMIDL_STUB_DESC struct which its definition is as the following:

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;
  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;

And here is how it looks like in windbg, when i put a breakpoint in the NdrClientCall2 function

0:006> .echo "Arguments:"; dds esp+4 L5
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
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)
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!<....!

So how exactly do i figure out what is the remote process it is going to communicate with, or what pipe it is using to communicate?

As far as i understand from the MSDN, it is supposed to call a remote procedure. if i understand that right, it means it should call a remote function as if its an exported dll function. How can i set a breakpoint there?


The main reason im posing this function is because the NdrClientCall2 seems to be pretty huge.

1 Answer 1


So how exactly do i figure out what is the remote process it is going to communicate with, or what pipe it is using to communicate?

The first step is to find the RPC client interface. This can be found via the first argument to NdrClientCall2(), named pStubDescriptor. In your question, pStubDescriptor points to SspiCli!sspirpc_StubDesc:

And here is how it looks like in windbg, when i put a breakpoint in the NdrClientCall2 function

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

SspiCli!sspirpc_StubDesc is a MIDL_STUB_DESC, and on my computer, here are its associated values (via 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>,
    offset word_22224B2,

As documented on MSDN, the first field in the structure above "points to an RPC client interface structure". Thus, we can parse the data at that address as an RPC_CLIENT_INTERFACE struct:

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

From the RPC_CLIENT_INTERFACE struct above, we can extract the InterfaceId GUID: 4F32ADC8-6052-4A04-8701-293CCF2096F0

We can now look up that interface GUID with RpcView to find the associated DLL, running process, and endpoints:

Interfaces Process Endpoints

To find out which specific endpoint is being used by the SSPI RPC server in the LSASS process, we can reverse engineer sspisrv.dll. In the exported function SspiSrvInitialize(), we see the following call:

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

To figure out which specific function is being called in sspisrv.dll, we need to look at the pFormat data passed to NdrClientCall2. In your example code above, the pFormat data is:

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

If we parse the pFormat data as an NDR_PROC_HEADER_RPC structure, we get:

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

From proc_num, we can see that this RPC call is calling the 6th RPC function in sspisrv.dll. We can use RpcView again to get the address for the 6th RPC function:


And with IDA Pro, we can see the function in sspisrv.dll at 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 also shows us a decompilation of that function's prototype:


(Note that on your computer, the 6th function might not be at virtual address 0x7573159D, and furthermore, the 6th function might not be SspirProcessSecurityContext(), but this is the approach you would use nonetheless.)

As such, we can now say the following:

  • The RPC server code for your NdrClientCall2() call is in sspisrv.dll
  • The RPC server for your NdrClientCall2() call is running in LSASS's process
  • The endpoint for your NdrClientCall2() call is named lsasspirpc
  • The RPC server function called by your NdrClientCall2() call in sspisrv.dll is SspirProcessSecurityContext()
  • Alright, this thought me a lot! thanks for that. But for the second part of my question, Is there a way to know what is the -final- called procedure? What you eventually found was the exported Initialization function which calls RpcServerUseProtseqEpW(), this is not the real function which is called. or am i missing something?
    – 0xAK
    Jan 30, 2015 at 23:00
  • Or better yet, where is the place i should place a BreakPoint at?
    – 0xAK
    Jan 30, 2015 at 23:44
  • 1
    Yes this was exactly what i was looking for. Just for future reference.. how did you figure out the Struct parameters type? How did you know that MIDL_STUB_DESC.RpcInterfaceInformation is of type The msdn just says it points to a ** RPC client interface structure ** and i couldnt see anywhere on the msdn the "RPC_CLIENT_INTERFACE". Where did you find that? And by the way thanks for the awesome answer
    – 0xAK
    Feb 1, 2015 at 6:35
  • 1
    With regard to RPC_CLIENT_INTERFACE, I actually relied on previous research I had done (see page 27 in Exporting Non-Exportable RSA Keys). When I did the initial research, I think I determined this by either by looking at the documented RPC structures and noticing the name RPC_CLIENT_INTERFACE, or by finding this in ReactOS's code. Feb 1, 2015 at 15:24
  • 1
    i guess there is noting compared to real experience. Thanks a ton
    – 0xAK
    Feb 1, 2015 at 15:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.