android 内联 hook

先回顾下 x86 下的内联 hook.
1.原理是找到你要 hook 的地址。
2.保存这个地址原来的数据。(这里要保存至少 5 个字节的数据因为一个 call指令为 5 个字节
3.把这个地址修改成 call 0Xxxxx(5 个字节 ) 也就是对应 opcode 为 E9 0Xxxxx 后面四个字节为一个函数地址
4.填充0Xxxxx 公式为 自己的函数地址-当前地址-5
5.把原来的那个 5 个字节的 opcode 还原重新从 hook点开始执行
arm 下
第一点有 俩种模式Arm模式与Thumb模式
在Arm版本7及以上的体系中,其指令集分为Arm指令集和Thumb指令集。Arm指令为4字节对齐,每条指令长度均为32位;Thumb指令为2字节对齐,又分为Thumb16、Thumb32,其中Thumb16指令长度为16位,Thumb32指令长度为32位。
第二点函数调用指令为:
B系列指令:B、BL、BX、BLX
PC寄存器 相当于 eip
第三点不这俩种模式流水线级数不同。arm一般为 3 级 。也就是说在 arm模式下 pc 指向下一条指令的向一条。因为 hook是 pc 要回退 4 个字节(4字节对齐)
因此对应:

LDR PC, [PC, #-4]
addr

而Thumb(32位)不用回退

LDR.W PC, [PC, #0]
addr

因为也就相当于 LDR.W PC, [PC, #0]/LDR PC, [PC, #-4] 对应x86 中的 call 对应 opcode
call : E9
LDR.W PC, [PC, #0]:0x00F0DFF8 (LDR.W强制以 4 字节)
LDR PC, [PC, #-4] :0xE51FF004
后面就与 x86 方式一样了修改地址:

x86
call 0Xxxxx
arm
LDR PC, [PC, #-4]
Thumb(32位)
addr
LDR.W PC, [PC, #0]
addr

最后要注意一个非常容易忽略的点就是地址一般为 4 个字节,也就是说Thumb(32位)的每条指令是 2 个字节对齐的因此只能 2 个字节的写。而 arm 每条指令为 4 字节对齐。因此应该考虑有时候只有 2 字节,也就必须后俩个字节用 nop填充。因为指令没有 4 字节对齐为非法指令。
也就是写法为:

//开始 hook
//arm 方式就一种
//thumb 方式有俩种  一种是转成arm 再 hook  一种是直接 hook
static void doInlineHook(struct inlineHookItem *item)
{
    //修改页属性
    mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_WRITE | PROT_EXEC);

    if (item->proto_addr != NULL) {
        *(item->proto_addr) = TEST_BIT0(item->target_addr) ? (uint32_t *) SET_BIT0((uint32_t) item->trampoline_instructions) : item->trampoline_instructions;
    }
    //Arm指令为4字节对齐,每条指令长度均为32位;Thumb指令为2字节对齐,
    // 又分为Thumb16、Thumb32,其中Thumb16指令长度为16位,Thumb32指令长度为32位。
    //这里要注意的是:
    //Arm处理器采用3级流水线来增加处理器指令流的速度,也就是说程序计数器R15(PC)总是指向“正在取指”的指令,
    // 而不是指向“正在执行”的,即PC总是指向当前正在执行的指令地址再加2条指令的地址。比如当前指令地址是0×8000,
    // 那么当前pc的值,在thumb下面是0×8000 + 2*2, 在arm下面是0×8000 + 4*2。
    //对于Arm指令集,跳转指令为:
    //
    //LDR PC, [PC, #-4]  要回退一条
    //addr
    //
    //LDR PC, [PC, #-4]对应的机器码为:0xE51FF004,addr为要跳转的地址。
    //该跳转指令范围为32位,对于32位系统来说即为全地址跳转。
    //对于Thumb32指令集,跳转指令为:
    //
    //LDR.W PC, [PC, #0]  不要回退刚好指向下一条
    //addr
    //
    //LDR.W PC, [PC, #0]对应的机器码为:0x00F0DFF8,addr为要跳转的地址。同样支持任意地址跳转。
    //thumb 模式(Thumb32指令集)为俩字节对齐
    if (TEST_BIT0(item->target_addr)) {
        int i;
        i = 0;
        //判断是否为4 字节对齐 用NOP填充
        if (CLEAR_BIT0(item->target_addr) % 4 != 0) {
            ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xBF00;  // NOP
        }
        //LDR伪指令 LDR.W 强制32位
        //俩个字节为一组
        //LDR.W PC, [PC, #0]对应的机器码为:0x00F0DFF8  每次一个四字节要分为俩个2 字节
        ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF8DF;
        ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = 0xF000; // LDR.W PC, [PC]
        //一个四字节地址  同样分为俩个2 字节
        //取低四位
        ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr & 0xFFFF;
        //取高四位
        ((uint16_t *) CLEAR_BIT0(item->target_addr))[i++] = item->new_addr >> 16;
    }
    //arm 模式  指令为4字节对齐
    else {
        //四个字节为一组
        ((uint32_t *) (item->target_addr))[0] = 0xe51ff004; // LDR PC, [PC, #-4]
        //下一条4字节地址
        ((uint32_t *) (item->target_addr))[1] = item->new_addr;
    }

    mprotect((void *) PAGE_START(CLEAR_BIT0(item->target_addr)), PAGE_SIZE * 2, PROT_READ | PROT_EXEC);

    item->status = HOOKED;

    cacheflush(CLEAR_BIT0(item->target_addr), CLEAR_BIT0(item->target_addr) + item->length, 0);
}

源码:
https://github.com/ele7enxxh/Android-Inline-Hook
其它博客地址:
http://ele7enxxh.com/Android-Arm-Inline-Hook.html
https://www.cnblogs.com/mmmmar/p/8185549.html

原文地址:http://blog.51cto.com/haidragon/2150761

时间: 2024-10-14 15:01:56

android 内联 hook的相关文章

合金弹头 逆向分析与外挂制作报告【内联HOOK】

一.工具及游戏介绍 使用工具:Ollydbg,PEID,Cheat Engine 实现功能:玩家无敌 目标:找到全局数据,或关键代码块. 游戏版本:合金弹头1-5代珍藏版 二.逆向逻辑 1.初始判断[CE数据] 通过游戏试玩,发现玩家是一次性死亡,但在复活开始阶段,有闪光的无敌状态. 可利用这点,实现无敌. 刚开始先通过CE找到类似秒数的 复活状态信息数据,并找到修改数据的代码段. 2.OD调试[OD追踪关键代码块] 在秒数数据下硬件写入断点,得到修改数据的代码. 回溯跟踪分析,分析DL的来源,

Android漫游记(5)---ARM GCC 内联汇编烹饪书(附实例分析)

原文链接(点击打开链接) 关于本文档 GNU C编译器针对ARM RISC处理器,提供了内联汇编支持.利用这一非常酷炫的特性,我们可以用来优化软件代码中的关键部分,或者可以使用针对特定处理的汇编处理指令. 本文假定,你已经熟悉ARM汇编语言.本文不是一篇ARM汇编教程,也不是C语言教程. GCC汇编声明 让我们从一个简单的例子开始.下面的一条ARM汇编指令,你可以添加到C源码中. /* NOP example-空操作 */ asm("mov r0,r0"); 上面的指令,讲r0寄存器的

Socket Android手机客户端与PC服务端局域网内联测试

Socket Android手机客户端与PC服务端局域网内联测试,笔者采用的是 PC服务器,Android平板客户端 ,PC模拟器客户端, 前段时间为了加深对Socket通信的印象和知识的深度掌握,我模仿了QQ的一些元素,也借鉴了其他牛人的一些源码思想,写了 一个 叫 IQ 的 Android 聊天软件,该软件大致由 Android客户端.JAVA pc服务端.mysql数据库组成,客户端和服务端都 比较多的用到了读写的IO流,SCOKET,线程等,这篇博客主要给大家介绍测试环境,不讲源码实现方

CSS3与页面布局学习总结(二)——Box Model、边距折叠、内联与块标签、CSSReset

一.盒子模型(Box Model) 盒子模型也有人称为框模型,HTML中的多数元素都会在浏览器中生成一个矩形的区域,每个区域包含四个组成部分,从外向内依次是:外边距(Margin).边框(Border).内边距(Padding)和内容(Content),其实盒子模型有两种,分别是 ie 盒子模型和标准 w3c 盒子模型,加上了doctype声明,让所有浏览器都会采用标准 w3c 盒子模型去解释你的盒子.当设置一个元素的样式如下: <!DOCTYPE html> <html> <

Linux C中内联汇编的语法格式及使用方法(Inline Assembly in Linux C)

在阅读Linux内核源码或对代码做性能优化时,经常会有在C语言中嵌入一段汇编代码的需求,这种嵌入汇编在CS术语上叫做inline assembly.本文的笔记试图说明Inline Assembly的基本语法规则和用法(建议英文阅读能力较强的同学直接阅读本文参考资料中推荐的技术文章 ^_^). 注意:由于gcc采用AT&T风格的汇编语法(与Intel Syntax相对应,二者的区别参见这里),因此,本文涉及到的汇编代码均以AT&T Syntax为准. 1. 基本语法规则 内联汇编(或称嵌入汇

去除MathType内联公式后自动生成的空格

使用MathType公式编辑器在Microsoft Word 2016 中输入的内联公式后会自动产生一个空格,每次都需要手动删除.以下是解决办法,不再需要每次输入公式以后都要点键盘上的backspace去删空格. 1. 打开注册表: 开始-运行-regedit 2. 找到 HKEY_CURRENT_USER\Software\Design Science\DSMT6\WordCommands\ 3. 在该目录里增加字符串NoSpaceAfterInline,并将值设置为1. 退出注册表,运行of

关于C++内联函数

关于C++内联函数有以下实验: 有三段测试代码 1.手动展开内联函数. 2.非内联函数. 3.inline标记的内联函数.(函数只有一行代码,以确保函数被内联) 测试三种情况: VS工程在Release版下的有关内联的三个设置选项 1./Ob0 禁用内联展开(默认情况下是打开的). 2./Ob1 只展开标记为 inline 或 __inline 的函数,或在类声明内定义的 C++ 成员函数中的函数. 3./Ob2 展开标记为 inline 或 __inline 的函数和编译器选择的任何其他函数(

内联框架

主文件: <!doctype html> <html> <head> <meta charset="utf-8"> <title>无标题文档</title> </head> <body> <iframe src="text.html" width="500" height="500" name="one"&

java内联函数

在说内联函数之前,先说说函数的调用过程. 调用某个函数实际上将程序执行顺序转移到该函数所存放在内存中某个地址,将函数的程序内容执行完后,再返回到  转去执行该函数前的地方.这种转移操作要求在转去前要保护现场并记忆执行的地址,转回后先要恢复现场,并按原来保  存地址继续执行.也就是通常说的压栈和出栈.因此,函数调用要有一定的时间和空间方面的开销.那么对于那些函数体  代码不是很大,又频繁调用的函数来说,这个时间和空间的消耗会很大. 那怎么解决这个性能消耗问题呢,这个时候需要引入内联函数了.内联函数