news 2026/2/3 11:46:58

【khbox补环境-3】原型链与 Illegal Invocation 保护机制

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【khbox补环境-3】原型链与 Illegal Invocation 保护机制

khbox 的项目已放gitee, https://gitee.com/sugarysp/khbox_pro ,欢迎各位大佬使用测试。

目前完成的有

  • 调用链追踪
  • document.all
  • 原型保护以及toString
  • c层调用链日志保存
  • illegal 保护机制
  • ps:可能有bug

待做:

  • 异步
  • console.log 业务代码保存日志

目录

  • 核心挑战
  • 原型链继承体系
  • Function.prototype.toString 保护
  • Illegal Invocation 保护机制
  • 测试验证
  • 总结


核心挑战

1. 原型链继承

浏览器中的 DOM 对象有着复杂的继承关系:

Document → Node → EventTarget → Object Navigator → Object Window → EventTarget → Object

检测手段:

// 网站检测代码Object.getPrototypeOf(Document.prototype).constructor.name==='Node'// 必须为 true'addEventListener'inEventTarget.prototype// 必须为 true

2. Native Code 伪装

真实浏览器的原生方法调用toString()会返回[native code]

// 真实浏览器navigator.javaEnabled.toString()// "function javaEnabled() { [native code] }"// 普通 JS 实现functionjavaEnabled(){returnfalse;}javaEnabled.toString()// "function javaEnabled() { return false; }" ❌ 暴露了

3. Illegal Invocation 保护

浏览器原生方法强制检查this的合法性:

// 真实浏览器constuaGetter=Object.getOwnPropertyDescriptor(Navigator.prototype,'userAgent').get;uaGetter.call(window);// TypeError: Illegal invocation// 普通 JS 实现functionget_userAgent(){returnthis._ua;}get_userAgent.call(window);// 可以执行 ❌ 行为不一致

原型链继承体系

实现方式

KhBox 通过在 sandbox 中暴露构造函数,结合 JSDOM 的原型链实现正确的继承关系:

// main.js - 构建 sandboxconstsandbox={// 暴露实例(经过 KhBox Proxy 包装)document:proxyDocument,navigator:proxyNavigator,window:proxyWindow,// 暴露构造函数(直接引用 JSDOM 的)Document:dom.window.Document,Navigator:dom.window.Navigator,Window:dom.window.Window,Node:dom.window.Node,EventTarget:dom.window.EventTarget,// ...};

验证结果

// 测试原型链Document.prototype.constructor.name// "Document"Object.getPrototypeOf(Document.prototype).constructor.name// "Node"Object.getPrototypeOf(Node.prototype).constructor.name// "EventTarget"// 测试属性定义位置'cookie'inDocument.prototype// true'addEventListener'inEventTarget.prototype// true

Function.prototype.toString 保护

技术原理

利用 V8 的Private Symbol机制,为函数对象存储自定义的 toString 返回值:

// native_protection.ccLocal<Function>CreateNativeWrapper(...){// 创建原生函数包装器Local<FunctionTemplate>tpl=FunctionTemplate::New(isolate,callback);Local<Function>nativeFunc=tpl->GetFunction(context).ToLocalChecked();// 生成 [native code] 字符串std::string native_code;if(strcmp(method_type,"get")==0){native_code="function get "+name+"() { [native code] }";}else{native_code="function "+name+"() { [native code] }";}// 🔥 关键:用 Private Symbol 存储nativeFunc->SetPrivate(context,GetNativeStringSymbol(isolate),String::NewFromUtf8(isolate,native_code.c_str()).ToLocalChecked());returnnativeFunc;}

Private Symbol 的优势

  • 不可枚举:Object.keys()无法获取
  • 不可访问:JavaScript 层无法读取或修改
  • V8 内部支持:Function.prototype.toString会优先读取此 Symbol

验证效果

navigator.javaEnabled.toString()// "function javaEnabled() { [native code] }" ✅String(navigator.javaEnabled)// "function javaEnabled() { [native code] }" ✅

Illegal Invocation 保护机制

什么是 Illegal Invocation?

当调用浏览器原生方法时,如果this不是预期的对象类型,会抛出TypeError: Illegal invocation

// 合法调用navigator.userAgent// ✅// 非法调用constuaGetter=Object.getOwnPropertyDescriptor(Navigator.prototype,'userAgent').get;uaGetter.call(window)// ❌ TypeError: Illegal invocation

实现架构

KhBox 通过三层检查机制实现 Illegal Invocation 保护:

第一层:焊死到 Prototype 上
// toolFuncs.jslazyLoader:function(receiver,target,prop,classNameFromC){// ...// 🔥 焊死到 Constructor.prototype 而不是实例上constwindow=khBox.memory.jsdom.window;constprotoTarget=window[protoOwner]?.prototype||receiver;return{impl:khBox.envFuncs[implKey],target:protoTarget,// Navigator.prototypeclassName:protoOwner,// "Navigator"// ...};}

为什么要焊死到 Prototype?

// 如果焊死到实例上:navigator.userAgent// ✅ 可以访问Navigator.prototype.userAgent// ❌ 拿不到我们的 wrapper// 焊死到 Prototype 上:navigator.userAgent// ✅ 继承自 prototypeNavigator.prototype.userAgent// ✅ 直接获取到我们的 wrapperObject.getOwnPropertyDescriptor(Navigator.prototype,'userAgent').get// ✅ 受保护
第二层:className 权威来源

使用 JSON 配置中的类名而不是 JSDOM 的constructor.name,因为jsdom某些可能不够全,优先用自己脱下来的环境:

