学习frida检测原理后, 向朋友寻找样本练手, 正好是某*加固企业版
根据网上资料成功绕过大部分样本的检测, 但某icbc是新版加固, 原方案不起作用
最终通过trace成功定位检测点并绕过, 写下此篇分享思路
注意: 由于复现时疏忽, 在Pixel6a上绕过旧版加固时使用了florida, 原版frida hook clone时进程仍然会被杀而非打印线程函数, 在Pixel3XL上绕过新版加固全程使用原版frida, 可参考新版定位和绕过思路 (我的锅)
工具:
设备环境:
文中样本均下载自应用商店,日期为12.8-12.10
声明:
关键SO: libDexHelper.so
样本: com.jxbank.mbank
搜集包名,加固方案,lib库等信息
hook dlopen 定位检测so, 低版本使用"dlopen", 高版本"android_dlopen_ext"
如果加载该so时, 在init, init_array, JNI_OnLoad等位置检测, 则加载该so后会立马杀掉进程
但在加载libDexHelper.so后一段时间才退出进程, 说明该so中创建了子线程进行检测

linux中最常用的线程创建函数是pthread_create, 定义如下
在hook dlopen中添加代码, 当加载libDexHelper.so时hook pthread_create减少多余输出(libart也会创建很多线程)
hook_pthread_create函数中打印创建的线程函数所在so以及偏移地址
结果如下, 没有成功打印

所以可能并没有走pthread_create创建线程,而是更底层的系统调用clone
hook clone, 打印线程函数所在模块和偏移
注意: 不能同时hook pthread_thread, 否则崩溃, 推测是因为libDexHelper检测了有没有hook libc的pthread_thread
结果如下, 输出的线程函数都在libc.so+0xc9c00中
注意: 由于复现时疏忽, 在Pixel6a上绕过旧版加固使用了florida, 原版frida hook clone时进程仍然会被杀而非打印线程函数
在Pixel3XL上绕过新版加固全程使用原版frida, 可参考新版定位和绕过思路

拉取libc.so扔进ida分析该地址
0xc9c00对应函数是"__pthread_start", 交叉引用发现在libc的"pthread_create"函数中通过clone封装了该函数
即pthread_create本质上通过clone创建子线程, 子线程函数地址作为clone的参数传递
可以发现pthread_create的参数3线程函数地址传给此处v30+96, 封装后作为clone的参数4

所以, 修改hook_clone, 通过参数4+96即可拿到真正的子线程函数地址
参考文章【APP 逆向百例】某当劳 Frida 检测 中的解释
可以发现成功输出libDexHelper.so中创建了哪些线程检测函数

一个简单的思路是直接nop检测函数
成功绕过检测进入app

至此某*加固旧版的frida检测成功绕过, 但检测函数到底做了什么? 有待进一步分析
想分析检测函数中具体做了什么需要分析libDexHelper.so, 直接提取apk中的so可以发现是加密状态
start没有指令全是数据, init_proc则是有elf相关结构, 可能是自定义linker

在dlopen刚加载该so后进行dump, 此时从文件加载到内存中, 应是解密状态
dump成功, 进入手机端的shell复制到"/sdcard/Download"下方便pull

adb pull到电脑端后使用SoFixer修复

修复后可以正常分析so, 这里贴上2个检测函数


经过以上分析, 可以总结出通杀脚本如下
首次执行时注释bypass_detect_func, 打印libDexHelper.so创建的线程函数地址
之后修改bypass_detect_func中nopFunc的偏移地址, 再次执行即可绕过
除此之外, 其他bank样本也可以使用相同方法绕过检测
大部分bank是某*企业版, 招商是网易易盾, 兴业是爱加密
BOC

ABC

CCB

四大行中ICBC比较特殊, 是新版某*加固, 且无法使用旧版的思路定位检测点
假设hook clone, 绕过了线程检测函数, 此时再hook pthread_create进程仍会结束
不难猜测是libDexHelper.so中检测了libc.so的pthread_create是否被hook
整理文章时发现此问题, 但由于新版加固中已经绕过该检测, 故不再深究旧版

继续使用上述通杀脚本, 分别hook pthread_create和clone, 均没有打印出线程函数, 进程直接被杀
hook pthread_create

hook clone

进程被杀, 没有其他信息, 但可以尝试trace进程崩溃前的函数执行流从而定位检测点
oacia师傅的 stalker_trace_so 插件可以实现该功能, 使用插件前需要dump so, 同上操作类似不多赘述
将stalker_trace_so.py复制到ida的plugins目录下即可使用
使用方式为Edit > Plugins > stalker trace so, 自动生成trace脚本, spawn模式运行即可

脚本需要修改so_name为app运行时so的真实名称 (默认为ida打开的文件名)
hook_dlopen 当加载目标so时, 执行trace

spawn模式执行脚本
trace结果如下
不难看出在JNI_OnLoad中执行了一系列操作以及检测, 调用 "_ZNK63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl512validAddressEPvS1" 后进程被杀
直接搜索validaddress, 向上交叉引用发现JNI_OnLoad中调用了
"p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl5::p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl5::p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISISSISlS5Sl5IS0SI5S5S5SI5lSl5"
很明显的检查操作并根据返回值决定是否杀掉进程, 没有调用exit, kill等函数, 而是jump 非法内存

