试图了解负责发送数据包的功能。
我不知道它是整数数组还是什么?或某些未在Hex-Rays中正确呈现的内联函数

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

如果语句应发送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 ; ---------------------------------------------------------------------------


评论

常见的优化技术是将单个缓冲区副本分为两个步骤,一个由DWORD组成,一个由BYTE组成。 Hex-Rays并非万无一失,它可能被循环中间的一些指令所欺骗。由于您没有提供反汇编的代码,因此很难确定,但是memcpy比memset更有可能。

大会现在也张贴在主题上。我遇到过类似的问题,其中memcpy原来是memset的卧底,而>> 2总是显示出它,因此我认为它可能是memset的变体。 rep movsd和rep movsb必须是它们。

通过反汇编,这很容易-rep movs *将数据从一个缓冲区复制到另一个缓冲区,例如memcpy。 rep stos *将常量数据写入缓冲区,就像memset一样。

那么两者都可能只是一个功能吗?我真的不明白为什么我需要做2个操作,就像将所有内容向右移动4个字节一样简单。 (我认为是这样)我有一个具有1024个字节的缓冲区,没有其他变量。老实说,除非我有一个数学大怪兽对我来说太难了,否则我不会幸运的。

可能是记忆记忆,然后记忆记忆。

#1 楼

这段代码很简单,只是有点混乱,因为它是由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);
 }


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

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

评论


消息头是数据包中前4个字节的一部分吗?因此,它的使用方式可能是GetTickCount,否则是填充零来表示消息?

–user3435580
2014-4-28 13:09



感谢您花时间进行重构。数据包的前4个字节会丢失吗?

–user3435580
2014年4月28日在13:15



别客气。

– Yaspr
14年4月28日在13:18

哦,我发现没有任何字节丢失,只是在前面填充了4个空字节。

–user3435580
2014年4月28日在13:21

正是:D那只是标题。我确定应该接收消息的例程会检查标头,以了解它是否应该同步或读取消息正文。

– Yaspr
2014年4月28日在13:23

#2 楼

它根本不是记忆载体,只是记忆。逐字复制dword比逐字节复制更有效率,因为每个副本可以使用32位数据总线(至少在数据按字对齐的情况下)。因此,编译器要做的是:


将字节数除以4,通过将cx右移2位
,复制适当的4字节双字数,使用movsd
计算剩余的字节数(通过将cx与3进行“与”运算)
使用movsb

逐个复制这些字节。
是编译器已经在准备执行下一个memcpy的同时为下一个函数调用准备了参数-push 0add eax, 4push eax属于jmp之后的send调用。

评论


好的信息会派上用场,抱歉,我不能将两个答案都当作最佳答案。

–user3435580
2014年4月28日13:20在

好吧,我确实同意以下事实:用DWORD复制DWORD比通过BYTE复制BYTE更快。但是,有时,如果编译器正确完成其工作以及架构可以处理,则将使用向量化版本(memcpy_SSE42,...)。这就是使用GCC(用于静态和动态链接的二进制文件)时通常发生的情况。

– Yaspr
2014年4月28日在13:32

同意,但是我想解释为什么OP发布的特定代码看起来像它;)

–贡特拉姆·布洛姆(Guntram Blohm)
2014年4月28日在13:36