Android Native Hook工具实践

> 本文章所对应项目长期维护与更新,因为在我自己的几台测试机上用得还挺顺手的。本项目作为作者本人的一个学习项目将会长期更新以修复当前可能存在的Bug以及跟进以后Android NDK可能出现的主流汇编模式。

> 本文为作者本人原创,转载请注明出处:GToad Blog银河安全实验室

> GToad Blog原文会有更好的排版

 

## 前言

 

在目前的安卓APP测试中对于Native Hook的需求越来越大,越来越多的APP开始逐渐使用NDK来开发核心或者敏感代码逻辑。个人认为原因如下:

1. 安全的考虑。各大APP越来越注重安全性,NDK所编译出来的so库逆向难度明显高于java代码产生的dex文件。越是敏感的加密算法与数据就越是需要用NDK进行开发。

2. 性能的追求。NDK对于一些高性能的功能需求是java层无法比拟的。

3. 手游的兴起。虚幻4,Unity等引擎开发的手游中都有大量包含游戏逻辑的so库。

 

因此,本人调查了一下Android Native Hook工具目前的现状。尽管Java层的Hook工具多种多样,但是Native Hook的工具却非常少并且在`安卓5.0以上`的适配工具更是寥寥无几。(文末说明1)而目前Native Hook主要有两大技术路线:

1. PLT Hook

2. Inline Hook

 

这两种技术路线本人都实践了一下,关于它们的对比,我在《Android Native Hook技术路线概述》中有介绍,所以这里就不多说了。最终,我用了`Inline Hook`来做这个项目。

 

本文篇幅已经较长,因此写了一些独立的学习笔记来对其中的细节问题进行解释:

1. 《Android Native Hook技术路线概述》

2. 《Android Inline Hook中的指令修复》

3. 项目仓库

4. 项目案例——Arm32

5. 项目案例——Thumb-2


 

## 目标效果

 

根据本人自身的使用需求提出了如下几点目标:

 

1. 工具运行原理中不能涉及调试目标APP,否则本工具在遇到反调试措施的APP时会失效。尽管可以先去逆向调试patch掉反调试功能,但是对于大多数情况下只是想看看参数和返回值的Hook需求而言,这样的前期处理实在过于麻烦。

2. 依靠现有的各大Java Hook工具就能运行本工具,换句话说就是最好能用类似这些工具的插件的形式加载起本工具从而获得Native Hook的能力。由于Java Hook工具如Xposed、YAHFA等对于各个版本的Android都做了不错的适配,因此利用这些已有的工具即可向目标APP的Native层中注入我们的Hook功能将会方便很多小伙伴的使用。

3. 既然要能够让各种Java Hook工具都能用本工具得到Native Hook的能力,那就这个工具就要有被加载起来以后自动执行自身功能逻辑的能力!而不是针对各个Java Hook工具找调用起来的方式。

4. 要适配Android NDK下的armv7和thumb-2指令集。由于现在默认编译为thumb-2模式,所以对于thumb16和thumb32的Native Hook支持是重中之重。

5. 修复Inline Hook后的原本指令。

6. Hook目标的最小单位至少是函数,最好可以是某行汇编代码。

 

## 最终方案

 

最后完成项目的方案是:本工具是一个so库。用Java Hook工具在APP的入口Activity运行一开始的onCreate方法处Hook,然后加载本so。

加载后,自动开始执行Hook逻辑。

为了方便叙述,接下来的Java Hook工具我就使用目前这类工具里最流行的`Xposed`,本项目的生成文件名为`libautohook.so`。


 

## 自动执行

 

我们只是用Xposed加载了这个libautohook.so,那其中的函数该怎么自动执行呢?

目前想到两个方法:

 

1. 利用JniOnload来自动执行。该函数是NDK中用户可以选择性自定义实现的函数。如果用户不实现,则系统默认使用NDK的版本为1.1。但是如果用户有定义这个函数,那Android VM就会在System.loadLibrary()加载so库时自动先执行这个函数来获得其返回的版本号。尽管该函数最终要返回的是NDK的版本号,但是其函数可以加入任意其它逻辑的代码,从而实现加载so时的自动执行。这样就能优先于所有其它被APP NDK调用的功能函数被调用,从而进行Hook。目前许多APP加固工具和APP初始化工作都会用此方法。

2. 本文采用的是第二种方法。该方法网络资料中使用较少。它是利用了`__attribute__((constructor))`属性。使用这个constructor属性编译的普通ELF文件被加载入内存后,最先执行的不是main函数,而是具有该属性的函数。同样,本项目中利用此属性编译出来的so文件被加载后,尽管so里没有main函数,但是依然能优先执行,且其执行甚至在JniOnload之前。于是逆向分析了一下编译出来的so库文件。发现具有`constructor`属性的函数会被登记在.init_array中。(相对应的`destructor`属性会在ELF卸载时被自动调用,这些函数会被登记入.fini_array)

 

 

值得一提的是,`constructor`属性的函数是可以有多个的,对其执行顺序有要求的同学可以通过在代码中对这些函数声明进行排序从而改变其在.init_array中的顺序,二者是按顺序对应的。而执行时,会从.init_array中自上而下地执行这些函数。所以图中的自动优先执行顺序为:main5->main3->main1->main2->main4。并且后面会说到,从+1可以看出这些函数是thumb模式编译的。

 

## 方案设计

 

先说一下使用的工具:

 

1. 使用`keystone`查找指定架构下汇编指令的机器码

2. 使用`MS VISIO`制作了下面的设计图

3. 调试工具用的是`IDA pro`

 

#### Arm32方案

 

现在我们的代码可以在一开始就执行了,那该如何设计这套Inline Hook方案呢?目标是thumb-2和arm指令集下是两套相似的方案。我参考了腾讯游戏安全实验室的一篇教程,其中给出了一个初步的armv7指令集下的Native Hook方案,整理后如下图:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值