从汇编中逆向工程C源代码

cpo*_*el2 5 c assembly reverse-engineering

我想知道是否有人可以帮助我解决我在学校参加的入门讲习班中的一个讲座幻灯片时遇到的问题.我遇到的问题是不了解程序集,它是如何根据程序集对C源代码进行排序的.我将发布我正在讨论的片段,也许我会更清楚地谈论我的内容.

C来源:

int arith(int x, int y, int z)
{ 
   int t1 = x+y;
   int t2 = z+t1;
   int t3 = x+4;
   int t4 = y * 48; 
   int t5 = t3 + t4;
   int rval = t2 * t5;
   return rval;
}
Run Code Online (Sandbox Code Playgroud)

大会给出:

arith:
pushl %ebp
movl %esp,%ebp

movl 8(%ebp),%eax
movl 12(%ebp),%edx
leal (%edx,%eax),%ecx
leal (%edx,%edx,2),%edx
sall $4,%edx
addl 16(%ebp),%ecx
leal 4(%edx,%eax),%eax
imull %ecx,%eax

movl %ebp,%esp
popl %ebp
ret
Run Code Online (Sandbox Code Playgroud)

我只是觉得我应该能够辨别出例如在汇编代码之后的汇编中第二行(在源代码中)添加z + t1(z + x + y)y * 48的例子,或者例如那个x + 4是第3行,当它在汇编时它本身甚至不是一行,它与最后一个leal声明混合在一起.当我有源代码时对我有意义但是我应该能够重现测试的源代码并且我确实理解编译器优化了一些东西但是如果有人有办法考虑可以帮助我的逆向工程如果他们能够引导我完成思考过程,我将不胜感激.

谢谢.

Aus*_*oke 9

我已经分解了反汇编,以显示如何从C源生成程序集.

8(%ebp)= x,12(%ebp)= y,16(%ebp)=z

arith:
Run Code Online (Sandbox Code Playgroud)

创建堆栈框架:

pushl %ebp
movl %esp,%ebp
Run Code Online (Sandbox Code Playgroud)


移动xeax,yedx:

movl 8(%ebp),%eax
movl 12(%ebp),%edx
Run Code Online (Sandbox Code Playgroud)


t1 = x + y.leal(加载有效地址)将添加edxeax,并将t1ecx:

leal (%edx,%eax),%ecx
Run Code Online (Sandbox Code Playgroud)


int t4 = y * 48;在下面的两个步骤中,乘以3,然后乘以16. t4最终将在edx:

乘以edx2,并添加edx到结果,即.edx = edx * 3:

leal (%edx,%edx,2),%edx
Run Code Online (Sandbox Code Playgroud)

向左移4位,即.乘以16:

sall $4,%edx
Run Code Online (Sandbox Code Playgroud)


int t2 = z+t1;.ecx 最初持有t1,z16(%ebp)指令结束时ecx将持有t2:

addl 16(%ebp),%ecx
Run Code Online (Sandbox Code Playgroud)


int t5 = t3 + t4;.t3很简单x + 4,而不是计算和存储t3,表达式t3是内联的.这条指令必不可少(x+4) + t4,与t3+ 相同t4.它添加edx(t4)和eax(x),并添加4作为偏移量来实现该结果.

leal 4(%edx,%eax),%eax
Run Code Online (Sandbox Code Playgroud)

int rval = t2 * t5;相当直截了当; ecx代表t2eax代表t5.返回值通过传递给调用者eax.

imull %ecx,%eax
Run Code Online (Sandbox Code Playgroud)


销毁堆栈框架并恢复espebp:

movl %ebp,%esp
popl %ebp
Run Code Online (Sandbox Code Playgroud)


从例行程序返回:

ret
Run Code Online (Sandbox Code Playgroud)


从这个例子中你可以看到结果是一样的,但结构有点不同.很可能这个代码是通过某种优化编译的,或者有人自己编写这样的代码来证明这一点.

正如其他人所说,你无法从反汇编中完全回到源头.这取决于阅读程序集的人的解释,以提出等效的C代码.


为了帮助学习汇编和理解C程序的反汇编,您可以在Linux上执行以下操作:

编译调试信息(-g),它将嵌入源:

gcc -c -g arith.c
Run Code Online (Sandbox Code Playgroud)

