frida反调试经验-动态库加载
德*罗 发表于 浙江 移动安全 3115浏览 · 2024-07-17 02:39

初学者学习记录在测试过程中遇到了特殊情况,当使用frida spawn或attach应用时,frida进程会被杀掉,提示process terminated,但spawn出来的APP进程仍然可以正常使用。
这种情况可以判断为APP具有反调试防护,将附加的frida进程杀掉了。

发现检测函数

frida检测函数通常都写在so文件中,因为如果把反调试代码写在app代码里,不是很容易被frida hook掉嘛( ^ _^ )。写在so文件中,不容易被反编译,也不容易被篡改(需要重新签名),执行效率高(不影响app运行)。
我们可以hook一些app用来加载动态库的函数,查看到底检测代码保存在哪个动态库中。如android_dlopen_ext() 、dlopen()、do_dlopen(),find_library()这些函数都是system.loadlibary底层的逻辑代码。

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);
                }
            }
        }
    );
}

通过这个脚本可以查看所有android_dlopen_ext打开的动态库,我们可以发现,当frida执行到libllvm1624362448.so这里时,发生了闪退,所以我们暂时确定检测代码就在这个库中。

绕过检测

既然我们定位到了检测的动态库,我们有下面几种绕过的思路。
1、 hook掉加载动态库的函数,将返回的指针置空,直接避免程序加载检测代码。

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);
        }
    }
});

2、 hook 加载动态库的函数,修改传入的动态库路径,让程序加载无害的代码。但这个假动态库中应该包含所有必要的导出函数,但这些函数不执行任何实际操作。

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文件,去掉或修改动态库中的检测代码。
4、动态修改动态库文件,修改内存中检测函数的逻辑。(这段不清楚,抄来的)

Memory.protect(targetAddress, size, 'rwx'); //库被加载到内存后是只读的,修改可写权限

var targetFunction = Module.findExportByName("libtarget.so", "function_name"); //找到函数地址

// 修改函数开头为 ret 指令(直接返回)
Memory.writeByteArray(targetFunction, [0xC0, 0x03, 0x5F, 0xD6]);  // ARM64 ret 指令

// 或修改数据
Memory.writeInt(targetAddress, newValue);

//刷新缓存
Memory.patchCode(targetFunction, 4, function(code) {
    Memory.writeByteArray(code, [0xC0, 0x03, 0x5F, 0xD6]);
});
//恢复内存保护
Memory.protect(targetAddress, size, 'r-x');

最后附上成功绕过这个app检测的代码:

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 条评论
某人
表情
可输入 255