遇见个汽车app 是个企业壳 有frida检测,导致无法注入,本文记录下绕过的过程(使用的frida版本葫芦嗒)


通过查壳发现,这是某企业壳 知其特点检测主要是在dexhelp这个so文件中,也就省去了定位检测位置这一步
尝试hook pthread_create 定位线程 通过杀线程方法干掉他
1
2
3
4
5
6
7
8
9
10
11
|
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){
console.log(args[ 2 ], Process.findModuleByAddress(args[ 2 ]).name);
},onLeave:function(retval){
}
});
}
hook_pthread_create();
|

发现没啥用 应该是检测到 pthread_create
被hook了 既然pthread_create
被检测了 那么是否还有其他方法定位线程呢,于是乎 我打开了逆向小助手 gpt 首先我去问了下pthread_create
实现原理 gpt 给出了这个回答

然后 我又去问了gpt hook pthread_create
被检测到了 该怎么办 他建议我去hook 其他相关函数 比如pthread_join
clone 通过gpt给出的答案 pthread_create
的实现会调用 clone
系统调用来创建新线程
他既然检测 pthread_create
函数头被hook 那clone函数应该没有把 通过hook clone函数 得到线程相关信息然后去nop 干掉线程 是不是 也能一把梭了 说干就干

首先 我去hook dlopen 在打开dexhelp这个so的时候 去hook下jnionload 看看它做手脚的地方是否在init里面 发现不是 那我只需要去hook clone函数 打印出相关线程信息 然后 加载这个so的时候 nop 掉它 测试是否能绕过即可
于是开始写hook 首先 找gpt要了一份 clone实现创建线程的例子
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
|
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#define STACK_SIZE 1024*1024 // 分配线程栈空间
/ / 子线程函数
int thread_func(void * arg) {
printf( "Hello from the new thread!\n" );
return 0 ;
}
int main() {
char * stack; / / 存放线程栈的空间
char * stackTop;
/ / 为线程分配栈
stack = malloc(STACK_SIZE);
if (stack = = NULL) {
perror( "malloc" );
exit( 1 );
}
stackTop = stack + STACK_SIZE; / / 设置栈顶
/ / 使用 clone 创建新线程
int pid = clone(thread_func, stackTop, SIGCHLD, NULL); / / 子进程退出时会发出 SIGCHLD 信号
if (pid = = - 1 ) {
perror( "clone" );
exit( 1 );
}
/ / 等待线程结束
printf( "Waiting for the thread to finish...\n" );
waitpid(pid, NULL, 0 );
free(stack); / / 释放栈空间
return 0 ;
}
|
传进去第一个参数是 子线程函数 于是乎 嘎嘎嘎hook 并且打印 结果发现全是libc.so 这是咋回事呢 看来还得好好学习 于是乎 我拿了一份libc.so ida打开它 看pthread_create 如何 去调用clone函数创建线程的
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
|
/ / Hook clone 系统调用
Interceptor.attach(Module.findExportByName(null, 'clone' ), {
onEnter: function (args) {
/ / 获取线程函数的地址(即第一个参数)
var thread_func = args[ 0 ];
/ / 打印线程函数地址
console.log( 'Thread function address: ' + thread_func);
/ / 获取线程函数所在模块(.so 文件)
var module = Process.findModuleByAddress(thread_func);
if (module) {
console.log( 'Thread function is located in module: ' + module.name);
} else {
console.log( 'Thread function is not in a loaded module or location could not be determined.' );
}
},
onLeave: function (retval) {
/ / 可以在这里修改 clone 的返回值(如需要)
}
});
输出
Thread function address: 0x749b4140dc
Thread function is located in module: libc.so
Thread function address: 0x749b4140dc
Thread function is located in module: libc.so
Thread function address: 0x749b4140dc
Thread function is located in module: libc.so
|
在上诉 clone 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
|
Interceptor.attach(Module.findExportByName(null, 'clone' ), {
onEnter: function (args) {
/ / 获取线程函数地址
var thread_func = args[ 0 ];
/ / 尝试获取线程函数所在模块
var module = Process.findModuleByAddress(thread_func);
if (module) {
console.log( 'Thread function is located in module: ' + module.name);
} else {
}
/ / 打印调用栈
console.log( 'Backtrace:' );
console.log(Thread.backtrace(this.context, Backtracer.ACCURATE)
. map (DebugSymbol.fromAddress).join( '\n' ));
},
onLeave: function (retval) {
/ / 返回值处理(如果需要)
}
});
结果
Thread function is located in module: libc.so
Backtrace:
0x749b413e40 libc.so!pthread_create + 0x27c
|
然后 IDA跳转到pthread_create+0x27c 此处 调用了函数E6850 于是进去瞧一瞧 发现是DCQ数据

继续使用frida hook 读取下 结果是clone 也就说这里调用了clone函数
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
|
Interceptor.attach(Module.findExportByName(null, 'clone' ), {
onEnter: function (args) {
/ / 获取线程函数地址
var thread_func = args[ 0 ];
/ / 尝试获取线程函数所在模块
var module = Process.findModuleByAddress(thread_func);
if (module) {
console.log( 'Thread function is located in module: ' + module.name);
const address = module.base.add( 0xF06E8 );
const value = Memory.readU64(address);
const symbol = DebugSymbol.fromAddress(ptr(value));
console.log( "name" ,symbol.name)
} else {
}
/ / 打印调用栈
},
onLeave: function (retval) {
/ / 返回值处理(如果需要)
}
});
结果 clone
|

观察 pthread_create 函数的传参走向 也就是 a3


此处的 E6850是clone 而 通过 pthread_create 函数创建线程传递的 线程函数 就是 v50 而clone函数的第四个参数 v27 + 96 是所需要的线程函数 这样 就可以去试试 写一下hook clone代码了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
var clone = Module.findExportByName( 'libc.so' , 'clone' );
Interceptor.attach(clone, {
onEnter: function(args) {
/ / args[ 3 ] 子线程的栈地址。如果这个值为 0 ,可能意味着没有指定栈地址
if (args[ 3 ] ! = 0 ){
var addr = args[ 3 ].add( 96 ).readPointer()
var so_name = Process.findModuleByAddress(addr).name;
var so_base = Module.getBaseAddress(so_name);
var offset = (addr - so_base);
console.log( "===============>" , so_name, addr,offset, offset.toString( 16 ));
}
},
onLeave: function(retval) {
}
});
|
然后运行后发现 非常阔以 打印出来了我们所需要的参数 非常nice

最后 我们 去给他nop掉 即可
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
|
function hook_dlopen(so_name) {
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext" ), {
onEnter: function (args) {
var pathptr = args[ 0 ];
if (pathptr ! = = undefined && pathptr ! = null) {
var path = ptr(pathptr).readCString();
/ / console.log(path)
if (path.indexOf(so_name) ! = = - 1 ) {
this.match = true
}
}
},
onLeave: function (retval) {
if (this.match) {
console.log(so_name, "加载成功" );
var base = Module.findBaseAddress( "libDexHelper.so" )
patch_func_nop(base.add( 322008 ));
patch_func_nop(base.add( 308756 ));
}
}
});
}
function patch_func_nop(addr) {
Memory.patchCode(addr, 8 , function (code) {
code.writeByteArray([ 0xE0 , 0x03 , 0x00 , 0xAA ]);
code.writeByteArray([ 0xC0 , 0x03 , 0x5F , 0xD6 ]);
console.log( "patch code at " + addr)
});
}
hook_dlopen( "libDexHelper.so" )
|
最后 看结果
