hook initarray

如果init、initarray的函数是反调试函数的话,我们需要把这些函数给hook掉

hook时机分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void* do_dlopen(const char* name, int flags,
const android_dlextinfo* extinfo,
const void* caller_addr) {

// 省略...
ProtectedDataGuard guard;
// 已经完成了so的加载,返回了一个soinfo*的结构体指针
soinfo* si = find_library(ns, translated_name, flags, extinfo, caller);
loading_trace.End();

if (si != nullptr) {
// 返回handle
void* handle = si->to_handle();
LD_LOG(kLogDlopen,
"... dlopen calling constructors: realpath=\"%s\", soname=\"%s\", handle=%p",
si->get_realpath(), si->get_soname(), handle);
// 最佳的hook位置,在执行call_constructors之前hook init和initarray
// 因为call_constructors有函数符号,不需要计算地址
// 而且此时so文件已经加载完毕
// 调用构造函数,在call_constructors内部调用了initarray和init
si->call_constructors();
failure_guard.Disable();
LD_LOG(kLogDlopen,
"... dlopen successful: realpath=\"%s\", soname=\"%s\", handle=%p",
si->get_realpath(), si->get_soname(), handle);
return handle;
}

return nullptr;
}

由于Android系统是64位,逆向分析/system/bin/linker64可执行文件

在符号表里搜索到call_constructorsEv函数,在call_constructorsEv函数onEnter的时候去hook掉initinitarray

代码逻辑就是要先通过hook dlopen来判断是否是我们要hook的so文件,因为其他的so文件也会调用call_constructorsEv,没有这个判断的话会影响其他so文件加载,然后通过Interceptor.replace替换到initarray函数,注意只能替换一次,所以要加上判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
function main() {
function hook_dlopen(addr, soName, callback) {
Interceptor.attach(addr, {
onEnter: function (args) {
var soPath = args[0].readCString();
if(soPath.indexOf(soName) != -1) hook_call_constructors();
}, onLeave: function (retval) {
}
});
}
var dlopen = Module.findExportByName("libdl.so", "dlopen");
var android_dlopen_ext = Module.findExportByName("libdl.so", "android_dlopen_ext");
hook_dlopen(dlopen, "libxiaojianbang.so", inlineHook);
hook_dlopen(android_dlopen_ext, "libxiaojianbang.so", inlineHook);

var isHooked = false;
function hook_call_constructors() {
var symbols = Process.getModuleByName("linker64").enumerateSymbols();
var call_constructors_addr = null;
for (let i = 0; i < symbols.length; i++) {
var symbol = symbols[i];
if(symbol.name.indexOf("__dl__ZN6soinfo17call_constructorsEv") != -1){
call_constructors_addr = symbol.address;
}
}
console.log("call_constructors_addr: ", call_constructors_addr);
Interceptor.attach(call_constructors_addr, {
onEnter: function (args) {
if(!isHooked) {
hook_initarray();
isHooked = true;
}
}, onLeave: function (retval) {
}
});
}

function hook_initarray(){
var xiaojianbangAddr = Module.findBaseAddress("libxiaojianbang.so");
// 这里都是目标initarray函数在so文件中的偏移
var func1_addr = xiaojianbangAddr.add(0x1C54);
var func2_addr = xiaojianbangAddr.add(0x1C7C);
var func3_addr = xiaojianbangAddr.add(0x1C2C);
// replace的第一个参数是函数地址,第二个参数类似于NativeFunction声明
Interceptor.replace(func1_addr, new NativeCallback(function () {
console.log("func1 is replaced!!!");
}, 'void', []));

Interceptor.replace(func2_addr, new NativeCallback(function () {
console.log("func2 is replaced!!!");
}, 'void', []));

Interceptor.replace(func3_addr, new NativeCallback(function () {
console.log("func3 is replaced!!!");
}, 'void', []));
}
}
main();

hook JNI_OnLoad

hook时机,在dlopen onleave的时候去hook,此时JNI_OnLoad还未执行,必须以no-pause启动hook,不然so文件完全加载完成之后也来不及hook了,而以no-pause启动的时候,此时js代码加载了而so文件未加载,如果不hook dlopen的话,也是找不到so的基址的

其实和hook普通so层函数没有区别,也是获取so文件加载基址(因为dlopen已经执行完毕,可以定位到so的基址,initinitarray可以理解为在dlopen内部执行,所以相对复杂一点)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//hook_dlopen
function hook_dlopen(addr, soName, callback) {
Interceptor.attach(addr, {
onEnter: function (args) {
var soPath = args[0].readCString();
if(soPath.indexOf(soName) != -1) this.hook = true;
}, onLeave: function (retval) {
if(this.hook) callback();
}
});
}

var dlopen = Module.findExportByName("libdl.so", "dlopen");
var android_dlopen_ext = Module.findExportByName("libdl.so", "android_dlopen_ext");
hook_dlopen(dlopen, "libxiaojianbang.so", hook_JNIOnload);
hook_dlopen(android_dlopen_ext, "libxiaojianbang.so", hook_JNIOnload);

function hook_JNIOnload() {
// 假设要hook的函数偏移是0x1CCC
var xiaojianbangAddr = Module.findBaseAddress("libxiaojianbang.so");
var funcAddr = xiaojianbangAddr.add(0x1CCC);
Interceptor.replace(funcAddr, new NativeCallback(function () {
console.log("this func is replaced !");
}, 'void', []));
}

hook pthread_create

一些检测函数需要实时运行,那么就有可能用pthread开启一个子线程

hook它来查看开启了哪些子线程,把和检测相关的子线程hook掉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function hook_pthread_create(){
var pthread_create_addr = Module.findExportByName("libc.so", "pthread_create");
console.log("pthread_create_addr: ", pthread_create_addr);
Interceptor.attach(pthread_create_addr,{
onEnter:function(args){
// 通过pthread_create的参数来得到子线程函数所处的模块以及相对偏移
console.log(args[0], args[1], args[2], args[3]);
var Module = Process.findModuleByAddress(args[2]);
if(Module != null) console.log(Module.name, args[2].sub(Module.base));
},onLeave:function(retval){
}
})
}
hook_pthread_create();