LPC服务器拒绝客户端的表白

开场白:

项目中遇见了一个奇怪的问题。在64位系统下,LPC的服务端是wow32位进程,在调用了ZwAcceptConnectPort拒绝客户端的连接后,ZwAcceptConnectPort返回

  //
// MessageId: STATUS_INVALID_MESSAGE
//
// MessageText:
//
// The ALPC message supplied is invalid.
//
#define STATUS_INVALID_MESSAGE           ((NTSTATUS)0xC0000702L)

然而此时的客户端还一如既往的等待,就像一个追求者思思等待,然后那个被追求的人没有把拒绝告白的信息传递给客户端。

表白的方式很简单 ,伪代码如下

bAccept = 0;

                ntStatus = ZwAcceptConnectPort(
                    &hRemotePort0,
                    (ULONG)0,
                    pPortMessageCorrected,
                    bAccept,
                    //pServerView,
                    NULL,
                    NULL);

对应 pPortMessageCorrected 已经做了wow转换,模拟了真正的64位程序。

1    我们需要知道STATUS_INVALID_MESSAGE 这个值是在哪里返回的,这个不难。我们仅需要知道R3-R0的服务调用的层层关系即可,

此处引用一种网上的图片

链接引用

https://blog.csdn.net/whatday/article/details/52782955

经过跟踪,我们一种跟踪到了如下堆栈

3: kd> k
 # Child-SP          RetAddr           Call Site
00 ffff8804`b78227a8 fffff803`7c8a1d5f nt!AlpcpLookupMessage
01 ffff8804`b78227b0 fffff803`7c89f3d8 nt!AlpcpAcceptConnectPort+0x2f7
02 ffff8804`b7822a20 fffff803`7c583c53 nt!NtAcceptConnectPort+0x58
03 ffff8804`b7822a90 00007ffe`f282fe84 nt!KiSystemServiceCopyEnd+0x13
04 00000000`0998e798 00000000`68df2bf1 ntdll!NtAcceptConnectPort+0x14
05 00000000`0998e7a0 00000000`68dd6463 wow64!whNtAcceptConnectPort+0x161
06 00000000`0998e850 00000000`68eb1923 wow64!Wow64SystemServiceEx+0x153
07 00000000`0998f110 00000000`68deac12 wow64cpu!ServiceNoTurbo+0xb
08 00000000`0998f1c0 00000000`68ddbcf0 wow64!RunCpuSimulation+0xee12
09 00000000`0998f1f0 00007ffe`f2809314 wow64!Wow64LdrpInitialize+0x120
0a 00000000`0998f4a0 00007ffe`f280920b ntdll!_LdrpInitialize+0xf4
0b 00000000`0998f520 00007ffe`f28091be ntdll!LdrpInitialize+0x3b
0c 00000000`0998f550 00000000`00000000 ntdll!LdrInitializeThunk+0xe
 

