Frida-Labs-Native层操作指南
字数 1112 2025-08-29 22:41:24
Frida Native层操作指南
1. Native层函数Hook基础
1.1 基本Hook模板
Interceptor.attach(targetAddress, {
onEnter: function(args) {
console.log("正在进入函数");
// 可以查看和修改传入参数
},
onLeave: function(retval) {
console.log("正在离开函数");
// 可以查看和修改返回值
}
});
1.2 获取函数地址的方法
-
Module.enumerateExports()
- 获取导出函数的名称、地址等信息
- 适用于查找模块自身导出的函数
-
Module.getExportByName()
- 通过函数名获取导出项地址
- 找不到会抛出异常
-
Module.findExportByName()
- 通过函数名获取导出项地址
- 找不到返回null
-
Module.getBaseAddress()
- 获取模块基址地址
- 可用于基于偏移量的计算
-
Module.enumerateImports()
- 获取模块导入的外部函数/变量信息
1.3 实际应用示例
Hook libc.so中的strcmp函数:
var strcmp = Module.findExportByName("libc.so", "strcmp");
Interceptor.attach(strcmp, {
onEnter: function(args) {
// 增加条件判断,避免hook所有strcmp调用
if(Memory.readUtf8String(args[0]).includes("666")) {
console.log("strcmp参数2:", Memory.readUtf8String(args[1]));
}
}
});
2. 函数返回值修改
修改Native层函数的返回值:
var check_flag = Module.enumerateExports("liba0x9.so")[0]["address"];
Interceptor.attach(check_flag, {
onLeave: function(retval) {
if(retval.toInt32() === 1337) {
console.log("Flag found!");
}
// 修改返回值
retval.replace(1);
}
});
3. 导入表与导出表
- 导入表(Imports): 模块调用的外部函数
- 导出表(Exports): 模块对外提供的函数
查看导出表示例:
var exports = Module.enumerateExports("liba0x9.so");
exports.forEach(function(exp) {
console.log("Name:", exp.name, "Address:", exp.address);
});
4. Hook未调用的Native方法
4.1 直接调用Native函数
var native_adr = new NativePointer(<address_of_the_native_function>);
var native_func = new NativeFunction(native_adr, <return_type>, ['argument_data_type']);
var result = native_func(<arguments>);
4.2 查找并调用get_flag函数示例
var base = Module.findBaseAddress("libfrida0xa.so");
var exports = Module.enumerateExports("libfrida0xa.so");
for(var i = 0; i < exports.length; i++) {
if(exports[i].name === "_Z8get_flagii") { // C++ name mangling
console.log("get_flag地址:", exports[i].address);
console.log("偏移:", exports[i].address - base);
var get_flag = new NativeFunction(exports[i].address, 'void', ['int', 'int']);
get_flag(0, 0); // 调用函数
break;
}
}
5. C++ Name Mangling问题
C++编译器会对函数名进行重整(name mangling),例如:
int add(int a, int b)→_Z3addiifloat add(float a, float b)→_Z3addff
在Hook时需要特别注意使用正确的重整名称。
6. Hook Android日志函数
直接截获__android_log_print的输出:
var android_log_print = Module.findExportByName("liblog.so", "__android_log_print");
Interceptor.attach(android_log_print, {
onEnter: function(args) {
// 第四个参数是日志内容
console.log("Log:", Memory.readUtf8String(args[3]));
}
});
7. 修改Native层汇编指令
7.1 x86架构模板
var writer = new X86Writer(opcodeaddr);
Memory.patchCode(opcodeaddr, 0x1000, function(code) {
try {
// 写入汇编指令
writer.putNop();
writer.putRet();
writer.flush();
} finally {
writer.dispose();
}
});
7.2 ARM64架构示例
修改跳转指令(如nop掉B.NE):
var target = base.add(0x15248); // 偏移地址
Memory.patchCode(target, 4, function(code) {
var writer = new Arm64Writer(code, {pc: target});
try {
writer.putNop(); // 替换B.NE为NOP
writer.flush();
} finally {
writer.dispose();
}
});
8. 关键注意事项
- 权限问题: 修改代码段前需要设置内存为可写(rwx)
- 架构差异: x86和ARM64的汇编指令不同
- 地址随机化(ASLR): 使用基地址+偏移量的方式更可靠
- 资源释放: 使用完Writer后必须调用dispose()
- 线程安全: 修改运行时代码需谨慎,可能导致崩溃
9. 实用技巧
- 批量Hook: 遍历导出表批量Hook特定模式的函数
- 条件Hook: 增加条件判断避免Hook无关调用
- 日志过滤: 结合logcat命令查看完整输出
- 错误处理: 添加try-catch防止脚本崩溃
- 性能优化: 避免在频繁调用的函数中执行复杂操作
通过以上方法,可以有效地对Android应用的Native层进行Hook、分析和修改,实现各种安全分析和逆向工程目的。