2.1.2 GCC-x86

现在我们尝试同样的C/C++代码在linux中的GCC 4.4.1编译

gcc 1.c -o 1

下一步,在IDA反汇编的帮助下,我们看看main()函数是如何被创建的。

(IDA与MSVC一样,也是显示Intel语法)。

我也可以是GCC生成Intel语法的汇编代码,添加参数-S -masm=intel

汇编代码:

  1. main proc near
  2. var_10 = dword ptr -10h
  3. push ebp
  4. mov ebp, esp
  5. and esp, 0FFFFFFF0h
  6. sub esp, 10h
  7. mov eax, offset aHelloWorld ;` `"hello, world"
  8. mov [esp+10h+var_10], eax
  9. call _printf
  10. mov eax, 0
  11. leave
  12. retn
  13. main endp

结果几乎是相同的,"hello,world"字符串地址(保存在data段的)一开始保存在EAX寄存器当中,然后保存到栈当中。

同样的在函数开始我们看到了

AND ESP, 0FFFFFFF0h

这条指令以16字节边界大小对齐ESP寄存器,这使得所有值的地址在栈上都会有相同的对齐方式。

SUB ESP,10H在栈上分配16个字节。 这里其实只需要4个字节。

这是因为,分配堆栈的大小也被排列在一个16字节的边界。

该字符串的地址(或这个字符串指针),不使用PUSH指令,直接写入到堆栈空间。var_10,是一个局部变量,也是printf()的参数。

然后调用printf()函数。

不像MSVC,当gcc编译不开启优化,它使用MOV EAX,0清空EAX,而不是更短的代码。

最后一条指令,LEAVE相当于MOV ESP,EBP和POP EBP两条指令。

换句话说,这相当于指令将堆栈指针(ESP)恢复,EBP寄存器到其初始状态。

这是必须的,因为我们在函数的开头修改了这些寄存器的值(ESP和EBP)(执行MOV EBP,ESP/AND ESP…)。