(切换到wow的堆栈可用

3: kd> !wow64exts.sw
Switched to Guest (WoW) mode
The context is partially valid. Only x86 user-mode context is available.
3: kd:x86> k
 # ChildEBP          RetAddr           
WARNING: Frame IP not in any known module. Following frames may be wrong.
00 09a8fc24 00136185 0x7737e76c
01 09a8fcb8 001367e7 ccavsrv!lpcHandleConnectionRequest+0x360 [g:\work\src\comodocloudantivirus_metadata\common\lpcsrv.cpp @ 627]
02 09a8fe3c 760d8654 ccavsrv!lpcWorkingThread+0x149 [g:\work\src\comodocloudantivirus_metadata\common\lpcsrv.cpp @ 970]
03 09a8fe50 77374a47 0x760d8654
04 09a8fe98 77374a17 0x77374a47

05 09a8fea8 00000000 0x77374a17

不过只能看到wow的栈,看不到64位的栈。所以还是切换到64位的栈吧。.effmach amd64 ; k

其中 AlpcpLookupMessage 的伪代码如下

signed __int64 __fastcall AlpcpLookupMessage(__int64 zero1, __int64 messageID, int zero2, unsigned __int64 *pStackOut)
{
 
     .......

  v4 = messageID;
  pStackOut1 = pStackOut;
  zero3 = zero2;
  zero4 = zero1;
  if ( (signed int)messageID < 0 )
  {
    if ( zero1 )

                .......
      }
    }
    else
    {
      result = 0xC0000702i64;                   // not here
    }
  }
  else
  {
    JUMPOUT(messageID & 0xFC000000, 0, sub_140635421);
    v8 = AlpcMessageTable;
    JUMPOUT(AlpcMessageTable, 0i64, &loc_1406354C3);
    v9 = (unsigned __int8)KeGetCurrentThread()->gap0[10];
    if ( messageID & 0x3FC )
    {
      pHandleEntryByMessageId = (volatile signed __int64 *)ExpLookupHandleTableEntry(
                                                             (unsigned int *)AlpcMessageTable,
                                                             messageID & 0x3FFFFFF);
      if ( pHandleEntryByMessageId )
      {
        while ( 1 )
        {
                        .......
        }
        JUMPOUT(pRealHandleEntryByMessageId, 0i64, &loc_140635441);
      }
    }
    ExHandleLogBadReference(v8, v4 & 0x3FFFFFF);
    result = 0xC0000702i64;                     // here2
  }
  return result;
}
 

大体可以知道,他是通过messageID找等待的客户端,其中messageid是  +0x010 MessageId        : 0x10a4的值

3: kd:x86> dt pPortMessageCorrected
Local var @ 0x9a8fc94 Type _PORT_MESSAGE*
0x00000000`0db74d58 
   +0x000 u1               : _PORT_MESSAGE::<unnamed-type-u1>
   +0x004 u2               : _PORT_MESSAGE::<unnamed-type-u2>
   +0x008 ClientId         : _CLIENT_ID
   +0x008 DoNotUseThisField : 7.0195620795348263e-311 
   +0x010 MessageId        : 0x10a4 //这里的值
   +0x014 ClientViewSize   : 0x59000
   +0x014 CallbackId       : 0x59000
 

通过一个接受的和一个拒绝的回应来看。拒绝的回应的messageID是错误的,AlpcpLookupMessage的第二个参数,通过rdx就可以知道,最后导致AlpcpLookupMessage返回无效的message,但是 但是通过断在NtAcceptConnectPort查看第三个参数是我们通过R3传入值0x00000000`0db74d58 (这个值在R3是调用ZwAcceptConnectPort 的第三个参数),没有错误啊,里面的内容都没有变。这是为什么那? 通过断下一个正常的回应来看,第三个参数的值,不是R3传入的值,What ? 搞错了把,正确的导致后面的错误,错误的最后是正确的,没有搞错把? 重复了几遍,一直是这样。

拒绝表白需要正确的姿态

 看样子调用到 nt!NtAcceptConnectPort的第三个参数值就有问题了,所以我们要从R3入手了。找到R3进入内核的最后一步

ntdll!NtAcceptConnectPort:
0010:00007ffe`f282fe70 4c8bd1          mov     r10,rcx
0010:00007ffe`f282fe73 b802000000      mov     eax,2 //SSDT的服务号
0010:00007ffe`f282fe78 f604250803fe7f01 test    byte ptr [SharedUserData+0x308 (00000000`7ffe0308)],1
0010:00007ffe`f282fe80 7503            jne     ntdll!NtAcceptConnectPort+0x15 (00007ffe`f282fe85)
0010:00007ffe`f282fe82 0f05            syscall  //进内核
0010:00007ffe`f282fe84 c3              ret
0010:00007ffe`f282fe85 cd2e            int     2Eh
0010:00007ffe`f282fe87 c3              ret
我们断在ntdll!NtAcceptConnectPort:发现 调用到这里的时候值就有问题了。所以要顺着堆栈继续往上找

上一层是05 00000000`0998e7a0 00000000`68dd6463 wow64!whNtAcceptConnectPort

