现在市面上很多SO都是基于动态解密或者自定义Linker把So加载到内存里,本身的Lib目录下面根本没有需要初始化的So,但是不管如何,只要在加载到内存里就一定会在Maps里面有所体现。不管是动态解密的So,还是自定义Linker的方式,我们只需要读取Maps把So这段内存的开始地址和结束地址的内存dump出来,然后配合修复工具就可以进行快速的修复和分析。保存的方式也很方便,IDA或者GG修改器都可以快速的进行内存的dump。那么有没有一种可以自动化dump和修复的办法呢?
通过Hook linker方式自动化dump ,并且使用工具完成修复。
我是用xposed在app未启动之前将我们的SO进行注入。(这个时候So已经加载并且初始化完毕,有内存解密的So也会在这个时候完成解密)我们在通过Hook linker的方式得到加载的时机点,再通过读取Maps获取So的开始地址和结束地址。
使用F8的sofix对保存下来的文件进行修复,修复完毕输出到fix目录。执行完毕。
注入的时间点一定要早,防止很多So是静态代码块初始化的,这个时候只要这个Application初始化了该So就会加载到内存里。也就是说我们需要在Application初始化之前进行注入即可。我直接在XPosed里面的handleLoadPackage进行初始化。
先尝试内存漫游的方式获取context,如果没找到则通过静态的方式创建Context。如果两者都失败了以后Hook createAppContext 方法,拿到返回值,也就是我们需要的Context
因为注入的时候需要获取模块的base.apk作为路径,对base.apk进行解压,得到注入的So,根据路径的内容进行判断目标app是64还是32位。如果对方App存在64位So则默认是64位。我们注入的So 也必须是64位。
这块注入的时候有个细节点问题,classloader的问题。
虚拟机里面确认一个Class或者Classloader
需要判断这个Class的签名和Classloader完全匹配的时候才认为是一个Class。不同Classloader在堆里面被划分成不同的作用域。
在调用nativeLoad进行So加载的时候的时候需要传入一个Object,这个Object是一个Classsloader,因为So和class一样也是有classloader限制。
你So的Classloader和class的classloader一样,才可以在So里面Findclass到这个Class,你才可以对一个native方法进行注册。
否则你Findclass是找不到这个Class的。平时我们开发的时候,一般我们默认system.loadlib用的都是当前进程的classloader
,所以不需要考虑这些问题。
分为三种情况:
讲这个问题之前,我们需要先了解edxp和xposed的classloader的加载区别。
正常xposed ,使用的是PathClassLoader,父类是系统的classloader也就是可以直接使用系统的classloader就可以得到加载模块的这个PathClassLoader。
而在edxp里面使用的classloader是InMemoryDexClassLoader
这个类是8.0以上独有的。
回到下面的问题:
因为我们在so在里面需要对一个native方法进行动态进行注册,所以第一步需要先找到模块加载的Class。所以选择不同的Classloader也会有不同的结果。
如果在native去find我们模块的class的时候,会直接提示class nout found。因为当前进程的classloader里面是没有xposed模块的class的。
使用系统Classloader
只能在只能在正常xposed的环境可以初始化成功,如果切换到edxp会失败。
因为edxp的InMemoryDexClassLoader不是系统的classloader,这显然是不允许的。
使用模块本身的Classloader(最优解)
直接用xposed模块初始化的类.class.getclassloader即可。
代码如下
注册完毕以后开始Linker的Hook
Hooklinker也很简单,遍历linker elf 在内存中的符号
尝试获取dlopen的地址,这个时候需要注意,linker so 这些函数是非导出表,所以需要解析elf的方式进行hook。根据符号获取到函数的地址。
我用的是sandhook里面的elf_utils
Hook成功以后处理linker的回调,需要过滤掉系统的So
所以需要加路径包名判断。只有加载的So包含选择的app的时候才进行dump和修复
通过读取maps把内存端保存到FunELF/temp下。
将修复的So保存到FunELF下,保存的时候别忘了通过mprotect修改so的可读写权限,有些So在保存的时候也需要RWX权限的
修复工具我用的是f8的sofix,因为代码是开源的,之前他是在win上面写的工具
被我改改移植到安卓上面了,项目地址如下。https://github.com/F8LEFT/SoFixer
(他这个got表和init表没有进行base的相减,貌似只做了text段的修复,我也懒得加以后用到再说吧)修复逻辑如下,分别对下面节点进行修复。
修复完毕保存&输出即可。
这种So是不会走linker的所以,我们在dump的时候Hooklinker无效
但是dump也很简单,先cat maps 查看到So的开始地址和结束地址。
直接导出内存即可。在配合win版本的sofix修复即可。
编译好的FunELF和win工具在下方。
private static void loadModule(String apk) {
*
log(
"Loading modules from "
+
apk);
*
*
if
(!new
File
(apk).exists()) {
*
log(
" File does not exist"
);
*
return
;
*
}
*
/
/
加载Xposed模块的 Classloader
*
ClassLoader mcl
=
new PathClassLoader(apk, BOOTCLASSLOADER);
*
*
InputStream
is
=
mcl.getResourceAsStream(
"assets/xposed_init"
);
*
if
(
is
=
=
null) {
*
log(
"assets/xposed_init not found in the APK"
);
*
return
;
*
}
*
.....
*
)
private static void loadModule(String apk) {
*
log(
"Loading modules from "
+
apk);
*
*
if
(!new
File
(apk).exists()) {
*
log(
" File does not exist"
);
*
return
;
*
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)