安卓编写常见的hook技巧(2)
字数 1222 2025-08-25 22:58:29

Android Hook 技术进阶:Native 层 Hook 技巧详解

基础概念

在 Android 逆向工程中,Hook 技术是分析应用行为的重要手段。Native 层 Hook 主要针对 so 文件中的函数调用进行拦截和修改。

1. Hook 导出函数基本框架

导出函数是指 so 文件中在 Exports 表中明确列出的函数,可以直接通过函数名进行定位和 Hook。

Java.perform(function x(){
    var str_name_so = "so文件名";    // 需要hook的so名
    var ptr_func = Module.findExportByName(str_name_so, "有导出的函数名");

    Interceptor.attach(ptr_func,{
        onEnter: function(args) {
            // 进入函数前的处理
            send("*******nativeGetPendingEntry");
            // 打印堆栈信息
            send("=============================Stack strat=======================");
            send(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n'));
            send("=============================Stack end  =======================");
        },
        onLeave: function(retval){
            // 函数返回前的处理
            send("return:"+retval); // 打印返回值
            // retval.replace(100); // 替换返回值
        }
    });
});

2. Hook 无导出函数基本框架

无导出函数需要通过计算偏移量来定位函数地址。

Java.perform(function x(){
    var str_name_so = "so文件名";    // 需要hook的so名
    var n_addr_func_offset = so中的偏移地址; // 需要hook的函数的偏移
    var n_addr_so = Module.findBaseAddress(str_name_so); // 获取so基址
    var n_addr_func = parseInt(n_addr_so, 16) + n_addr_func_offset;
    var ptr_func = new NativePointer(n_addr_func);

    Interceptor.attach(ptr_func,{
        onEnter: function(args) {
            // 进入函数前的处理
            send("*******target func");
            // 打印堆栈信息
            send("=============================Stack strat=======================");
            send(Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n'));
            send("=============================Stack end  =======================");
        },
        onLeave: function(retval){
            // 函数返回前的处理
            send("return:"+retval); // 打印返回值
        }
    });
});

3. Interceptor 拦截器 API

Interceptor 提供了一系列 Hook 相关操作:

  • Interceptor.attach(target, callbacks[, data]): 拦截指定地址的方法调用
  • Interceptor.detachAll(): 分离所有之前附加上的回调
  • Interceptor.replace(target, replacement[, data]): 替换指定地址的方法
  • Interceptor.revert(target): 还原指定地址的方法到之前实现
  • Interceptor.flush(): 确保任何待定的更改已提交到内存

实战案例

案例1:简单验证绕过

目标应用:com.testjava.jack.pingan2

  1. 分析发现点击按钮时触发 onClick 方法,调用 cyberpeace.so 中的 CheckString 方法
  2. 该方法返回值为1时验证通过
  3. Hook 脚本:
if(Java.available){
    Java.perform(function(){
        var n_addres_func = Module.findExportByName("libcyberpeace.so","Java_com_testjava_jack_pingan2_cyberpeace_CheckString");
        console.log("hooking address :" + n_addres_func);
        Interceptor.attach(n_addres_func,{
            onEnter:function (args){
                console.log("success hook so");
            },
            onLeave:function (retval){
                console.warn("the retval is : "+retval);
                var change = 1;
                retval.replace(change); // 强制修改返回值为1
                console.error("the sec retval is "+retval);
            }
        })
    });
}

案例2:关键参数获取

目标应用:某新闻类APP(版本7.45.1,腾讯加固)

  1. 抓包发现请求中包含 sn 参数
  2. 分析发现 sn 参数由 NativeSecureparam.readMD5Key() 生成
  3. Hook 脚本:
if(Java.available){
    Java.perform(function (){
        var method_address = Module.findExportByName("libifeng_secure.so","Java_com_ifeng_daemon_facade_NativeSecureparam_readMD5Key");
        Interceptor.attach(method_address,{
            onEnter:function (args){
                result_pointer = args[2].toInt32();
                console.log("success hook so ");
                send("args:"+Memory.readCString(args[0])+","+args[1]+","+args[2]);
            },
            onLeave:function (retval){
                console.warn("the retval is :"+retval);
            }
        });
    });
}

案例3:注册机破解(2016腾讯CTF题)

  1. 分析 libCheckRegister.so 中的 NativeCheckRegister 函数
  2. 定位关键验证函数 sub_1498
  3. Hook 脚本:
Java.perform(function () {
    send("Running Script");
    var base_addr = Module.findBaseAddress("libCheckRegister.so");
    var nativePointer = base_addr.add(0x1498+1)  // ARM指令需要+1
    var result_pointer;
    Interceptor.attach(nativePointer,{
        onEnter: function(args){
            result_pointer = args[0] // 保存数组指针
            console.log(Memory.readCString(args[1])); // 打印第二个参数
        },
        onLeave: function(retval){
            var resultPointer = new NativePointer(result_pointer);
            var resultstr = Memory.readUtf8String(resultPointer); // 读取数组内容
            send(retval.toInt32());  // 打印结果
            send("result pointer:" + resultPointer +", result:" + resultstr);
        }
    });
    send("Hooks installed.");
});

关键技巧总结

  1. 函数定位

    • 导出函数使用 Module.findExportByName
    • 无导出函数需要计算基址+偏移量
  2. 参数处理

    • so层函数第一个参数通常是 JniEnv
    • 第二个参数通常是 jclass
    • 从第三个参数开始是Java层传入的参数
  3. 返回值修改

    • 使用 retval.replace() 方法修改返回值
    • 注意类型匹配(整数、字符串等)
  4. ARM指令处理

    • 在ARM架构下,函数地址可能需要+1(Thumb模式)
  5. 调试技巧

    • 使用 Thread.backtrace 打印调用堆栈
    • 使用 DebugSymbol.fromAddress 解析符号信息

通过掌握这些Hook技巧,可以有效地分析和修改Native层的行为,为逆向分析和安全研究提供有力工具。

Android Hook 技术进阶:Native 层 Hook 技巧详解 基础概念 在 Android 逆向工程中,Hook 技术是分析应用行为的重要手段。Native 层 Hook 主要针对 so 文件中的函数调用进行拦截和修改。 1. Hook 导出函数基本框架 导出函数是指 so 文件中在 Exports 表中明确列出的函数,可以直接通过函数名进行定位和 Hook。 2. Hook 无导出函数基本框架 无导出函数需要通过计算偏移量来定位函数地址。 3. Interceptor 拦截器 API Interceptor 提供了一系列 Hook 相关操作: Interceptor.attach(target, callbacks[, data]) : 拦截指定地址的方法调用 Interceptor.detachAll() : 分离所有之前附加上的回调 Interceptor.replace(target, replacement[, data]) : 替换指定地址的方法 Interceptor.revert(target) : 还原指定地址的方法到之前实现 Interceptor.flush() : 确保任何待定的更改已提交到内存 实战案例 案例1:简单验证绕过 目标应用: com.testjava.jack.pingan2 分析发现点击按钮时触发 onClick 方法,调用 cyberpeace.so 中的 CheckString 方法 该方法返回值为1时验证通过 Hook 脚本: 案例2:关键参数获取 目标应用:某新闻类APP(版本7.45.1,腾讯加固) 抓包发现请求中包含 sn 参数 分析发现 sn 参数由 NativeSecureparam.readMD5Key() 生成 Hook 脚本: 案例3:注册机破解(2016腾讯CTF题) 分析 libCheckRegister.so 中的 NativeCheckRegister 函数 定位关键验证函数 sub_1498 Hook 脚本: 关键技巧总结 函数定位 : 导出函数使用 Module.findExportByName 无导出函数需要计算基址+偏移量 参数处理 : so层函数第一个参数通常是 JniEnv 第二个参数通常是 jclass 从第三个参数开始是Java层传入的参数 返回值修改 : 使用 retval.replace() 方法修改返回值 注意类型匹配(整数、字符串等) ARM指令处理 : 在ARM架构下,函数地址可能需要+1(Thumb模式) 调试技巧 : 使用 Thread.backtrace 打印调用堆栈 使用 DebugSymbol.fromAddress 解析符号信息 通过掌握这些Hook技巧,可以有效地分析和修改Native层的行为,为逆向分析和安全研究提供有力工具。