反汇编测试


任务详情

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;
}

完成过程

1.编译情况

反汇编测试_堆栈

2.发现在树莓派arm64架构下无法完成,便转到kali上做了。

反汇编测试_入栈_02

使用​​gcc - g example.c -o example -m32​​​指令在64位的机器上产生32位汇编,然后使用​​gdb example​​指令进入gdb调试器:

反汇编测试_入栈_03

进入之后先在main函数处设置一个断点,再run一下,使用​​disassemble​​​指令获取汇编代码,用​​i(info) r(registers)​​指令查看各寄存器的值:

反汇编测试_堆栈_04

用x mian查看主函数的内存地址:

首先,结合display命令和寄存器或pc内部变量,做如下设置:​​display /i $pc​​,这样在每次执行下一条汇编语句时,都会显示出当前执行的语句。

反汇编测试_堆栈_05

call指令将下一条指令的地址入栈,此时%esp,%ebp和堆栈的值为:

反汇编测试_入栈_06

将上一个函数的基址入栈,从当前%esp开始作为新基址:

反汇编测试_入栈_07

先为传参做准备:

反汇编测试_堆栈_08

f函数的汇编代码:

反汇编测试_堆栈_09

实参入栈:

反汇编测试_入栈_10

主函数汇编代码:

反汇编测试_入栈_11


知识点

跟着博客做完整个人还是很晕,于是想梳理一下知识点。

1.寄存器

(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位寄存器。作为通用寄存器,也可存储算术逻辑运算的操作数和运算结果。它们可作一般的存储器指针使用。在字符串操作指令的执行过程中,对它们有特定的要求,而且还具有特殊的功能。



重新尝试反汇编

试图理解反汇编

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。

反汇编测试_寄存器_12

4.使用​​si​​​步入下一条指令,使用​​x(examine) /nfu + 内存地址​​查看堆栈内容(之后每一步都要用到,不重复说明)。

call指令调用f函数,f函数的地址在0x5655619e

反汇编测试_堆栈_13

5.push命令将ebp里的值写入f函数这个帧,内容为0x565561bd,这是因为后面要用到这个寄存器,就先把里面的值取出来,用完后再写回去。这时,​​push​​指令会再将 ESP 寄存器里面的地址减去4个字节(累计减去8)。

反汇编测试_入栈_14

6.mov指令用于将一个值写入某个寄存器。这一行代码表示,从ebp寄存器存的地址在 Stack 取出数据。根据前面的步骤,可以推算出这里取出的是​​8​​​,再将​​8​​写入ESP寄存器。

反汇编测试_入栈_15

这里没有使用push指令,但是esp减去了4(累计减去12),堆栈中也存入了ebp的地址0xffffd168,参照阮一峰的理解是

反汇编测试_寄存器_16

7.sub指令代表第一个寄存器中的值减去第二个寄存器中的值,将结果存入第一个寄存器中。这里就是用10(我的学号)减去esp中的值(此处应该是8),得到了2,存入?

(此处不理解)将我的学号传入f函数中,堆栈中存入了0xf7de2fd6。

反汇编测试_入栈_17

此处因为进入了f函数中,所以ebp指向了f函数的栈底0xffffd15c。

8.call指令调用(此处应该是一个奇奇怪怪的函数,估计是某个库函数,用来初始化函数调用的?),建立该函数的帧。

esp减去了16,说明存入了4个东西(为什么呢)

反汇编测试_堆栈_18

9.mov指令将esp的地址存入了eax寄存器中,地址为0x565561a9,存入了堆栈中,esp中的值减去4。

反汇编测试_堆栈_19

10.ret指令用于终止当前函数的执行,将运行权交还给上层函数。也就是,当前函数的帧将被回收。

随着函数的终止,系统就回到刚才f函数中断的地方,继续往下执行。

反汇编测试_寄存器_20

11.add指令是将0x2e57与eax寄存器中的值相加,存到?

此处应该发生了pop指令,esp寄存器的值加上4。

反汇编测试_入栈_21

12.movl指令将0xa(10,我的学号)传入地址(ebp的地址减去4,这里应该是​​i​​的地址)中,此处movl中的l为长字节的意思(虽然感觉用mov也可以)。

反汇编测试_入栈_22

13.push将地址(ebp的值0+8,就是8)中的值取出来写入f函数这个帧,esp减去4。

反汇编测试_堆栈_23

14.call指令调用g函数,建立g函数的帧。

反汇编测试_堆栈_24

15.push命令将ebp中的值(地址0xffffd15c的值,此处应是0xa)写入g函数帧里,esp的地址减去4。

反汇编测试_入栈_25

16.mov将esp中的值写入ebp中(为传回上层函数做准备)。

反汇编测试_堆栈_26

17.call指令调用不知名函数。

反汇编测试_入栈_27

18.mov指令将寄存器eax中的值存入地址(寄存器esp的值)中。

反汇编测试_堆栈_28

19.ret指令终止函数,返回上层函数的帧。

反汇编测试_入栈_29

20.add指令将​​0x2e6f​​与寄存器eax中的值(0x56556191)相加,存入eax中。

反汇编测试_堆栈_30

21.mov指令将寄存器ebp中的值(此处为0)加8,存入eax寄存器中。

反汇编测试_寄存器_31

22.add指令将3加上eax中的值存入eax中(3+8=11)。

反汇编测试_入栈_32

23.pop指令

反汇编测试_寄存器_33

24.ret指令终止当前函数。

反汇编测试_寄存器_34

25.add指令将4与esp寄存器中的值(此处为8)相加,存入esp寄存器中。

反汇编测试_寄存器_35

25.mov指令将地址(寄存器ebp的值减去4)中的值存入寄存器edx中。

反汇编测试_入栈_36

26.add指令将edx中的值与eax中的值相加(11+8=21),存入eax中。

反汇编测试_入栈_37

27.leave指令用于释放当前堆栈中的内容。

反汇编测试_堆栈_38

28.ret指令用于终止函数。

反汇编测试_堆栈_39

29.add指令将4与寄存器esp中的值相加,存到esp中(4+8 = 12)

反汇编测试_寄存器_40

30.add指令将1与寄存器eax中的值相加,存到eax中(21+1 = 22)。

反汇编测试_寄存器_41

31.leave指令释放当前堆栈的内容。

反汇编测试_入栈_42

32.ret指令结束当前函数调用,之后就是系统调用,很多看不完。

反汇编测试_寄存器_43

总结

看了一整天,看麻了!