【Frida Android】基础篇12:Native层hook基础——调用原生函数
文章目录
- 1. Hook 语法
- 2. 案例解析
- 2.1 源码分析
- 2.2 IDA使用步骤(定位目标函数)
- 2.3 Hook脚本说明
- 3. 技术总结
⚠️本博文所涉安全渗透测试技术、方法及案例,仅用于网络安全技术研究与合规性交流,旨在提升读者的安全防护意识与技术能力。任何个人或组织在使用相关内容前,必须获得目标网络 / 系统所有者的明确且书面授权,严禁用于未经授权的网络探测、漏洞利用、数据获取等非法行为。
1. Hook 语法
Native层Hook是通过动态修改程序运行时行为,实现对原生函数(Native Function)的拦截、调用或修改的技术。在Frida框架中,针对Native函数的Hook核心语法如下:
-
模块枚举与基地址获取
由于Native函数的实际地址 = 模块基地址 + 函数偏移量(静态分析得到),需先通过Process.enumerateModules()枚举进程加载的模块,找到目标SO库的基地址:var modules = Process.enumerateModules(); var targetModuleBase = null; for (var i = 0; i < modules.length; i++) {if (modules[i].name === "目标库名.so") {targetModuleBase = modules[i].base; // 模块基地址(运行时动态分配)break;} } -
函数地址计算
结合静态分析得到的函数偏移量(如IDA中获取),计算函数实际运行地址:var functionOffset = 0x12345; // 静态偏移量(IDA中获取) var functionAddress = targetModuleBase.add(functionOffset); // 实际地址 = 基地址 + 偏移量 -
Native函数封装
使用NativeFunction将内存地址封装为可调用的函数,需指定返回值类型和参数类型(参考IDA伪代码的函数签名):// 示例:封装一个返回void、接收两个int参数的函数 var targetFunction = new NativeFunction(functionAddress, // 函数实际地址'void', // 返回值类型(如'pointer'、'int'、'string'等)['int', 'int'] // 参数类型列表 ); -
函数调用与Hook
直接调用封装后的函数,或通过Interceptor.attach拦截函数执行(修改参数/返回值):// 调用函数 targetFunction(1, 2); // 传入符合类型的参数// 拦截函数(示例) Interceptor.attach(functionAddress, {onEnter: function(args) {console.log("函数被调用,参数1:" + args[0].toInt32());},onLeave: function(retval) {console.log("函数返回值:" + retval.toInt32());} });
2. 案例解析
本章示例应用的链接:
https://pan.baidu.com/s/16EE2XE-OZS_xBRPlWUODbw?pwd=n2vb
提取码: n2vb
使用APK:Challenge 0xA.apk
2.1 源码分析
目标APK的核心逻辑通过Java层与Native层交互实现,使用JADX反编译后关键代码如下:
public final class MainActivity extends AppCompatActivity {private ActivityMainBinding binding;public final native String stringFromJNI(); // 声明Native方法@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 布局初始化逻辑...binding.sampleText.setText(stringFromJNI()); // 调用Native方法并显示结果}static {System.loadLibrary("frida0xa"); // 加载Native库libfrida0xa.so}
}
分析可知:
- 应用启动时加载
libfrida0xa.so库; - 通过
stringFromJNI()调用Native层代码,最终显示Hello Hackers; - 目标是找到并调用库中隐藏的“获取Flag”函数(静态分析发现未被主动调用)。
2.2 IDA使用步骤(定位目标函数)
IDA工具获取参考:https://blog.csdn.net/qq_40037555/article/details/153777782
-
获取SO文件
将APK修改为ZIP格式并解压,在lib目录下可看到不同CPU架构的子目录(如arm64-v8a、x86_64等),每个目录下包含对应架构的libfrida0xa.so:

-
确定设备架构
通过ADB命令获取运行设备的CPU架构,用于选择匹配的SO文件:adb shell getprop ro.product.cpu.abi本例中模拟器架构为
x86_64,因此选择x86_64/libfrida0xa.so。
-
IDA打开SO文件
启动IDA Pro,选择“File- Open” -> 打开x86_64/libfrida0xa.so,默认加载即可(本地需要 python3 环境)。左侧“Functions”窗口会显示库中所有函数:
-
查找目标函数
-
在“Functions”窗口找到
stringFromJNI,查看伪代码确认其功能为返回Hello Hackers,无Flag相关逻辑;
-
继续查找发现一个“获取Flag”的函数(名为
get_flag),点击该函数查看伪代码,可以看到在if ( a2 + a1 == 3 )即传入的2个参数和为3时,通过日志_android_log_print输出解密后的Flag;
-
记录该函数的偏移量(静态地址),本例中为
0x206B0。
-
-
确认基地址
IDA中默认基地址可通过“View -> Open subviews -> Segments”查看,x86_64架构默认基地址通常为0x0,因此函数静态地址 = 基地址 + 偏移量 =0x206B0:


2.3 Hook脚本说明
脚本核心目标是找到libfrida0xa.so的运行时基地址,计算get_flag函数的实际地址并主动调用,从而触发Flag输出:
function hook() {// 枚举模块,获取libfrida0xa.so的基地址(运行时动态分配)var modules = Process.enumerateModules();var libnative_addr = null;for (var i = 0; i < modules.length; i++) {if (modules[i].name === "libfrida0xa.so") {libnative_addr = modules[i].base;break;}}if (!libnative_addr) {console.log("未能找到 libfrida0xa.so 模块");return;}// 计算get_flag函数的实际地址(基地址 + 静态偏移量0x206B0)var adr = libnative_addr.add(0x206B0);var get_flag_ptr = new NativePointer(adr);// 封装函数:返回值为void,参数为两个int(根据IDA伪代码的函数签名)const get_flag = new NativeFunction(get_flag_ptr, 'void', ['int', 'int']);console.log("get_flag function address is =>", adr);// 调用函数,传入任意符合条件(参数和为3)的参数get_flag(1, 2);
}hook();
这里单独说明下,伪代码中get_flag的返回值为unsigned __int64(64 位无符号整数),但上面 Frida 脚本中声明为'void'(无返回值)。
这一差异不影响函数调用成功的原因是:
- Frida 的
NativeFunction对返回值的处理是 “按需解析”:如果声明为'void',则会忽略函数的实际返回值,但不会阻止函数本身的执行。 - 目标函数
get_flag的核心逻辑(解密 Flag 并通过__android_log_print输出)不依赖返回值,只要参数正确,函数内部逻辑就能正常执行,因此即使返回值类型声明不匹配,也能成功触发 Flag 输出。
脚本执行后,通过ADB查看日志即可获取Flag:
adb logcat -c # 清除旧日志
adb logcat > log.txt # 保存新日志(执行脚本后查看)
日志中Debug级别输出包含Flag:Decrypted Flag: FRIDA{DONT_CALL_ME}

3. 技术总结
Native层Hook的核心流程与关键要点如下:
- 核心流程
- 静态分析:通过JADX解析Java层代码,定位加载的SO库及关键Native方法;
- 逆向SO库:使用IDA分析SO文件,找到目标函数(如隐藏的Flag获取函数)并记录偏移量;
- 动态Hook:通过Frida脚本枚举模块获取基地址,计算函数实际地址,封装并调用函数;
- 结果验证:通过ADB日志或其他方式获取Hook后的输出。
- 关键工具
- JADX:反编译APK,分析Java层与Native层的交互逻辑;
- IDA Pro:逆向SO文件,查看函数列表、伪代码及偏移量。
- 注意事项
- 函数地址计算:需区分静态偏移量(IDA中获取)和运行时基地址(Frida动态获取),实际地址 = 基地址 + 偏移量;
- 函数签名匹配:
NativeFunction的返回值类型和参数类型必须与IDA伪代码一致,否则会导致调用异常; - 架构匹配:需选择与目标设备CPU架构一致的SO文件进行分析(如x86_64、arm64-v8a)。
通过上述步骤,可实现对Native层函数的精准Hook,进而分析或修改程序的原生逻辑。
