RPC总结 ---发文于2013-12-20

来源:互联网 发布:淘宝企业店铺开店资料 编辑:程序博客网 时间:2024/05/01 07:31
 http://andylin02.iteye.com/blog/457411 win32 rpc编程系列四篇 已看
http://www.ibm.com/developerworks/cn/aix/library/au-rpc_programming/  rpc编程 已看
sudami的sandbox代码
http://bbs.pediy.com/showthread.php?t=172274&highlight=rpc
http://www.hsc.fr/ressources/articles/win_net_srv/index.html
http://bbs.pediy.com/showthread.php?t=77899&highlight=rpc
http://msdn.microsoft.com/en-us/library/cc213372.aspx
http://bbs.pediy.com/showthread.php?t=79437&highlight=rpc
http://bbs.pediy.com/showthread.php?t=140154
RPC/XDR/NFS系列  文章

LPC:连接端口(服务进程),通信端口。大于256字节,报文是共享内存。小于则是报文发送。
csrss,lsass->lpc.跨进程的过程调用。port是进程间通信机制,lpc是建立在这上面的应用。
NtCreatePort
NtCreateWaitablePort()
NtListenPort()
NtConnectPort()
NtAcceptConnectPort()
NtCompleteConnectPort()
NtRequestPort()
NtRequestWaiReplyPort()
NtReplyPort()
NtReplyWaitReceivePort()
NtReplyWaitReceivePortEx()。同上,但是带有超时控制
NtReadRequestData()
NtWriteRequestData()
NtQueryInformationPort()

客户             服务端
       NtCreatePort(端口服务线程)
