大脸猫讲逆向之ARM汇编中PC寄存器详解
近日,在研究一些开源native层hook方案的实现方式,并据此对ARM汇编层中容易出问题的一些地方做了整理,以便后来人能有从中有所收获并应用于现实问题中。当然,文中许多介绍参考了许多零散的文章,本文重点工作在于对相关概念的整理收集,并按相对合理顺序引出后文中对hook技术中的一些难点的解读。
Android平台大多采用了ARM架构的CPU,而ARM属RISC,与X86架构的处理器有不同的特征,本文讲介绍ARM中不容易理解的PC寄存器各种问题,包括ARM流水线、PC寄存器指向问题、ARM和Thumb指令的区分及相关概念在native层hook中的应用问题。
1.ARM寄存器介绍
ARM微处理器共有37个32位寄存器,其中31个为通用寄存器,6个为状态寄存器。但是这些寄存器不能被同时访问,具体哪些寄存器是可以访问的,取决ARM处理器的工作状态及具体的运行模式。但在任何时候,通用寄存器R14~R0、程序计数器PC、一个状态寄存器都是可访问的。
简言之,在用户模式下ARM可见的寄存器有16个32位的寄存器(R0到R15)和一个当前程序状态寄存器CPSR,其中R15是程序计数器PC,R14用于存储子程序的返回地址LR,R13用于存储堆栈栈顶SP。
本文我们将着重介绍一下R15寄存器,即PC寄存器。理论上,PC寄存器应指向即将执行的下一条指令的地址,然而在实际应用中却发现PC寄存器总不是如此。经过翻看资料发现,该问题是由于ARM存在的指令流水线导致的。
2.ARM流水线介绍
流水线技术通过多个功能部件并行工作来缩短程序执行时间,提高处理器核的效率和吞吐率,从而成为微处理器设计中最为重要的技术之一。也就是说,通过划分指令执行过程中的不同阶段,并通过并行执行,从而提高指令的执行效率。ARM7处理器采用了三级流水线结构,包括取指(fetch)、译码(decode)、执行(execute)三级。
3.PC寄存器的指向问题
如上图所示,在执行add r0, r1, #5指令时,第二条指令正在译码阶段,而第三条指令正在取指阶段。在执行第一条指令时,PC寄存器应指向第三条指令。也即,当处理器为三级流水线结构时,PC寄存器总是指向随后的第三条指令。
- 当处理器处于ARM状态时,每条ARM指令为4个字节,所以PC寄存器的值为当前指令地址 + 8字节
- 当处理器处于Thumb状态时,每条Thumb指令为2字节,所以PC寄存器的值为当前指令地址 + 4字节
此外,在ARM9中,采用了五级流水线结构,是在ARM7的三级流水线结构后面添加了两个新的过程。因此,指令的执行过程和取指过程还是相隔一个译码过程,因而PC还是指向当前指令随后的第三条指令。
另外,关于PC寄存器需要注意的一点是:当使用指令STR或STM对R15进行保存时,保存的可能是当前指令地址加8或当前指令地址加12。具体是加8还是加12,取决于具体的处理器设计。但是,同一个芯片只能是其中一种的方案,即只能是加8或加12中的一种。
可以通过如下的代码确定处理器采用的那种方式:
SUB R1,PC, #4 ;R1中存放STR指令地址
STR PC,[R0] ;用STR指令将PC保存到R0指向的地址单元中,PC=STR指令地址+偏移量(偏移量为8或者12)。
LDR R0,[R0] ;读取STR指令地址+偏移量的值
SUB R0,R0,R1 ; STR指令地址+偏移量的值减去STR指令的地址,得到偏移量值(8或者12)。
4. ARM/Thumb指令的区分
众所周知,ARM体系结构分为ARM状态和Thumb状态及Thumb-2状态。在ARM状态时执行32位长度的字对齐的ARM指令,Thumb状态时执行16位长度的半字对齐的Thumb指令。
Thumb指令集与 ARM 指令的区别一般有如下几点:
- 跳转指令
程序相对转移,特别是条件跳转与 ARM 代码下的跳转相比,在范围上有更多的限制,转向子程序是无条件的转移.
- 数据处理指令
数据处理指令是对通用寄存器进行操作,在大多数情况下,操作的结果须放入其中一个操作数寄存器中,而不是第 3 个寄存器中.数据处理操作比 ARM 状态的更少,访问寄存器 R8~R15 受到一定限制.除 MOV 和 ADD 指令访问器 R8~R15 外,其它数据处理指令总是更新 CPSR 中的 ALU 状态标志.访问寄存器 R8~R15 的 Thumb 数据处理指令不能更新 CPSR 中的 ALU 状态标志.
- 单寄存器加载和存储指令
在 Thumb 状态下,单寄存器加载和存储指令只能访问寄存器 R0~R7
- 批量寄存器加载和存储指令
LDM 和 STM 指令可以将任何范围为 R0~R7 的寄存器子集加载或存储. PUSH 和 POP 指令使用堆栈指令 R13 作为基址实现满递减堆栈.除 R0~R7 外,PUSH 指令还可以存储链接寄存器 R14,并且 POP 指令可以加载程序指令PC
而程序在执行过程中,是如何区分ARM状态和Thumb状态的呢?在逆向分析过程中,经常会看到许多函数调用过程为形如BX sub_84C0 + 1,即函数地址为奇数。在ARM运行过程中,函数调用的地址最后一位为1时,表示目标函数为Thumb指令;否则为ARM指令。然而,不管是ARM和Thumb状态指令,均是偶数字节对齐的,即函数地址最后一位肯定为0。因此,可以用最后一位判断目标函数是否为Thumb和ARM状态。
综上,程序状态切换可以用如下方式实现:
- 从ARM切换到Thumb:
LDR R0, =label + 1
BX R0
- 从Thumb切换到ARM:
LDR R0, = label
BX R0
上文中,label为符号的地址,因为字节对齐缘故,最后一位肯定为0。
5.native层hook技术解析
以上 问题均是我在分析开源hook框架adbi源码时遇到的问题的解答,下面我将介绍一下上述几个问题在hook中的应用。
在adbi源文件中,hijack.c文件中的sc数组用于存储在对目标函数前几个字节的指令修改过程中的相关指令和寄存器值。
第一条指令为ldr r0, [pc, #64],即将pc + 64位置处的内存的4个字节读到r0寄存器中。经过上面几个章节的介绍,且此处看出指令均为4个字节即ARM指令,可知读取的位置为当前位置 + 8 + 64 位置处的内容,即sc[18]处的内容,也即addr of libname的内容,即将函数调用时第一个参数R0设置为动态库的字符串名。
第二条指令将r1寄存器,即函数的第二个参数设置为0。
第三条指令将pc的值赋值给lr寄存器,即将随后函数调用后的返回地址设置为559行的那条指令,即第五条指令。
第四条指令将pc + 56位置处存储的值赋值给pc寄存器,即当前位置 + 8 + 56位置处的值,也即sc[16]处的值,即被hook的dlopen函数的地址。
随后函数调转到目标函数执行,并在返回后执行559行的执行,从此开始恢复寄存器环境。也即通过这种方式完成了对目标函数的hook劫持过程。
在hook.c文件中,实现了对目标函数的查找及指令替换功能,详情如下图所示。图中,通过对find_name函数的调用,得到了目标so库中的函数funcname的地址并存储于addr中。在代码编译过程中,编译为Thumb指令的函数,通过函数名得到的目标函数地址最后一位为1,用于表示目标函数为Thumb指令集。在adbi代码中,即根据该方法判断需要hook的目标函数是Thumb指令集还是ARM指令集。ARM指令集由于是4字节对齐的,因此最后2位总是为0,据此判断是否是ARM指令。根据不同的指令集实现不同的指令替换,并完成hook功能。
以上即是我在研究adbi源码过程中碰到的需要深入了解的一些基本概念及其具体应用,了解这些能对hook的实现原理能有较为深刻的理解,并据此编写自己的简易的hook方案。
6.总结
在ARM处理器架构中,PC寄存器通常是指向当前指令后的第三条指令地址,即在ARM指令是+8,Thumb指令时+4。
ARM/Thumb状态切换是根据目标函数地址最后一位是否为0来进行判断,并用BX指令实现。
参考链接
https://blog.csdn.net/Sandeldeng/article/details/52954781
https://blog.csdn.net/zhi_yong_chen/article/details/51314377
http://www.eepw.com.cn/article/201611/318735.htm
有问题大家可以留言哦,也欢迎大家到春秋论坛中来耍一耍 >>>点击跳转
大脸猫讲逆向之ARM汇编中PC寄存器详解的更多相关文章
- 逆向知识第八讲,if语句在汇编中表达的方式
逆向知识第八讲,if语句在汇编中表达的方式 一丶if else的最简单情况还原(无分支情况) 高级代码: #include "stdafx.h" int main(int argc ...
- C++中的STL中map用法详解(转)
原文地址: https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html C++中的STL中map用法详解 Map是STL的一个关联容器,它提供 ...
- php中关于引用(&)详解
php中关于引用(&)详解 php的引用(就是在变量或者函数.对象等前面加上&符号) 在PHP 中引用的意思是:不同的变量名访问同一个变量内容. 与C语言中的指针是有差别的.C语言中的 ...
- Objective-C中的@Property详解
Objective-C中的@Property详解 @Property (属性) class vairs 这个属性有nonatomic, strong, weak, retain, copy等等 我把它 ...
- springcloud中Feign配置详解
Spring Cloud中Feign配置详解 到目前为止,小伙伴们对Feign的使用已经掌握的差不多了,我们在前文也提到Feign是对Ribbon和Hystrix的整合,那么在Feign中,我们要如何 ...
- 【转】linux中inittab文件详解
原文网址:http://www.2cto.com/os/201108/98426.html linux中inittab文件详解 init的进程号是1(ps -aux | less),从这一点就能看出, ...
- 【转】linux 中fork()函数详解
在看多线程的时候看到了这个函数,于是学习了下,下面文章写的通俗易懂,于是就开心的看完了,最后还是很愉快的算出了他最后一个问题. linux 中fork()函数详解 一.fork入门知识 一个进程,包括 ...
- SVD在推荐系统中的应用详解以及算法推导
SVD在推荐系统中的应用详解以及算法推导 出处http://blog.csdn.net/zhongkejingwang/article/details/43083603 前面文章SVD原理及推 ...
- MyBatis中@MapKey使用详解
MyBatis中@MapKey使用详解我们在上一篇文章中讲到在Select返回类型中是返回Map时,是对方法中是否存在注解@MapKey,这个注解我也是第一次看到,当时我也以为是纯粹的返回单个数据对象 ...
随机推荐
- PHP中的__get()和__set()方法获取设置私有属性
在类的封装中,获取属性可以自定义getXXX()和setXXX()方法,当一个类中有多个属性时,使用这种方式就会很麻烦.为此PHP5中预定义了__get()和__set()方法,其中__get()方法 ...
- 2018.12.15 bzoj3998: [TJOI2015]弦论(后缀自动机)
传送门 后缀自动机基础题. 求第kkk小的子串(有可能要求本质不同) 直接建出samsamsam,然后给每个状态赋值之后在上面贪心选最小的(过程可以类比主席树/平衡树的查询操作)即可. 代码: #in ...
- git安装项目步骤
1.git clone git@gitee(github).com:项目地址.git 2.cd 项目根目录 3.composer install 4.如果需要数据迁移,cmd中到项目根目录 php a ...
- 创建Java程序并设置快捷提示
1.new Java project 创建项目 2.new package 创建包,cn.com.test 3.创建Java文件 4.Java智能提示的设置 window/preference 在Au ...
- Codeforces Round #513 by Barcelona Bootcamp C. Maximum Subrectangle(双指针+思维)
https://codeforces.com/contest/1060/problem/C 题意 给两个数组,a数组有n个元素,b数组有m个元素,两个数组元素互相相乘形成n*m的矩阵,找一个子矩阵,元 ...
- 张奎师弟参与devexpress chartControl绘图--解决了devexpress的chartControl控件不能添加系列的问题
using DevExpress.XtraCharts; using System; using System.Collections.Generic; using System.ComponentM ...
- IntelliJ IDEA 2017版 使用笔记(十一) Debug操作:IDEA 快捷键
调试功能; 缩短项目时间,调高阅读源码的能力. 一.添加断点,选中一行代码,双击即可生成断点(快捷键:ctrl+F8) 二.单步运行,快捷键:s ...
- hadoop hive组件介绍及常用cli命令
Hive架构图 Hive产生原因 1 关系型数据库以产生多年sql成熟 2 简化开发降低成本 3 java成员可编写udf函数 Hive是什么 Hive是基于hadoop的一个数据库工具,使用Hql作 ...
- php输出语句
看不懂? 抄一遍代码吧. echo 和 print 在 PHP 中有两个基本的输出方式: echo 和 print echo 和 print 区别: echo - 可以输出一个或多个字符串 print ...
- ERR_CACHE_MISS 上一页提示确认重新提交表单
SITUATION: 设备搜索后,根据返回结果 list.php 进入特定设备的详细页面 one.php,但点击后退按钮(上一页)返回 list.php,会出现确认重新提交表单的错误页面 ERR_CA ...