x64汇编和逆向0x03

Reverse

主要讲述 arm64 中的栈帧分析

不知道是编译器的原因还是什么其他原因,按照常理来说 x64 应该使用 rsp 寻址,但是为什么我的用的全是 rbp 寻址😐

栈帧

什么是栈帧???

在 32 位中,进入函数之后一般会有一个 push rbp 的操作,在两个 rbp 之间的这个区段就叫做栈帧。所有的东西都是那 rbp 来进行偏移寻址

64位和32位异曲同工,虽然不再使用 rbp 但是依旧可以进行栈帧定义。

在64位中,栈上存放着函数的返回地址,那我们将栈帧定义成:在栈上保存的两个函数返回地址之间的区段就叫做栈帧。

利用栈帧分析程序

这里利用栈帧分析局部变量和参数,可能是编译器的原因吧,我的 x64 用的都是 rbp 寻址

image-20220704161805496

没什么办法了,那就只能按照视频里面的例子来了

先看一下大致的程序

栈帧空间规划

在刚进入一个函数的时候 rsp 所指向的地方是栈中储存调用函数的返回地址的地方,在栈帧上我们将它标记为 r。

随后进行了 push rdisub rsp,30h 的操作,此时栈帧的结构如下,箭头表示 rsp 当前所在的位置,蓝色阴影区域就是当前函数的栈帧

image-20220704172139705

非叶函数的栈空间分配

非叶函数:在函数实现过程中有调用其他函数的函数就是非叶函数

叶函数:在函数的实现过程中不再调用其他函数,自己实现函数的功能。其他函数包括系统里面的自带的 API

在 x64 中,非叶函数至少分配 4 个参数的栈空间

在下图中,尽管在 main 函数中只调用了一个只有一个参数的 system( ) 函数,但是依旧为它分配了 4 个参数的栈空间

image-20220707143435827

变量类型判断

不知道自己的描述是不是准确,这里类型的判断是指:判断该变量是函数参数还是局部变量

在对 rsp 进行 sub 操作之前,进行了参数入栈操作,这里的参数是该函数所需的参数,对于本例来说就是 argc 和 argv

image-20220704174204599

在对栈帧的大致结构做了规划之后,接下来我们要找到 call ,并且观察所 call 的函数所需的参数。

因为 x64 是一次性分配栈空间的,所以我们需要找出该函数中所有 call 中所需参数最多的那个,并且分配栈空间

image-20220704180237465

这个例子比较简单,print 函数是不定长参数,所以必须分配4字节的空间,所以参数空间就是 rsp 当前所指向的位置向下划分 4 个字节的空间。

位于红色框区域内的就是参数,否则就是局部变量。

栈帧空间结构

所以例子中的程序分析下来,栈帧的结构大致就是这样

image-20220704180842828

对栈帧空间进行整理的话就是就如图所示

image-20220704182731955

ida中看栈帧空间

这个和上面的不是相匹配的,是我自己的编译器编译出来的64位 exe 程序,里面用的是 rbp 相对寻址

在 ida 中,栈帧空间的栈顶就是在该函数中 rsp 所在的位置

image-20220704192907117

结合这两个语句, rbp 所这样的位置正好是 0 处

image-20220704193041868

所以简单来说,ida 就是将栈帧中的0处当成基址,然后在此基础上进行便偏移寻址

image-20220704193223118

  • arg 表示整数,指向0处向下的位置
  • var 表示复数,指向0处向上的位置

other

理的有点乱糟糟的,只是说了个大概意思,感觉重点就是要理解这个栈帧空间的主要结构,后续自己要多练练,先多看看自己写的程序,然后再看看别人写的,多练熟吧。

本文作者:GhDemi

本文链接: https://ghdemi.github.io/2022/07/13/x64%E6%B1%87%E7%BC%96%E5%92%8C%E9%80%86%E5%90%910x02-1/

文章默认使用 CC BY-NC-SA 4.0 协议进行许可,使用时请注意遵守协议。