frida反调试经验-动态库加载
字数 955 2025-08-22 12:23:18
Frida反调试检测与绕过技术详解
一、问题现象分析
当使用Frida进行动态分析时,可能会遇到以下特殊现象:
- 使用
frida spawn或attach应用时,Frida进程被终止(提示"process terminated") - 但spawn出来的APP进程仍然正常运行
这表明目标应用具有反调试防护机制,能够检测并终止附加的Frida进程。
二、反调试检测机制原理
1. 检测代码存放位置
反调试代码通常存放在so动态库中,原因如下:
- 相比放在Java层代码更难以被Frida hook
- 不容易被反编译分析
- 需要重新签名才能篡改
- 执行效率高,不影响应用性能
2. 动态库加载检测技术
应用通过以下系统函数加载动态库:
android_dlopen_ext()dlopen()do_dlopen()find_library()
这些函数都是System.loadLibrary()的底层实现。
三、检测定位技术
1. 动态库加载监控
通过hook动态库加载函数,可以定位检测代码所在的so文件:
function hook_dlopen() {
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("load " + path);
}
}
});
}
2. 检测库定位
当Frida执行到特定so文件(如libllvm1624362448.so)时发生闪退,可以确定检测代码位于该库中。
四、绕过反调试的四种方法
方法1:阻止检测库加载
hook加载函数,将目标so的路径参数置空:
Interceptor.attach(Module.findExportByName(null, "dlopen"), {
onEnter: function(args) {
var path = args[0].readCString();
if (path.indexOf(targetSo) !== -1) {
console.log(`Attempt to dlopen ${targetSo} blocked`);
args[0] = ptr(0); // 将路径设为NULL
}
}
});
方法2:重定向到无害库
修改加载路径,指向一个无害的假库:
var fake_so = Memory.allocUtf8String("/path/to/fake_libnllvm1624362448.so");
Interceptor.attach(Module.findExportByName(null, "dlopen"), {
onEnter: function(args) {
var path = args[0].readCString();
if (path.indexOf(targetSo) !== -1) {
console.log(`Redirecting ${targetSo} to fake library`);
args[0] = fake_so;
}
}
});
注意:假库需要包含所有必要的导出函数,但这些函数不执行实际检测操作。
方法3:静态修改so文件
直接修改目标so文件:
- 反编译定位检测代码
- 修改或删除检测逻辑
- 重新打包so文件
方法4:动态内存补丁
在内存中修改检测函数逻辑:
// 修改内存保护属性
Memory.protect(targetAddress, size, 'rwx');
// 找到目标函数地址
var targetFunction = Module.findExportByName("libtarget.so", "function_name");
// 修改函数开头为ret指令(ARM64)
Memory.writeByteArray(targetFunction, [0xC0, 0x03, 0x5F, 0xD6]);
// 或者修改关键数据
Memory.writeInt(targetAddress, newValue);
// 刷新缓存
Memory.patchCode(targetFunction, 4, function(code) {
Memory.writeByteArray(code, [0xC0, 0x03, 0x5F, 0xD6]);
});
// 恢复内存保护
Memory.protect(targetAddress, size, 'r-x');
五、完整绕过示例
以下是成功绕过检测的完整Frida脚本:
var targetSo = "libnllvm1624362448.so";
function blockLibrary(functionName) {
var func = Module.findExportByName(null, functionName);
if (func) {
Interceptor.attach(ptr(func), {
onEnter: function(args) {
var path = args[0].readCString();
if (path.indexOf(targetSo) !== -1) {
console.log(`Blocked ${targetSo} in ${functionName}`);
args[0] = ptr(0); // 将路径参数设为NULL
}
}
});
}
}
// 拦截所有可能的动态库加载函数
blockLibrary("open");
blockLibrary("dlopen");
blockLibrary("android_dlopen_ext");
六、技术要点总结
- 检测定位:通过hook动态库加载函数定位检测代码位置
- 绕过思路:
- 阻止检测库加载
- 重定向到无害库
- 静态修改so文件
- 动态内存补丁
- 关键函数:需要拦截所有可能的动态库加载函数(open/dlopen/android_dlopen_ext)
- ARM指令:了解基本的ARM指令(如ret指令)对内存补丁很重要
这些技术可以灵活组合使用,根据目标应用的具体防护机制选择最合适的绕过方法。