zoukankan      html  css  js  c++  java
  • Android inline hook手记[转载]

    原网址:http://blog.dbgtech.net/blog/?p=51

    作者:NetRoc

    Android inline hook手记

    说到Inline hook,了解这个词的同志们都应该知道,无非是修改目标函数处的指令,跳转到自己的函数,并且提供调用原函数的stub,即可完成整个流程。但是在ARM下面情况和我们熟悉的x86有所不同。ARM芯片的运行状态分为arm和thumb两种模式,分别有不同的指令集,arm指令为定长32位,thumb指令为定长16位(thumb-2中进行了扩展,可以使用32位thumb指令)。同一段代码中可以混用两套指令集,通过一些带有interworking功能的跳转或者load指令可以在两种模式间切换。做ARM下的inline,首先遇到的就是指令模式的问题。另外,ARM架构下,CPU也具有分开的指令缓存和数据缓存,类似x86下的DTLB和ITLB。但是在实现过程中发现,arm的缓存作用非常明显,而且刷新机制不太确定,因此自修改代码需要经常主动控制缓存的刷新。这一点,可以通过NDK的API cacheflush实现。

    下面简单说说一些主要问题:

    • 关于页保护

      这一点对于熟悉Linux编程的同志们应该不是问题,mprotect修改为PROT_READ | PROT_WRITE | PROT_EXEC即可。页面大小可以通过包含ndk下面<asm/page.h>文件,里面定义的一系列宏用于获得页面大小和进行对齐运算。

    • 关于模式转换和跳转

      Arm下主要的分支指令如BX,BLX等,都可以切换指令模式。详见arm的用户手册。这里主要讨论模式的选择和切换时机。只有一个问题需要注意,arm处理器执行时,由于流水线的关系,会预取两条指令,因此当前指令取到的pc值,始终是之后第三条指令的地址。比如当前指令地址是0x8000,那么当前pc的值,在thumb下面是0x8000 + 2 * 2, 在arm下面是0x8000 + 4 * 2。

      由于运行时我没有找到简便的办法能够确切知道被hook的目标函数指令集,所以这个问题留给了hook的使用者来决定。Hook之前应通过逆向工具获知所有目标函数是arm还是thumb指令。

      如果要根据目标函数指令集的不同而对hook函数采用不同的 编译选项,显然是一件麻烦的事情。而arm模式的指令由于单条指令包含的语义更多,是我们的首选。因此可以考虑主仅使用arm指令编译hook函数,而在跳转的同时切换到arm模式。

      关于跳转插入的指令方面,由于arm指令带立即数的跳转范围只有4M,thumb的跳转范围只有256字节。所以首选ldr pc,xxxx指令来实现。对于arm指令的目标,这个指令很容易选择。如下:

      ldr pc, [pc,#-4]

      32位跳转绝对地址

      指令为单个32位数字:0xE51FF004。

      但是thumb模式下的16位ldr指令没有办法向pc中load,选择就很成问题。如果单纯使用16位thumb指令的话,跳转部分需要占用大量字节数,而因为arm下面编译器常常使用pc的值作为基址来计算地址,被搬动过的指令中就极有可能存在这种指令。搬动过后的代码中就必须对这部分指令进行修正,而又由于thumb所能够支持的立即数很小,跳转范围也很小,这种修正往往非常麻烦,需要用几条同等指令来替换一条指令。经过考虑,还是决定放弃对ARMv5的支持,直接使用ARMv6T2之后支持的thumb-2指令集。thumb-2支持32位thumb指令,也支持ldr以pc为目标寄存器:

      ldr.w pc,[pc,#0]

      32位跳转绝对地址

      指令为单个32位数字:0x00F0DFF8

      所有需要跳转的地址,需要注意的是bit0的处理。如果bit0为1,跳转后会切换到thumb指令模式,如果bit0为0,会切换至arm模式。当目标为arm的时候,我们不需要特殊处理,编译器会处理地址的计算。但是当目标为thumb的时候,从hook指令跳转到hook函数,以及调用原始函数的时候,都需要注意地址bit0的处理。

    • 关于搬出来的原始指令

      按照win32下Detours库的实现方式,被HOOK函数的前面几条指令,会搬到一个trampoline中,并在这些指令后添加跳转至原代码后续部分的指令。在搬动过程中,需要对被移动的指令进行地址修正。在处理ARM平台inline的过程中也需要作这样的工作。但是实际上在处理的时候会发现,要做到这一点是非常困难的。ARM下常常会生成下面这种将pc作为地址参照的指令块

      而由于arm平台寻址范围较小,编译器通常选择将数据和指令在内存中的存放混杂在一起。thumb模式下,由于指令中能包含的立即数非常小,这种问题会表现得异常突出,修正的时候也常常一条指令被拉长为数条。因此代码修正会有非常大的工作量。这部分问题由于太消耗时间,我也仅仅是对arm下的inline进行研究性实现,也就没有管这个问题了。实际项目如果要用到hook,这个部分花费的时间应该比单纯hook跳转的实现要大得多。在不考虑并发和效率的情况下,当hook函数中要调用原函数时,可以考虑临时恢复hook,并在调用完成后再次hook来解决。但是始终是相当不优雅的实现。

    • 关于线程处理

      修改hook目标的指令时,和x86平台下一样,也需要注意有可能某些线程刚好执行到被修改的指令中的问题。Win32下可以枚举线程并修改context到被搬迁的指令中去。但是Linux内核系统下很难进行线程的控制。估计可以采用接管信号处理,并向进程内所有线程调用pthread_kill来实现。

  • 相关阅读:
    什么是ORM
    ORM优缺点
    Azure 中快速搭建 FTPS 服务
    连接到 Azure 上的 SQL Server 虚拟机(经典部署)
    在 Azure 虚拟机中配置 Always On 可用性组(经典)
    SQL Server 2014 虚拟机的自动备份 (Resource Manager)
    Azure 虚拟机上的 SQL Server 常见问题
    排查在 Azure 中新建 Windows 虚拟机时遇到的经典部署问题
    上传通用化 VHD 并使用它在 Azure 中创建新 VM
    排查在 Azure 中新建 Windows VM 时遇到的部署问题
  • 原文地址:https://www.cnblogs.com/chyl411/p/4571957.html
Copyright © 2011-2022 走看看