frida反调试经验-动态库加载
字数 955 2025-08-22 12:23:18

Frida反调试检测与绕过技术详解

一、问题现象分析

当使用Frida进行动态分析时,可能会遇到以下特殊现象:

  • 使用frida spawnattach应用时,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文件:

  1. 反编译定位检测代码
  2. 修改或删除检测逻辑
  3. 重新打包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");

六、技术要点总结

  1. 检测定位:通过hook动态库加载函数定位检测代码位置
  2. 绕过思路
    • 阻止检测库加载
    • 重定向到无害库
    • 静态修改so文件
    • 动态内存补丁
  3. 关键函数:需要拦截所有可能的动态库加载函数(open/dlopen/android_dlopen_ext)
  4. ARM指令:了解基本的ARM指令(如ret指令)对内存补丁很重要

这些技术可以灵活组合使用,根据目标应用的具体防护机制选择最合适的绕过方法。

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文件: 2. 检测库定位 当Frida执行到特定so文件(如 libllvm1624362448.so )时发生闪退,可以确定检测代码位于该库中。 四、绕过反调试的四种方法 方法1:阻止检测库加载 hook加载函数,将目标so的路径参数置空: 方法2:重定向到无害库 修改加载路径,指向一个无害的假库: 注意 :假库需要包含所有必要的导出函数,但这些函数不执行实际检测操作。 方法3:静态修改so文件 直接修改目标so文件: 反编译定位检测代码 修改或删除检测逻辑 重新打包so文件 方法4:动态内存补丁 在内存中修改检测函数逻辑: 五、完整绕过示例 以下是成功绕过检测的完整Frida脚本: 六、技术要点总结 检测定位 :通过hook动态库加载函数定位检测代码位置 绕过思路 : 阻止检测库加载 重定向到无害库 静态修改so文件 动态内存补丁 关键函数 :需要拦截所有可能的动态库加载函数(open/dlopen/android_ dlopen_ ext) ARM指令 :了解基本的ARM指令(如ret指令)对内存补丁很重要 这些技术可以灵活组合使用,根据目标应用的具体防护机制选择最合适的绕过方法。