观察if条件, 当该函数返回0时, if条件必定不满足, 那么hook replace该函数, 强制返回值为0便可以绕过
成功绕过该检测, 但仍有其他检测

绕过该检测后继续尝试hook pthread_create 和 clone
结果如下, hook pthread_create和旧版类似, 仍然会被杀(检测libc.so的pthread_create是否被hook)
但hook clone成功输出创建的检测线程 (此处可能出现6或7个, 似乎有2个并不是检测frida)

基于此, 同旧版类似, 尝试直接nop线程检测函数
绕过线程检测函数后成功进入app

至此可以得出结论: 新版相对旧版额外添加了检测函数0x36390, 绕过后和旧版思路基本相同
新旧版都检测了libc.so的pthread_create, 到底在哪里检测? 这是接下来的重点内容
在stalker_trace_so脚本中绕过检测并trace
可以发现最后崩溃时调用了几个函数,逐一查看后发现sub_54B20函数疑似检测libc和libart

交叉引用可以发现该函数有多处调用, 并且传入了libart.so

libc.so有1处调用, 并且根据结果判断是否要jump 非法内存退出

绕过思路同上类似, replace该函数, 强制返回0, 打印函数参数观察检测了哪些东西
同时hook pthread_create观察进程是否会被杀
可以发现成功绕过了检测libc.so

那么libart.so在哪里检测的呢? 只需要注释 bypass_detect_func (nop线程检测函数) 即可

完整日志如下, libart主要检测用于注册/注销native方法的函数, 以及修复静态类的跳板函数
关于绕过某*加固的新版和旧版frida检测的绕过至此结束, 以上分析比较基础, 没有深入检测原理, 以及深究是否存在其他检测
整理时发现不够细心没考虑到不同情况导致走了很多弯路, 下面介绍trace指令流定位而非函数调用链定位
以下内容基于最初认为: 新版加固没有创建线程检测函数 (不走pthread_create以及clone,可能有其他创建线程的骚操作), 而是在JNI_OnLoad进行所有检测, 仅通过stalker_trace_so无法成功定位, 必须使用更强大的trace工具, 从trace函数调用链向指令流进发
那么,回到最开始的地方: hook pthread_create , 并通过trace JNI_OnLoad的指令流定位检测点
工具使用追佬的 vm-trace , 详情可参考文章[原创]基于VM的全新Trace框架发布!功能强大,一分钟1.5g,提高你的逆向体验~
使用前需要准备环境:
Android 14以下的测试机
推送test.so到app私有目录 "/data/data/<package_name>/"
由于我的Pixel 3XL Android10关闭selinux后有bug, 不能spawn启动app
所以不将test.so推送到/data/local/tmp, 而是app私有目录
虽然提取log更麻烦但不需要额外权限
脚本如下:
hook_soload 和 prepareArgs 是原脚本自带的函数
hook_soload主动加载了test.so到app中, 加载目标so后执行hook和trace函数
hook_JNI_OnLoad 是hook函数模版, 用于hook指定地址的函数, 可自定义
设置好函数偏移地址即可, 注意函数参数个数和类型不能弄错
调用了test.so的vmtrace进行trace
test.so和log均使用app私有目录
spawn模式启动

这里有个小技巧:
我们的主要目的是定位崩溃点和检测函数, 而非根据trace还原算法
log.txt可能几千万行代码, 4-5GB, 提取到pc再分析太慢, 使用tail命令打印最后几行trace即可
tail命令默认打印10行, 可通过-n参数指定行数
不难发现在0x382c4处跳转到非法内存0x12dc导致app进程被杀

跟进so发现关键检测函数, 根据上述内容, 直接hook replace该函数并强制返回0即可绕过

绕过函数
trace结果, 成功绕过

新的崩溃点在0x3a5d8

跟进后发现调用sub_54B20后根据返回值判断是否jump非法内存
而54b20前文提到过, 用于检测libc.so和libart.so的关键函数是否被hook

同上, replace该函数, 强制返回0
绕过该检测后trace发现hook pthread_create成功, 打印出检测线程函数

同上nop这些检测函数即可
绕过检测函数后成功进入app

另外虽然此处崩溃, 但trace结果反复循环一段代码, 即单字节读取maps的内容

跟进发现代码属于sub_5d368函数, 扫描maps

值得一提的是之前使用stalker_trace_so没有发现该函数, 不hook该函数也能正常绕过检测
测试发现如果trace JNI_OnLoad, 无论是否hook该函数都会崩溃, 推测有其他检测, 或者trace对环境有影响
不hook 5D368时

hook 5D368时

trace结果如下 (两次trace报错结果一致)

列举分析过程中遇到的检测函数和疑似的检测函数
函数内部有多处疑似inline hook检测的特征

交叉引用该函数发现疑似hook 了自身的read, open, openat等函数

所以该函数大概率是hook自身的read/open系列函数, 使用自定义的函数, 防止被逆向人员hook
在尝试hook ae1f4后会导致崩溃, 即执行到frida的hook跳转代码后被识别为非法内存
0x42098函数伪代码如下, 打开maps后单字节读取指定so的部分内存属性, 但没有杀进程

检测root, frida, xposed, hook等特征

使用原版SoFixer会出现导入表符号不匹配问题
如图所示左边是正确符号,右边是错误符号, 非常影响分析

修复方案参考 [原创] SoFixer导入表问题修复及ELF解析简记 但修复版 SoFixer 没有Release包
感谢小风提供编译好的修复版 SoFixer
【APP 逆向百例】某当劳 Frida 检测
从inlinehook角度检测frida
[原创]《安卓逆向这档事》第十九课、表哥,你也不想你的Frida被检测吧!(下)
[原创]基于VM的全新Trace框架发布!功能强大,一分钟1.5g,提高你的逆向体验~
function hook_dlopen() {
const funcName = "android_dlopen_ext";
const libc = Module.findBaseAddress("libc.so");
var funcPtr = Module.findExportByName(null, funcName);
if (funcPtr !== null && funcPtr !== undefined) {
console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`);
Interceptor.attach(funcPtr, {
onEnter: function (args) {
this.pathPtr = args[0];
if (this.pathPtr !== null && this.pathPtr !== undefined) {
try {
var path = this.pathPtr.readCString();
console.log("\x1b[36m[dlopen] \x1b[0m" + path);
} catch (e) {
console.log("[!] Error reading path string in " + this.funcName);
}
}
}
});
} else {
console.log("[-] Warning: " + funcName + " not found in exports.");
}
}
function main(){
hook_dlopen();
}
setImmediate(main);
function hook_dlopen() {
const funcName = "android_dlopen_ext";
const libc = Module.findBaseAddress("libc.so");
var funcPtr = Module.findExportByName(null, funcName);
if (funcPtr !== null && funcPtr !== undefined) {
console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`);
Interceptor.attach(funcPtr, {
onEnter: function (args) {
this.pathPtr = args[0];
if (this.pathPtr !== null && this.pathPtr !== undefined) {
try {
var path = this.pathPtr.readCString();
console.log("\x1b[36m[dlopen] \x1b[0m" + path);
} catch (e) {
console.log("[!] Error reading path string in " + this.funcName);
}
}
}
});
} else {
console.log("[-] Warning: " + funcName + " not found in exports.");
}
}
function main(){
hook_dlopen();
}
setImmediate(main);
#include <pthread.h>
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg);
#include <pthread.h>
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg);
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) {
var thread_func_addr = args[2];
var module = Process.findModuleByAddress(thread_func_addr);
console.log(`pthread_create thread func: ${module.name}+0x${(thread_func_addr - module.base).toString(16)}`);
}, onLeave: function (retval) {
}
});
}
function hook_dlopen() {
const funcName = "android_dlopen_ext";
const libc = Module.findBaseAddress("libc.so");
var funcPtr = Module.findExportByName(null, funcName);
if (funcPtr !== null && funcPtr !== undefined) {
console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`);
Interceptor.attach(funcPtr, {
onEnter: function (args) {
this.pathPtr = args[0];
if (this.pathPtr !== null && this.pathPtr !== undefined) {
try {
var path = this.pathPtr.readCString();
console.log("\x1b[36m[dlopen] \x1b[0m" + path);
if (path.indexOf("libDexHelper.so") !== -1) {
this.isTarget = true;
}
} catch (e) {
console.log("[!] Error reading path string in " + this.funcName);
}
}
}, onLeave: function (retval) {
if (this.isTarget) {
hook_pthread_create();
}
}
});
} else {
console.log("[-] Warning: " + funcName + " not found in exports.");
}
}
function main(){
hook_dlopen();
}
setImmediate(main);
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) {
var thread_func_addr = args[2];
var module = Process.findModuleByAddress(thread_func_addr);
console.log(`pthread_create thread func: ${module.name}+0x${(thread_func_addr - module.base).toString(16)}`);
}, onLeave: function (retval) {
}
});
}
function hook_dlopen() {
const funcName = "android_dlopen_ext";
const libc = Module.findBaseAddress("libc.so");
var funcPtr = Module.findExportByName(null, funcName);
if (funcPtr !== null && funcPtr !== undefined) {
console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`);
Interceptor.attach(funcPtr, {
onEnter: function (args) {
this.pathPtr = args[0];
if (this.pathPtr !== null && this.pathPtr !== undefined) {
try {
var path = this.pathPtr.readCString();
console.log("\x1b[36m[dlopen] \x1b[0m" + path);
if (path.indexOf("libDexHelper.so") !== -1) {
this.isTarget = true;
}
} catch (e) {
console.log("[!] Error reading path string in " + this.funcName);
}
}
}, onLeave: function (retval) {
if (this.isTarget) {
hook_pthread_create();
}
}
});
} else {
console.log("[-] Warning: " + funcName + " not found in exports.");
}
}
function main(){
hook_dlopen();
}
setImmediate(main);
#define _GNU_SOURCE
#include <sched.h>
int clone(int (*fn)(void *), void *stack, int flags, void *arg, ...);
#define _GNU_SOURCE
#include <sched.h>
int clone(int (*fn)(void *), void *stack, int flags, void *arg, ...);
function hook_clone() {
var clone_addr = Module.findExportByName("libc.so", "clone");
if (clone_addr) {
Interceptor.attach(clone_addr, {
onEnter: function (args) {
var addr = args[0];
if (!addr.isNull()) {
var mod = Process.findModuleByAddress(addr);
if (mod) {
var offset = addr.sub(mod.base);
console.log(`[+]${mod.name}!${offset}`);
} else {
console.log(`[+] Unknown Module (Anonymous Memory), Addr: ${addr}`);
}
}
}
});
} else {
console.log("[-] clone export not found in libc.so");
}
}
function hook_dlopen() {
const funcName = "android_dlopen_ext";
const libc = Module.findBaseAddress("libc.so");
var funcPtr = Module.findExportByName(null, funcName);
if (funcPtr !== null && funcPtr !== undefined) {
console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`);
Interceptor.attach(funcPtr, {
onEnter: function (args) {
this.pathPtr = args[0];
if (this.pathPtr !== null && this.pathPtr !== undefined) {
try {
var path = this.pathPtr.readCString();
console.log("\x1b[36m[dlopen] \x1b[0m" + path);
if (path.indexOf("libDexHelper.so") !== -1) {
this.isTarget = true;
}
} catch (e) {
console.log("[!] Error reading path string in " + this.funcName);
}
}
}, onLeave: function (retval) {
if (this.isTarget) {
hook_clone();
}
}
});
} else {
console.log("[-] Warning: " + funcName + " not found in exports.");
}
}
function main() {
hook_dlopen();
}
setImmediate(main);
function hook_clone() {
var clone_addr = Module.findExportByName("libc.so", "clone");
if (clone_addr) {
Interceptor.attach(clone_addr, {
onEnter: function (args) {
var addr = args[0];
if (!addr.isNull()) {
var mod = Process.findModuleByAddress(addr);
if (mod) {
var offset = addr.sub(mod.base);
console.log(`[+]${mod.name}!${offset}`);
} else {
console.log(`[+] Unknown Module (Anonymous Memory), Addr: ${addr}`);
}
}
}
});
} else {
console.log("[-] clone export not found in libc.so");
}
}
function hook_dlopen() {
const funcName = "android_dlopen_ext";
const libc = Module.findBaseAddress("libc.so");
var funcPtr = Module.findExportByName(null, funcName);
if (funcPtr !== null && funcPtr !== undefined) {
console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`);
Interceptor.attach(funcPtr, {
onEnter: function (args) {
this.pathPtr = args[0];
if (this.pathPtr !== null && this.pathPtr !== undefined) {
try {
var path = this.pathPtr.readCString();
console.log("\x1b[36m[dlopen] \x1b[0m" + path);
if (path.indexOf("libDexHelper.so") !== -1) {
this.isTarget = true;
}
} catch (e) {
console.log("[!] Error reading path string in " + this.funcName);
}
}
}, onLeave: function (retval) {
if (this.isTarget) {
hook_clone();
}
}
});
} else {
console.log("[-] Warning: " + funcName + " not found in exports.");
}
}
function main() {
hook_dlopen();
}
setImmediate(main);
adb pull /system/lib64/libc.so ./libc64.so
adb pull /system/lib64/libc.so ./libc64.so
function hook_clone() {
var clone = Module.findExportByName('libc.so', 'clone');
Interceptor.attach(clone, {
onEnter: function (args) {
if (args[3] != 0) {
var thread_func_addr = args[3].add(96).readPointer()
var module = Process.findModuleByAddress(thread_func_addr);
var offset = (thread_func_addr - module.base);
console.log(`[+]libc.so clone thread func: ${module.name}+0x${offset.toString(16)}`);
}
}
});
}
function hook_clone() {
var clone = Module.findExportByName('libc.so', 'clone');
Interceptor.attach(clone, {
onEnter: function (args) {
if (args[3] != 0) {
var thread_func_addr = args[3].add(96).readPointer()
var module = Process.findModuleByAddress(thread_func_addr);
var offset = (thread_func_addr - module.base);
console.log(`[+]libc.so clone thread func: ${module.name}+0x${offset.toString(16)}`);
}
}
});
}
function nopFunc(addr) {
Memory.protect(addr, 4, 'rwx');
var writer = new Arm64Writer(addr);
writer.putRet();
writer.flush();
writer.dispose();
console.log("nop " + addr + " success");
}
function bypass_detect_func() {
var base = Module.findBaseAddress("libDexHelper.so")
nopFunc(base.add(0x561d0));
nopFunc(base.add(0x52cc0));
nopFunc(base.add(0x5ded4));
nopFunc(base.add(0x5e410));
nopFunc(base.add(0x5fb48));
nopFunc(base.add(0x592c8));
nopFunc(base.add(0x69470));
}
function hook_dlopen() {
const funcName = "android_dlopen_ext";
const libc = Module.findBaseAddress("libc.so");
var funcPtr = Module.findExportByName(null, funcName);
if (funcPtr !== null && funcPtr !== undefined) {
console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`);
Interceptor.attach(funcPtr, {
onEnter: function (args) {
this.pathPtr = args[0];
if (this.pathPtr !== null && this.pathPtr !== undefined) {
try {
var path = this.pathPtr.readCString();
console.log("\x1b[36m[dlopen] \x1b[0m" + path);
if (path.indexOf("libDexHelper.so") !== -1) {
this.isTarget = true;
}
} catch (e) {
console.log("[!] Error reading path string in " + this.funcName);
}
}
}, onLeave: function (retval) {
if (this.isTarget) {
bypass_detect_func();
}
}
});
} else {
console.log("[-] Warning: " + funcName + " not found in exports.");
}
}
function main() {
hook_dlopen();
}
setImmediate(main);
function nopFunc(addr) {
Memory.protect(addr, 4, 'rwx');
var writer = new Arm64Writer(addr);
writer.putRet();
writer.flush();
writer.dispose();
console.log("nop " + addr + " success");
}
function bypass_detect_func() {
var base = Module.findBaseAddress("libDexHelper.so")
nopFunc(base.add(0x561d0));
nopFunc(base.add(0x52cc0));
nopFunc(base.add(0x5ded4));
nopFunc(base.add(0x5e410));
nopFunc(base.add(0x5fb48));
nopFunc(base.add(0x592c8));
nopFunc(base.add(0x69470));
}
function hook_dlopen() {
const funcName = "android_dlopen_ext";
const libc = Module.findBaseAddress("libc.so");
var funcPtr = Module.findExportByName(null, funcName);
if (funcPtr !== null && funcPtr !== undefined) {
console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`);
Interceptor.attach(funcPtr, {
onEnter: function (args) {
this.pathPtr = args[0];
if (this.pathPtr !== null && this.pathPtr !== undefined) {
try {
var path = this.pathPtr.readCString();
console.log("\x1b[36m[dlopen] \x1b[0m" + path);
if (path.indexOf("libDexHelper.so") !== -1) {
this.isTarget = true;
}
} catch (e) {
console.log("[!] Error reading path string in " + this.funcName);
}
}
}, onLeave: function (retval) {
if (this.isTarget) {
bypass_detect_func();
}
}
});
} else {
console.log("[-] Warning: " + funcName + " not found in exports.");
}
}
function main() {
hook_dlopen();
}
setImmediate(main);
function dump_so(so_name, package_name) {
var libso = Process.getModuleByName(so_name);
console.log("[name]:", libso.name);
console.log("[base]:", libso.base);
console.log("[size]:", ptr(libso.size));
console.log("[path]:", libso.path);
var file_path = "/data/data/" + package_name + "/" + libso.name + "_" + libso.base + "_" + ptr(libso.size) + ".so";
var file_handle = new File(file_path, "wb");
if (file_handle && file_handle != null) {
Memory.protect(ptr(libso.base), libso.size, 'rwx');
var libso_buffer = ptr(libso.base).readByteArray(libso.size);
file_handle.write(libso_buffer);
file_handle.flush();
file_handle.close();
console.log("[dump]:", file_path);
}
}
function hook_dlopen_dump_so(soName, package_name) {
var once_flag = true;
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();
if (path.indexOf(soName) >= 0) {
this.is_can_hook = true;
}
}
},
onLeave: function (retval) {
if (this.is_can_hook && once_flag) {
dump_so(soName, package_name);
once_flag = false;
}
}
}
);
}
function main(){
hook_dlopen_dump_so("libDexHelper.so","com.icbc");
}
setImmediate(main);
function dump_so(so_name, package_name) {
var libso = Process.getModuleByName(so_name);
console.log("[name]:", libso.name);
console.log("[base]:", libso.base);
console.log("[size]:", ptr(libso.size));
console.log("[path]:", libso.path);
var file_path = "/data/data/" + package_name + "/" + libso.name + "_" + libso.base + "_" + ptr(libso.size) + ".so";
var file_handle = new File(file_path, "wb");
if (file_handle && file_handle != null) {
Memory.protect(ptr(libso.base), libso.size, 'rwx');
var libso_buffer = ptr(libso.base).readByteArray(libso.size);
file_handle.write(libso_buffer);
file_handle.flush();
file_handle.close();
console.log("[dump]:", file_path);
}
}
function hook_dlopen_dump_so(soName, package_name) {
var once_flag = true;
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();
if (path.indexOf(soName) >= 0) {
this.is_can_hook = true;
}
}
},
onLeave: function (retval) {
if (this.is_can_hook && once_flag) {
dump_so(soName, package_name);
once_flag = false;
}
}
}
);
}
function main(){
hook_dlopen_dump_so("libDexHelper.so","com.icbc");
}
setImmediate(main);
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) {
var thread_func_addr = args[2];
var module = Process.findModuleByAddress(thread_func_addr);
console.log(`pthread_create thread func: ${module.name}+0x${(thread_func_addr - module.base).toString(16)}`);
}, onLeave: function (retval) {
}
});
}
function hook_clone() {
var clone = Module.findExportByName('libc.so', 'clone');
Interceptor.attach(clone, {
onEnter: function (args) {
if (args[3] != 0) {
var thread_func_addr = args[3].add(96).readPointer()
var module = Process.findModuleByAddress(thread_func_addr);
var offset = (thread_func_addr - module.base);
console.log(`[+]libc.so clone thread func: ${module.name}+0x${offset.toString(16)}`);
}
}
});
}
function nopFunc(addr) {
Memory.protect(addr, 4, 'rwx');
var writer = new Arm64Writer(addr);
writer.putRet();
writer.flush();
writer.dispose();
console.log("nop " + addr + " success");
}
function bypass_detect_func() {
var base = Module.findBaseAddress("libDexHelper.so")
nopFunc(base.add(0x561d0));
nopFunc(base.add(0x52cc0));
nopFunc(base.add(0x5ded4));
nopFunc(base.add(0x5e410));
nopFunc(base.add(0x5fb48));
nopFunc(base.add(0x592c8));
nopFunc(base.add(0x69470));
}
function hook_dlopen() {
const funcName = "android_dlopen_ext";
const libc = Module.findBaseAddress("libc.so");
var funcPtr = Module.findExportByName(null, funcName);
if (funcPtr !== null && funcPtr !== undefined) {
console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`);
Interceptor.attach(funcPtr, {
onEnter: function (args) {
this.pathPtr = args[0];
if (this.pathPtr !== null && this.pathPtr !== undefined) {
try {
var path = this.pathPtr.readCString();
console.log("\x1b[36m[dlopen] \x1b[0m" + path);
if (path.indexOf("libDexHelper.so") !== -1) {
this.isTarget = true;
}
} catch (e) {
console.log("[!] Error reading path string in " + this.funcName);
}
}
}, onLeave: function (retval) {
if (this.isTarget) {
hook_clone();
bypass_detect_func();
}
}
});
} else {
console.log("[-] Warning: " + funcName + " not found in exports.");
}
}
function main() {
hook_dlopen();
}
setImmediate(main);
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) {
var thread_func_addr = args[2];
var module = Process.findModuleByAddress(thread_func_addr);
console.log(`pthread_create thread func: ${module.name}+0x${(thread_func_addr - module.base).toString(16)}`);
}, onLeave: function (retval) {
}
});
}
function hook_clone() {
var clone = Module.findExportByName('libc.so', 'clone');
Interceptor.attach(clone, {
onEnter: function (args) {
if (args[3] != 0) {
var thread_func_addr = args[3].add(96).readPointer()
var module = Process.findModuleByAddress(thread_func_addr);
var offset = (thread_func_addr - module.base);
console.log(`[+]libc.so clone thread func: ${module.name}+0x${offset.toString(16)}`);
}
}
});
}
function nopFunc(addr) {
Memory.protect(addr, 4, 'rwx');
var writer = new Arm64Writer(addr);
writer.putRet();
writer.flush();
writer.dispose();
console.log("nop " + addr + " success");
}
function bypass_detect_func() {
var base = Module.findBaseAddress("libDexHelper.so")
nopFunc(base.add(0x561d0));
nopFunc(base.add(0x52cc0));
nopFunc(base.add(0x5ded4));
nopFunc(base.add(0x5e410));
nopFunc(base.add(0x5fb48));
nopFunc(base.add(0x592c8));
nopFunc(base.add(0x69470));
}
function hook_dlopen() {
const funcName = "android_dlopen_ext";
const libc = Module.findBaseAddress("libc.so");
var funcPtr = Module.findExportByName(null, funcName);
if (funcPtr !== null && funcPtr !== undefined) {
console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`);
Interceptor.attach(funcPtr, {
onEnter: function (args) {
this.pathPtr = args[0];
if (this.pathPtr !== null && this.pathPtr !== undefined) {
try {
var path = this.pathPtr.readCString();
console.log("\x1b[36m[dlopen] \x1b[0m" + path);
if (path.indexOf("libDexHelper.so") !== -1) {
this.isTarget = true;
}
} catch (e) {
console.log("[!] Error reading path string in " + this.funcName);
}
}
}, onLeave: function (retval) {
if (this.isTarget) {
hook_clone();
bypass_detect_func();
}
}
});
} else {
console.log("[-] Warning: " + funcName + " not found in exports.");
}
}
function main() {
hook_dlopen();
}
setImmediate(main);
frida -Uf com.icbc -l trace_libDexHelper_fixed_icbc_kwajw.js
frida -Uf com.icbc -l trace_libDexHelper_fixed_icbc_kwajw.js
[Pixel 3 XL::com.icbc ]-> start Stalker!
Stalker end!
call1:JNI_OnLoad
call2:readlink
call3:j_p5SS$Sl5S5IS55I5_5ISI5I5SS5SIS0SlSISlS5S55_SOSI5lS0SlSIS_SOS05$S0
call4:p5SS$Sl5S5IS55I5_5ISI5I5SS5SIS0SlSISlS5S55_SOSI5lS0SlSIS_SOS05$S0
call5:j_pSO5_5SSI5$S$SI5I5l5ISlSOS05l5ISISISIS0SISISI5$5_S$SISIS5S05S5$SI
call6:pSO5_5SSI5$S$SI5I5l5ISlSOS05l5ISISISIS0SISISI5$5_S$SISIS5S05S5$SI
call7:j_pS_5$SlSO5SS$SI5ISlSISOSl5SSIS0Sl5I5S5I5S5IS$S_SISI5I5ISl5I5_S$S5
call8:pS_5$SlSO5SS$SI5ISlSISOSl5SSIS0Sl5I5S5I5S5IS$S_SISI5I5ISl5I5_S$S5
call9:sub_41FE0
call10:setsockopt
call11:j_pS$SlS_S_S$5ISI5_5S5ISlSI5_SlS55lSIS0SI5IS$SIS0SOS05_5IS_Sl5_SI5I
call12:pS$SlS_S_S$5ISI5_5S5ISlSI5_SlS55lSIS0SI5IS$SIS0SOS05_5IS_Sl5_SI5I
call13:j_pSl5I5$S5Sl5lSOSISOSIS0Sl5$5lSlS05lSlSI5_SISlS5SlSOSl5S5I5ISlSIS0
call14:pSl5I5$S5Sl5lSOSISOSIS0Sl5$5lSlS05lSlSI5_SISlS5SlSOSl5S5I5ISlSIS0
call15:getppid
call16:j_p5lS05ISISOSl5lS$SIS$5S5I5ISIS$S_5lSIS05S5I5SS_5I5lSO5_SOSI5_S05I
call17:p5lS05ISISOSl5lS$SIS$5S5I5ISIS$S_5lSIS05S5I5SS_5I5lSO5_SOSI5_S05I
call18:sub_32B78
call19:sched_yield
call20:socket
call21:fscanf
call22:atoi
call23:strrchr
call24:__strlen_chk
call25:__strncpy_chk2
call26:__vsprintf_chk
call27:connect
call28:fread
call29:regfree
call30:readdir
call31:__strncpy_chk
call32:wcstoul
call33:__errno
call34:sub_42098
call35:fputc
call36:dl_iterate_phdr
call37:j__ZN63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl5C2ENSt6__ndk117basic_string_viewIcNS1_11char_traitsIcEEEE
call38:_ZN63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl5C2ENSt6__ndk117basic_string_viewIcNS1_11char_traitsIcEEEE
call39:j__ZN63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl562p5lS_SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SIlS5_Ev
call40:_ZN63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl562p5lS_SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SIlS5_Ev
call41:fileno
call42:__sF
call43:sub_309C0
call44:sub_72CA8
call45:sub_308E0
call46:sub_45D38
call47:sub_2F8A0
call48:sub_B3B40
call49:sub_30270
call50:sub_B85DC
call51:sub_30E50
call52:sub_33590
call53:sub_335FC
call54:sub_3360C
call55:strtoll
call56:j_j_strtol
call57:j_strtol
call58:strtol
call59:sub_31500
call60:sub_35E24
call61:memcmp
call62:strtoul
call63:pthread_mutex_lock
call64:j__ZN63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl55parseEP9elf64_hdr
call65:_ZN63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl55parseEP9elf64_hdr
call66:__strchr_chk
call67:j__ZNK63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISISSISlS5Sl5IS0SI5S5S5SI5lSl5Ev
call68:_ZNK63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISISSISlS5Sl5IS0SI5S5S5SI5lSl5Ev
call69:fdopen
call70:j__ZNK63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl512validAddressEPvS1_
call71:_ZNK63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl512validAddressEPvS1_
call72:__open_2
call73:mprotect
Process terminated
[Pixel 3 XL::com.icbc ]->
Thank you for using Frida!
[Pixel 3 XL::com.icbc ]-> start Stalker!
Stalker end!
call1:JNI_OnLoad
call2:readlink
call3:j_p5SS$Sl5S5IS55I5_5ISI5I5SS5SIS0SlSISlS5S55_SOSI5lS0SlSIS_SOS05$S0
call4:p5SS$Sl5S5IS55I5_5ISI5I5SS5SIS0SlSISlS5S55_SOSI5lS0SlSIS_SOS05$S0
call5:j_pSO5_5SSI5$S$SI5I5l5ISlSOS05l5ISISISIS0SISISI5$5_S$SISIS5S05S5$SI
call6:pSO5_5SSI5$S$SI5I5l5ISlSOS05l5ISISISIS0SISISI5$5_S$SISIS5S05S5$SI
call7:j_pS_5$SlSO5SS$SI5ISlSISOSl5SSIS0Sl5I5S5I5S5IS$S_SISI5I5ISl5I5_S$S5
call8:pS_5$SlSO5SS$SI5ISlSISOSl5SSIS0Sl5I5S5I5S5IS$S_SISI5I5ISl5I5_S$S5
call9:sub_41FE0
call10:setsockopt
call11:j_pS$SlS_S_S$5ISI5_5S5ISlSI5_SlS55lSIS0SI5IS$SIS0SOS05_5IS_Sl5_SI5I
call12:pS$SlS_S_S$5ISI5_5S5ISlSI5_SlS55lSIS0SI5IS$SIS0SOS05_5IS_Sl5_SI5I
call13:j_pSl5I5$S5Sl5lSOSISOSIS0Sl5$5lSlS05lSlSI5_SISlS5SlSOSl5S5I5ISlSIS0
call14:pSl5I5$S5Sl5lSOSISOSIS0Sl5$5lSlS05lSlSI5_SISlS5SlSOSl5S5I5ISlSIS0
call15:getppid
call16:j_p5lS05ISISOSl5lS$SIS$5S5I5ISIS$S_5lSIS05S5I5SS_5I5lSO5_SOSI5_S05I
call17:p5lS05ISISOSl5lS$SIS$5S5I5ISIS$S_5lSIS05S5I5SS_5I5lSO5_SOSI5_S05I
call18:sub_32B78
call19:sched_yield
call20:socket
call21:fscanf
call22:atoi
call23:strrchr
call24:__strlen_chk
call25:__strncpy_chk2
call26:__vsprintf_chk
call27:connect
call28:fread
call29:regfree
call30:readdir
call31:__strncpy_chk
call32:wcstoul
call33:__errno
call34:sub_42098
call35:fputc
call36:dl_iterate_phdr
call37:j__ZN63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl5C2ENSt6__ndk117basic_string_viewIcNS1_11char_traitsIcEEEE
call38:_ZN63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl5C2ENSt6__ndk117basic_string_viewIcNS1_11char_traitsIcEEEE
call39:j__ZN63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl562p5lS_SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SIlS5_Ev
call40:_ZN63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl562p5lS_SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SIlS5_Ev
call41:fileno
call42:__sF
call43:sub_309C0
call44:sub_72CA8
call45:sub_308E0
call46:sub_45D38
call47:sub_2F8A0
call48:sub_B3B40
call49:sub_30270
call50:sub_B85DC
call51:sub_30E50
call52:sub_33590
call53:sub_335FC
call54:sub_3360C
call55:strtoll
call56:j_j_strtol
call57:j_strtol
call58:strtol
call59:sub_31500
call60:sub_35E24
call61:memcmp
call62:strtoul
call63:pthread_mutex_lock
call64:j__ZN63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl55parseEP9elf64_hdr
call65:_ZN63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl55parseEP9elf64_hdr
call66:__strchr_chk
call67:j__ZNK63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISISSISlS5Sl5IS0SI5S5S5SI5lSl5Ev
call68:_ZNK63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISISSISlS5Sl5IS0SI5S5S5SI5lSl5Ev
call69:fdopen
call70:j__ZNK63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl512validAddressEPvS1_
call71:_ZNK63p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISlS5Sl5IS0SI5S5S5SI5lSl563p5lS5SISISlSl5lSlSl5SIS05l5SIS5I5ISI5SISIS5Sl5IS0SI5S5S5SI5lSl512validAddressEPvS1_
call72:__open_2
call73:mprotect
Process terminated
[Pixel 3 XL::com.icbc ]->
Thank you for using Frida!
function hook_36390() {
var targetAddr = Module.findBaseAddress("libDexHelper.so").add(0x36390);
var patch_detection = new NativeCallback(function (arg0) {
console.log(">>> 检测函数 sub_36390 已触发, 拦截并返回 0");
return 0;
}, 'int', ['pointer']);
Interceptor.replace(targetAddr, patch_detection);
}
function hook_dlopen() {
const funcName = "android_dlopen_ext";
const libc = Module.findBaseAddress("libc.so");
var funcPtr = Module.findExportByName(null, funcName);
if (funcPtr !== null && funcPtr !== undefined) {
console.log(`[*] Hooking ${funcName} at libc.so!0x${(funcPtr - libc.base).toString(16)}`);
Interceptor.attach(funcPtr, {
onEnter: function (args) {
this.pathPtr = args[0];
if (this.pathPtr !== null && this.pathPtr !== undefined) {
try {
var path = this.pathPtr.readCString();
console.log("\x1b[36m[dlopen] \x1b[0m" + path);
if (path.indexOf("libDexHelper.so") !== -1) {
this.isTarget = true;
}
} catch (e) {
console.log("[!] Error reading path string in " + this.funcName);
}
}
}, onLeave: function (retval) {
if (this.isTarget) {
hook_36390();
}
}
});
} else {
console.log("[-] Warning: " + funcName + " not found in exports.");
}
}
function main() {
hook_dlopen();
}
setImmediate(main);
function hook_36390() {
var targetAddr = Module.findBaseAddress("libDexHelper.so").add(0x36390);
var patch_detection = new NativeCallback(function (arg0) {
console.log(">>> 检测函数 sub_36390 已触发, 拦截并返回 0");
return 0;
}, 'int', ['pointer']);
Interceptor.replace(targetAddr, patch_detection);
}
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 4小时前
被东方玻璃编辑
,原因: 添加旧版复现时florida避坑