inline hook 之 ARM64 汇编基础

   日期:2020-08-07     浏览:130    评论:0    
核心提示:iOS 内存分区及栈区栈区是存放临时变量、记录函数调用的区域。函数因为有了栈,所以才会具有递归的特点。向低地址增长的特性iOS 内存的栈空间是向低地址生长的,在图示中靠下的位置是栈顶,靠上方的位置是栈底:字节序、端序、尾序计算机硬件有两种储存数据的方式,分别为大端(big endian)和小端(little endian),当然还有混合的,在这里我们先不研究。举例来说,数值0x2211使用两个字节储存:高位字节是0x22,低位字节是0x11。大端字节序:高位字节在前,低位字节

iOS 内存分区及栈区

栈区是存放临时变量、记录函数调用的区域。
函数因为有了栈,所以才会具有递归的特点。

向低地址增长的特性

iOS 内存的栈空间是向低地址生长的,在图示中靠下的位置是栈顶,靠上方的位置是栈底:

字节序、端序、尾序

计算机硬件有两种储存数据的方式,分别为大端(big endian)和小端(little endian),当然还有混合的,在这里我们先不研究。

举例来说,数值0x2211使用两个字节储存:高位字节是0x22,低位字节是0x11。

  • 大端字节序:高位字节在前,低位字节在后,这是人类读写数值的方法。
  • 小端字节序:低位字节在前,高位字节在后,即以0x1122形式储存(小端就是大端的逆序)。

再举个例子,这里有 0x100 0x101 0x102 0x103 这么一段数据,在大端跟小端的存储情况如下图:

啊。。为什么要多此一举呢?都用大端这种人能看懂的不好吗,为什么会有小端字节序呢?

其实道理很简单,计算机电路先处理低位字节,效率比较高,计算都是从低位开始的。

所以,计算机的内部处理都是小端字节序。

但是,人类还是习惯读写大端字节序。

因此,除了计算机的内部处理,其他的场合几乎都是大端字节序,比如网络传输和文件储存。

iOS是小端的操作系统

寄存器

ARM64 有34个寄存器,包括31个通用寄存器、SP、PC、CPSR:

  • x0-x30 64bit 通用寄存器,如果有需要可以当做32bit使用:W0-W30。其中x0 - x7:这 8 个寄存器主要用来存储传递参数 。如果参数超过 8 个,则会通过栈来传递 ;x0 也用来存放上文方法的返回值。
  • FP(x29) 64bit 保存栈帧地址(栈底指针),指向当前方法栈的底部。
  • LR(x30) 64bit 通常称x30为程序链接寄存器,因为这个寄存器会记录着当前方法的调用方地址 ,即当前方法调用完成时应该返回的位置。例如我们遇到 Crash 要获取方法堆栈,其本质就是不断的向上递归每一个 x30 寄存器的记录状态(也就是栈上 X30 寄存器的内容) 来找到上层调用方。
  • SP 64bit 保存栈指针,使用 SP/WSP来进行对SP寄存器的访问。指向当前方法栈的顶部。
  • PC 64bit 程序计数器,俗称PC指针,总是指向即将要执行的下一条指令,在arm64中,软件是不能改写PC寄存器的。
  • CPSR 64bit 状态寄存器

ARM64 有 31 个通用寄存器,每个寄存器可以存取一个 64 位的数据。
我们可以通过 X0 - X30 来对这些寄存器进行寻址。
对应 X0 - X30,W0 - W30 对应的就是相同单元数的低 32 位。
W0 - W30 当进行写入操作时,会将高 32 位清零。

栈空间开辟和释放

大角度指导,如果我们不断的递归调用一个方法,就会造成 Stack Overflow 。

由于FP 始终指向当前方法的栈底,SP 始终指向当前方法的栈顶。
如果我要在不断的调用一个方法,那么SP指针就一直在上升(大家记住,iOS里上下是反的):


对应的,当我们需要释放一个区域,其实也是 SP 指针下沉的过程(上下是反的):


由于 SP 是始终指向栈顶(低地址)的,每次进行 Push 压栈的时候,其顶部指针就会向低地址偏移,而当进行弹栈 Pop 操作时,指针向高地址偏移。

在方法栈中,我们的方法就相当于一个又一个栈内元素,与之对应的就是 SP 指针的上移和下沉。

这样我们也就可以解释的通什么是Stack Overflow了,方法压栈操作执行时,在汇编层执行的是类似于 sub sp, sp, #0xN 这种操作,这个指令决定了 SP 是往低地址逐渐逼近。

所以当 SP 移动到栈区的最低位置(接近于堆区),则称之为Stack Overflow 。

函数的栈区结构

假设我们在执行main方法,此时在栈上会分配一个区域,即从 FP(main 方法的栈底)到 SP (main 方法的栈顶):

函数调用时栈区表现






基于寄存器的方法栈回溯示意:

关于 stp 指令的解释

stp 指令是 str 的变种指令,p 可以理解成 pair 的意思,可以同时操作两个寄存器。举一个例子:

stp x29, x30, [sp, #0x10]  ; 将 x29, x30 的值存入 sp 偏移 16 个字节的位置 




这里我们需要注意的一个点,在后面对于 objc_msgSend 方法 Hook 的汇编代码中,会有这么一条指令:

stp q6, q7, [sp, #-32]!

这与上面所说的 stp 命令不太一样。我们注意到差异是在 sp 偏移寻址的标记后多了一个 ! 。
则这条命令可以等效于:

sub sp, sp, #32
stp q6, q7, [sp]

也就是说,在执行完上文的 stp 指令后,还会使 sp 寄存器也产生偏移。
如此就可以达到持续入栈的效果,类似于 ARM32 中的 push 指令。

 
打赏
 本文转载自:网络 
所有权利归属于原作者,如文章来源标示错误或侵犯了您的权利请联系微信13520258486
更多>最近资讯中心
更多>最新资讯中心
0相关评论

推荐图文
推荐资讯中心
点击排行
最新信息
新手指南
采购商服务
供应商服务
交易安全
关注我们
手机网站:
新浪微博:
微信关注:

13520258486

周一至周五 9:00-18:00
(其他时间联系在线客服)

24小时在线客服