文章总结: 本文介绍了利用Frida向ReactNative应用注入JavaScript代码的技术方案。针对ReactNative0.74版本引入的Bridgeless架构及旧版Legacy架构,详细阐述了在Android和iOS平台下拦截JSBundle加载及主动注入JS的具体实现方法。文章改进了以往通过定时器获取实例的低效方式,提出利用Frida的chooseAPI直接获取实例,并通过Hook原生Alert模块解决了执行结果回传的难题,实现了类似浏览器F12控制台的调试效果,具有较高的实战参考价值。 综合评分: 93 文章分类: 移动安全,逆向分析,安全工具,实战经验
G.O.S.S.I.P 阅读推荐 2026-02-26 Frida + js -> React Native
安全研究GoSSIP
2026年2月26日 20:20 上海
以下文章来源于非尝咸鱼贩 ,作者0xcc
非尝咸鱼贩 .
临渊羡鱼,不如在家咸鱼
动机和背景
我好像有个偏小众的恶趣味,就是给别人生产环境的应用开 js 控制台。虽然几年前发的那个某小程序的思路早就不能用了。
最近看到国外开发者 Pilfer 一直在社交网络上宣传他的新产品 Bytecode Studio,这是一款专门用于反编译和分析 React Native 字节码的工具。
https://bytecodestudio.com
他在两年前写过一篇博客 Reverse Engineering and Instrumenting React Native Apps:
https://pilfer.github.io/mobile-reverse-engineering/react-native/reverse-engineering-and-instrumenting-react-native-apps/
这篇文章介绍了在 Android 平台的 legacy 架构下动态向当前运行的 React Native 应用注入 JavaScript 代码的过程。通过 js 层的 hook,他可以实现拦截网络请求、JSON 序列化,以及无意中 dump 一些 UI 层级结构等功能。文章里的相关代码开源了:
https://github.com/Pilfer/heresy
他的 GitHub 主页还有一个基于 Rust 的 hermes 字节码反编译工具。有了这些技术积累,并不奇怪他会做 Bytecode Studio。
而数天前 React Native 发布了 0.74 版本,默认启用 Bridgeless 架构。请注意之前引用的文章只讲了 legacy 架构。
https://reactnative.dev/blog/2024/04/22/release-0.74
而还是这几天,radare2 发布了一款插件 r2hermes,专门用于分析 hermes 字节码。
https://github.com/radareorg/r2hermes
虽然笔者不做客户端,这一系列内容勾起了我的兴趣,也就有了今天这篇文章。
Legacy 和 Bridgeless 是什么鬼
React Native 有两套架构。
Legacy 架构下,JavaScript 运行在独立线程,通过 Bridge 与 Native 通信。Native 侧核心类在 iOS 是 RCTCxxBridge,Android 是 CatalystInstanceImpl。所有跨语言调用都要序列化成 JSON 经过 Bridge 传递。
0.74 版本默认启用的 Bridgeless 架构移除了这座”桥”,JavaScript 直接调用 Native 方法,性能更好。iOS 侧核心类变为 RCTInstance,Android 侧变为 ReactInstance。
向其中注入 JS 代码可以拦截网络请求、修改界面、调试业务逻辑等,静态反编译和动态修改运行时是软件逆向常见的手法。下面结合具体代码来说明实现思路。
脚本实现
我们一共要支持 4 种情况:Android 和 iOS 的 Legacy 和 Bridgeless 架构。
frida 里可以简单实用 ObjC.classes 和 Java.classes 来检查类是否存在。
| 平台 | Legacy 架构 | Bridgeless 架构 |
| — | — | — |
| iOS | RCTCxxBridge | RCTInstance |
| Android | CatalystInstanceImpl | com.facebook.react.runtime.ReactInstance |
拦截 JS Bundle 加载
React Native 的 JS 代码以 Bundle 的形式加载。如果应用版本很旧,可能用的是压缩混淆后的 js,分析很简单。不过目前多数情况都是 hemes 字节码,分析门槛比前者显著提高。
我们可以拦截以下方法拿到 js 或者字节码。
iOS legacy 架构:
-[RCTCxxBridge executeSourceCode:withSourceURL:sync:] -[RCTCxxBridge executeApplicationScript:url:async:]
iOS bridgeless 架构:
-[RCTInstance _loadJSBundle:]
Android legacy 架构:
CatalystInstanceImpl 的 loadScriptFromAssets 以及 loadScriptFromFile
Android bridgeless 架构:
com.facebook.react.runtime.ReactInstance 的 loadJSBundleFromFile 和 loadJSBundleFromAssets
主动注入 JS 代码
React Native 核心的逻辑使用 C++ 实现,用 frida 直接交互虽然不是不可能,但是构造参数非常麻烦,还得处理内存管理和偏移量适配等问题。
从 Java 层或者 Objective-C 层并没有提供可以传入字符串的接口,只能把 js 写入临时的 bundle 然后载入。
在文章开头提到的 Pilfer 的博客里,作者为了拿到当前运行的 CatalystInstanceImpl 实例,用了比较 hack 的方法,创建定时器等待 loadScriptFromAssets 被调用,然后在 hook 里把实例保存下来。
// This is the app identifier you're trying to hookconst package_name = 'com.foo.bar';// Write the hermes-hook.js payload to fileconst f = new File(`/data/data/${package_name}/files/hermes-hook.js`, 'w');f.write(`console.log(Object.keys(this)); console.log('hello from React Native!');`);f.close();Java.perform(function () { // Lazily wait for the class to be available to us var looper = setInterval(function () { try { const CatalystInstanceImpl = Java.use("com.facebook.react.bridge.CatalystInstanceImpl"); CatalystInstanceImpl.loadScriptFromAssets.implementation = function (assetManager, assetURL, z) { // Load the original index.android.bundle this.loadScriptFromAssets(assetManager, assetURL, z); // Load custom JS into the global hermes context this.loadScriptFromFile(`/data/data/${package_name}/files/hermes-hook.js`, `/data/data/${package_name}/files/hermes-hook.js`, z); }; clearInterval(looper); } catch (error) { console.log('failed'); } }, 10);});
其实 frida 本身的 Java.choose 和 ObjC.choose 就可以直接在内存里检索到实例。
以 iOS 的 legacy 架构为例:
const nsData = ObjC.classes.NSData.dataWithContentsOfFile_(path);const nsURL = ObjC.classes.NSURL.fileURLWithPath_(path);instance["- enqueueApplicationScript:url:onComplete:"](nsData, nsURL, NULL);
新架构注入 js bundle 用的是 RCTInstance 的 _loadJSBundle: 方法。但是没想到吧,还有惊喜。这个方法在三年前的提交改过名字,之前是没有下划线的
https://github.com/facebook/react-native/commit/0dcf81b4f19484a4e43
不过这个适配好做,直接 respondsToSelector: 判断一下就行。
获取返回值
在这里遇到了另一个问题,上层封装的加载 js 接口并不等待脚本执行完成,也没有提供获取执行结果的接口。虽然我们在 js 脚本里使用 console.log 可以在 iOS 的系统日志或者 Android 的 logcat 里看到输出,对手工测试的场景绰绰有余,但如果想开发自动化工具,到处 grep 就不太优雅。
很容易想到一个很糟糕的思路:js 里内置了 XMLHttpRequest,直接把执行的结果回传到一个本地监听的 http 服务器上。也不是不可以。
那么既然我们想到了 React Native 内置函数这一点,又有二进制级别的函数插桩,不妨直接用 alert 当作 callback 回传。这并不是笔者原创,多年以前就有人用这个思路实现 WebView 的 js 和 native 互传数据了。
先把待执行代码包装一下:
const wrapped = `try { var r = (function() { return ${script} })(); alert('frida-callback:${id}:' + JSON.stringify(r));} catch (e) { alert('frida-callback:${id}:' + JSON.stringify({ error: e.message }));}`;
接着这个字符串 frida-callback:… 会被封装成字典格式传到 native 层。以 iOS 为例,就是一个 NSMutableDictionary,其中的 key 是 “message”。但这里有一个小坑。从 6 年前的一个提交到截止本文发布的版本,这个 native 方法接受的参数是一个 C++ 的对象,解引用第一个指针才是 NSMutableDictionary:
RCT_EXPORT_METHOD(alertWithArgs : (JS::NativeAlertManager::Args &)args callback : (RCTResponseSenderBlock)callback)
而 2019 年的这个 a5ad0bf12468fc831c2a 提交当中,函数原型曾经是直接传的:
RCT_EXPORT_METHOD(alertWithArgs:(NSDictionary *)args callback:(RCTResponseSenderBlock)callback)
这就导致同样的代码会崩,还得特殊处理一下。不过都 7 年了,如果不是特别执着兼容性,直接按照新的函数原型来构造参数就好了。
const { RCTAlertManager } = ObjC.classes; const method = RCTAlertManager["- alertWithArgs:callback:"]; const original = method.implementation; method.implementation = ObjC.implement( method, function ( handle: NativePointer, selector: NativePointer, args: NativePointer, callback: NativePointer, ) { const message = new ObjC.Object(args.readPointer()) // <- 注意 readPointer .objectForKey_("message") .toString(); console.debug(`React Native alert(${message})`); return original(handle, selector, args, callback); }, ); console.log('replaced RCTAlertManager["- alertWithArgs:callback:"]');
这个 Module 看上去不受 bridgeless 架构的影响,都可以用同样的方式 hook。
最后看看效果,搞了个类似 F12 的东西:
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:安全研究GoSSIP 《G.O.S.S.I.P 阅读推荐 2026-02-26 Frida + js -> React Native》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论