您的位置:首页 > 理论基础

分析单任务计算机操作之反汇编C程序

2013-05-12 22:58 330 查看

-中科大_243_石润成

本文旨在借助分析一段c语言程序反汇编代码来描述单任务计算机是如何工作的,不考虑中断等复杂操作。如有错误,希望读者能够反馈给我,不胜感激!

1.预备阶段

本文以example.c为例,代码如下:

#include<stdio.h>
int g(int x)
{
return x+3;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(8)+1;
}

实验环境:Ubuntu10.04

工具:gcc、gdb、makefile

(1)编写makefile



(2)make后生成预编译文件(*.cpp)、汇编文件(*.s)、目标文件(*.o)、执行文件elf文件



(3)example.s无法看到实际的代码运行地址,本人使用gdb的disas反汇编查看.

命令:

# gdb example

# disas main

# disas f

# disas g

main反汇编:






f函数反汇编:






g函数反汇编:






2.分析汇编代码

从main入口函数开始执行,但切记在main函数并不是整个程序第一个执行的函数(详情请看点击打开链接)。

从main函数反汇编可以看到,eip指向main函数地址0x080483d2

[以下整个内存地址由高到低展开]

#1 push %ebp //%ebp入栈,该ebp是调用main函数的基栈地址

mov %esp,%ebp //%ebp = %esp,ebp指向新函数的基栈地址,此时与esp一样指向栈底,用来保护整个函数的栈变量



#2 sub $0x4,%esp //esp = esp - 4,分配4个字节,由高地址向低地址分配



#3 movl #0x8,(%esp) //8存入esp所指的内存空间,即入栈,一般对于本函数来说就是个局部变量保存



#4 call 0x80483bf <f> 等价于 push %eip

mov $0x80483bf,%eip

本机测试此时%eip为 0x080483e4,将其设为%eip_m表示当main调用f函数结束时返回main函数的下一个指令(add $0x1,%eax)的地址



#5 push %ebp //同上所述,入栈保存main函数的基栈地址,此处设为exp_m

mov %esp,%ebp //同时ebp指向esp,ebp指向新的调用函数基栈地址,在此ebp和esp又开始维护新的函数栈空间



#6 sub $0x4,%esp //%esp = %esp - 4 [意味着申请了四个字节的空间]



#7 /* %eax = %ebp + 8内存地址的值(即8)
*/

mov 0x8(%ebp),%eax

/* %eax入栈,即将8入栈,这里可以看到实参8传了过来,但是传给的函数f并没有直接使用原实参的8,而是重新分配了一个空间保存,即所谓的副本,函数参数传递为值传递。*/

mov %eax,(%esp)



#8 call 0x80483b4 <g> 等价于 push %eip

mov $0x80483bf,%eip

本机测试此时%eip为 0x080483d0,将其设为%eip_f表示当f函数调用g函数结束时返回f函数的下一个指令(leave)的地址



#9 push %ebp //将f函数的基栈地址入栈,此处用%ebp_f表示

mov %esp,%ebp //ebp指向新的基栈地址%esp



#10 mov 0x8(%ebp),%eax //%eax = ebp + 8 内存地址的值(即8)

add $0x3,%eax //%eax = %eax +3 = 11

#11 pop %ebp // %ebp = %esp,esp = esp-4

注意在pop之前,esp指向的是ebp_f即f函数的基栈地址,pop之后,ebp指向f函数的基栈地址。



#12 ret 等价于 pop %eip

注意未ret之前,esp指向的是返回f函数的下一个指令的地址,即上述说的leave地址,ret后,eip将指向leave地址0x080483d0,此时程序会跳转到该处执行。

此处,所申请的栈空间并没有释放掉,还有个8。



#13 leave
等价于 mov %ebp,%esp //可以看到其实是在返回函数f将g函数的栈空间释放的

pop %ebp //ebp又恢复main函数的基站地址





#14 ret 等价于 pop %eip //eip指向返回main函数的下一个指令(add $0x1,%eax)地址eip:0x080483e4,并开始从此处执行



#15 leave 等价于 mov %ebp,%esp // esp指向main函数的基地址

pop %ebp //这里ebp将指向%ebp_x(调用main函数的基站地址)





3.总结

这里简单总结一下,C语言是以函数为基本单元的,从本次实验分析来看,整个程序也是从一个函数进入到另一个函数的顺序调用,然后顺序返回,比如本次实验程序的main函数调用f函数,f函数调用g函数,g函数再反馈结果给f,f再反馈结果给main函数,然后程序结束,如下图所示。



值得注意的是,当一个函数跳转到另一个函数的时候,系统会利用寄存器来保存上下文,从本次实验中可以看到,寄存器ebp和esp一起用来保护函数的栈空间,通过esp来申请和释放栈空间,ebp来保存调用函数的基栈地址,当进行函数跳转时,会保存函数跳转处的下一条指令和基栈地址(入栈保存),这样在函数返回的时候可以根据栈的顺序来恢复调用函数的基栈地址,这里常用的汇编指令是leave,分析中我也提到,当函数返回到调用函数中时,会用leave指令来释放被调用函数的堆栈空间,并恢复自己的基栈地址,这样函数与函数之间的调用就能完好的单步执行下去,并能够进行相互传参返回,直到整个单任务程序结束。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: