遇见个汽车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" ) |
最后 看结果
