首页
社区
课程
招聘
8
[原创]对AndroidInlineHook的一些研究以及对抗策略
发表于: 2019-9-28 17:20 9629

[原创]对AndroidInlineHook的一些研究以及对抗策略

2019-9-28 17:20
9629
    之前有分析过kstools以及XX管理器的去签名校验功能,XX管理器普通版以及kstools的去签名校验使用动态代理PackageManagerService接口生成的对象替换系统原PMS服务,增强版Hook了Native层标准函数库里的open函数以及对Java层的各种IO类进行了继承重写,然后使用反射替换系统的IO。重写与构造虚假Context绕过一般签名校验的思路一样。
    Android Hook技术目前非常火热,典型产品功能有App插件化,热修复等。在Android逆向破解方面就是注入了,不妨看看这篇:
   上面提到的去签名校验以及替换系统PMS服务都源自于这篇这篇帖子,有详细介绍,下面也会简单带过一下。

    Hook系统服务(修改系统API函数的执行逻辑),有全局Hook和单进程Hook,全局Hook需要Root权限,典型代表开源框架Xposed,Frida。单个进程Hook,代表开源框架Whale,爱奇艺的XHook。单个进程Hook就是自己Hook自己啦。前面提到的替换PMS服务对象就是。那么有人说不要Root权限能不能实现全局Hook呢?可以,你搞定SELinux就行。
     我们知道JDK原生动态代理只能代理接口,因为代理接口后经过动态字节码技术生成的代理class是继承Proxy类的。那为什么代理类一定要继承Proxy类呢?这个问题我查看了许多答案,看的也不是特别明白。如大家有兴趣,可以去搜索一下。下面是一个动态字节码class命名:
public final class $Proxy0 extends Proxy implements MyInterface

    $Proxy0有多少个代理类,0-N
    MyInterface即我们需要代理的接口

    其实我们知道Java平台还有一个动态代理框架:cglib,它不仅仅能够代理接口,还可以代理非接口的类,并且还能够为类的非final方法提供代理,为JDK的动态代理提供了很好的补充。那为什么我们为什么不在Android平台上使用它呢?


     cglib如此强大,但是,它有一个很致命的缺点是:cglib底层是采用著名的ASM字节码生成框架,使用字节码技术生成代理类,也就是通过操作字节码来生成的新的.class文件,而我们在android中加载的是优化后的.dex文件,也就是说我们需要可以动态生成.dex文件代理类,因此cglib在android中是不能使用的。不过,网上已经有人根据dexmaker框架(dex代码生成工具)来仿照cglib库动态生成.dex文件,实现了类似于cglib的AOP的功能,不过还有bug尚待修复,对高版本的Android支持不太友好。

    好了,我们回到正题上来,Android IPackageManager接口正是我们需要代理的接口,因为里面有获取apk包名,签名信息等一些列方法,而且通常我们也会通过这个接口来获取Apk的一些信息。通过代理这个接口,我们可以修改这些函数的返回值,达到我们想要的目的。代理又分为静态代理和动态代理,静态代理就是本文一开始提到的继承重写,这两种代理的优缺点这里不做作阐述。我们现在一般都是使用动态代理来实现一些功能,比如日志功能。现在Java平台非常火热的Spring框架的AOP切面编程就是采用的JDK原生动态代理,原生代理是非侵入式的。通过动态代理掉IPackageManager这个接口生成代理对象后,我们将代理对象替换掉CurrentActivityThread对象里的sPackageManager字段和PackageManager对象里的mPM字段里的原始IPM对象,这样只要进程内调用了IPackageManager这个接口内的任意方法,都会被转发到代理对象的invoke方法里面去,在里面我们就可以执行自定义的逻辑了。


    另外,如果一个类不是接口如何实现Hook呢?我们可以先写一个类继承它,然后重写里面的方法,再使用反射替换就行了。hook 点必须满足以下的条件:需要 hook 的方法,所属的对象必须是静态的,因为我们是通过反射来获取对象的,我们获取的是系统的对象,所以不能够 new 一个新的对象,必须用系统创建的那个对象,所以只有静态的才能保证和系统的对象一致。


     简单介绍了一下Hook,那么我们如何防止被Hook?从而拿到Apk的真实签名信息呢?下面介绍两种方案,一种是恢复被Hook的服务对象,另一种则是我们通过从 /proc/self/maps中解析 SO 符号表得到dlopen和dlsym函数的地址,再通过这两个函数去到系统的/system/lib目录下的libc.so或者libart.so去寻找open函数的真实地址,最后读取Apk的META-INF目录下的CERT.RSA/DSA文件,然后解析出公钥信息。

