有人可以告诉我这个 memcpy 是什么或者它是一个 memset 吗?看起来像一个memset,rep movsd 和rep movsb 这是一个包发送功能

逆向工程 艾达 反编译
2021-07-03 06:35:44

试图了解负责发送数据包的功能。我不明白它可能是一个整数数组还是什么?或一些在 Hex-Rays 中没有正确渲染的内联函数

我知道 else 语句会发送一个 4 字节的数据包,其中包含 GetTickCount API 的时间戳。

if 语句应该发送传入的数据包a2是指向字符的指针,a3 是所有字符的大小。

用法与此类似

char buffer[448];
memset(buffer, 0, sizeof(buffer));
//blah blah packet stuff here
strncpy(&buffer[90], "blah blah blah", 250u);
buffer[339] = 0;
//then the call below.
// 91+250+91 = 432, yet memset is 448, 16 extra probably stack padding.
test(*v28, buffer, strlen(&buffer[90]) + 91);

这是从 Hex-Rays 反编译的原始代码。

void __thiscall test(void *this, const void *a2, unsigned int a3)
{
  void *v3; // ebx@1
  char *v4; // eax@3
  int v5; // [sp-8h] [bp-418h]@3
  int v6; // [sp-4h] [bp-414h]@3
  char v7[4]; // [sp+Ch] [bp-404h]@4
  char buf[1024]; // [sp+10h] [bp-400h]@3

  v3 = this;
  if ( a2 && (signed int)a3 > 0 )
  {
    *(_DWORD *)buf = 0;
    memcpy(&buf[4], a2, 4 * (a3 >> 2));
    v6 = 0;
    v5 = a3 + 4;
    v4 = buf;
    memcpy(&buf[4 * (a3 >> 2) + 4], (char *)a2 + 4 * (a3 >> 2), a3 & 3);// Looks like Copy by DWORDs, not by Bytes.
  }
  else
  {
    v6 = 0;
    *(_DWORD *)v7 = GetTickCount() / 0xA;
    v5 = 4;
    v4 = v7;
  }
  send(*(_DWORD *)v3, v4, v5, v6);
}

这里我手工修复了一下,还是没看懂。

void __thiscall test(void *this, const void *a2, unsigned int a3)
{
  void *v3; // ebx@1
  char *v4; // eax@3
  int v5; // [sp-8h] [bp-418h]@3
  int v6; // [sp-4h] [bp-414h]@3
  char v7[4]; // [sp+Ch] [bp-404h]@4
  char buf[1024]; // [sp+10h] [bp-400h]@3

  v3 = this;
  if ( a2 && (signed int)a3 > 0 )
  {
    *(_DWORD *)buf = 0;
    //Might be a swap of the 5th offset DWORD to end of the packet?
    //Or maybe it fills in the packet offsetted by the first 4 bytes?
    memcpy(&buf[4], a2, 4 * (a3 / 4)); // Looks like Copy by DWORDs, not by Bytes.
    v6 = 0;
    v5 = a3 + 4;
    v4 = buf;
    //Might be a swap of the end of the packet to the 5th offset DWORD?
    //Looks like some kind of footer to above memcpy function like to finish what the first function couldn't do?
    memcpy(&buf[4 * (a3 / 4) + 4], (char *)a2 + 4 * (a3 / 4), a3 & 3);// Looks like Copy by DWORDs, not by Bytes.
  }
  else
  {
    v6 = 0;
    *(_DWORD *)v7 = GetTickCount() / 0xA;
    v5 = 4;
    v4 = v7;
  }
  send(*(_DWORD *)v3, v4, v5, v6);
}

好吧,我再给它一些时间,这可能是正确的吗?

void __thiscall test(void *this, const void *a2, unsigned int a3)
{
  void *v3; // ebx@1
  char *v4; // eax@3
  int v5; // [sp-8h] [bp-418h]@3
  int v6; // [sp-4h] [bp-414h]@3
  char v7[4]; // [sp+Ch] [bp-404h]@4
  char buf[1024]; // [sp+10h] [bp-400h]@3

  v3 = this;
  if ( a2 && (signed int)a3 > 0 )
  {
    *(_DWORD *)buf = 0;
    memmove(&buf[4],a2,a3 - 4); 

    v6 = 0;
    v5 = a3 + 4;
    v4 = buf;
  }
  else
  {
    v6 = 0;
    *(_DWORD *)v7 = GetTickCount() / 0xA;
    v5 = 4;
    v4 = v7;
  }
  send(*(_DWORD *)v3, v4, v5, v6);
}

下面组装

