安卓逆向——Frida的进阶用法
字数 840 2025-08-22 12:23:18
Frida进阶用法:Native层Hook与修改指南
一、Hook Native函数基础
1.1 基本Hook流程
- 定位目标函数:使用
Module.findExportByName()获取目标函数地址 - 附加拦截器:使用
Interceptor.attach()附加回调函数 - 处理参数和返回值:在
onEnter和onLeave回调中操作
var strcmp_adr = Module.findExportByName("libc.so", "strcmp");
Interceptor.attach(strcmp_adr, {
onEnter: function(args) {
console.log("Hooking the strcmp function");
var flag = Memory.readUtf8String(args[1]);
console.log("The flag is " + flag);
},
onLeave: function(retval) {
}
});
1.2 过滤特定调用
通过检查参数值来过滤无关调用:
Interceptor.attach(strcmp_adr, {
onEnter: function(args) {
var arg0 = Memory.readUtf8String(args[0]);
var flag = Memory.readUtf8String(args[1]);
if(arg0.includes("HELLO")) {
console.log("Hooking the strcmp function");
console.log("Input " + arg0);
console.log("The flag is " + flag);
}
}
});
二、修改Native函数返回值
2.1 修改返回值方法
在onLeave回调中使用retval.replace()修改返回值:
var check_flag = Module.findExportByName("liba0x9.so", "Java_com_ad2001_a0x9_MainActivity_check_1flag");
Interceptor.attach(check_flag, {
onLeave: function(retval) {
console.log("Original return value:" + retval);
retval.replace(1337); // 修改返回值为1337
}
});
2.2 完整模板
Interceptor.attach(targetAddress, {
onEnter: function(args) {
console.log('Entering ' + functionName);
},
onLeave: function(retval) {
console.log('Leaving ' + functionName);
retval.replace(value); // 修改返回值
}
});
三、调用Native函数
3.1 调用流程
- 获取函数地址
- 创建NativePointer对象
- 创建NativeFunction对象
- 调用函数
var adr = Module.findBaseAddress("libfrida0xa.so").add(0x1DD60);
var get_flag_ptr = new NativePointer(adr);
const get_flag = new NativeFunction(get_flag_ptr, 'void', ['int', 'int']);
get_flag(1, 2);
3.2 完整模板
var native_adr = new NativePointer(<address_of_the_native_function>);
const native_function = new NativeFunction(native_adr, '<return type>', ['argument_data_type']);
native_function(<arguments>);
四、修改Native指令
4.1 修改指令步骤
- 获取指令地址
- 修改内存保护属性
- 使用ARM64Writer修改指令
- 刷新并释放资源
var jnz_adr = Module.getBaseAddress("libfrida0xb.so").add(0x170CE);
Memory.protect(jnz_adr, 0x1000, "rwx");
var writer = new Arm64Writer(jnz_adr);
try {
writer.putNop(); // 将jnz指令替换为NOP
writer.flush();
console.log("Command modification successful.");
} finally {
writer.dispose();
}
4.2 完整模板
var writer = new ARM64Writer(<address_of_the_instruction>);
try {
/* 自定义指令实现 */
writer.flush();
} finally {
writer.dispose();
}
五、实用API参考
5.1 函数地址获取方法
-
从导出表获取:
Module.findExportByName("libc.so", "strcmp") Module.getExportByName("libc.so", "strcmp") // 找不到会抛出异常 -
从导入表获取:
Module.enumerateImports("libfrida0x8.so")[4]["address"] -
基地址+偏移量:
Module.getBaseAddress("libfrida0x8.so").add(0x864)
5.2 内存操作API
-
读取内存字符串:
Memory.readUtf8String(address) -
修改内存保护:
Memory.protect(address, size, "rwx")
六、实战技巧
-
处理ASLR:Android的地址空间随机化会导致每次运行地址不同,建议使用基地址+偏移量的方式
-
错误处理:使用
try-finally确保资源释放 -
性能考虑:在Hook频繁调用的函数时,尽量减少日志输出
-
多架构支持:注意区分ARM和x86架构的指令差异
-
调试技巧:使用
console.log()输出关键变量值辅助调试
七、总结
Frida在Native层的Hook能力非常强大,通过掌握这些进阶用法,可以:
- 拦截和修改任意Native函数调用
- 动态修改程序逻辑
- 调用未暴露的Native函数
- 修改机器指令改变程序行为
关键是要理解Native函数调用的原理和内存操作的基本概念,结合具体场景灵活运用这些技术。