本篇文章我们主要讲述 hello world 程序的执行,这一次我们不从 main 函数开始,我 们从更原始的起点开始跟踪程序的执行,让大家看到一些不应该看到的东西。 首先给出最简单 helloworld 源代码,如下所示: #include <stdio.h> int main() { printf("Hello World!!!\n"); return(0); } 通常我们编写程序的时候,不管书里或者教授之人,都会告诉我们程序是从 main 函数开 始执行的,main()是程序的开始,但是真的是这样吗,main 是如何开始的呢?我们使用 GCC 进行编译,如下:gcc hello\ world.c -o test 我们已经得到了我们编译后的 ELF 可执行文件,结果就是打印一个”hello world!”字 符串。我们如何开始进行分析呢,这里我们使用 IDA 进行分析,我们打开 IDA 载入 ELF 目标 文件,我们可以看到 IDA 对程序进行了自动分析,并进行了绘图,如下所示: ![]()
我们看到根为_start,我们 text view 反汇编代码,代码如下: .text:08048310 public _start .text:08048310 _start proc near .text:08048310 xor ebp, ebp .text:08048312 pop esi .text:08048313 mov ecx, esp .text:08048315 and esp, 0FFFFFFF0h .text:08048318 push eax .text:08048319 push esp .text:0804831A push edx .text:0804831B push offset libc_csu_fini .text:08048320 push offset libc_csu_init .text:08048325 push ecx .text:08048326 push esi .text:08048327 push offset main .text:0804832C call .text:08048331 hlt .text:08048331 _start endp _libc_start_main 我们通过对这段代码进行分析,可以发现,这个地方最后调用了 libc_start_main。 这里共传入了七个参数,第一个函数是 main 的地址,第二个参数 esi 为 argc,第三个参数 为 argv 的地址,第四个地址为_init 地址,第五个参数为_finit 地址,第六个参数为 finit, 第七个为 esp 顶。我们可以看到_start 的第一行为 xor ebp ebp,设置 ebp,这里才是一个程 序最外层的函数。那么这段_start 是如何进入我们的 helloworld 的呢,是由 GCC 编译的时 候链接的,我们在 glibc 里可以找到_start 的源代码,代码如下: 057 #include "bp-sym.h" 058 059 .text 060 .globl _start 061 .type _start,@function 062 _start: 063 /** Clear the frame pointer. The ABI suggests this be done, to mark 064 the outermost frame obviously. */ 065 xorl %ebp, %ebp 066 067 /** Extract the arguments as encoded on the stack and set up |