优化与未优化的代码比较

逆向工程 x86 英特尔
2021-07-07 16:26:42

在阅读了大量博客文章、论坛和观看教程后,我想我将开始学习以旧时尚的方式逆向软件。创建简单的 C 文件并查看它们的反汇编。在我寻求真正理解逆向的过程中,我认为比较优化和未优化的代码也是有益的。在查看时,我发现了几行似乎什么也没做的代码。

如果有人可以解释未优化代码中的“mov”[es] 正在做什么,我会很高兴。所有这些都是使用 Hopper v4 拆卸的。

代码:

 #include <stdio.h>

 int main(int arg, char** arg) {
    printf("Hello World!\n");
    return 0;
 }

未优化的代码 (gcc -m32):

; Variables:
        ;    arg_4: 12
        ;    arg_0: 8
        ;    var_4: -4
        ;    var_8: -8
        ;    var_C: -12
        ;    var_10: -16
        ;    var_18: -24
push       ebp
mov        ebp, esp
sub        esp, 0x18
call       _main+11
pop        eax                            ; CODE XREF=_main+6

-- What purpose do these moves serve? --
mov        ecx, dword [ebp+arg_4]
mov        edx, dword [ebp+arg_0]
--                                    --
lea        eax, dword [eax-0x1f5b+0x1fa6] ; "Hello World!\\n"
-- And what do these moves also serve? --
mov        dword [ebp+var_4], 0x0
mov        dword [ebp+var_8], edx
mov        dword [ebp+var_C], ecx
--                                    --
mov        dword [esp+0x18+var_18], eax  ; method imp___symbol_stub__printf
call       imp___symbol_stub__printf
xor        ecx, ecx                           
mov        dword [ebp+var_10], eax
mov        eax, ecx                                    
add        esp, 0x18                           
pop        ebp
ret

优化代码 (gcc -m32 -O3):

push       ebp
mov        ebp, esp
sub        esp, 0x8
call       _main+11
pop        eax                             ; CODE XREF=_main+6
lea        eax, dword [eax-0x1f6b+0x1f9e]  ; "Hello World!"
mov        dword [esp+0x8+var_8], eax      ; "%s" for imp___symbol_stub__puts
call       imp___symbol_stub__puts
xor        eax, eax
add        esp, 0x8
pop        eep
ret
1个回答

首先,我建议您阅读有关 i386 和 amd64 的 SystemV ABI。您可以在此处找到文档:

这些文档尽可能精确地定义了编译器编码器应该如何将一些 C/C++ 代码转换为类 Unix 系统的 i386/amd64 汇编代码。

它们是极其重要的文件,您应该尽可能多地参考它们,因为它们包含您大部分问题的大量答案。

现在,回到您最初的问题,在您的情况下,两个代码之间的主要区别在于gcc我们将看到优化了内存中的数据移动。

第一个代码片段

-- What purpose do these moves serve? --
mov        ecx, dword [ebp+arg_4]
mov        edx, dword [ebp+arg_0]
--                                    --

在这里,ecxedx加载了main(很可能argcargv的参数

请注意,函数中的任何一个argcargv没有任何用处main()但是,编译器不知道它,因为它没有在这个优化级别执行死代码/死变量分析。当然,当进行适当的分析时,此代码将被删除。

第二个代码片段

-- And what do these moves also serve? --
mov        dword [ebp+var_4], 0x0
mov        dword [ebp+var_8], edx
mov        dword [ebp+var_C], ecx
--                                    --

在这里,程序似乎将参数存储在本地内存框架中(如下ebp)。请注意,参数在上面ebp,自动变量在下面(我们说函数范围内的变量的自动变量)。

当然,这些数据移动是完全没有必要的,但编译器只是应用一个默认模板来启动一个函数,该函数在本地内存堆栈帧中传输参数的副本。而且,再一次,当编译器意识到这些变量没有用时,这些动作就会消失。