【APP测试】某次hook动态注册的踩坑经历

admin 2026-01-17 02:07:44 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 记录使用FridaHook动态注册函数RegisterNatives的实战经历。在静态确认调用关系后Hook失败,排查发现libart.so中存在CheckJNI与非CheckJNI两个版本符号导致拦截失效。通过分析符号地址并指定正确目标进行拦截,最终成功捕获方法信息,解决了多符号环境下的Hook难题。 综合评分: 85 文章分类: 移动安全,逆向分析,实战经验


cover_image

【APP测试】某次hook动态注册的踩坑经历

原创

d0n9x1e d0n9x1e

蝉SEC

2026年1月15日 14:16 江苏

frida首先,上一段hook_RegisterNatives代码

function hook_RegisterNatives() {
    var RegisterNatives_addr = null;
    var symbols = Process.findModuleByName("libart.so").enumerateSymbols();
&nbsp; &nbsp; for (var i = 0; i < symbols.length; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; var symbol = symbols[i].name;
&nbsp; &nbsp; &nbsp; &nbsp; if ((symbol.indexOf("CheckJNI") == -1) && (symbol.indexOf("JNI") >= 0)) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (symbol.indexOf("RegisterNatives") >= 0) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; RegisterNatives_addr = symbols[i].address;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log("RegisterNatives_addr: ", RegisterNatives_addr);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
&nbsp; &nbsp; Interceptor.attach(RegisterNatives_addr, {
&nbsp; &nbsp; &nbsp; &nbsp; onEnter: function (args) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var env = args[0];
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var jclass = args[1];
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var class_name = Java.vm.tryGetEnv().getClassName(jclass);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var methods_ptr = ptr(args[2]);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var method_count = args[3].toInt32();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log("RegisterNatives method counts: ", method_count);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (var i = 0; i < method_count; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var name = methods_ptr.add(i * Process.pointerSize * 3).readPointer().readCString();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var sig = methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize).readPointer().readCString();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var fnPtr_ptr = methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2).readPointer();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var find_module = Process.findModuleByAddress(fnPtr_ptr);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log("类: ", class_name, "方法: ", name, "签名: ", sig, "函数地址: ", fnPtr_ptr, "模块名: ", find_module.name, "函数偏移: ", ptr(fnPtr_ptr).sub(find_module.base));
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp; &nbsp; onLeave: function (retval) {}
&nbsp; &nbsp; });
}
hook_RegisterNatives()

通过ida可以很明确的看到调用了动态注册函数

但是怎么都hook不到

很奇怪,不可能的啊,明明静态看的时候调用了

打印出addr_RegisterNatives地址查看