如果您使用的是64位计算机,则可以告诉编译器使用该-m32标志创建一个32位二进制文​​件(我在下面的示例中这样做).


使用objdump转储目标文件,其源交错:

objdump -d -S arith.o
Run Code Online (Sandbox Code Playgroud)

-d=反汇编,-S=显示源.-M intel-mnemonic如果您喜欢使用英特尔ASM语法,则可以添加以使用您的示例使用的AT&T语法.

输出:

arith.o:     file format elf32-i386


Disassembly of section .text:

00000000 <arith>:
int arith(int x, int y, int z)
{ 
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 20                sub    $0x20,%esp
   int t1 = x+y;
   6:   8b 45 0c                mov    0xc(%ebp),%eax
   9:   8b 55 08                mov    0x8(%ebp),%edx
   c:   01 d0                   add    %edx,%eax
   e:   89 45 fc                mov    %eax,-0x4(%ebp)
   int t2 = z+t1;
  11:   8b 45 fc                mov    -0x4(%ebp),%eax
  14:   8b 55 10                mov    0x10(%ebp),%edx
  17:   01 d0                   add    %edx,%eax
  19:   89 45 f8                mov    %eax,-0x8(%ebp)
   int t3 = x+4;
  1c:   8b 45 08                mov    0x8(%ebp),%eax
  1f:   83 c0 04                add    $0x4,%eax
  22:   89 45 f4                mov    %eax,-0xc(%ebp)
   int t4 = y * 48; 
  25:   8b 55 0c                mov    0xc(%ebp),%edx
  28:   89 d0                   mov    %edx,%eax
  2a:   01 c0                   add    %eax,%eax
  2c:   01 d0                   add    %edx,%eax
  2e:   c1 e0 04                shl    $0x4,%eax
  31:   89 45 f0                mov    %eax,-0x10(%ebp)
   int t5 = t3 + t4;
  34:   8b 45 f0                mov    -0x10(%ebp),%eax
  37:   8b 55 f4                mov    -0xc(%ebp),%edx
  3a:   01 d0                   add    %edx,%eax
  3c:   89 45 ec                mov    %eax,-0x14(%ebp)
   int rval = t2 * t5;
  3f:   8b 45 f8                mov    -0x8(%ebp),%eax
  42:   0f af 45 ec             imul   -0x14(%ebp),%eax
  46:   89 45 e8                mov    %eax,-0x18(%ebp)
   return rval;
  49:   8b 45 e8                mov    -0x18(%ebp),%eax
}
  4c:   c9                      leave  
  4d:   c3                      ret
Run Code Online (Sandbox Code Playgroud)

如您所见,没有优化,编译器会生成比您拥有的示例更大的二进制文件.你可以玩的是和编译时添加一个编译器优化标志(即-O1,-O2,-O3).优化级别越高,反汇编看起来就越抽象.

例如,只使用1级优化(gcc -c -g -O1 -m32 arith.c1),生成的汇编代码要短得多:

00000000 <arith>:
int arith(int x, int y, int z)
{ 
   0:   8b 4c 24 04             mov    0x4(%esp),%ecx
   4:   8b 54 24 08             mov    0x8(%esp),%edx
   int t1 = x+y;
   8:   8d 04 11                lea    (%ecx,%edx,1),%eax
   int t2 = z+t1;
   b:   03 44 24 0c             add    0xc(%esp),%eax
   int t3 = x+4;
   int t4 = y * 48; 
   f:   8d 14 52                lea    (%edx,%edx,2),%edx
  12:   c1 e2 04                shl    $0x4,%edx
   int t5 = t3 + t4;
  15:   8d 54 11 04             lea    0x4(%ecx,%edx,1),%edx
   int rval = t2 * t5;
  19:   0f af c2                imul   %edx,%eax
   return rval;
}
  1c:   c3                      ret
Run Code Online (Sandbox Code Playgroud)


Cod*_*aos 6

您无法重现原始来源,您只能重现等效来源.

在您的情况下,计算t2可以出现在t1之前和之后的任何地方retval.

源可能只是一个表达式:

return (x+y+z) * ((x+4) + (y * 48));
Run Code Online (Sandbox Code Playgroud)


nin*_*alj 5

逆向工程时,你不关心原始源代码,你关心它的作用.副作用是你看到代码的作用,而不是程序员想要的代码.