.text:00408750 ; =============== S U B R O U T I N E =======================================
.text:00408750
.text:00408750
.text:00408750 ; void __thiscall test(void *this, const void *a2, unsigned int a3)
.text:00408750 test proc near
.text:00408750                                         ; CODE XREF: ServerMainLoop+5DDp
.text:00408750                                         ; ServerMainLoop+64Dp
.text:00408750
.text:00408750 var_404         = byte ptr -404h
.text:00408750 buf             = byte ptr -400h
.text:00408750 a2              = dword ptr  4
.text:00408750 a3              = dword ptr  8
.text:00408750
.text:00408750                 sub     esp, 404h
.text:00408756                 push    ebx
.text:00408757                 push    esi
.text:00408758                 mov     esi, [esp+40Ch+a2]
.text:0040875F                 push    edi
.text:00408760                 test    esi, esi
.text:00408762                 mov     ebx, ecx
.text:00408764                 jz      short loc_408799
.text:00408766                 mov     eax, [esp+410h+a3]
.text:0040876D                 test    eax, eax
.text:0040876F                 jle     short loc_408799
.text:00408771                 mov     ecx, eax
.text:00408773                 lea     edi, [esp+410h+buf+4]
.text:00408777                 mov     edx, ecx
.text:00408779                 mov     dword ptr [esp+410h+buf], 0
.text:00408781                 shr     ecx, 2
.text:00408784                 rep movsd
.text:00408786                 mov     ecx, edx
.text:00408788                 push    0
.text:0040878A                 and     ecx, 3
.text:0040878D                 add     eax, 4
.text:00408790                 push    eax
.text:00408791                 lea     eax, [esp+418h+buf]
.text:00408795                 rep movsb
.text:00408797                 jmp     short loc_4087B7
.text:00408799 ; ---------------------------------------------------------------------------
.text:00408799
.text:00408799 loc_408799:                             ; CODE XREF: test+14j
.text:00408799                                         ; test+1Fj
.text:00408799                 call    ds:GetTickCount
.text:0040879F                 mov     edx, eax
.text:004087A1                 mov     eax, 0CCCCCCCDh
.text:004087A6                 mul     edx
.text:004087A8                 shr     edx, 3
.text:004087AB                 push    0               ; flags
.text:004087AD                 mov     dword ptr [esp+414h+var_404], edx
.text:004087B1                 push    4               ; len
.text:004087B3                 lea     eax, [esp+418h+var_404]
.text:004087B7
.text:004087B7 loc_4087B7:                             ; CODE XREF: test+47j
.text:004087B7                 mov     ecx, [ebx]
.text:004087B9                 push    eax             ; buf
.text:004087BA                 push    ecx             ; s
.text:004087BB                 call    send
.text:004087C0                 pop     edi
.text:004087C1                 pop     esi
.text:004087C2                 pop     ebx
.text:004087C3                 add     esp, 404h
.text:004087C9                 retn    8
.text:004087C9 test endp
.text:004087C9
.text:004087C9 ; ---------------------------------------------------------------------------
2个回答

这段代码很简单,只是有点乱,因为它是由decompiler. 这是一个更简单的评论版本:

void __thiscall test(void *this, const void *a2, unsigned int a3)
 {
    char v7[4]; 
    int  v5 = 4;
    void *v3 = this;
    char buf[1024], *v4;

    if (a2 != NULL && (signed int)a3 > 0)
    {
      //Setting the 4 first bytes to 0. Certainly the message header !
      *(_DWORD *)buf = 0;

      /*
         Same as :
         memset(buff, 0, 4);
         buf[0] = buf[1] = buf[2] = buf[3] = 0; 
       */

      /*
         Copying the first a3 bytes of a2 into buff + 4. 
         The + 4 is to jump the 4 bytes header set to 0 previously. 
       */ 
      memcpy(buf + 4, a2, a3); //There's no point in 4 * a3 / 4;

      //Size has changed to a3 + 4 (4 bytes for the header)
      v5 += a3;

      //
      v4 = buf;
    }
    else
    {
       /* _DWORD is 4 bytes. This line converts v7 into an integer to
           copy the value of GetTickCount() / 10 byte by byte into it.  
        */
      *(_DWORD *)v7 = GetTickCount() / 10;

      //
      v4 = v7;
    }

    send(*(_DWORD *)v3, v4, v5, 0);
 }

从这里我会说,如果没有消息(a2 == NULL 或 a3 <= 0),则此例程发送标头设置为 GetTickCount() / 10 的数据包,否则它将消息标头设置为 0,消息本身为a2 并发送数据包。

您提供的原始版本的问题是它正在执行 4 个元素 x 4 的副本......这就是为什么它memcpy被分成两部分。第一个复制元素 4 * (a3 / 4)(如果 a3 = 19 那么它会复制 16 个元素),第二个复制剩余的 a3 % 4 (a3 % 4 <==> a3 & (4 - 1) <==> a3 & 3) 元素(如果 a3 = 19 那么它会复制 3)。

它根本不是 memset,它只是一个 memcpy。逐个复制 dword 比逐个字节复制更有效,因为每个副本可以使用 32 位数据总线(至少如果数据是字对齐的)。所以编译器的作用是:

  • 通过将 cx 右移 2 位,将字节数除以 4
  • 使用 movsd 复制适当数量的 4 字节双字
  • 计算剩余的字节数(通过 AND-ing cx 与 3)
  • 使用 movsb 逐个复制这些字节

更令人困惑的是,编译器已经为下一个函数调用准备了参数,同时仍在执行 memcpy-the push 0add eax, 4并且push eax属于jmp.