[英]Reverse engineering C-source code from assembly
我想知道是否有人可以帮助我解决我在学校参加的入门讲习班中的一个讲座幻灯片时遇到的问题。 我遇到的问题是不了解程序集,它是如何根据程序集对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;
}
大会给出:
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
我只是很困惑,我应该能够辨别出例如在第二行(在源代码中)中添加z + t1
( z + x + y
)时,在汇编之后它出现在汇编代码中的y * 48
或者例如x + 4
是汇编时的第3行,它本身甚至不是一行,它与最后一个leal
语句混合在一起。 当我有源代码时对我有意义但是我应该能够重现测试的源代码并且我确实理解编译器优化了一些东西但是如果有人有办法考虑可以帮助我的逆向工程如果他们能够引导我完成思考过程,我将不胜感激。
谢谢。
我已经分解了反汇编,以显示如何从C源生成程序集。
8(%ebp)
= x
, 12(%ebp)
= y
, 16(%ebp)
= z
arith:
创建堆栈框架:
pushl %ebp
movl %esp,%ebp
x
移动到eax
, y
转换为edx
:
movl 8(%ebp),%eax movl 12(%ebp),%edx
t1 = x + y
。
leal
(加载有效地址)将添加edx
和eax
, t1
将在ecx
:
leal (%edx,%eax),%ecx
int t4 = y * 48;
在下面的两个步骤中,乘以3,再乘以16. t4
最终将在edx
:
将edx
乘以2,并将edx
添加到结果中,即。 edx = edx * 3
:
leal (%edx,%edx,2),%edx
向左移4位,即。 乘以16:
sall $4,%edx
int t2 = z+t1;
。
ecx
最初持有t1
, z
为16(%ebp)
,在指令结束时ecx
将持有t2
:
addl 16(%ebp),%ecx
int t5 = t3 + t4;
。
t3
只是x + 4
,而非计算并存储t3
,表达t3
被内嵌放置。
该指令必不可少(x+4) + t4
,与t3
+ t4
相同。
它添加了edx
( t4
)和eax
( x
),并添加了4作为偏移量来实现该结果。
leal 4(%edx,%eax),%eax
int rval = t2 * t5;
相当直截了当; ecx
代表t2
, eax
代表t5
。 返回值通过eax
传递回调用者。
imull %ecx,%eax
esp
和ebp
:
movl %ebp,%esp popl %ebp
ret
正如其他人所说,你无法从反汇编中完全回到源头。 这取决于阅读程序集的人的解释,以提出等效的C代码。
编译调试信息( -g
),它将嵌入源:
gcc -c -g arith.c
如果您使用的是64位计算机,则可以告诉编译器使用-m32
标志创建一个32位二进制文件(我在下面的示例中这样做了)。
使用objdump转储目标文件,其源交错:
gcc -c -g arith.c
-d
=反汇编, -S
=显示源。 您可以添加-M intel-mnemonic
以使用Intel ASM语法,如果您更喜欢使用您的示例使用的AT&T语法。
输出:
objdump -d -S arith.o
如您所见,没有优化,编译器会生成比您拥有的示例更大的二进制文件。 您可以使用它并在编译时添加编译器优化标志(即-O1
, -O2
, -O3
)。 优化级别越高,反汇编看起来就越抽象。
例如,只有1级优化( gcc -c -g -O1 -m32 arith.c1
),生成的汇编代码要短得多:
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
您无法重现原始来源,您只能重现等效来源。
在您的情况下, t2
的计算可以出现在t1
之后和retval
之前的任何地方。
源可能只是一个表达式:
return (x+y+z) * ((x+4) + (y * 48));
逆向工程时,你不关心原始源代码,你关心它的作用。 副作用是你看到代码的作用,而不是程序员想要的代码。
反编译并不是完全可以实现的:当从源代码(其中注释和名称给出了原始程序员的意图的线索)到二进制机器代码(其中指令将由处理器执行)时,存在一些知识损失。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.