ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JSDebugger 源码解析:JDEngine 如何用 JavaScriptCore 实现全局类名劫持?

JSDebugger 源码解析:JDEngine 如何用 JavaScriptCore 实现全局类名劫持? JSDebugger 源码解析JDEngine 如何用 JavaScriptCore 实现全局类名劫持【免费下载链接】JSDebuggerJavaScript-Based Debugger For Inspecting Running State Of Your Application项目地址: https://gitcode.com/gh_mirrors/js/JSDebuggerJSDebugger 源码解析系列的第一篇我们从最核心的 JDEngine 说起一个 iOS 运行时调试工具凭什么能让你在 JavaScript 里直接写UIView.alloc().init()、UIColor.redColor()无需任何 JSExport 声明就能调用任意 Objective-C 方法答案就是JavaScriptCore 全局对象劫持——本文以源码为线索拆解 JDEngine 是如何把全局类名一步步变成真实的 OC 类与方法的。先看效果JS 里凭空出现的 OC 类名打开 demo.js你会看到这样的代码var view UIView.alloc().init(); var vc choose(ViewController)[0]; vc.view().setBackgroundColor_(UIColor.redColor());UIView、ViewController、UIColor这些名字既没有在 JS 里定义也没有通过 JSExport 暴露却在运行时凭空可用。这背后正是 JDEngine 用 JavaScriptCore 实现的一套全局类名劫持机制。JDEngine 如何劫持全局对象三个关键步骤第一步用自定义 JSClass 创建全局上下文在 JDEngine.m 的初始化方法里JDEngine 没有使用普通的 JSContext而是手工构造了一个名为JDGlobal的 JSClass并给它挂上了getProperty回调globalDefinition kJSClassDefinitionEmpty; globalDefinition.className JDGlobal; globalDefinition.getProperty JDGlobalGetProperty; _globalObject JSClassCreate(globalDefinition); _ctxRef JSGlobalContextCreate(_globalObject);关键点在于全局对象本身就是一个可编程的 JSClass。JS 代码中任何读取全局变量的操作都会先经过JDGlobalGetProperty回调。第二步getProperty 回调里的变身术全局对象劫持的核心逻辑就在 JDEngine.m 的这个回调函数中static JSValueRef JDGlobalGetProperty(JSContextRef ctx, JSObjectRef object, JSStringRef propertyName, JSValueRef *exception) { NSString *className JDCreateNSStringFromJSString(ctx, propertyName); Class cls NSClassFromString(className); // 用类名字符串查 OC 类 if (!cls) return NULL; // 没找到就返回 NULL让 JS 继续向上查找 return JSObjectMake(ctx, JDClass4JS(), (__bridge void *)cls); }整个劫持其实只有三步非常精妙把 JS 访问的属性名如UIView转成 NSString用NSClassFromString去Objective-C 运行时里查找同名类若找到就用 JDClass4JS 这个 JSClass 把 OC 类包装成 JS 对象返回。注意第 3 行的注释如果类不存在回调返回NULLJavaScriptCore 会继续沿着原型链/全局链向上查找——也就是说劫持是可穿透的不会破坏正常的 JS 语义。✅第三步从类到方法的二次劫持拿到包装后的类对象后JS 里访问UIView.alloc又触发了一层劫持。JDClass4JS.m 同样注册了getProperty把属性名格式化后通过class_getClassMethod查找类方法找不到实例方法还会向上查父类找到后包装成 JDMethod4JS 的函数对象返回。也就是说全局类名劫持是层层递进的全局属性 → OC 类 → 类方法/实例方法 → 真正调用。从 JS 调用到 OC 方法完整调用链路当 JS 最终执行view.setBackgroundColor_(color)时链路是这样的层级组件职责1JDGlobalGetProperty劫持全局类名返回类对象2JDClassGetProperty类上取方法返回方法对象3JDMethodCallAsFunctionJS 调用时解析 thisObject 与 selector4JDMethodBridge组装 NSMethodSignature 与 IMP5JDFFIContext通过 FFI 完成真正的 C/OC 函数调用其中第 4、5 步在 JDMethodBridge.m 与 JDFFIContext.m 中实现负责 JS 参数与 OC 类型int、double、指针、结构体之间的互相转换。这套设计让JSDebugger 无需任何 JSExport 声明就能调用任意 OC 方法——这正是它区别于 JSPatch 等方案的最大亮点。附赠能力属性读写、setter 与 choose 插件除了方法调用实例属性读写同样被劫持。JDInstance4JS.m 同时实现了getProperty与setProperty读取属性时若命中关联对象则直接返回否则按方法调用处理所以 getter 要用view.backgroundColor()的形式赋值时则自动拼出setXxx:并走方法调用。此外JDEngine 启动时还会通过 JDFunctionPlugin.m 把choose、introspect等插件函数注册到全局对象上。例如 JDChoose.mm 的choose(ViewController)会遍历堆内存找出该类的全部实例堪称堆内存扫描器。️总结全局类名劫持的本质回过头看JDEngine 的实现思路可以浓缩成一句话用自定义 JSClass 接管全局对象的属性读取把 JS 标识符当作 OC 类名去运行时查找再用 JSClass 层层包装类与方法最终通过 FFI 打通调用。掌握了这条主线再看 JSDebugger.h、JDStruct 等模块就很容易融会贯通了。如果你也想在自己的调试工具或动态化方案里复刻这套能力建议从三个文件入手JDEngine.m全局劫持入口、JDClass4JS.m类包装、JDInstance4JS.m实例包装。跟着 demo 工程 JSDebuggerDemo 跑一遍你会更直观地感受到JS 与 OC 零缝隙互通的乐趣【免费下载链接】JSDebuggerJavaScript-Based Debugger For Inspecting Running State Of Your Application项目地址: https://gitcode.com/gh_mirrors/js/JSDebugger创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表