并且在05 00000000`0998e7a0 00000000`68dd6463 wow64!whNtAcceptConnectPort+0x161出调用了nt!NtAcceptConnectPort

IDA看下此处的伪代码

whNtAcceptConnectPort+143                    mov     [rsp+0A8h+var_88], rax
whNtAcceptConnectPort+148                    mov     r9b, r13b
whNtAcceptConnectPort+14B                    mov     r8, [rsp+0A8h+arg_18] ; rdx == message
whNtAcceptConnectPort+14B                                            ; rsp+c8
whNtAcceptConnectPort+153                    mov     rdx, [rsp+0A8h+var_60]
whNtAcceptConnectPort+158                    mov     rcx, rsi
whNtAcceptConnectPort+15B                    call    cs:__imp_NtAcceptConnectPort ; Indirect Call Near Procedure

可以看到mov     r8, [rsp+0A8h+arg_18]出是第三个参数,即 rsp+c8

我们断下wow64!whNtAcceptConnectPort+0处,对比拒绝请求和接受请求的参数,完全一样。所以了参数的转变是在

wow64!whNtAcceptConnectPort中。那就好说了,我们只需要在rsp+0A8h+arg_18处设置硬件的写入断点我们就知道在哪里把原始的第三个参数做了转变

(

通过调试得到调用了wow64!whNtAcceptConnectPort后,rcx寄存器指向了程序调用ZwAcceptConnectPort时候的堆栈信息

3: kd> r rcx
rcx=0000000009d0fa44
3: kd> dd 0000000009d0fa44
00000000`09d0fa44  09d0fa90 00000000 0d6c71e8 00000000
00000000`09d0fa54  00000000 09d0fa6c

rcx 第一个参数  09d0fa90
rcx+4  第二个参数  00000000
rcx+8  第三个参数  0d6c71e8
rcx+12 第四个参数  00000000

以此类推

)

