前置知识
32位-x86 cpu的寄存器 简单的汇编指令解释 栈长这样 小数字是标号,大数字是地址
实验一 反汇编c程序
先写一个小文件用来试验
// main.c
int g(int x)
{
return x + 2824;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(66) + 2021;
}
然后把这个小程序汇编一下,命令如下 gcc –S –o main.s main.c -m32 然后加下来开始分析一下这个汇编后的文件
图是没有截全的,但是可以看见第五行有个g: ,这就是对应原来.c 中的g函数 且代码中有很多. 开头的字符串,如.text ,.cfi_def_cfa 啥的。这些. 打头的字符串是链接阶段会用到的辅助信息
. 打头的字符串有点多,可能会影响我们分析汇编代码,所以接下来可以把这些辅助信息都删除,精简一下。 删除代码如红框中所示g/\.s*/d 得到精简后的汇编文件 17行的main: 和8行的f: 代表.c文件中的俩函数(还有一个g函数,截图没截进来) 首先执行main 函数
pushl %ebp 1//将ESP寄存器指向堆栈中标号1的位置,然后讲EBP中的值放入标号1中(其实就是ebp入栈),与此同时EIP寄存器已经指向了下一行代码
movl %esp, %ebp 2//使EBP也指向标号1的位置,同时EIP寄存器指向下一行代码
sub $4, %esp 3//将ESP寄存器的值减4,其实就是把ESP向下移动一个标号,指向标号2,同时EIP指向下一行代码
movl $66, (%esp) 4//把立即数66放入ESP指向的标号2的位置,同时EIP指向下一行代码
call f 5//EIP自动+1指向下一行,eip入栈,调用f函数。eip的入栈是为了执行完f函数的时候能回到main函数
addl $2021,%eax 20//立即数2021放到eax里头
leave 21//栈回到初始状态
ret 22//pop EIP,所有东西都结束
f函数的分析
pushl %ebp 6//把ESP的值向下移一位到标号4,然后把EBP的值标号1放到标号4的位置,
movl %esp,%ebp 7//让EBP指向ESP的位置,即标号4
sub 4,%esp 8//ESP寄存器的值-4,即指向标号5
movl 8(%ebp),%eax 9//通过EBP变址寻址,EBP的值+8,指向标号2的位置,然后标号2存储的是立即数66.将66放入到EAX中
movl %eax,(%esp) 10//把EAX中的值放到ESP所指向的位置,即标号5
call g 11//eip入栈,调用g函数,eip的入栈是为了执行完g函数之后,能够回到f函数
leave 18//这东西就相当于move %ebp,%esp;pop %ebp
ret 19//pop eip,返回到main函数
g函数(其实都大同小异)
pushl %ebp 12//ebp入栈
movl %esp,%ebp 13//让EBP和ESP指向当前栈顶
movl 8(%ebp),%eax 14//EBP指向的位置向上移两个
addl $2824,%eax 15//把立即数2824加到EAX里头 2824+66=2890
popl %ebp 16//把第12步中入栈的ebp ,pop掉
ret 17//返回到f函数
所有函数的头两条指令都用于初始化自己的调用堆栈空间
总结
1.每次执行call func 指令的时候,都会把eip+1 的值压到栈里,为了之后用 2.栈是向下长的 3.如果搞不清楚,就画一个栈自己整一下 4.这里有个动图,虽然里头的立即数和我的不一样,但是总体的执行过程是一样的
实验二 时间片轮转多道程序内核
patch 用于修补文件的命令
|