2020-2021-1 20209315《Linux内核原理与分析》第二周作业
实验部分:
反汇编一个简单的C程序
开始程序的编写,创建一个main.c文件,打开main.c文件在其中输入一个简单的c程序,并进行保存。
程序如下图所示:
程序编写完成后,对main.c文件进行编译,会自动生成一个目标文件a.out。
执行a.out文件,此处无输出。
利用如下指令可以将main.c编译成一个汇编代码,给这个汇编代码起名main.s。
打开mian.s文件 ,可以看到上面所写的c程序相应的汇编代码:
函数g的汇编部分:
函数f的汇编部分:
main函数:
其中的重要指令有:pushl/pop和call/ret。此处的pushl表示32位的push.
pushl %eax
就是将EAX寄存器寄存器的值压到堆栈栈顶,实际上分为两个动作,其中一个为:
subl $4 %esp
把堆栈的栈顶ESP寄存器的值减4,因为堆栈是向下增长的,所以用subl减指令,在堆栈栈顶留出一个存储单元给要存进来的值,第二个动作为:
movl %eax, (%esp)
把ESP存储器加一个小括号(间接寻址),就是把EAX寄存器的值放到ESP寄存器所指向的地方,这时ESP寄存器已经指向预留出的存储单元。
接下来介绍popl指令,如:
popl %eax
就是从堆栈的栈顶取一个存储单元(32位数值),从堆线枝顶的位置放到EAX寄存器里,这称为出栈。出栈同样对应两个操作:
movl (%esp) ,%eax
addl $4 , %esp
第一步是把栈顶的数值放到EAX寄存器里,然后用指令addl把栈顶加4,相当于栈向上回退了一个存储单元的位置,也就是栈在收缩。每次执行指令pushl 栈都在增长,执行指令popl栈都在收缩。
汇编程序中main函数的执行过程:
程序从main函数开始执行,也就是“"main:" 下面的第一 条指令“pushl %ebp”,这是开始执行的第一 条指令,这条指令的作用实际上就是把EBP寄存器的值压栈,pushl指令的功能是先把ESP寄存器指向标号1的位置,即标号加1或地址减4 (向下移动4个字节),然后将EBP寄存器的值标号0 (地址2000) 放到堆栈标号1的位置。。
开始执行上一条指令时,EIP寄存器已经自动加1指向了“movl%esp,%ebp",是将EBP寄存器也指向标号1 的位置,这条语句只修改了EBP寄存器, 栈空间的内容并没有变化。第18行和第19行语句是建立main函数自己的函数调用堆栈空间。
开始执行上一条指令时,EIP寄存器已经自动加1指向了“subl $4,%esp”,把ESP寄存器减4,实际上是ESP寄存器向下移动一个标号,指向标号2的位置。这条语句只修改了ESP寄存器,栈空间的内容并没有变化。
开始执行上一条指令时,EIP 寄存器已经自动加1指向了“movI $8, (%esp)”,把立即数8放入ESP寄存器指向的标号2位置,也就是"subl $4,%esp"预留出来的标号2的位置。这条语句的EBP和ESP寄存器没有变化,栈空间发生了变化。这两个语句是在为接下来调用f函数做准备,即压栈f函数所需的参数。
开始执行上一条指令时; EIP寄存器已经自动加1指向了指令"call f", call 指令相当于如下两条伪指令:
pushl %eip(*)
movl f %eip(*)
"call f"开始执行时,EIP 寄存器已经自加1指向了下一.条指令,实际上把EIP寄存器的值放到了栈空间标号3的位置。因为压栈前ESP寄存器的值是标号2,压栈时ESP寄存器先减4个字节,即指向下一个位置标号3,然后将EIP寄存器的行号23入栈到栈空间标号3的位置。接着将f函数的第一条指令的行号9放入EIP寄存器,这样EIP寄存器指向了f函数。这条语句既改变了栈空间,又改变了ESP寄存器,更重要的是它改变了EIP寄存器。原来EIP寄存器自加1指令是按顺序执行的,现在EIP寄存器跳转到了f函数的位置。