function hook_dlopen(so_name) {
&nbsp; &nbsp; Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), {
&nbsp; &nbsp; &nbsp; &nbsp; onEnter: function (args) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var pathptr = args[0];
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (pathptr !== undefined && pathptr != null) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var path = ptr(pathptr).readCString();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // console.log(path)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (path.indexOf(so_name) !== -1) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this.match = true
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp; &nbsp; onLeave: function (retval) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (this.match) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log(so_name, "加载成功")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // var base = Module.findBaseAddress("libhello-jni.so")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // console.log(hexdump(base.add(0x37070),{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // &nbsp; &nbsp; length: 200,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // &nbsp; &nbsp; header: true,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // &nbsp; &nbsp; ansi: true
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // }))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; });
}
function hook_RegisterNatives() {
&nbsp; &nbsp; var addr_RegisterNatives = null;
&nbsp; &nbsp; var symbols = Process.findModuleByName("libart.so").enumerateSymbols();
&nbsp; &nbsp; for (var i = 0; i < symbols.length; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; var symbol = symbols[i].name;
&nbsp; &nbsp; &nbsp; &nbsp; if ((symbol.indexOf("CheckJNI") == -1) && (symbol.indexOf("JNI") >= 0)) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (symbol.indexOf("RegisterNatives") >= 0) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; addr_RegisterNatives = symbols[i].address;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log("addr_RegisterNatives: ", addr_RegisterNatives);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
&nbsp; &nbsp; //赋值addr_RegisterNatives1地址0x71676be210
&nbsp; &nbsp; // var addr_RegisterNatives1 = ptr("0x71676be210");
&nbsp; &nbsp; console.log("addr_RegisterNatives==",addr_RegisterNatives)
&nbsp; &nbsp; if (addr_RegisterNatives) {
&nbsp; &nbsp; &nbsp; &nbsp; Interceptor.attach(addr_RegisterNatives, {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onEnter: function (args) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var java_class = Java.vm.tryGetEnv().getClassName(args[1]);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var methods = args[2];
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var method_count = parseInt(args[3]);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log("addr_RegisterNatives java_class:", java_class, "method_count:", method_count);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (var i = 0; i < method_count; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log(methods.add(i * Process.pointerSize * 3).readPointer().readCString());
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log(methods.add(i * Process.pointerSize * 3 + Process.pointerSize).readPointer().readCString());
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var fnPtr = methods.add(i * Process.pointerSize * 3 + Process.pointerSize * 2).readPointer();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var module_so = Process.findModuleByAddress(fnPtr);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log("=====================")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log(module_so.name + "!" + fnPtr.sub(module_so.base));
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log("=====================")

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }, onLeave: function (retval) {

&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; })
&nbsp; &nbsp; }
}

function main(){
&nbsp; &nbsp; Java.perform(function () {
&nbsp; &nbsp; &nbsp; &nbsp; hook_RegisterNatives();
&nbsp; &nbsp; &nbsp; &nbsp; hook_dlopen("libhello-jni.so");
&nbsp; &nbsp; });

}
setImmediate(main)

怎么有两个addr_RegisterNatives

询问gpt

RegisterNatives的地址问题

你通过 Module.enumerateSymbolsSync("libart.so") 找到了两个 RegisterNatives 的地址,分别是:

复制

0x71676be210 _ZN3art3JNIILb0EE15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi
0x716771ece8 _ZN3art3JNIILb1EE15RegisterNativesEP7_JNIEnvP7_jclassPK15JNINativeMethodi

这两个地址分别对应于 非 CheckJNI 模式 和 CheckJNI 模式 的 RegisterNatives 实现。通常情况下,实际使用的地址取决于应用是否启用了 CheckJNI 模式。

  • CheckJNI 模式:用于调试目的,会进行额外的检查以确保 JNI 调用的正确性。
  • 非 CheckJNI 模式:用于生产环境,性能更好。

如果你的应用启用了 CheckJNI 模式,那么应该拦截的是第二个地址(0x716771ece8)。否则,应该拦截第一个地址(0x71676be210)。你需要确认应用是否启用了 CheckJNI 模式,并选择正确的地址进行拦截。

好吧我们来hook另一个addr_RegisterNatives

function hook_dlopen(so_name) {
&nbsp; &nbsp; Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), {
&nbsp; &nbsp; &nbsp; &nbsp; onEnter: function (args) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var pathptr = args[0];
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (pathptr !== undefined && pathptr != null) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var path = ptr(pathptr).readCString();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // console.log(path)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (path.indexOf(so_name) !== -1) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; this.match = true
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp; &nbsp; onLeave: function (retval) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (this.match) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log(so_name, "加载成功")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // var base = Module.findBaseAddress("libhello-jni.so")
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // console.log(hexdump(base.add(0x37070),{
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // &nbsp; &nbsp; length: 200,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // &nbsp; &nbsp; header: true,
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // &nbsp; &nbsp; ansi: true
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; // }))
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; });
}
function hook_RegisterNatives() {
&nbsp; &nbsp; var addr_RegisterNatives = null;
&nbsp; &nbsp; var symbols = Process.findModuleByName("libart.so").enumerateSymbols();
&nbsp; &nbsp; for (var i = 0; i < symbols.length; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; var symbol = symbols[i].name;
&nbsp; &nbsp; &nbsp; &nbsp; if ((symbol.indexOf("CheckJNI") == -1) && (symbol.indexOf("JNI") >= 0)) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (symbol.indexOf("RegisterNatives") >= 0) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; addr_RegisterNatives = symbols[i].address;
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log("addr_RegisterNatives: ", addr_RegisterNatives);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
&nbsp; &nbsp; //赋值addr_RegisterNatives1地址0x71676be210
&nbsp; &nbsp; var addr_RegisterNatives1 = ptr("0x71676be210");
&nbsp; &nbsp; console.log("addr_RegisterNatives==",addr_RegisterNatives)
&nbsp; &nbsp; if (addr_RegisterNatives) {
&nbsp; &nbsp; &nbsp; &nbsp; Interceptor.attach(addr_RegisterNatives1, {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onEnter: function (args) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var env = args[0];
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var jclass = args[1];
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var class_name = Java.vm.tryGetEnv().getClassName(jclass);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var methods_ptr = ptr(args[2]);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var method_count = args[3].toInt32();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log("RegisterNatives method counts: ", method_count);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for (var i = 0; i < method_count; i++) {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var name = methods_ptr.add(i * Process.pointerSize * 3).readPointer().readCString();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var sig = methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize).readPointer().readCString();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var fnPtr_ptr = methods_ptr.add(i * Process.pointerSize * 3 + Process.pointerSize * 2).readPointer();
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var find_module = Process.findModuleByAddress(fnPtr_ptr);
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; console.log("类: ", class_name, "方法: ", name, "签名: ", sig, "函数地址: ", fnPtr_ptr, "模块名: ", find_module.name, "函数偏移: ", ptr(fnPtr_ptr).sub(find_module.base));
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; onLeave: function (retval) {}
&nbsp; &nbsp; &nbsp; &nbsp; })
&nbsp; &nbsp; }
}

function main(){
&nbsp; &nbsp; Java.perform(function () {
&nbsp; &nbsp; &nbsp; &nbsp; hook_RegisterNatives();
&nbsp; &nbsp; &nbsp; &nbsp; hook_dlopen("libhello-jni.so");
&nbsp; &nbsp; });

}
setImmediate(main)

终于得到了┭┮﹏┭┮


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:蝉SEC d0n9x1e d0n9x1e《【APP测试】某次hook动态注册的踩坑经历》

评论:0   参与:  0