1.还原被Hook的服务对象

我们分析Android系统源码:
android.app.ActivityThread发现类里面有这样一个方法:

   如果sPackageManager字段为空就会调用IPackageManager内部Stub类的asInterface方法初始化IPackageManager对象,那么Hook PMS就是将 sPackageManager 字段替换了,到这里我们就很容易得出一种还原PMS服务的方法:先将sPackageManager 字段置空,然后反射调用getPackageManager方法,这样得到的IPM对象就是原始的服务对象,再利用反射将PMS对象替换成原始的对象就好了。

   但是,我们这里不采用这种方式,因为跟进asInterface这个方法我们不难发现: 这里的queryLocalInterface方法也是一个Hook点。

   为什么说 queryLocalInterface方法也是一个Hook点呢?这里我以Hook queryLocalInterface方法的思路来阐述:

   这个方法的意思就是:先查看本进程是否存在这个Binder对象,如果有那么直接就是本进程调用了;

如果不存在那么创建一个代理对象,让代理对象委托驱动完成跨进程调用。

观察这个方法,前面的那个if语句判空返回肯定动不了手脚;最后一句调用构造函数然后直接返回我们也是无从下手,

要修改asInterface方法的返回值,我们唯一能做的就是从这一句下手:

1
2
3
4
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); // Hook点
 
     
IBinder b = ServiceManager.getService("service_name");

   我们修改这个getService方法的返回值,让这个方法返回一个我们伪造过的IBinder对象;这样,

我们可以在自己伪造的IBinder对象的queryLocalInterface方法作处理,进而使得asInterface方法返回

在queryLocalInterface方法里面处理过的值,最终实现hook系统服务的目的。


   在跟踪这个getService方法之前我们思考一下,由于系统服务是一系列的远程Service,它们的本体,

也就是Binder本地对象一般都存在于某个单独的进程,在这个进程之外的其他进程存在的都是这些Binder本地对象的代理。

因此在我们的进程里面,存在的也只是这个Binder代理对象,我们也只能对这些Binder代理对象下手。然后,

这个getService是一个静态方法,如果此方法什么都不做,拿到Binder代理对象之后直接返回;

那么我们就无能为力了:我们没有办法拦截一个静态方法,也没有办法获取到这个静态方法里面的局部变量(即我们希望修改的那个Binder代理对象)。


   接下来就可以看这个getService的代码了:


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2020-2-24 21:20 被LivedForward编辑 ,原因:
收藏
免费 8
支持
分享
赞赏记录
参与人
雪币
留言
时间
PLEBFE
为你点赞~
2023-1-22 01:54
白小菜
为你点赞~
2019-12-4 16:52
        _109563