重新运行断在whNtAcceptConnectPort,并且走过00000000`68df2a9e 4883ec70        sub     rsp,70h这条指令。
然后硬件断点
ba w4 rsp的值 +C8,看下那里往这个地址写了数据 。我们先来一次接受请求的过程


第一次断下来
wow64!whNtAcceptConnectPort:
00000000`68df2a90 4c8bdc          mov     r11,rsp
00000000`68df2a93 53              push    rbx
00000000`68df2a94 56              push    rsi
00000000`68df2a95 57              push    rdi
00000000`68df2a96 4154            push    r12
00000000`68df2a98 4155            push    r13
00000000`68df2a9a 4156            push    r14
00000000`68df2a9c 4157            push    r15
00000000`68df2a9e 4883ec70        sub     rsp,70h
00000000`68df2aa2 4889642438      mov     qword ptr [rsp+38h],rsp
00000000`68df2aa7 8b39            mov     edi,dword ptr [rcx]
00000000`68df2aa9 448a690c        mov     r13b,byte ptr [rcx+0Ch]
00000000`68df2aad 448b7910        mov     r15d,dword ptr [rcx+10h]
00000000`68df2ab1 448b7114        mov     r14d,dword ptr [rcx+14h]
00000000`68df2ab5 33d2            xor     edx,edx
00000000`68df2ab7 41885308        mov     byte ptr [r11+8],dl
00000000`68df2abb 49895318        mov     qword ptr [r11+18h],rdx
00000000`68df2abf 8bc7            mov     eax,edi
00000000`68df2ac1 f7d8            neg     eax
00000000`68df2ac3 481bf6          sbb     rsi,rsi
00000000`68df2ac6 498d4318        lea     rax,[r11+18h]
00000000`68df2aca 4823f0          and     rsi,rax
00000000`68df2acd 8b4104          mov     eax,dword ptr [rcx+4]
00000000`68df2ad0 498943a0        mov     qword ptr [r11-60h],rax
00000000`68df2ad4 448b6108        mov     r12d,dword ptr [rcx+8] ds:002b:00000000`09d0fa4c=0d6c71e8  //取出原始的第三个参数, //然后写入,算是初始化
00000000`68df2ad8 4d896320        mov     qword ptr [r11+20h],r12//这个写入。  [r11+20h] == 0000000009d4e5d8,就是调用__imp_NtAcceptConnectPort时候的第三个参数的地址
00000000`68df2adc 4585ff          test    r15d,r15d


第二次断下
wow64!Wow64pThunkLegacyLpcMsgIn:
00000000`68deed4c 488bc4          mov     rax,rsp
00000000`68deed4f 884808          mov     byte ptr [rax+8],cl
00000000`68deed52 53              push    rbx
00000000`68deed53 56              push    rsi
00000000`68deed54 57              push    rdi
00000000`68deed55 4154            push    r12
00000000`68deed57 4155            push    r13
00000000`68deed59 4156            push    r14
00000000`68deed5b 4157            push    r15
00000000`68deed5d 4883ec30        sub     rsp,30h
00000000`68deed61 4d8be8          mov     r13,r8
00000000`68deed64 488bf2          mov     rsi,rdx
00000000`68deed67 33db            xor     ebx,ebx
00000000`68deed69 8958b8          mov     dword ptr [rax-48h],ebx
00000000`68deed6c 498910          mov     qword ptr [r8],rdx//here
00000000`68deed6f 4885d2          test    rdx,rdx


3: kd> r r8
r8=0000000009d4e5d8 

 0000000009d4e5d8,就是调用__imp_NtAcceptConnectPort时候的第三个参数的地址

 
写入的值为rdx, 
 3: kd> r rdx
rdx=000000000db747d8  依然是原始值的初始化


//第三次断下


 # Child-SP          RetAddr           Call Site
00 00000000`09d4e4a0 00000000`68df2ba2 wow64!Wow64pThunkLegacyLpcMsgIn+0x1a1
01 00000000`09d4e510 00000000`68dd6463 wow64!whNtAcceptConnectPort+0x112
02 00000000`09d4e5c0 00000000`68eb1923 wow64!Wow64SystemServiceEx+0x153
03 00000000`09d4ee80 00000000`68deac12 wow64cpu!ServiceNoTurbo+0xb
04 00000000`09d4ef30 00000000`68ddbcf0 wow64!RunCpuSimulation+0xee12
05 00000000`09d4ef60 00007ffe`f2809314 wow64!Wow64LdrpInitialize+0x120
06 00000000`09d4f210 00007ffe`f280920b ntdll!_LdrpInitialize+0xf4
07 00000000`09d4f290 00007ffe`f28091be ntdll!LdrpInitialize+0x3b
08 00000000`09d4f2c0 00000000`00000000 ntdll!LdrInitializeThunk+0xe

Wow64pThunkLegacyLpcMsgIn中

00000000`68deeed8 4c0fbf06        movsx   r8,word ptr [rsi]
00000000`68deeedc 488d5618        lea     rdx,[rsi+18h]
00000000`68deeee0 488d4f28        lea     rcx,[rdi+28h]
00000000`68deeee4 e8b781ffff      call    wow64!memcpy (00000000`68de70a0)
00000000`68deeee9 49897d00        mov     qword ptr [r13],rdi //here
00000000`68deeeed 8b5c2420        mov     ebx,dword ptr [rsp+20h] ss:002b:00000000`09d4e4c0=00000000

 3: kd> r r13
r13=0000000009d4e5d8
 0000000009d4e5d8,就是调用__imp_NtAcceptConnectPort时候的第三个参数的地址
 
 
 rdi=0000000009d4edd0  值发生了改变
 
 
 之后直接调用到__imp_NtAcceptConnectPort ,以上都是正常的逻辑,
 


 下面看下拒绝的逻辑断点
 
 只有上面的第一次断下的过程,没有第二三次
 然后直接调用到了ntdll!NtAcceptConnectPort
 

可以猜测第二三次对message内存做了重新的包装 

 通过IDA 看下伪代码

__int64 __fastcall whNtAcceptConnectPort(unsigned int *pOriginParameterStack, __int64 a2, __int64 a3, __int64 a4)
{
  _DWORD *v4; // rdi
  char v5; // r13
  __int64 pWritesection; // r15
  __int64 pReadsection; // r14
  signed __int16 *v8; // r12
  __int64 v9; // r8
  int *v11; // rbx
  __int64 v12; // r8
  unsigned int v13; // eax
  signed int v14; // er8
  unsigned int v15; // eax
  unsigned int v16; // eax
  __int64 v17; // [rsp+0h] [rbp-A8h]
  unsigned int v18; // [rsp+30h] [rbp-78h]
  __int64 *v19; // [rsp+38h] [rbp-70h]
  int *v20; // [rsp+40h] [rbp-68h]
  __int64 v21; // [rsp+48h] [rbp-60h]
  int v22; // [rsp+50h] [rbp-58h]
  __int64 v23; // [rsp+58h] [rbp-50h]
  __int64 v24; // [rsp+60h] [rbp-48h]
  int v25; // [rsp+B0h] [rbp+8h]
  __int64 v26; // [rsp+B8h] [rbp+10h]
  __int64 v27; // [rsp+C0h] [rbp+18h]
  __int64 v28; // [rsp+C8h] [rbp+20h]

  v19 = &v17;
  v4 = (_DWORD *)*pOriginParameterStack;
  v5 = *((_BYTE *)pOriginParameterStack + 12);
  pWritesection = pOriginParameterStack[4];     // v6 为第5个参数,即writesection
  pReadsection = pOriginParameterStack[5];      // r7 为第6个参数,即readsection
  LOBYTE(v25) = 0;
  v27 = 0i64;
  v21 = pOriginParameterStack[1];
  v8 = (signed __int16 *)pOriginParameterStack[2];
  v28 = pOriginParameterStack[2];
  if ( (_DWORD)pWritesection )
  {
    v18 = Wow64pThunkLegacyPortViewIn((unsigned int)pWritesection, &v26, &v25);
    if ( (v18 & 0x80000000) != 0 )
    {
      local_unwind_0(v19, &loc_6B022B0E, v9);
      return v18;
    }
  }
  else
  {
    v26 = 0i64;
  }
  if ( (_DWORD)pReadsection )//如果pWritesection 为NULL,这里一定不能为NULL
  {
    v11 = (int *)pReadsection;
    if ( *(_DWORD *)pReadsection == 12 )        // 这个地方做了大小校验
    {
      v22 = 24;
      v24 = 0i64;
      v23 = 0i64;
      v11 = &v22;
      v20 = &v22;
      LOBYTE(v25) = 1;
    }
    else
    {
      v20 = (int *)pReadsection;
    }
  }
  else
  {
    v11 = 0i64;
    v20 = 0i64;
  }
  if ( (_BYTE)v25 == 1 )这里要满足
  {
    v18 = Wow64pThunkLegacyLpcMsgIn((__int64)pOriginParameterStack, v8, &v28);//这里要调用
    if ( (v18 & 0x80000000) != 0 )
    {
      local_unwind_0(v19, &loc_6B022BBD, v12);
      return v18;
    }
  }
  LOBYTE(a4) = v5;
  v13 = NtAcceptConnectPort((unsigned __int64)&v27 & -(signed __int64)((_DWORD)v4 != 0), v21, v28, a4, v26, v11);
  v14 = v13;
  v18 = v13;
  if ( (_DWORD)v4 )
    *v4 = *(_DWORD *)((unsigned __int64)&v27 & -(signed __int64)((_DWORD)v4 != 0));
  if ( (v13 & 0x80000000) == 0 && (_BYTE)v25 == 1 )
  {
    v15 = Wow64pThunkLegacyPortViewOut(v26, pWritesection, v13);
    v14 = v15;
    v18 = v15;
    if ( (v15 & 0x80000000) != 0 )
    {
      local_unwind_0(v19, &loc_6B022C3C, v15);
      return v18;
    }
  }
  if ( v14 >= 0 )
  {
    if ( (_BYTE)v25 == 1 )
    {
      v16 = Wow64pThunkLegacyRemoteViewOut(v11, pReadsection);
      v14 = v16;
      v18 = v16;
      if ( (v16 & 0x80000000) != 0 )
      {
        local_unwind_0(v19, &loc_6B022C7A, v16);
        return v18;
      }
    }
    if ( v14 >= 0 && (_BYTE)v25 == 1 )
      *v4 |= 1u;
  }
  return (unsigned int)v14;
}

看了ZwAcceptConnectPort的最后一个参数不能为NULL.而且大小有要求

已经明确上面的第二三次硬件断点的过程是对message的一次重新包装,对比一下处理后的和处理前的内存

3: kd> db 000000000998f060  ///这个是对第三个参数的content重新处理之后的内存。
00000000`0998f060  4c 00 74 00 0a 00 00 00-e4 17 00 00 00 00 00 00  L.t.............
00000000`0998f070  ec 0c 00 00 00 00 00 00-a4 10 00 00 32 00 5c 00  ............2.\.
00000000`0998f080  00 90 05 00 00 00 00 00-4c 00 00 00 33 00 32 00  ........L...3.2.
00000000`0998f090  31 00 32 00 30 00 30 00-00 00 00 00 00 00 00 00  1.2.0.0.........
00000000`0998f0a0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`0998f0b0  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`0998f0c0  00 00 00 00 00 00 00 00-00 00 00 00 00 20 68 bf  ............. h.
00000000`0998f0d0  04 20 68 bf 2e 00 65 00-78 00 65 00 00 00 00 00  . h...e.x.e.....

3: kd> !wow64exts.sw
Switched to Guest (WoW) mode
The context is partially valid. Only x86 user-mode context is available.
3: kd:x86> dt pPortMessageCorrected
Local var @ 0x9a8fc94 Type _PORT_MESSAGE*
0x00000000`0db74d58 
   +0x000 u1               : _PORT_MESSAGE::<unnamed-type-u1>
   +0x004 u2               : _PORT_MESSAGE::<unnamed-type-u2>
   +0x008 ClientId         : _CLIENT_ID
   +0x008 DoNotUseThisField : 7.0195620795348263e-311 
   +0x010 MessageId        : 0x10a4
   +0x014 ClientViewSize   : 0x59000
   +0x014 CallbackId       : 0x59000
