C 理解将对象反编译为源代码

C 理解将对象反编译为源代码,c,assembly,x86,reverse-engineering,C,Assembly,X86,Reverse Engineering,首先,我是一名学生,我还没有关于C、C++和汇编程序的广泛知识,所以我正在竭尽全力去理解它 我有一段来自英特尔x86-32位处理器的汇编代码 我的目标是将其转换为源代码 0x80483dc <main>: push ebp 0x80483dd <main+1>: mov ebp,esp 0x80483df <main+3>: sub esp,0x10 0x80483e2 <mai

首先,我是一名学生,我还没有关于C、C++和汇编程序的广泛知识,所以我正在竭尽全力去理解它

我有一段来自英特尔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
价值观:

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
}
这对你有什么意义吗?有人能给我解释一下吗? 有人真的这样编程吗


非常感谢

x86汇编语言继承了悠久的历史,并且基本上保持了兼容性。我们需要回到故事开始的8086/8088芯片。这些是16位处理器,这意味着它们的寄存器的字大小为16位。通用寄存器被命名为AX、BX、CX和DX。8086具有操作这些寄存器的上下8位部分的指令,这些寄存器被命名为AH、AL、BH、BL、CH、CL、DH和DL。描述这个,请看一看

这些寄存器的32位版本前面有一个
E
:EAX、EBX、ECX等

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


对于8086指令集。您的程序是32位代码,因此您至少需要一个80386指令参考。

您已经分析了变量,这是一个很好的起点。您应该尝试在开始时向它们添加类型注释、大小,以及当用作指针(如
b
)时,指向什么类型/大小的指针

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

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

另一个需要分析的是控制流。对于大多数指令来说,控制流是顺序的,但某些指令故意改变它。广义地说,当电脑向前移动时,它会跳过一些代码,而当电脑向后移动时,它会重复一些已经运行过的代码。跳过代码用于构造if-then、if-then-else和断开循环的语句。跳回用于继续循环

有些指令称为条件分支,在某些动态条件为真时:向前跳(或向后跳),在为假时,执行简单的顺序前进到下一条指令(有时称为条件分支失效)

此处的控制顺序为:

...
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]    ******
...
由于这两个条件分支是函数中唯一的流控制修改,因此if没有其他部分,也没有循环或其他if


此代码似乎有一个小问题

如果该值不是“O”,则部件将从第一次测试开始点火。然而,如果我们达到第二个测试,我们已经知道字母是“O”,所以测试字母“K”是愚蠢的,并且是正确的(“O”不是“K”)

因此,这个if-then将始终启动


它要么效率很低,要么存在一个bug,可能是字符串中的下一个字母(可能是)应该测试“K”而不是同一个字母。

您的程序集完全疯了。这大致相当于C:

intmain(){
int i=0x80484d0;//在ebp-8中
int*p=&i;//在ebp-4中
p[3]+=(短)p[4];//将argc添加到返回地址(!)
if((char)*p!=0x4f | | |(char)*p!=0x4b)//由于| |而不是&&
p[1]=p[3]&0xf0f;//注意p[1]是p
返回0;
}

很明显,这是非常糟糕的代码,几乎肯定不会达到程序员的目的。

long-long(32位)=DWORD
是不正确的。C++标准要求长Lo>代码>为64位。(嗯,不完全是这样,但它必须支持的最大值是需要64位来存储它)如果您需要询问
al
是什么,那么您需要对处理器进行更深入的研究,然后才能理解反汇编或汇编列表。
al
寄存器是
eax
寄存器的最低有效8位,而
ax
eax
的l.s.16位。类似于
bl
cl
dl
bx
cx
dx
。另外,
ah
eax
的下8位,因此
ah
al
一起构成
ax
。。请看标题为“基本程序执行寄存器”的部分@PeterCordes我认为你没有大脑放屁。我觉得那个集会有点奇怪。特别是,在
0x80483f2:mov-edx,DWORD-PTR[eax+0xc]
之后,在我看来,返回地址将在
edx
中结束,这似乎是不可取的,因为代码会继续处理它。除非这一章是关于模糊代码,否则我会写信给作者。代码中有一个很小的逻辑错误,而且这本书没有出现在Google上(你给了我们正确的名字吗?),所以我倾向于相信这本书有一个关于控制流模糊处理的精彩章节,或者是由不应该的人写的。
[ebp-4]
b
-不是
b
一个指针变量,是否持有ebp-8的地址?因此,从
[eax+0xc]
开始的偏移是索引到函数自己的堆栈帧中,同时
mov-edx[eax+0xc]
加载函数的返回地址。(Joseph Sible和我;在我们看来这很疯狂。我想知道原始代码是否在本地数组的边界之外建立索引,如
char*arr[1]={“hello”};
?)我仍然很困惑。。。但是非常感谢你提供的信息。是否有任何方法可以自动将目标代码转换为源代码,即使它与源代码非常相似。。。但不完全是吗?感谢
0x80484d0
可能是一个地址
c = [b + 0xc]
d = [b + 0x10]
e = [b + 0], size = byte
...
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]    ******
...
if ( a->e != 'O' || a->e != 'K' ) {
    then-part
}