Frida是一款强大的动态插桩工具,同样可以用于对Android Native层的代码进行Hook。本文将深入探讨Frida在Native层的Hook技术,涵盖基础语法和实际示例,帮助你掌握如何在Native .so
文件中进行有效的操作
基础语法
Frida使用JavaScript作为脚本语言来进行插桩和Hook操作。在Native层的Hook中,主要用到以下几个Frida API:
-
Interceptor.attach()
:用于Hook指定的函数。 -
Interceptor.replace()
:用于替换指定的函数实现。 -
Module.getExportByName()
:获取指定模块导出函数的地址。 -
Memory.readPointer()
和Memory.writePointer()
:用于读取和写入内存指针。
完整语法请参考https://frida.re/docs/javascript-api/#java
Native层Hook常用脚本
基础Hook目标函数
已知native层so文件及其目标函数
// 获取Native模块和函数
Interceptor.attach(Module.getExportByName('libtarget.so', 'target_function'), {
onEnter: function(args) {
// 在函数被调用时执行
console.log('target_function called');
console.log('arg0: ' + args[0].toInt32()); // 打印第一个参数
},
onLeave: function(retval) {
// 在函数返回时执行
console.log('target_function returned');
console.log('return value: ' + retval.toInt32());
retval.replace(42); // 修改返回值
}
});
-
Module.getExportByName()
:获取目标函数的地址。 -
Interceptor.attach()
:在目标函数调用前后插入自定义的逻辑。 -
args[]
和retval
:分别代表函数的参数和返回值。
动态查找函数地址
如果目标函数的地址不确定,可以动态查找导出函数
// 列出模块中的所有导出函数
Module.enumerateExports('libtarget.so', {
onMatch: function(exp) {
console.log(exp.name + ' at ' + exp.address);
if (exp.name === 'target_function') {
// 找到目标函数并Hook
Interceptor.attach(exp.address, {
onEnter: function(args) {
console.log('target_function called');
}
});
}
},
onComplete: function() {}
});
替换函数实现
有时需要替换目标函数的实现
Interceptor.replace(Module.getExportByName('libtarget.so', 'target_function'), new NativeFunction(function() {
console.log('Custom implementation');
return 0; // 自定义返回值
}, 'int', []));
-
Interceptor.replace()
:用自定义的实现替换目标函数。 -
NativeFunction
:定义新函数的返回值类型和参数类型。
内存操作
通过对内存的操作,可以读取和修改字符串等数据,向内存写入新数据,修改内存权限,甚至还可以修改函数代码
var ptr = Module.getBaseAddress('libtarget.so').add(0x1234); // 偏移量
var value = Memory.readInt(ptr);
console.log('Value at ' + ptr + ': ' + value);
Memory.writeInt(ptr, 42); // 修改内存中的值
实战示例
源代码
假设我们有一个名为libexample.so
的Native库,其源代码如下:
// example.cpp
extern "C" {
void __attribute__((visibility("default"))) target_function(int value) {
std::cout << "Original target_function called with value: " << value << std::endl;
}
int __attribute__((visibility("default"))) calculate_sum(int a, int b) {
return a + b;
}
}
Frida Hook脚本示例
-
Hook
target_function
// Hook target_function
Interceptor.attach(Module.getExportByName('libexample.so', 'target_function'), {
onEnter: function(args) {
console.log('target_function called');
console.log('Original value passed: ' + args[0].toInt32()); // 打印传入的参数
// 修改传入的参数
args[0] = ptr(100);
},
onLeave: function(retval) {
console.log('target_function finished execution');
// 打印修改后的返回值
}
});
在这个脚本中:
-
Module.getExportByName('libexample.so', 'target_function')
:获取target_function
的地址。 -
args[0]
:访问并修改传递给函数的第一个参数。 -
console.log
:用于打印调试信息。
-
Hook
calculate_sum
// Hook calculate_sum
Interceptor.attach(Module.getExportByName('libexample.so', 'calculate_sum'), {
onEnter: function(args) {
console.log('calculate_sum called');
console.log('Original values: a = ' + args[0].toInt32() + ', b = ' + args[1].toInt32());
// 修改传入的参数
args[0] = ptr(10);
args[1] = ptr(20);
},
onLeave: function(retval) {
console.log('calculate_sum finished execution');
console.log('Original return value: ' + retval.toInt32());
retval.replace(99); // 修改返回值
}
});
在这个脚本中:
-
args[0]
和args[1]
:分别访问并修改calculate_sum
函数的两个参数。 -
retval.replace(99)
:修改函数的返回值为99。
-
运行和验证
将上述Frida脚本保存为hook.js
,然后使用Frida命令行工具注入到目标进程中:
frida -U -p <PID> -l hook.js
其中<PID>
是目标进程的ID,你可以通过adb shell ps
命令找到它。
原文始发于微信公众号(暴暴的皮卡丘):Frida Hook(9) - 安卓native层hook基础
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论