3: kd:x86> db 0x00000000`0db74d58 //处理之前的
00000000`0db74d58  4c 00 64 00 0a 00 00 00-e4 17 00 00 ec 0c 00 00  L.d.............
00000000`0db74d68  a4 10 00 00 00 90 05 00-4c 00 00 00 33 00 32 00  ........L...3.2.
00000000`0db74d78  31 00 32 00 30 00 30 00-00 00 00 00 00 00 00 00  1.2.0.0.........
00000000`0db74d88  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`0db74d98  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`0db74da8  00 00 00 00 00 00 00 00-00 00 00 00 00 20 68 bf  ............. h.
00000000`0db74db8  04 20 68 bf 00 00 00 00-00 00 00 00 00 00 00 00  . h.............
00000000`0db74dc8  00 00 00 00 00 00 00 00-5b 66 1d de 00 27 00 80  ........[f...'..

3 正确的表白

            HANDLE hRemotePort0 = INVALID_HANDLE_VALUE;  

           BOOL bAccept = 0;

            REMOTE_PORT_VIEW ClientView = {0};
            ClientView.Length = sizeof(ClientView);
            ClientView.ViewBase = 0;
            ClientView.ViewSize = 0;

                ntStatus = ZwAcceptConnectPort(
                    &hRemotePort0,
                    (ULONG)0,
                    pPortMessageCorrected,
                    bAccept,
                    //pServerView,
                    NULL,
                    &ClientView);

重新编译后,发现正常的拒绝了,不再让追求者白白等待了。

猜你喜欢

转载自blog.csdn.net/xiaohua_de/article/details/82846616