反向-对象到源代码-帮助理解一段代码

问题描述 投票:0回答:3

首先,我要澄清我是一个学生,我对C,C ++和汇编器尚不具备广泛的知识,因此,我正在竭尽全力来理解它。

我有一段来自Intel x86-32位处理器的汇编代码。

我的目标是将其转换为源代码。

0x80483dc <main>:    push       ebp        
0x80483dd <main+1>:  mov        ebp,esp     
0x80483df <main+3>:  sub        esp,0x10
0x80483e2 <main+6>:  mov        DWORD PTR [ebp-0x8],0x80484d0   
0x80483e9 <main+13>: lea        eax,[ebp-0x8]   
0x80483ec <main+16>: mov        DWORD PTR [ebp-0x4],eax     
0x80483ef <main+19>: mov        eax,DWORD PTR [ebp-0x4]     
0x80483f2 <main+22>: mov        edx,DWORD PTR [eax+0xc]
0x80483f5 <main+25>: mov        eax,DWORD PTR [ebp-0x4]         
0x80483f8 <main+28>: movzx      eax,WORD PTR [eax+0x10]
0x80483fc <main+32>: cwde
0x80483fd <main+33>: add        edx, eax
0x80483ff <main+35>: mov        eax,DWORD PTR [ebp-0x4]         
0x8048402 <main+38>: mov        DWORD PTR [eax+0xc],edx     
0x8048405 <main+41>: mov        eax,DWORD PTR [ebp-0x4]     
0x8048408 <main+44>: movzx      eax,BYTE PTR [eax]
0x804840b <main+47>: cmp        al,0x4f     
0x804840d <main+49>: jne        0x8048419 <main+61> 
0x804840f <main+51>: mov        eax,DWORD PTR [ebp-0x4] 
0x8048412 <main+54>: movzx      eax,BYTE PTR [eax] 
0x8048415 <main+57>: cmp        al,0x4b 
0x8048417 <main+59>: je         0x804842d <main+81> 
0x8048419 <main+61>: mov        eax,DWORD PTR [ebp-0x4] 
0x804841c <main+64>: mov        eax,DWORD PTR [eax+0xc]
0x804841f <main+67>: mov        edx, eax
0x8048421 <main+69>: and        edx,0xf0f0f0f
0x8048427 <main+75>: mov        eax,DWORD PTR [ebp-0x4] 
0x804842a <main+78>: mov        DWORD PTR [eax+0x4],edx
0x804842d <main+81>: mov        eax,0x0
0x8048432 <main+86>: leave
0x8048433 <main+87>: ret

这是我从代码中了解的内容:

有4个变量:

a = [ebp-0x8] ebp
b = [ebp-0x4] eax
c = [eax + 0xc] edx
d = [eax + 0x10] eax

值:

0x4 = 4
0x8 = 8
0xc = 12
0x10 = 16
0x4b = 75
0x4f = 79

类型:

char (8 bits) = 1 BYTE
short (16 bits) = WORD
int (32 bit) = DWORD
long (32 bits) = DWORD
long long (32 bit) = DWORD

这是我能够创建的:

#include <stdio.h>
int main (void)
{
   int a = 0x80484d0;
   int b
   short c;
   int d;

   c + b?
if (79 <= al) {
instructions
} else {
instructions
}

   return 0
}

但是我被困住了。我也无法理解句子“ cmp al ..”与什么相比,什么是“ al”?

有人可以向我提供有关说明如何工作的解释吗?

c assembly x86 reverse-engineering
3个回答
1
投票

x86汇编语言具有悠久的历史,并且在很大程度上保持了兼容性。我们需要回到8086/8088芯片开始的故事。它们是16位处理器,这意味着它们的寄存器的字长为16位。通用寄存器分别命名为AX,BX,CX和DX。 8086的指令可操纵这些寄存器的高8位和低8位,然后分别命名为AH,AL,BH,BL,CH,CL,DH和DL。 This Wikipedia page描述了这一点,请看一看。

这些寄存器的32位版本前面带有E:EAX,EBX,ECX等

您提到的特定指令,例如cmp al,0x4f正在将AX寄存器的低字节与0x4f进行比较。比较实际上与减法相同,但不保存结果,仅设置标志。

对于8086指令集,为there is a nice reference here。您的程序是32位代码,因此您至少需要80386指令参考。


1
投票

您的组装完全疯了。这大致相当于C:

int main() {
    int i = 0x80484d0; // in ebp-8
    int *p = &i; // in ebp-4
    p[3] += (short)p[4]; // add argc to the return address(!)
    if((char)*p != 0x4f || (char)*p != 0x4b) // always true because of || instead of &&
        p[1] = p[3] & 0xf0f0f0f; // note that p[1] is p
    return 0;
}

应该立即很明显,这是糟糕透顶的代码,几乎可以肯定不会满足程序员的意图。


0
投票

您已经分析了变量,这是一个很好的起点。您应尝试在开始时为其添加类型注释,大小,并在用作指针(例如b)时使用指向哪种类型/大小的指针。

知道[ebp-4]b,我可能会如下更新变量图:

c = [b + 0xc]
d = [b + 0x10]
e = [b + 0], size = byte

要分析的另一件事是控制流程。对于大多数指令,控制流是顺序的,但是某些指令有意地对其进行了更改。广义上讲,当PC向前移动时,它将跳过一些代码,而当PC向后移动时,它将重复它已经运行的某些代码。跳过代码用于构造if-then,if-then-else和中断循环的语句。回跳用于继续循环。

[某些指令,称为条件分支,在某些动态条件为true时:向前(或向后)跳过,在为false时,将简单的顺序前进到下一条指令(有时称为条件分支失败)。

这里的控制顺序:

...
0x8048405 <main+41>: mov        eax,DWORD PTR [ebp-0x4]    b
0x8048408 <main+44>: movzx      eax,BYTE PTR [eax]         b->e

0x804840b <main+47>: cmp        al,0x4f                    b->e <=> 'O'
0x804840d <main+49>: jne        0x8048419 <main+61>        b->e != 'O'  skip to 61

** we know that the letter, a->e, must be 'O' here

0x804840f <main+51>: mov        eax,DWORD PTR [ebp-0x4]    b      
0x8048412 <main+54>: movzx      eax,BYTE PTR [eax]         b->e

0x8048415 <main+57>: cmp        al,0x4b                    b->e <=> 'K'
0x8048417 <main+59>: je         0x804842d <main+81>        b->e == 'K' skip to 81

** we know that the letter, a->e must not be 'K' here if we fall thru the above je 

** this line can be reached by taken branch jne or by fall thru je
0x8048419 <main+61>: mov        eax,DWORD PTR [ebp-0x4]    ******
...

控制流到达标记的最后一行,我们知道该字母不是'O'或不是'K'。

jne指令用于跳过另一个测试的结构是短路||运算符。因此,控件构造为:

if ( a->e != 'O' || a->e != 'K' ) {
    then-part
}

由于这两个条件分支是函数中唯一的流控制修改,因此if中没有其他部分,也没有循环或其他if。


此代码似乎有一个小问题。

如果该值不为'O',则从第一项测试开始,然后触发部分。但是,如果我们进行第二次测试,我们已经知道字母为'O',那么对其进行'K'测试是很愚蠢的,并且是正确的('O'不是'K')。

因此,此if-then将始终触发。

或者效率很低,或者有一个错误,也许是(大概)字符串中的下一个字母应该测试'K'而不是完全相同的字母。

© www.soinside.com 2019 - 2024. All rights reserved.