反汇编测试
# 反汇编测试
任务详情
1 通过输入gcc -S -o main.s main.c 将下面c程序”week0303学号.c“编译成汇编代码
int g(int x){
return x+3;
}
int f(int x){
int i = 学号后两位;
return g(x)+i;
}
int main(void){
return f(8)+1;
}
2. 参考http://www.cnblogs.com/lxm20145215----/p/5982554.html,使用gdb跟踪汇编代码,在纸上画出f中每一条语句引起的eip(rip),ebp(rbp),esp(rsb),eax(rax)的值和栈的变化情况。提交照片,要有学号信息。
完成过程
1.编译情况
2.发现在树莓派arm64架构下无法完成,便转到kali上做了。
使用gcc - g example.c -o example -m32
指令在64位的机器上产生32位汇编,然后使用gdb example
指令进入gdb调试器:
进入之后先在main函数处设置一个断点,再run一下,使用disassemble
指令获取汇编代码,用i(info) r(registers)
指令查看各寄存器的值:
用x mian查看主函数的内存地址:
首先,结合display命令和寄存器或pc内部变量,做如下设置:display /i $pc
,这样在每次执行下一条汇编语句时,都会显示出当前执行的语句。
call指令将下一条指令的地址入栈,此时%esp,%ebp和堆栈的值为:
将上一个函数的基址入栈,从当前%esp开始作为新基址:
先为传参做准备:
f函数的汇编代码:
实参入栈:
主函数汇编代码:
知识点
跟着博客做完整个人还是很晕,于是想梳理一下知识点。
1.寄存器
参考博客:https://www.cnblogs.com/lihaozy/archive/2011/08/01/2124315.html
(1)ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
(2)EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部。
(3)Eax用来保存所有API函数的返回值。
(4)寄存器AX和AL通常称为累加器(Accumulator),用累加器进行的操作可能需要更少时间。累加器可用于乘、除、输入/输出等操作,它们的使用频率很高;
(5)寄存器BX称为基地址寄存器(Base Register)。它可作为存储器指针来使用;
(6)寄存器CX称为计数寄存器(Count Register)。在循环和字符串操作时,要用它来控制循环次数;在位操作中,当移多位时,要用CL来指明移位的位数;
(7)寄存器DX称为数据寄存器(Data Register)。在进行乘、除运算时,它可作为默认的操作数参与运算,也可用于存放I/O的端口地址。
(8)寄存器ESI、EDI、SI和DI称为变址寄存器(Index Register),它们主要用于存放存储单元在段内的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。变址寄存器不可分割成8位寄存器。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。它们可作一般的存储器指针使用。在字符串操作指令的执行过程中,对它们有特定的要求,而且还具有特殊的功能。
2.gdb指令
参考博客:https://blog.csdn.net/moonsheep_liu/article/details/39099969
3.学习汇编语言
参考博客:https://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html
重新尝试反汇编
试图理解反汇编
1.首先在main函数设置断点 b main
2.使用display设置显示内容。
display /x $esp
display /x $ebp
display /x $eax
display /i $pc
3.使用run指令跳到main函数开始处,使用x(examine) /nfu + 内存地址
查看堆栈内容。
push 命令将$0x8放入stack,因为是int型占用4个字节,所以esp寄存器减去4。
这时可以看到栈底地址是0xffffd168。
4.使用si
步入下一条指令,使用x(examine) /nfu + 内存地址
查看堆栈内容(之后每一步都要用到,不重复说明)。
call指令调用f函数,f函数的地址在0x5655619e。
5.push命令将ebp里的值写入f函数这个帧,内容为0x565561bd,这是因为后面要用到这个寄存器,就先把里面的值取出来,用完后再写回去。这时,push
指令会再将 ESP 寄存器里面的地址减去4个字节(累计减去8)。
6.mov指令用于将一个值写入某个寄存器。这一行代码表示,从ebp寄存器存的地址在 Stack 取出数据。根据前面的步骤,可以推算出这里取出的是8
,再将8
写入ESP寄存器。
这里没有使用push指令,但是esp减去了4(累计减去12),堆栈中也存入了ebp的地址0xffffd168,参照阮一峰的理解是
7.sub指令代表第一个寄存器中的值减去第二个寄存器中的值,将结果存入第一个寄存器中。这里就是用10(我的学号)减去esp中的值(此处应该是8),得到了2,存入?
(此处不理解)将我的学号传入f函数中,堆栈中存入了0xf7de2fd6。
此处因为进入了f函数中,所以ebp指向了f函数的栈底0xffffd15c。
8.call指令调用(此处应该是一个奇奇怪怪的函数,估计是某个库函数,用来初始化函数调用的?),建立该函数的帧。
esp减去了16,说明存入了4个东西(为什么呢)
9.mov指令将esp的地址存入了eax寄存器中,地址为0x565561a9,存入了堆栈中,esp中的值减去4。
10.ret指令用于终止当前函数的执行,将运行权交还给上层函数。也就是,当前函数的帧将被回收。
随着函数的终止,系统就回到刚才f函数中断的地方,继续往下执行。
11.add指令是将0x2e57与eax寄存器中的值相加,存到?
此处应该发生了pop指令,esp寄存器的值加上4。
12.movl指令将0xa(10,我的学号)传入地址(ebp的地址减去4,这里应该是i
的地址)中,此处movl中的l为长字节的意思(虽然感觉用mov也可以)。
13.push将地址(ebp的值0+8,就是8)中的值取出来写入f函数这个帧,esp减去4。
14.call指令调用g函数,建立g函数的帧。
15.push命令将ebp中的值(地址0xffffd15c的值,此处应是0xa)写入g函数帧里,esp的地址减去4。
16.mov将esp中的值写入ebp中(为传回上层函数做准备)。
17.call指令调用不知名函数。
18.mov指令将寄存器eax中的值存入地址(寄存器esp的值)中。
19.ret指令终止函数,返回上层函数的帧。
20.add指令将0x2e6f
与寄存器eax中的值(0x56556191)相加,存入eax中。
21.mov指令将寄存器ebp中的值(此处为0)加8,存入eax寄存器中。
22.add指令将3加上eax中的值存入eax中(3+8=11)。
23.pop指令
24.ret指令终止当前函数。
25.add指令将4与esp寄存器中的值(此处为8)相加,存入esp寄存器中。
25.mov指令将地址(寄存器ebp的值减去4)中的值存入寄存器edx中。
26.add指令将edx中的值与eax中的值相加(11+8=21),存入eax中。
27.leave指令用于释放当前堆栈中的内容。
28.ret指令用于终止函数。
29.add指令将4与寄存器esp中的值相加,存到esp中(4+8 = 12)
30.add指令将1与寄存器eax中的值相加,存到eax中(21+1 = 22)。
31.leave指令释放当前堆栈的内容。
32.ret指令结束当前函数调用,之后就是系统调用,很多看不完。
总结
看了一整天,看麻了!