安卓编写常见的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
- 分析发现点击按钮时触发
onClick方法,调用cyberpeace.so中的CheckString方法 - 该方法返回值为1时验证通过
- 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,腾讯加固)
- 抓包发现请求中包含
sn参数 - 分析发现
sn参数由NativeSecureparam.readMD5Key()生成 - 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题)
- 分析
libCheckRegister.so中的NativeCheckRegister函数 - 定位关键验证函数
sub_1498 - 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.");
});
关键技巧总结
-
函数定位:
- 导出函数使用
Module.findExportByName - 无导出函数需要计算基址+偏移量
- 导出函数使用
-
参数处理:
- so层函数第一个参数通常是
JniEnv - 第二个参数通常是
jclass - 从第三个参数开始是Java层传入的参数
- so层函数第一个参数通常是
-
返回值修改:
- 使用
retval.replace()方法修改返回值 - 注意类型匹配(整数、字符串等)
- 使用
-
ARM指令处理:
- 在ARM架构下,函数地址可能需要+1(Thumb模式)
-
调试技巧:
- 使用
Thread.backtrace打印调用堆栈 - 使用
DebugSymbol.fromAddress解析符号信息
- 使用
通过掌握这些Hook技巧,可以有效地分析和修改Native层的行为,为逆向分析和安全研究提供有力工具。