通过反汇编代码探究计算机运行过程

来源:互联网 发布:淘宝的官换机靠谱吗 编辑:程序博客网 时间:2024/05/18 02:46

在线学习了Mooc的《计算机内核分析》课程,为了探究计算机运行过程,现做博文记录实验过程。


首先打开虚拟机中的linux环境,输入C语言代码:

int g(int x){  return x + 3;}int f(int x){  return g(x);}int main(void){  return f(8) + 1;}


保存为main.c文件


使用反汇编命令

gcc -S -o main.s main,c -m32

-S : 编译为汇编语言程序

-o : 指定目标程序名称

-m32 : 指定编译为32位环境的汇编代码


编译结果:

.file"main.c".text.globlg.typeg, @functiong:.LFB0:.cfi_startprocpushl%ebp.cfi_def_cfa_offset 8.cfi_offset 5, -8movl%esp, %ebp.cfi_def_cfa_register 5movl8(%ebp), %eaxaddl$3, %eaxpopl%ebp.cfi_def_cfa 4, 4.cfi_restore 5ret.cfi_endproc.LFE0:.sizeg, .-g.globlf.typef, @functionf:.LFB1:.cfi_startprocpushl%ebp.cfi_def_cfa_offset 8.cfi_offset 5, -8movl%esp, %ebp.cfi_def_cfa_register 5subl$4, %espmovl8(%ebp), %eaxmovl%eax, (%esp)callgleave.cfi_restore 5.cfi_def_cfa 4, 4ret.cfi_endproc.LFE1:.sizef, .-f.globlmain.typemain, @functionmain:.LFB2:.cfi_startprocpushl%ebp.cfi_def_cfa_offset 8.cfi_offset 5, -8movl%esp, %ebp.cfi_def_cfa_register 5subl$4, %espmovl$8, (%esp)callfaddl$1, %eaxleave.cfi_restore 5.cfi_def_cfa 4, 4ret.cfi_endproc.LFE2:.sizemain, .-main.ident"GCC: (GNU) 4.6.2 20111027 (Red Hat 4.6.2-1)".section.note.GNU-stack,"",@progbits

除去代码中的标志符号后:

g:pushl%ebpmovl%esp, %ebpmovl8(%ebp), %eaxaddl$3, %eaxpopl%ebpretf:pushl%ebpmovl%esp, %ebpsubl$4, %espmovl8(%ebp), %eaxmovl%eax, (%esp)callgleaveretmain:pushl%ebpmovl%esp, %ebpsubl$4, %espmovl$8, (%esp)callfaddl$1, %eaxleaveret

下面对此段汇编代码进行分析,首先要熟悉一些基本的汇编知识:

为了计算方便,加快计算机运行速度,计算机中配置了很多寄存器,这些寄存器在计算机工作中起到各种不同的重要作用:

eax默认作为函数返回值保存寄存器

ebp为栈低指针寄存器

esp为栈顶指针寄存器(运行中的每个程序都有一段堆栈空间,用来存放程序运行时数据)

eip为当前指令位置寄存器

push为压栈指令

pop为出栈指令

mov为转移指令,通过该指令有七种寻址方式:
mov eax,0x1234 //立即寻址
mov eax,ebx //寄存器寻址
mov eax,[ebx] //寄存器间接寻址
mov eax,[0x1234] //直接寻址
mov eax,[ebx+0x1234] //寄存器相对寻址
mov eax,[esi+edi]//基址变址寻址
mov eax,[esi+edi+0x1234] //基址变址相对寻址

add为加法指令

此外还有一些宏指令,它们对应一行或多行汇编代码:

ret : pop %eip(实则返回指令)

leave : mov %ebp %esp

pop %ebp

enter :  push %ebp

mov %esp %ebp

call 0x12345 : push %eip

mov $0x12345 , %eip

在指令后加上b、l、w、q分别代表8位、16位、32位、64位操作


下面直接通过分析代码来熟悉这些指令:

首先程序从main函数开始执行,

pushl%ebp

将基址寄存器的值进栈,用于系统控制main函数返回,此时堆栈情况如图:


movl%esp, %ebp

此行代码将esp寄存器的内容赋值给ebp,即ebp和esp都指向地址1的位置

subl$4, %esp

将esp的指针指向下面一个位置

movl$8, (%esp)

将立即数8存放在当前esp所指位置,堆栈的情况如图:


callf

调用f函数,即执行下面的指令

push %eip //将当前的eip入栈,即将下一条指令[addl $1, %eax]的地址入栈

mov f, %eip //函数f的入口地址赋值到eip中,即程序跳转到f函数入口处

pushl%ebp

f函数的第一条指令,同main函数,如图:


movl%esp, %ebp

初始化f函数的堆栈,如图:


subl$4, %esp<span style="white-space:pre"></span>//栈顶下移

movl8(%ebp), %eax<span style="white-space:pre"></span>//栈低加8对应地址的数据存入eax中movl%eax, (%esp)<span style="white-space:pre"></span>//eax中数据存入esp所指空间
如图:


callg

调用g函数

pushl%ebpmovl%esp, %ebpmovl8(%ebp), %eaxaddl$3, %eax

如图:


popl%ebp

出栈,如图:


ret<span style="white-space:pre"></span>//pop %eip

eip指向f函数中leave指令的地址,开始继续执行


leave

mov %ebp %esp

pop %ebp


ret

返回main函数


addl$1, %eax<span style="white-space:pre"></span>//eax = 12leave<span style="white-space:pre"></span>//ret

如图:



程序执行完毕,main函数返回交给系统处理。


计算机执行就是顺序执行的过程,借助着寄存器和堆栈实现程序的跳转执行。

0 0
原创粉丝点击