i春秋作家:v4ever

近日,在研究一些开源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寄存器详解的更多相关文章

  1. 逆向知识第八讲,if语句在汇编中表达的方式

    逆向知识第八讲,if语句在汇编中表达的方式 一丶if else的最简单情况还原(无分支情况) 高级代码: #include "stdafx.h" int main(int argc ...

  2. C++中的STL中map用法详解(转)

    原文地址: https://www.cnblogs.com/fnlingnzb-learner/p/5833051.html C++中的STL中map用法详解   Map是STL的一个关联容器,它提供 ...

  3. php中关于引用(&)详解

    php中关于引用(&)详解 php的引用(就是在变量或者函数.对象等前面加上&符号) 在PHP 中引用的意思是:不同的变量名访问同一个变量内容. 与C语言中的指针是有差别的.C语言中的 ...

  4. Objective-C中的@Property详解

    Objective-C中的@Property详解 @Property (属性) class vairs 这个属性有nonatomic, strong, weak, retain, copy等等 我把它 ...

  5. springcloud中Feign配置详解

    Spring Cloud中Feign配置详解 到目前为止,小伙伴们对Feign的使用已经掌握的差不多了,我们在前文也提到Feign是对Ribbon和Hystrix的整合,那么在Feign中,我们要如何 ...

  6. 【转】linux中inittab文件详解

    原文网址:http://www.2cto.com/os/201108/98426.html linux中inittab文件详解 init的进程号是1(ps -aux | less),从这一点就能看出, ...

  7. 【转】linux 中fork()函数详解

    在看多线程的时候看到了这个函数,于是学习了下,下面文章写的通俗易懂,于是就开心的看完了,最后还是很愉快的算出了他最后一个问题. linux 中fork()函数详解 一.fork入门知识 一个进程,包括 ...

  8. SVD在推荐系统中的应用详解以及算法推导

    SVD在推荐系统中的应用详解以及算法推导     出处http://blog.csdn.net/zhongkejingwang/article/details/43083603 前面文章SVD原理及推 ...

  9. MyBatis中@MapKey使用详解

    MyBatis中@MapKey使用详解我们在上一篇文章中讲到在Select返回类型中是返回Map时,是对方法中是否存在注解@MapKey,这个注解我也是第一次看到,当时我也以为是纯粹的返回单个数据对象 ...

随机推荐

  1. 【Redis】Redis cluster集群搭建

    Redis集群基本介绍 Redis 集群是一个可以在多个 Redis 节点之间进行数据共享的设施installation. Redis 集群不支持那些需要同时处理多个键的 Redis 命令, 因为执行 ...

  2. 关于ueditor 在struts2 中 上传图片 ,未找到上传文件 问题的解决方法

    问题原因: ueditor 上传图片需请求imageUp.jsp文件,struts2 自带的拦截器(/*)把所有请求的文件都做了处理,所以导致无法上传图片. 解决方法: 方法一:自定义拦截器,让它在请 ...

  3. C语言实现用位移运算符进行加减乘…

      最近,在百度知道上回答问题,然后看见有的人问如何用位移运算符去进行加减乘除运算,于是巩固今天就在这总结一下.   先讲讲总体思路: 加法运算:将一个整数用二进制表示,其加法运算就是:相异(^)时, ...

  4. windows10安装ubuntu on windows10

    安装向导 https://msdn.microsoft.com/en-us/commandline/wsl/install_guide

  5. 2019.01.19 codeforces896C.Willem, Chtholly and Seniorious(ODT)

    传送门 ODTODTODT出处(万恶之源) 题目简述: 区间赋值 区间加 区间所有数k次方和 区间第k小 思路:直接上ODTODTODT. 不会的点这里 代码: #include<bits/st ...

  6. HDU 5663 Hillan and the girl (莫比乌斯反演 + 分块)

    题意:给定n,m,求,其中F(x)=0,,如果x是完全平方数,否则是1. 析: 由于按照题意的F,不好筛选,所以我们反过来,F(x),x是平方数,就是1,否则是0. 这个是可以预处理出来的,可以用筛选 ...

  7. iframe父子元素获取

    jquery.js调用iframe父窗口与子窗口元素的方法 1. jquery在iframe子页面获取父页面元素代码如下: $("#objid",parent.document) ...

  8. keras CNN解读

    loss是训练集损失值.  acc是训练集准确率.val_loss是测试集上的损失值,val_acc是测试集上的准确率. 用深度学习框架跑过实际问题的朋友一定有这样的感觉: 太神奇了, 它竟然能自己学 ...

  9. L1范式和L2范式

    正则化(Regularization) 机器学习中几乎都可以看到损失函数后面会添加一个额外项,常用的额外项一般有两种,一般英文称作ℓ1ℓ1-norm和ℓ2ℓ2-norm,中文称作L1正则化和L2正则化 ...

  10. poj 2299 Ultra-QuickSort(归并排序,树状数组,线段树)

    Description In this problem, you have to analyze a particular sorting algorithm. The algorithm proce ...