将此 x86 ASM 转换为 C?

逆向工程 部件 x86 C
2021-07-03 00:35:40

这个 32 位 x86 程序集如何用 C 编写?

loc_536FB0:
mov cl, [eax]
cmp cl, ' '
jb short loc_536FBC
cmp cl, ','
jnz short loc_536FBF

loc_536FBC:
mov byte ptr [eax], ' '

loc_536FBF
mov cl, [eax+1]
inc eax
test cl, cl
jnz short loc_536FB0

我已经发现这是一个 for 循环,在退出之前循环了 23 次。

4个回答

手动反编译这样的小片段并不太难。让我们试试吧。

您已经发现它cl包含一个字符,这意味着eax它的读取位置是一个指向字符数组的指针。让我们称之为p现在,让我们对每个汇编语句进行愚蠢的 C 语言翻译:

l1:            ; l1:
mov cl, [eax]  ;   cl = *p;
cmp cl, ' '    ;   if ( cl < ' ' )
jb short l2    ;     goto l2
cmp cl, ','    ;   if ( cl != ',' )
jnz short l3   ;     goto l3

l2:                      ; l2:
mov byte ptr [eax], ' '  ;   *p = ' '

l3:                  ; l3:
mov cl, [eax+1]      ; cl = *(p+1)
inc eax              ; p = p + 1
test cl, cl          ; if ( cl != 0 )
jnz short l1         ;   goto l1

并清理:

l1:               
  cl = *p;
  if ( cl < ' ' ) 
    goto l2;
  if ( cl != ',' )
    goto l3;       
l2:       
  *p = ' ';
l3:           
  cl = *(p+1);
  p = p + 1;     
  if ( cl != 0 )
    goto l1;

现在,让我们看看第二个if. 它具有以下形式:

if ( condition )
  goto end_of_if;
  <if body>
end_of_if:

这是我们如何摆脱goto

if ( !condition )
{
  <if body>
}

将其应用于我们的代码段:

l1:               
  cl = *p;
  if ( cl < ' ' ) 
    goto l2;
  if ( cl == ',' )  {
l2:       
    *p = ' ';
  }
  cl = *(p+1);
  p = p + 1;     
  if ( cl != 0 )
    goto l1;

现在,我们怎样才能摆脱goto l2如果您仔细查看它,您会发现l2如果或 中的任一个 ,则主体 at will 将被执行因此,我们可以将两个条件与逻辑 OR ( )结合起来cl < ' ' cl == ','||

l1:               
  cl = *p;
  if ( cl < ' ' || cl == ',' ) {
    *p = ' ';
  }
  cl = *(p+1);
  p = p + 1;     
  if ( cl != 0 )
    goto l1;

现在我们还goto剩下一个我们有:1) 语句块开头的标签 2) 在块的末尾检查 3) 如果检查成功,则转到块的开头。这是一个典型的do-while循环模式,我们可以很容易地转换它:

do {
  cl = *p;
  if ( cl < ' ' || cl == ',' ) {
    *p = ' ';
  }
  cl = *(p+1);
  p = p + 1;     
} while ( cl != 0 )

现在代码几乎漂亮了,但我们可以通过替换等效语句来进一步压缩它:

do {
  if ( *p < ' ' || *p == ',' )
    *p = ' ';
  cl = *++p;
} while ( cl != 0 )

最后,最后一个赋值可以移动到条件中:

do {
  if ( *p < ' ' || *p == ',' )
    *p = ' ';
} while ( *++p != 0 )

现在代码在做什么很明显:它遍历字符串,并用空格替换所有特殊字符(那些代码小于 0x20 又名空格的字符)和逗号。

嗯,特别是为此,发明了Hex-Rays Decompiler它将 ASM 代码反编译为伪 C,然后您可以编写基于 C 的汇编代码逻辑。

这是它在源代码中的样子。Fastcall 替代了编译器在优化时使用的自定义叶约定。

void __fastcall __forceinline RemoveControlChars(char* szInput) {
    int i;
    for (i = 0; i < 23 && *szInput; ++i, ++szInput) {
        if (*szInput < ' ' || *szInput == ',')
            *szInput = ' ';
    }
}

您可以通过命令radare2使用r2dec插件pdda

[0x08048060]> pdda
; assembly                               | /* r2dec pseudo code output */
                                         | /* ret @ 0x8048060 */
                                         | #include <stdint.h>
                                         |  
; (fcn) entry0 ()                        | int32_t entry0 (void) {
                                         |     do {
                                         |         /* [01] -r-x section size 23 named .text */
0x08048060 mov cl, byte [eax]            |         cl = *(eax);
0x08048062 cmp cl, 0x20                  |         
                                         |         if (cl >= 0x20) {
0x08048065 jb 0x804806c                  |             
0x08048067 cmp cl, 0x2c                  |             
                                         |             if (cl != 0x2c) {
0x0804806a jne 0x804806f                 |                 goto label_0;
                                         |             }
                                         |         }
0x0804806c mov byte [eax], 0x20          |         *(eax) = 0x20;
                                         | label_0:
0x0804806f mov cl, byte [eax + 1]        |         cl = *((eax + 1));
0x08048072 inc eax                       |         eax++;
0x08048073 test cl, cl                   |         
0x08048075 jne 0x8048060                 |         
                                         |     } while (cl != 0);
                                         | }
[0x08048060]>