NtListenPort->NtReplyWaitReceivePort(无线for 循环,如果是应答报文,EiReplyOrRequestPort发送应答报文(EiEnqueueMessagePort()发送操作只是挂入目标端口的接收
报文队列,拷贝数据时,只是拷贝报文本身,通过共享内存共享数据部分),如果是连接请求报文,EiEnqueueConnectMessagePort()把它挂入端口的ConnectQueueListHead队列(缓存连接请求),这是为随后的“接受连接”操作、即系

统调用NtAcceptConnectPort()留下一个参考).
NtConnectPort目标是一个连接端口(EiConnectPort(ObCreateObject,EiReplyOrRequestPort(把报文的容器挂入目标连接端口的接收队列),KeReleaseSemaphore(唤醒服务线程))
把数据存入LPC_SECTION_WRITE/READ中
                                NtAcceptConnectPort创建无名通信端口,返回handle(如果是拒绝,EiReplyOrRequestPort在对方端口挂个拒绝报文。)
NtCompleteConnectPort唤醒客户线程
EiDequeueMessagePort()从通讯端口队列中获取对方应答报文。
                                创建线程(LPC服务线程),提供LPC服务。从通信端口接受报文,等待客户端服务请求而阻塞。
                                NtListenPort等待新连接
NtRequestPort(不等回复),NtRequestWaitReplyPort(等待回复)发送报文,请求LPC服务而阻塞      服务端接受报文提供服务,NtReplyPort回答报文,客户端解除阻塞。
eport中有信号量,实现通信双方同步。
三种报文,两种特殊,一种数据。特殊:连接请求,连接应答。在报文的data部分。

com:
1. 杨老的com教程
2. com原理与应用

com实现-潘爱民
com原理与应用
com本质论
com技术内幕  64页
ole2对象链接与嵌入技术高级编程技术
http://blog.chinaunix.net/uid-20071539-id-1978405.html com实现过程。 

进程外的COM以及DCOM,前者是基于LPC 本地过程调用,后者是基于RPC远程过程调用。除了协议不同外,其他的都一样。
exports 
DllGetClassObject,  返回类工厂接口。
DllCanUnloadNow,  告诉客户端该COM是否可以被正常卸载。 
DllRegisterServer, 向系统注册COM组件信息。Regsvr32.exe 就是调用这个函数来进行注册的。 
DllUnregisterServer  从系统中反注册一个COM。Regsvr32.exe 就是调用这个函数来进行反注册的。 

1. CreateCOMObject -- CoCreateInstance。 CoCreateInstance 在注册表中查找COM的注册信息。 
2. CoCreateInstance - CoGetClassObject 。注册信息被交给CoGetClassObject。这时候CoGetClassObject将知道COM组件在磁盘上的位置。 
3.CoGetClassObject - LoadLibrary 。LoadLibrary 将寻找COM DLL的入口,然后GetProcAddress调用其输出函数DllGetClassObject 
4. DllGetClassObject 的输出参数将向我们返回“类工厂”接口IClassFactory。 
5. IclassFactory -- CreateInstance 。CreateInstance方法建立其我们实现接口的类。该类将调用自身的QueryInterface 方法,查看用户指定的接口是否被自己实现,如果实现了,则向返回自定义的接口。 
6. 调用结束后,COM客户将调用COM的DLL输出函数DllCanUnloadNow 。如果该函数返回S_OK,则释放该组件。

CLASS_xx 类型的GUID,是用来标识COM组件的。 
IID_xx类型的GUID,是用来标识一个接口的。可以有多个。 
LIBID_xx类型的GUID,是用来标识一个TypeLib的。

AddRef 方法: 
该方法用于增加一个COM的计数器。我们知道COM Server是需要支持多客户调用的,在COM中每实现一次调用,就必须进行计数。如果COM的计数>0 ,则COM继续生存。否则,该COM接口实例就必须被注销。 
_Release 方法: 
该方法和_AddRef方法相反,用于减少一个计数,当计数为0,则注销该接口。 
QueryInterface 方法: 
该方法用于查询一个接口是否存在,如果存在则在输出参数里返回该接口指针。 

ProgID:程序标志,由项目名称和类名两部分组成。如: project.class1
LocalServer32:进程外组件,如ActiveX EXE这样的进程外服务器中的组件需要该子键
该子键保存了服务器文件的物理路径。
InprocServer32:进程内组件,如ActiveX DLL这样的进程内服务器中的组件需要该子键
该子键保存了服务器文件的物理路径。
TypeLib:类型库。它所对应的数据在HKEY_CLASSES_ROOT\TypeLib下面互相联系。
Vb的工程->引用对话框,就是通过该键来填充列表的。

typedef struct _PORT_MESSAGE
{
    union
    {
        struct
        {
            CSHORT DataLength;     //数据部分大小
            CSHORT TotalLength;
        } s1;
        ULONG Length;
    } u1;
    union
    {
        struct
        {
            CSHORT Type;
            CSHORT DataInfoOffset;
        } s2;
        ULONG ZeroInit;
    } u2;
    union
    {
        CLIENT_ID ClientId;        //内含本进程和线程的句柄
        QUAD DoNotUseThisField;
    };
    ULONG MessageId;           //报文序号,每次递增
    union
    {
        SIZE_T ClientViewSize; // only valid for LPC_CONNECTION_REQUEST messages
        ULONG CallbackId; // only valid for LPC_REQUEST messages
    };                    //后面数据部分就开始了,通信双方协定
} PORT_MESSAGE, *PPORT_MESSAGE;

修改密码:
目标端口是\\LsaAuthenticationPort, 
netapi32!netuserchangepassword->secur32!lsacallauthenticationpackage->ntdll!ntrequestwaitreplyport

kd> dd 0012f718                  //sendmessage
0012f718  00a80020 00000000 ffffffff 7c937de9
0012f728  7c937ea0 77fc0000 00000002 00000000   //00000002之前是PORT_MESSAGE,占6个ulong,24个字节
0012f738  00000006 0015f7c8 00000068 7c80ae6e   //下面0015f7c8是第10个ulong,
0012f748  0012f768 7c80ae80 77fc0000 00000000
0012f758  00000002 5fe1b580 001d001c 5fe1be88
kd> dd 0015f7c8+c     //指向unicode的指针
0015f7d4  001c001a 0015f810 00027f00 0015f82c   //0015f810指向用户名

end task 下断 nt!NtReplyPort,Bprotect!IsProtectLpcRequest+0x8c,看nt4源码中的 CSRSRV!CsrApiRequestThread
typedef struct _PORT_MESSAGE
{
USHORTDataSize;
USHORTMessageSize;
USHORTMessageType;
USHORTVirtualRangesOffset;
CLIENT_IDClientId;
ULONGMessageId;
ULONGSectionSize;
//UCHARData[];
} PORT_MESSAGE, *PPORT_MESSAGE;

typedef struct _CSR_CAPTURE_HEADER {
    ULONG Length;
    struct _CSR_CAPTURE_HEADER *RelatedCaptureBuffer;
    ULONG CountMessagePointers;
    PCHAR FreeSpace;
    ULONG_PTR MessagePointerOffsets[1]; // Offsets within CSR_API_MSG of pointers
} CSR_CAPTURE_HEADER, *PCSR_CAPTURE_HEADER;

typedef ULONG CSR_API_NUMBER;

typedef struct  _BASE_CREATETHREAD_MSG
{
    HANDLE hThread;
    CLIENT_ID ClientId;
}BASE_CREATETHREAD_MSG, *PBASE_CREATETHREAD_MSG;


typedef struct _PBASE_CREATEPROCESS_MSG 
{
HANDLE hProcess;
HANDLE hThread;
DWORD dwProcessId;
DWORD dwThreadId;
DWORD CreationFlags;
CLIENT_ID DebuggerClientId;
DWORD VdmBinaryType;
}BASE_CREATEPROCESS_MSG ,*PBASE_CREATEPROCESS_MSG;

typedef struct _BASE_API_MSG
{
    PORT_MESSAGE          PortMessage;
    PCSR_CAPTURE_HEADER   CaptureBuffer;
CSR_API_NUMBER        ApiNumber;
ULONG                 Status;
ULONG                 Reserved;
union 
{
  BASE_CREATETHREAD_MSG  BaseCreateThread;
  BASE_CREATEPROCESS_MSG BaseCreateProcess;
}u;
}BASE_API_MSG,*PBASE_API_MSG;

typedef struct _CSR_API_CONNECTINFO 
{
    OUT HANDLE ObjectDirectory;
    OUT PVOID SharedSectionBase;
    OUT PVOID SharedStaticServerData;
    OUT PVOID SharedSectionHeap;
    OUT ULONG DebugFlags;
    OUT ULONG SizeOfPebData;
    OUT ULONG SizeOfTebData;
    OUT ULONG NumberOfServerDllNames;
    OUT HANDLE ServerProcessId;
} CSR_API_CONNECTINFO, *PCSR_API_CONNECTINFO;

typedef struct _CSR_CLIENTCONNECT_MSG 
{
    IN ULONG ServerDllIndex;
    IN OUT PVOID ConnectionInformation;
    IN OUT ULONG ConnectionInformationLength;
} CSR_CLIENTCONNECT_MSG, *PCSR_CLIENTCONNECT_MSG;

typedef struct _CSR_API_MSG 
{
    PORT_MESSAGE h;   //24个字节
    union 
{
            CSR_API_CONNECTINFO ConnectionRequest;
            struct 
{
                PCSR_CAPTURE_HEADER CaptureBuffer;
                CSR_API_NUMBER ApiNumber;   //第28个字节
                ULONG ReturnValue;   //32
                ULONG Reserved;      //36
                union 
{
                    CSR_CLIENTCONNECT_MSG ClientConnect;
                    ULONG_PTR ApiMessageData[39];    //40 ,end task 的窗口句柄是第44个.
                } u;
};
};
} CSR_API_MSG, *PCSR_API_MSG;     --------->end task时的消息结构

服务service.exe向svchost通信的rpc msg头部
typedef struct _CTRL_MSG_HEADER {
 
   DWORD                   Count;              // num bytes in buffer.
  
   DWORD                   OpCode;             // control opcode.
    
   DWORD                   NumCmdArgs;         // number of command Args.
   参数个数 
   SERVICE_STATUS_HANDLE   StatusHandle;       // handle used for status messages
    
   DWORD                   ServiceNameOffset;  // pointer to ServiceNameString
   //xp 下固定为20,
   DWORD                   ArgvOffset;         // pointer to Argument Vectors.  指向参数部分的偏移地址
 
} CTRL_MSG_HEADER, *PCTRL_MSG_HEADER, *LPCTRL_MSG_HEADER;
在nt4\private\windows\screg\sc\server\control.c的ScSendControl函数中有具体发包格式
[rpc 头PORT_MESSAGE,24个字节] + [CTRL_MSG_HEADER,24 个字节] + [serviceName] +[4字节对齐] + [n个参数的的str指针,每个指针4个字节,按顺序指向后面每个字串的地址] + [n个参数字串]

加载驱动的服务的rpc头:
[rpc 头port_msg, 24个字节] +(从DataInfoOffset开始,如果它大于0)[Oif_style_header_descriptor<> ] + {-Oif_style_parameter_descriptor<6>}*
-Oif_style_header_descriptor<>描述格式是
 
handle_type<1> 
Oi_flags<1>
[rpc_flags<4>]
proc_num<2>  
stack_size<2>
[explicit_handle_description<>]
constant_client_buffer_size<2>
constant_server_buffer_size<2>
INTERPRETER_OPT_FLAGS<1>
number_of_params<1>
 
 
Parameter Descriptors的格式是,这里只说-OIF,服务就是使用这种类型,-OIF参数描述有俩种不同的方式,一种用于描述base types, 另一种用于描述其它types
 
Base types:
PARAM_ATTRIBUTES<2> 
stack_offset<2> 
type_format_char<1> 
unused<1>
 
Other:
PARAM_ATTRIBUTES<2> 
stack_offset<2> 
type_offset<2>


kd> bl
 0 e 77e071e9     0001 (0001) ADVAPI32!CreateServiceA
 1 e 77ed44d0     0001 (0001) RPCRT4!NdrClientCall2
当在NdrClientCall2断下时
kd> db 77db6742 
77db6742  00 (handle_type)48(Oi_flags) 00 00 00 00(rpc_flags) 18 00(proc_num)-44 00(stack_size) 30(FC_BIND_CONTEXT) 48 (flags )00 00(offset) 00(context_rundown_routine_index) 00(param_num)  .H......D.0H....
77db6752  70 00(constant_client_buffer_size) 5c 00(constant_server_buffer_size) 46(INTERPRETER_OPT_FLAGS) 11(number_of_params) 08(extension_version) 05(INTERPRETER_OPT_FLAGS2)-00 00(ClientCorrHint) 02 00(ServerCorrHint) 00 00
(NotifyIndex) 08 00(PARAM_ATTRIBUTES)  p.\.F...........
77db6762  00 00(stack_offset) 0a 00(type_offset) 0b 01 04 00-b2 01 0b 00 08 00 ac 01  ................
77db6772  48 00 0c 00 08 00 48 00-10 00 08 00 48 00 14 00  H.....H.....H...
77db6782  08 00 48 00 18 00 08 00-0b 01 1c 00 b2 01 0b 00  ..H.............
77db6792  20 00 ac 01 1a 00 24 00-54 00 0b 00 28 00 68 00   .....$.T...(.h.
77db67a2  48 00 2c 00 08 00 0b 00-30 00 ac 01 0b 00 34 00  H.,.....0.....4.
77db67b2  7c 00 48 00 38 00 08 00-10 01 3c 00 90 00 70 00  |.H.8.....<...p.
 
30  表示FC_BIND_CONTEXT
FC_BIND_CONTEXT的explicit_handle_description<>格式是
FC_BIND_CONTEXT flags<1> offset<2> context_rundown_routine_index<1> param_num<1>
flags 48  表示 HANDLE_PARAM_IS_IN | NDR_STRICT_CONTEXT_HANDLE
offset 00 00 在堆栈中的偏移
context_rundown_routine_index 00
param_num 00

INTERPRETER_OPT_FLAGS  46  HasExtensions | HasReturn | ClientMustSize      
扩展:
size_extension_version<1>   08
INTERPRETER_OPT_FLAGS2<1>  05(ServerCorrCheck| HasNewCorrDesc )
ClientCorrHint<2> 0000
ServerCorrHint<2> 0002
NotifyIndex<2>0000
[ FloatDoubleMask<2> ]

开始参数部分:
第一个参数  08 00 00 00 0a 00
(PARAM_ATTRIBUTES)  0008 IsIn ,not base type
stack_offset 0000 --->参数在栈中的偏移,即参数地址
type_offset 000a 
第二个参数  0b 01 04 00 b2 01
(PARAM_ATTRIBUTES)  010b  IsSimpleRef,is in, must free, must size, not base type      
stack_offset 0004
type_offset 01b2 
第三个参数 0b 00 08 00 ac 01
 
定位参数类型,及type_offset, msdn上介绍 For other types, the type_offset<2> field gives the offset in the type format string table where the type descriptor for the argument is located.
type_offset 是索引哪里的呢?答案是
MIDL_STUB_MESSAGE
...
    +const struct _MIDL_STUB_DESC* StubDesc;
    ....
        + const unsigned char* pFormatTypes;
就是 pFormatTypes
CLIENT_CALL_RETURN RPC_VAR_ENTRY NdrClientCall2(
  __in          PMIDL_STUB_DESC pStubDescriptor,
  __in          PFORMAT_STRING pFormat,      --->77db1f60 
  __in_out       ...
);
kd> dds 77db1f60 
77db1f60  77db1fb0 ADVAPI32!`string'+0x6c
77db1f64  77db2da5 ADVAPI32!MIDL_user_allocate
77db1f68  77db2dd6 ADVAPI32!MIDL_user_free
77db1f6c  77e16294 ADVAPI32!svcctl_handle
77db1f70  00000000
77db1f74  77db23ec ADVAPI32!`string'+0x1040
77db1f78  77db23fc ADVAPI32!`string'+0x1050
77db1f7c  00000000
77db1f80  77db1ffa ADVAPI32!`string'+0xc4e ---->type_offset

typedef struct _MIDL_STUB_DESC
    
{

   
void __RPC_FAR *    RpcInterfaceInformation;

    
void __RPC_FAR *    (__RPC_FAR __RPC_API * pfnAllocate)(size_t);
  
  void                (__RPC_FAR __RPC_API * pfnFree)(void __RPC_FAR *);

 
   union
        {
     
   handle_t __RPC_FAR *            pAutoHandle;
  
     handle_t __RPC_FAR *            pPrimitiveHandle;
   
     PGENERIC_BINDING_INFO           pGenericBindingInfo;
  
         } IMPLICIT_HANDLE_INFO;

    
const NDR_RUNDOWN __RPC_FAR *                   apfnNdrRundownRoutines;

        const GENERIC_BINDING_ROUTINE_PAIR __RPC_FAR *  aGenericBindingRoutinePairs;

 
   const EXPR_EVAL __RPC_FAR *                     apfnExprEval;

 
   const XMIT_ROUTINE_QUINTUPLE __RPC_FAR *        aXmitQuintuple;

 
   const unsigned char __RPC_FAR *                 pFormatTypes;

   ----->type_offset 
   int                                   fCheckBounds;

    
/* Ndr library version. */

    unsigned long                                   Version;

  
  /*
   
  * Reserved for future use. (no reserves )
 
        */


    MALLOC_FREE_STRUCT __RPC_FAR *                  pMallocFreeStruct;

 
   long                                MIDLVersion;

    
const COMM_FAULT_OFFSETS __RPC_FAR *    CommFaultOffsets;

  
  // New fields for version 3.0+

    
const USER_MARSHAL_ROUTINE_QUADRUPLE __RPC_FAR * aUserMarshalQuadruple;

 
   long                                    Reserved1;
  
  long                                    Reserved2;
 
   long                                    Reserved3;
  
  long                                    Reserved4;
 
   long                                    Reserved5;

  
} MIDL_STUB_DESC;
typedef const unsigned char __RPC_FAR * PFORMAT_STRING;  
参数类型:
kd> db 77db1ffa 
77db1ffa  00 00 11 04 02 00 30 e9-00 00 30 49 00 00 11 04  ......0...0I....
77db200a  02 00 15 03 1c 00 08 08-08 08 08 08 08 5b 11 04  .............[..
77db201a  02 00 30 a8 01 01 11 00-02 00 1b 00 01 00 29 00  ..0...........).
77db202a  0c 00 00 00 02 5b b7 08-00 00 00 00 00 00 04 00  .....[..........
77db203a  11 0c 08 5c 11 00 cc ff-11 04 02 00 30 e9 01 00  ...\........0...
77db204a  12 08 25 5c 12 08 08 5c-12 00 02 00 1b 00 01 00  ..%\...\........
77db205a  29 00 20 00 00 00 02 5b-12 00 02 00 1b 00 01 00  ). ....[........
77db206a  29 00 2c 00 00 00 02 5b-11 08 25 5c 12 00 02 00  ).,....[..%\....
第一个参数类型 type_offset 000a, pFormatTypes[000a] = 30 ,pFormatTypes是指向char字符的指针。关于数字代表的类型,在
http://msdn.microsoft.com/en-us/library/windows/desktop/aa374387(v=vs.85).aspx
http://doxygen.reactos.org/df/d78/ndrtypes_8h_a3a87921fd20c99c442f820551f85261e.html
该值30表示FC_BIND_CONTEXT,格式为
FC_BIND_CONTEXT flags<1> offset<2> context_rundown_routine_index<1> param_num<1>
Note  A context handle description in the type format string will not have the offset<2> in the description.所以应该是
FC_BIND_CONTEXT flags<1> context_rundown_routine_index<1> param_num<1>
即49 (HANDLE_PARAM_IS_IN | NDR_STRICT_CONTEXT_HANDLE | NDR_CONTEXT_HANDLE_CANNOT_BE_NULL) 00  00.
第二个参数类型 type_offset 01b2 , pFormatTypes[01b2] = 22,

简单类型封装在函数中,复杂类型在表中,表为
const PMARSHALL_ROUTINE MarshallRoutinesTable[] =
                    {
                    
NdrPointerMarshall,
                    
NdrPointerMarshall,
                    
NdrPointerMarshall,
                    
NdrPointerMarshall,

                    
NdrSimpleStructMarshall,

NdrSimpleStructMarshall,
                    
NdrConformantStructMarshall,
                    NdrConformantStructMarshall,
                    NdrConformantVaryingStructMarshall,

                    NdrComplexStructMarshall,

                    NdrConformantArrayMarshall,
               

     NdrConformantVaryingArrayMarshall,
                    NdrFixedArrayMarshall,
                    NdrFixedArrayMarshall,
                    NdrVaryingArrayMarshall,
                    NdrVaryingArrayMarshall,

                    

NdrComplexArrayMarshall,

                    NdrConformantStringMarshall,
                    NdrConformantStringMarshall,
                    NdrConformantStringMarshall,
                    NdrConformantStringMarshall,

                    

NdrNonConformantStringMarshall,
                    NdrNonConformantStringMarshall,
                    NdrNonConformantStringMarshall,
                    NdrNonConformantStringMarshall,

                    NdrEncapsulatedUnionMarshall,
     

               NdrNonEncapsulatedUnionMarshall,

                    NdrByteCountPointerMarshall,

                    NdrXmitOrRepAsMarshall,    // transmit as
                    NdrXmitOrRepAsMarshall,    // represent as

                    

NdrInterfacePointerMarshall,

                    NdrMarshallHandle,

                    // New Post NT 3.5 token serviced from here on.

                    NdrHardStructMarshall,

                    NdrXmitOrRepAsMarshall,  // transmit as 

ptr
                    NdrXmitOrRepAsMarshall,  // represent as ptr

                    NdrUserMarshalMarshall

                    
};

NdrClientCall2(本篇看到的消息格式)->RPCRT4!NdrpClientMarshal(对消息封装)->NdrSendReceive->I_RpcSendReceive->MESSAGE_OBJECT::SendReceive->WMSG_CCALL::AsyncSendReceive(填充rpc头)->WMSG_CCALL::AsyncSendReceiveHelper->NtRequestWaitReplyPort
创建服务堆栈:
kd> kv
ChildEBP RetAddr  Args to Child              
0012f224 77e5cac1 000000ac 00147f68 00147f68 ntdll!ZwRequestWaitReplyPort (FPO: [3,0,0])
0012f270 77e5a33e 0014d830 0012f290 77e5a36f RPCRT4!LRPC_CCALL::SendReceive+0x228 (FPO: [Non-Fpo])
0012f27c 77e5a36f 0012f2ac 77db1f60 0012f688 RPCRT4!I_RpcSendReceive+0x24 (FPO: [Non-Fpo])
0012f290 77ed4675 0012f2d8 0014d8e8 00000000 RPCRT4!NdrSendReceive+0x2b (FPO: [Non-Fpo])
0012f66c 77e081f1 77db1f60 77db6742 0012f688 RPCRT4!NdrClientCall2+0x222 (FPO: [Non-Fpo])
0012f680 77e072fd 00151dc8 00386108 00386108 ADVAPI32!RCreateServiceA+0x1b (FPO: [Non-Fpo])
0012f714 0040290d 00151dc8 00386108 00386108 ADVAPI32!CreateServiceA+0x114 (FPO: [Non-Fpo])
WARNING: Stack unwind information not available. Following frames may be wrong.
0012f77c 73d323bf 0012fd44 00000001 00000000 InstDrv+0x290d
封装参数堆栈
kd> kv
ChildEBP RetAddr  Args to Child              
0012f290 77ed463c 0012f2d8 00000000 00000000 RPCRT4!NdrpClientMarshal (FPO: [Non-Fpo])
0012f66c 77e081f1 77db1f60 77db6742 0012f688 RPCRT4!NdrClientCall2+0x1c8 (FPO: [Non-Fpo])
0012f680 77e072fd 00151bd0 003860b8 003860b8 ADVAPI32!RCreateServiceA+0x1b (FPO: [Non-Fpo])
0012f714 0040290d 00151bd0 003860b8 003860b8 ADVAPI32!CreateServiceA+0x114 (FPO: [Non-Fpo])
WARNING: Stack unwind information not available. Following frames may be wrong.
0012f77c 73d323bf 0012fd44 00000001 00000000 InstDrv+0x290d

有了上面的信息之后,总算可以分析核心函数NdrpClientMarshal了。
 
1) 参数的占用的内存大小
计算是在NdrpSizing这个函数里面做的。根据不同的类型去索引表SizeRoutinesTable来计算相关大小。
 
2) 封装参数
NdrpClientMarshal
 
RPCRT4!NdrpClientMarshal:
001b:77e58aa0 8bff            mov     edi,edi
001b:77e58aa2 55              push    ebp
001b:77e58aa3 8bec            mov     ebp,esp
001b:77e58aa5 83ec0c          sub     esp,0Ch
001b:77e58aa8 8365fc00        and     dword ptr [ebp-4],0
001b:77e58aac 53              push    ebx
001b:77e58aad 8b5d08          mov     ebx,dword ptr [ebp+8] ;PMIDL_STUB_MESSAGE
001b:77e58ab0 57              push    edi
001b:77e58ab1 8bbbc4000000    mov     edi,dword ptr [ebx+0C4h] ;struct _NDR_PROC_CONTEXT* pContext;
001b:77e58ab7 837f1000        cmp     dword ptr [edi+10h],0
001b:77e58abb 8b4728          mov     eax,dword ptr [edi+28h]   
001b:77e58abe 8b5714          mov     edx,dword ptr [edi+14h]
 
                              ;
                              ;根据弄到的值推算就是PFORMAT_STRING pFormat 参数描述开始部分。
                              ;1: kd> r edx
                              ; edx=77db6b96
                              ; pFormat 77db6b74 
                              ;  ? 77db6b96 - 77db6b74  == 34刚好是描述头的大小
 
001b:77e58ac1 897d08          mov     dword ptr [ebp+8],edi
001b:77e58ac4 8945f4          mov     dword ptr [ebp-0Ch],eax
001b:77e58ac7 7663            jbe     RPCRT4!NdrpClientMarshal+0x104 (77e58b2c)
001b:77e58ac9 83c204          add     edx,4
001b:77e58acc 8955f8          mov     dword ptr [ebp-8],edx                   ;pFormat_Pra
001b:77e58acf 56              push    esi
 
                              ;@@@@参数从这里开始处理
001b:77e58ad0 668b4afc        mov     cx,word ptr [edx-4] ds:0023:77db6b92=0008
001b:77e58ad4 f6c108          test    cl,8                                      ;IsIn 
001b:77e58ad7 7441            je      RPCRT4!NdrpClientMarshal+0xee (77e58b1a)
 
001b:77e58ad9 f6c104          test    cl,4                                      ;IsPipe
001b:77e58adc 753c            jne     RPCRT4!NdrpClientMarshal+0xee (77e58b1a)
 
                              ;只对 有 IsIn 而且不没有IsPipe属性的 参数进行Marshall
 
001b:77e58ade 0fb772fe        movzx   esi,word ptr [edx-2]                      ; 参数的堆栈偏移
001b:77e58ae2 037718          add     esi,dword ptr [edi+18h]                   ;[edi+18h] 就是参数堆栈的基地址
 
001b:77e58ae5 f6c508          test    ch,8
001b:77e58ae8 0f85e8990300    jne     RPCRT4!NdrpClientMarshal+0x56 (77e924d6)
 
001b:77e58aee f6c140          test    cl,40h                                    ;是否IsBasetype,基础类型不走MarshallRoutines
001b:77e58af1 0f85d10b0000    jne     RPCRT4!NdrpClientMarshal+0x76 (77e596c8)
 
001b:77e58af7 0fb702          movzx   eax,word ptr [edx]        ;type_offset 
001b:77e58afa 0345f4          add     eax,dword ptr [ebp-0Ch]   ;获取类型描述
 
001b:77e58afd 84c9            test    cl,cl
001b:77e58aff 7802            js      RPCRT4!NdrpClientMarshal+0xd7 (77e58b03)
 
001b:77e58b01 8b36            mov     esi,dword ptr [esi] ; 取参数,就是00154be0,就是句柄的值
001b:77e58b03 33c9            xor     ecx,ecx
001b:77e58b05 8a08            mov     cl,byte ptr [eax]   ;eax的第一个字节就是参数的类型
                              
                              ;开始进行数据封装
001b:77e58b07 50              push    eax   ;PFORMAT_STRING pFormat,但是从参数描述开始的
001b:77e58b08 56              push    esi   ;参数
001b:77e58b09 53              push    ebx   ;pStubMsg
001b:77e58b0a 83e13f          and     ecx,3Fh
001b:77e58b0d ff148d7814e577  call    dword ptr RPCRT4!MarshallRoutinesTable (77e51478)[ecx*4]
 

001b:77e58b14 8b55f8          mov     edx,dword ptr [ebp-8]
001b:77e58b17 8b7d08          mov     edi,dword ptr [ebp+8]
001b:77e58b1a ff45fc          inc     dword ptr [ebp-4]
001b:77e58b1d 8b45fc          mov     eax,dword ptr [ebp-4]  
001b:77e58b20 83c206          add     edx,6                 ;因为参数描述结构的大小是6字节
001b:77e58b23 3b4710          cmp     eax,dword ptr [edi+10h] ds:0023:0012fa5c=00000006   ;[edi+10h]保存参数个数
001b:77e58b26 8955f8          mov     dword ptr [ebp-8],edx
001b:77e58b29 72a5            jb      RPCRT4!NdrpClientMarshal+0x34 (77e58ad0)
 

好,下面看NDR怎么处理 IsBasetype 为TRUE的参数
001b:77e596c8 f6c501          test    ch,1  ;MustSize
001b:77e596cb 0f8527240000    jne     RPCRT4!NdrpClientMarshal+0x7b (77e5baf8) [br=0]
 
001b:77e596d1 803a0d          cmp     byte ptr [edx],0Dh  ;type_format_char, D == FC_ENUM16
001b:77e596d4 0f84cd9e0000    je      RPCRT4!NdrpClientMarshal+0x82 (77e635a7)
 
;通过 type_format_char 索引 RPCRT4!SimpleTypeAlignment,基础类型的对齐大小
001b:77e596da 0fb602          movzx   eax,byte ptr [edx]
001b:77e596dd 0fb6805816e577  movzx   eax,byte ptr RPCRT4!SimpleTypeAlignment (77e51658)[eax] 
001b:77e596e4 8b7b04          mov     edi,dword ptr [ebx+4] ; 0012fcfc, MIDL_STUB_MESSAGE::Buffer 
001b:77e596e7 03f8            add     edi,eax
001b:77e596e9 f7d0            not     eax
001b:77e596eb 23f8            and     edi,eax
001b:77e596ed 897b04          mov     dword ptr [ebx+4],edi
 
;通过 type_format_char 索引 RPCRT4!SimpleTypeBufferSize,基础类型的大小
001b:77e596f0 0fb602          movzx   eax,byte ptr [edx]
001b:77e596f3 0fb6881817e577  movzx   ecx,byte ptr RPCRT4!SimpleTypeBufferSize (77e51718)[eax]
                              ;开始复制参数
001b:77e596fa 8bc1            mov     eax,ecx
001b:77e596fc c1e902          shr     ecx,2
001b:77e596ff f3a5            rep movs dword ptr es:[edi],dword ptr [esi]
 
          
001b:77e59701 8bc8            mov     ecx,eax
001b:77e59703 83e103          and     ecx,3
001b:77e59706 f3a4            rep movs byte ptr es:[edi],byte ptr [esi]
001b:77e59708 0fb602          movzx   eax,byte ptr [edx]
001b:77e5970b 0fb6801817e577  movzx   eax,byte ptr RPCRT4!SimpleTypeBufferSize (77e51718)[eax]
001b:77e59712 014304          add     dword ptr [ebx+4],eax
001b:77e59715 e9fdf3ffff      jmp     RPCRT4!NdrpClientMarshal+0xeb (77e58b17)
001b:77e5971a f6c401          test    ah,1
001b:77e5971d 0f85dc230000    jne     RPCRT4!NdrpServerUnMarshal+0x9a (77e5baff)
001b:77e59723 33c0            xor     eax,eax
001b:77e59725 8a4704          mov     al,byte ptr [edi+4]
001b:77e59728 50              push    eax
 

上面可知参数处理后是放在  MIDL_STUB_MESSAGE::Buffer 里面的


发送消息类型:
typedef union _WMSG_MESSAGE
{
   
 WMSG_CONNECT_MESSAGE Connect;
    
WMSG_BIND_MESSAGE Bind;
    
WMSG_RPC_MESSAGE Rpc;
    
WMSG_FAULT_MESSAGE Fault;
    
WMSG_CLOSE_MESSAGE Close;
    
PORT_MESSAGE LpcHeader;
    
WMSG_RESPONSE_MESSAGE Response ;
    
WMSG_ACK_MESSAGE Ack ;
    
WMSG_PUSH_MESSAGE Push ;
    
WMSG_BIND_BACK_MESSAGE BindBack ;
    
WMSG_PARTIAL_MESSAGE Partial ;

} WMSG_MESSAGE;
服务的rpc msg格式是:
typedef struct _WMSG_RPC_MESSAGE
{
    
PORT_MESSAGE LpcHeader;
       --->  格式前面有 24 字节
WMSG_RPC_HEADER RpcHeader;
    --->  Oif_style_header_descriptor  共0x38个字节
union
    {
        
unsigned char Buffer[MAXIMUM_MESSAGE_BUFFER];
   ---->参数,自保中的ParamOffset.     
PORT_DATA_INFORMATION Request;
        
WMSG_SERVER_BUFFER Server;
    
};

} WMSG_RPC_MESSAGE;
typedef struct _WMSG_RPC_HEADER
{
    
unsigned char MessageType;
    
unsigned char Flags ;
    
unsigned char PresentationContext;
    
unsigned char ObjectUuidFlag;
    
unsigned short ProcedureNumber;
    
unsigned short ConnectionKey ;
    
UUID ObjectUuid;

} WMSG_RPC_HEADER;

NtRequestWaitReplyPort参数中的PortHandle是自己端口对象。 
typedefstruct_LPCP_PORT_OBJECT{
    struct_LPCP_PORT_OBJECT *ConnectionPort;
    struct_LPCP_PORT_OBJECT *ConnectedPort;
    LPCP_PORT_QUEUEMsgQueue;
    CLIENT_ID Creator;
    ……
}LPCP_PORT_OBJECT, *PLPCP_PORT_OBJECT;
 
过滤时比较主要的就是ConnectionPort,这里面指向的是对方端口对象,得到对方端口对象后查询对象名,services建立的服务端口名是 \RPC Control\ntsvcs
在buffer中的数据封装:
基本类型,字节对齐,然后是数据直接拷到buffer。
字符串,先对齐,然后maxCount,然后对齐,然后0占4个字节,然后4个字节的字符串实际长度,然后字符串的ascii码。然后空4个字节,然后是下个参数。
在函数NdrConformantStringMarshall中封装。如果已经对齐就不填充字节。字符串需要按sizeof(ulong)对齐,即如果字串"mysys",5个字节,加一个'\0',共
6个字节,按4字节对齐,后面的内容存需要加上8给字节对齐。

猜测createService的bprotect过滤中,参数+20个字节才是服务名,是因为4个字节的 explicit handle +16字节的 sc_handle结构

0 0
原创粉丝点击