为你点赞~
2019-11-7 13:13
GitHub_Y
为你点赞~
2019-9-29 11:03
mb_clactxcr
为你点赞~
2019-9-29 10:23
FanciSun
为你点赞~
2019-9-29 10:00
mb_rpehfvtr
为你点赞~
2019-9-29 09:55
LivedForward
为你点赞~
2019-9-28 22:38
打赏 + 2.00雪花
打赏次数 2 雪花 + 2.00
收起 
赞赏  GitHub_Y   +1.00 2019/09/29 期待作者有更加优秀的作品哦!
赞赏  FanciSun   +1.00 2019/09/29 优秀!
最新回复 (25)
雪    币: 37711
活跃值: (7365)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
转给mt开发者会如何?  你们接着PK 
2019-9-28 21:27
1
雪    币: 6124
活跃值: (4806)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
3
这种对抗真的毫无意义
2019-9-28 23:01
0
雪    币: 1892
活跃值: (1618)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
黑洛 这种对抗真的毫无意义
这样说,攻防都是相对的,都是彼此互相发展.
2019-9-28 23:31
4
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
还可以
2019-9-29 10:00
3
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
优秀!
2019-9-29 10:00
3
雪    币: 162
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
楼主研究的是Inline Hook,针对所有的Inline Hook都有效,麻烦你看清楚再发表意见!
2019-9-29 10:24
2
雪    币: 46
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
不错,期待作者有更加优秀的作品哦!
2019-9-29 11:07
1
雪    币: 7
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
11
黑洛 这种对抗真的毫无意义
你以为世界上的都是以你为中心吗?你做的都是伟大的事情,别人做的都是毫无意义!再说楼主是研究的InlineHook,你搞破解也就算了,还不允许别人做防护是吗?
2019-9-29 17:51
1
雪    币: 15407
活跃值: (6673)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
hook不一定要修改APK?也就是说签名并没有修改呀
2019-9-30 08:10
0
雪    币: 1892
活跃值: (1618)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
13
tDasm hook不一定要修改APK?也就是说签名并没有修改呀
这里是InlineHook,不是全局Hook哦。
2019-9-30 13:11
0
雪    币: 35
活跃值: (88)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
LivedForward 这里是InlineHook,不是全局Hook哦。
inline hook也可以通过virtual app之类的方式进行,不需要修改签名吧
2019-9-30 14:00
0
雪    币: 1892
活跃值: (1618)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
15
winkar inline hook也可以通过virtual app之类的方式进行,不需要修改签名吧
VirtualApp检测防护:https://www.cnblogs.com/ichunqiu/p/9340364.html
最后于 2019-9-30 17:40 被LivedForward编辑 ,原因:
2019-9-30 17:37
0
雪    币: 1109
活跃值: (3626)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
19
楼主的方法还能再改进一下:
1. libc 的函数大多可以用系统调用替换或封装实现,如 `open` 对应的 `SYS_OPEN` ,从 Bionic 的源码里拷出来就能用;
2. dlopen/dlsym 也是被 Hook 的常见点,可以换成从 `/proc/self/maps` 中解析 SO 符号表
2019-10-8 10:47
0
雪    币: 1892
活跃值: (1618)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
20
Amun 楼主的方法还能再改进一下: 1. libc 的函数大多可以用系统调用替换或封装实现,如 `open` 对应的 `SYS_OPEN` ,从 Bionic 的源码里拷出来就能用; 2. dlopen/ ...
是的,感谢您的批评指正,小弟学到了!
2019-10-10 07:34
0
雪    币: 1892
活跃值: (1618)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
21
Amun 楼主的方法还能再改进一下: 1. libc 的函数大多可以用系统调用替换或封装实现,如 `open` 对应的 `SYS_OPEN` ,从 Bionic 的源码里拷出来就能用; 2. dlopen/ ...
大佬,我想再问一下,是否可以Hook SYS_OPEN呢?
2019-10-12 10:44
0
雪    币: 1109
活跃值: (3626)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
22
LivedForward 大佬,我想再问一下,是否可以Hook SYS_OPEN呢?
可以,用内核模块对 `sys_call_table` 里的函数地址进行替换,可能需要重新编译内核使其支持内核模块功能。
2019-10-12 12:41
0
雪    币: 1892
活跃值: (1618)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
23
Amun 楼主的方法还能再改进一下: 1. libc 的函数大多可以用系统调用替换或封装实现,如 `open` 对应的 `SYS_OPEN` ,从 Bionic 的源码里拷出来就能用; 2. dlopen/ ...
dlopen,dlsym我已经自己实现了,但是您说的第一种方案,我我从bionic拷贝出来后,我不能很好的编译它,请问您能对第一种方案作详细一点的解释吗?
最后于 2019-10-13 21:06 被LivedForward编辑 ,原因:
2019-10-13 16:56
0
雪    币: 1109
活跃值: (3626)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
24
LivedForward Amun 楼主的方法还能再改进一下: 1. libc 的函数大多可以用系统调用替换或封装实现,如 `open` 对应的 `SYS_OPEN` ,从 Bion ...
已经够清楚了,再好好研究下 CMakeLists.txt 的用法 [\doge]
2019-10-14 17:44
0
雪    币: 15407
活跃值: (6673)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
你用哪个函数获取签名?
既然inlinehook,别人不会hook你那个函数返回原始签名?
2019-10-21 09:37
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册