// watcher.cc - Watch 函数voidWatch(constv8::FunctionCallbackInfo<Value>&args){// 优先使用传入的 className(来自 JSON 配置)constchar*class_name=nullptr;if(args.Length()>=2&&args[1]->IsString()){String::Utf8Valuecn(isolate,args[1]);class_name_str=*cn;class_name=class_name_str.c_str();}// 存储到 Proxy 的 InternalFieldLocal<Object>proxy=CreateWatcherProxy(isolate,target,source_path,class_name);// ...}
// main.js - 调用时明确指定类名constproxyNavigator=addon.watch(navigator,'Navigator');constproxyWindow=addon.watch(window,'Window');

降级策略:

// toolFuncs.jsconstclassName=classNameFromC// 优先:JSON 配置||target[Symbol.toStringTag]// 降级 1||target.constructor?.name// 降级 2:JSDOM||"Unknown";

测试验证

测试脚本

完整的测试代码位于khbox_vm/pages/demo_proto/input.js

// Part 1: 原型链测试console.log('Document.prototype.constructor.name:',Document.prototype.constructor.name);console.log('Document 的父类:',Object.getPrototypeOf(Document.prototype).constructor.name);// Part 2: toString 保护console.log(navigator.javaEnabled.toString());// "function javaEnabled() { [native code] }"// Part 3: Illegal InvocationconstuaGetter=Object.getOwnPropertyDescriptor(Navigator.prototype,'userAgent').get;// 正常调用console.log(navigator.userAgent);// ✅ 成功// 非法调用uaGetter.call(window);// ❌ TypeError: Illegal invocation

运行结果

$nodemain.js## Part 1: 原型链继承测试✅ Document.prototype.constructor.name: Document ✅ Document.prototype 的父类: Node ✅ Node.prototype 的父类: EventTarget## Part 2: toString 保护机制测试✅ navigator.javaEnabled.toString():functionjavaEnabled(){[native code]}## Part 3: Illegal Invocation 保护测试---3.1正常调用 --- ✅ navigator.userAgent: Mozilla/5.0(Windows NT10.0;Win64;x64)... ✅ navigator.javaEnabled():false---3.2非法调用 ---[Illegal Invocation Check]Expected:'Navigator', Actual:'Window'✅ 抛出 Illegal Invocation: Illegal invocation

总结

KhBox 框架通过以下机制实现了完整的浏览器行为模拟:

1. 原型链继承

  • 在 sandbox 中暴露 JSDOM 的构造函数
  • 保持正确的prototype链关系
  • 属性定义位置符合浏览器标准

2. Native Code 伪装

  • 使用 V8 Private Symbol 存储自定义 toString
  • Function.prototype.toString返回[native code]
  • JavaScript 层无法篡改或检测

3. Illegal Invocation 保护

  • 焊死到Constructor.prototype而不是实例
  • className 优先使用 JSON 配置(权威来源)
  • 运行时严格检查this的类型
  • 类型不匹配时抛出TypeError: Illegal invocation


更多文章,敬请关注gzh:零基础爬虫第一天天

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/28 20:03:43

计算机毕业设计springboot旅游信息交流网站的设计与实现 基于SpringBoot的“行走圈”旅游分享与商品交易平台 SpringBoot+Vue 全域旅游互动门户的设计与实现

计算机毕业设计springboot旅游信息交流网站的设计与实现29fsewmo &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。 疫情之后&#xff0c;国内旅游出现“短途、高频、个性化”新趋…

作者头像 李华
网站建设 2026/2/3 4:38:22

Rembg API文档生成:Swagger集成最佳实践

Rembg API文档生成&#xff1a;Swagger集成最佳实践 1. 背景与需求分析 1.1 智能万能抠图 - Rembg 在图像处理领域&#xff0c;自动去背景是一项高频且关键的需求&#xff0c;广泛应用于电商商品展示、证件照制作、设计素材提取等场景。传统方法依赖人工标注或简单阈值分割&…

作者头像 李华
网站建设 2026/1/30 7:57:23

Rembg模型部署:Docker容器化方案

Rembg模型部署&#xff1a;Docker容器化方案 1. 智能万能抠图 - Rembg 在图像处理与内容创作领域&#xff0c;自动去背景是一项高频且关键的需求。无论是电商商品图精修、社交媒体素材制作&#xff0c;还是AI绘画中的角色提取&#xff0c;传统手动抠图效率低下&#xff0c;而…

作者头像 李华
网站建设 2026/1/31 7:10:37

零基础玩转单目深度估计|基于AI单目深度估计-MiDaS镜像快速实践

零基础玩转单目深度估计&#xff5c;基于AI单目深度估计-MiDaS镜像快速实践 从零开始理解单目深度估计&#xff1a;3D感知的视觉革命 你是否曾想过&#xff0c;一张普通的2D照片其实“藏着”整个三维世界&#xff1f;通过人工智能技术&#xff0c;我们如今可以让计算机“看懂…

作者头像 李华
网站建设 2026/1/30 8:10:10

Rembg抠图应用:PPT制作素材处理指南

Rembg抠图应用&#xff1a;PPT制作素材处理指南 1. 引言&#xff1a;智能万能抠图 - Rembg 在日常办公、教学演示或产品展示中&#xff0c;PPT 制作常常需要高质量的图像素材。然而&#xff0c;原始图片往往带有复杂背景&#xff0c;直接插入幻灯片会显得突兀且影响整体视觉效…

作者头像 李华
网站建设 2026/1/29 19:16:03

Rembg抠图数据隐私:合规处理用户图片

Rembg抠图数据隐私&#xff1a;合规处理用户图片 1. 引言&#xff1a;智能万能抠图 - Rembg 在图像处理领域&#xff0c;自动去背景技术一直是提升内容创作效率的关键工具。无论是电商商品精修、社交媒体配图设计&#xff0c;还是AI生成内容的后处理&#xff0c;精准高效的抠…

作者头像 李华