当前位置: 首页 > news >正文

js补环境工具使用技巧、补环境实例、重点环境检测点详解

什么是补环境,模拟浏览器环境让浏览器js运行,为什么需要补环境,因为浏览器和本地nodejs环境有差异,网站开发者为了检测用户是否是本地环境运行

主要补的环境Document,Window,Navigator,Location,Element

这是内置原始类型,需要将其实例化并补齐使用的方法,由于本身是函数所以方法只能赋值在原型链,补环境要和扣代码相配合要不然仅凭调试台返回信息,无法补全缺失环境。

一般来说补环境的基本流程是:先补基础环境,添加代理,在加密代码运行报错的地方去源代码处打断点分析具体缺少什么环境,代码整个运行不报错后再用打印环境工具分析或者去代码中进行插桩进行调试。

补环境前准备:

本地代码与浏览器联调

运行此代码即可在浏览器调试台运行本地代码,省去了vscode繁琐的配置,非常方便调试和打印。

node --inspect-brk .\补环境代码文件.js

打印浏览器环境工具

这里用的是大佬无私分享的工具,具体使用方式和作用这里都有,这里就不在重复。爬虫利器SpiderTools谷歌插件教程v1.0.0!!!web端JavaScript环境检测!!!_爬虫谷歌插件-CSDN博客

补环境代理

代理的重要不用多说,这里分享一个代理。


function watch(obj, name) {return new Proxy(obj, {get: function (target, property) {const value = target[property];const type = typeof value;if (type === "symbol") {// 获取 Symbol 的描述,如果没有描述则显示 'no description'const symbolDescription = property.description || 'no description';console.log(`对象=>${name},读取属性:${symbolDescription},这是一个 Symbol 类型的值`);} else if (type === "function") {// 对于函数,我们可以打印函数名,如果有的话const functionName = value.name || 'anonymous';console.log(`对象=>${name},读取属性:${property.toString()},这是一个名为 ${functionName} 的函数`);} else {// if(name==='window' &&  String(property)==='Symbol(Symbol.toPrimitive)' ){//     debugger//// }console.log(`对象=>${name},读取属性:${String(property)},值为:${value},类型为:${type}`);}return value;},set: (target, property, newValue, receiver) => {const valueType = typeof newValue;if (valueType === "symbol") {// 获取新设置的 Symbol 的描述const symbolDescription = newValue.description || 'no description';console.log(`对象=>${name},设置属性:${String(property)},这是一个 Symbol 类型的新值, 描述为: ${symbolDescription}`);} else {console.log(`对象=>${name},设置属性:${String(property)},值为:${newValue},类型为:${valueType}`);}return Reflect.set(target, property, newValue, receiver);}});
}//第一个参数是要代理的对象,第二个是打印的时候的表示哪个参数
window = watch(window, 'window')
location = watch(new Location(), 'location')

批量赋值对象属性代码

可以把对象中的值都复制到内存中

function copyObj(obj) {var newObject = {};for (var key in obj) {var result = obj[key];if (['string', 'boolean', 'number'].indexOf(typeof result) !== -1 || Object.prototype.toString.call(result) === '[object Array]') {newObject[key] = result;}}copy(newObject);
};copyObj(location) 
copyObj(navigator) 

常用补环境方法

设置原型链


//原型链直接设置函数,一般无检测函数直接等于即可
Document.prototype.getElementsByTagName = function getElementsByTagName(tagName) {console.log('docuemnt.getElementsByTagName=>', {tagName});const tag = {i: [], script: [script1,script2],base:[]}console.log(tag[tagName])return tag[tagName]
}//给对象设置内置属性[Symbol.toStringTag]
Object.defineProperties(window, {[Symbol.toStringTag]: {value: "Window",configurabble: true,},[Symbol.toPrimitive]: function (a1) {console.log("Symbol.toPrimitive==>a1", a1);}
})//设置一个函数的原型链是另一个函数的原型链
HTMLDocument = function HTMLDocument() {}
Object.setPrototypeOf(HTMLDocument.prototype, Document.prototype)//修改一个对象中值的不可修改属性
const obj = {_name: 'John'
};Object.defineProperty(obj, 'name', {get() {return this._name;},set(value) {this._name = value;},enumerable: true,configurable: true
});
//查询对象属性不可修改值
console.log(Object.getOwnPropertyDescriptor(obj,'name'))

补环境示例

补上window、location、document、navigator,并删除__filename、__dirname、global、(浏览器中没有这两个环境),navigator也要删除,因为nodejs会内置一个navigator属性,在自己补充环境时可能会造成一些错误,同时对一些常用检测环境针对性还原。

delete __filename
delete __dirname
delete navigatorconst locationInfo = {};
const navigatorInfo = {};//使用箭头函数把locationInfo的属性添加到Location函数的this中
function Location() {Object.keys(locationInfo).forEach(key => {this[key] = locationInfo[key];})
}function Navigator() {
}Object.keys(navigatorInfo).forEach(key => {Navigator.prototype[key] = navigatorInfo[key];
})function Document() {
}function Window() {
}location = new Location()
navigator = new Navigator()
document = new Document()window = global
//删除global检测
delete global
window.__proto__  = new Window()
window.top = window;
window.self = window

重点属性检测

以下是一些经常用到的环境检测,也是补环境中的重点难点。

✅ document.all 的 IE 兼容检测

✅ navigator.webdriver 反自动化检测

✅ window.chrome 存在性检测

✅ MutationObserver DOM 变化检测

✅ WebSocket 连接检测

✅ getBattery 设备信息检测

✅ localStorage / sessionStorage 访问检测

✅ indexedDB 可用性检测

✅ CanvasRenderingContext2D 指纹检测

✅ setTimeout 和 setInterval 时间检测

✅ fetch 和 XMLHttpRequest API 访问检测

✅ visibilityState 页面可见性检测

✅ window.top / window.self 反 iframe 检测

通过补环境实例解决这些环境检测

delete __dirname;
delete __filename;window = global;delete global;Object.defineProperties(window, {[Symbol.toStringTag]: {value: 'Window',configurable: true}
});l_obj = {};l_input = {};l2_input = {};l3_input = {};var form = {
};form_action = '';Object.defineProperty(form, 'action',{get() {console.log('form->action.get--------->', l_input)return l_input;},set(v) {console.log('form->action.set--------->', v)form_action = v;}
});form_textContent = {};Object.defineProperty(form, 'textContent',{get() {console.log('form->textContent.get--------->', l2_input)return l2_input;},set(v) {console.log('form->textContent.set--------->', v)form_action = v;}
});form_id = '';Object.defineProperty(form, 'id',{get() {console.log('form->id.get--------->', l3_input)return l3_input;},set(v) {console.log('form->id.set--------->', v)form_id = v;}
});form_innerText = '';Object.defineProperty(form, 'innerText',{get() {console.log('form->innerText.get--------->', l3_input)return l3_input;},set(v) {console.log('form->innerText.set--------->', v)form_innerText = v;}
});a_labl = {//去浏览器里拿href: 'xxxxxxxxxxxxxx',protocol: 'https:',port: '',//去浏览器里拿hostname: 'xxxxxxxxxxxxxxx',//去浏览器里拿pathname: 'xxxxxxxxxxxxxxx'
}window.HTMLAnchorElement = function (){};scripts = [{type: "text/javascript",r: 'm',parentElement: {getAttribute: function(args) {console.log('head1->parentElement->getAttribute: ', args)console.log(arguments)debugger;if (args == 'r'){return 'm';}},getElementsByTagName: function(args) {console.log('head1->getElementsByTagName: ', args)console.log(arguments)debugger},removeChild: function (args) {console.log('head1->parentElement->removeChild', args);console.log(arguments);debugger;},},getAttribute: function(args) {console.log('script1->getAttribute: ', args)console.log(arguments)debugger;if (args == 'r'){return 'm';}}},{type: "text/javascript",r: 'm',parentElement: {getAttribute: function(args) {console.log('head2->parentElement->getAttribute: ', args);console.log(arguments);debugger;},getElementsByTagName: function(args) {console.log('head2->getElementsByTagName: ', args);console.log(arguments);debugger},removeChild: function (args) {console.log('head2->parentElement->removeChild', args);console.log(arguments);debugger;},},getAttribute: function(args) {console.log('script2->getAttribute: ', args);console.log(arguments);debugger;if (args == 'r'){return 'm';}},//去浏览器里拿src: "xxxxxxxxxx",}]var input_count = 0;var l_meta = {id: 'FbkwzLN5XOx0',content: 'tyGGg5AdQlANmSX9z3xbpGEoEKVuG9rmj_VCz71ozkpQ9tph9oDZE2RjIwQz8iL5oWgiCSPtU67jWlcPgf7DyTWP8X_.29Z5B0y9OtqwW4e6THU9dqdapsjx4a81rlUo',r: 'm',getAttribute: function(args){console.log('meta->getAttribute: ', args);console.log(arguments);debugger;if (args == 'r'){return 'm';}},parentNode: {removeChild: function (args) {console.log('meta->parentNode->removeChild', args)debugger;return {};},}
}div_i = [];div = {getElementsByTagName:function (args){console.log('document->div->getElementsByTagName', args)console.log(arguments)debugger;if(args === "i"){return div_i;}}
}doc_base = []Document = function Document(){}Object.defineProperty(Document.prototype,'createElement',{configurable: true,enumerable: true,value: function createElement(args) {console.log('document->createElement', args)console.log(arguments);debugger;if (args == 'div'){return div;}else if (args == 'form'){return form;}else if (args == 'input'){if (input_count == 0){input_count++;return l_input;}else if (input_count == 1){input_count++;return l2_input;}else if (input_count == 2){return l3_input;}}else if (args == 'a'){return a_labl;}else{return l_obj;}},writable: true,
})const v8 =require('v8');
const vm= require('vm');
v8.setFlagsFromString('--allow-natives-syntax');
let undetectable = vm.runInThisContext("%GetUndetectable()");
v8.setFlagsFromString('--no-allow-natives-syntax');Object.defineProperty(Document.prototype,'all',{configurable: true,enumerable: true,value: undetectable,writable: true,
})Object.defineProperty(Document.prototype,'body',{configurable: true,enumerable: true,value: null,writable: true,
})Object.defineProperty(Document.prototype,'visibilityState',{configurable: true,enumerable: true,value: 'hidden',writable: true,
})Object.defineProperty(Document.prototype,'toString',{configurable: true,enumerable: true,value: function toString() {return '[object HTMLDocument]';},writable: true,
})Object.defineProperty(Document.prototype,'addEventListener',{configurable: true,enumerable: true,value: function addEventListener(args) {console.log('document->addEventListener', args)console.log(arguments);debugger;return {};},writable: true,
})documentElement = {};
Object.defineProperty(Document.prototype,'documentElement',{configurable: true,enumerable: true,// value: function documentElement(args) {//     console.log('document->documentElement', args)//     console.log(arguments);//     debugger;//     return {};// },value:documentElement,writable: true,
})Object.defineProperty(Document.prototype,'appendChild',{configurable: true,enumerable: true,value: function appendChild(args) {console.log('document->appendChild', args)console.log(this)console.log(arguments);debugger;return {};},writable: true,
})Object.defineProperty(Document.prototype,'removeChild',{configurable: true,enumerable: true,value: function removeChild(args) {console.log('document->removeChild', args)console.log(arguments);debugger;return {};},writable: true,
})frist_get_script = 1;
Object.defineProperty(Document.prototype,'getElementsByTagName',{configurable: true,enumerable: true,value: function getElementsByTagName(args) {console.log('document->getElementsByTagName: ', args);console.log(arguments)debuggerif (args == 'script'){if (frist_get_script == 1){frist_get_script = 0;return scripts;}return [];}if (args === 'base') {debugger;return doc_base;}return [];},writable: true,
})Object.defineProperty(Document.prototype,'getElementById',{configurable: true,enumerable: true,value: function getElementById(args) {console.log('document->getElementById', args)console.log(arguments);debugger;return l_meta;},writable: true,
})HTMLDocument = function HTMLDocument(){}Object.setPrototypeOf(HTMLDocument.prototype,Document.prototype)
document = new HTMLDocument()
// console.log(document.createElement('script'));Object.defineProperty(document.all,'length',{get : function (){console.log('document.all.length ------------------------------------->')return Object.keys(document.all).length}
})document.all[0] = null;
document.all[1] = null;
document.all[2] = null;
document.all[3] = null;
document.all[4] = null;
document.all[5] = null;// document.all = [{},{},{},{},{},{}];function Window(){};window.Window = Window;window.__proto__ = Window.prototype;_null = function (){debugger;console.log(arguments)return {};
}_mutationObserver = {observe:function(args){console.log('_mutationObserver->observe', args)console.log(arguments);return {};}
};window.innerHeight = 945;
window.innerWidth = 1920;
window.outerHeight = 1022;
window.outerWidth = 1910;
window.TEMPORARY = 0;window.MutationObserver = function(args)
{console.log('window->mutationObserver', args)console.log(arguments);return _mutationObserver;
}CanvasRenderingContext2D = function () {};getImageData = {toString() {console.log('getImageData');return 'function getImageData() { [native code] }'}
}Object.defineProperty(CanvasRenderingContext2D.prototype,'getImageData',{get : function (){return getImageData;}
})HTMLCanvasElement = function () {};toBlob = {toString() {console.log('toBlob');return 'function toBlob() { [native code] }'}
}toDataURL = {toString() {console.log('toDataURL');return 'function toDataURL() { [native code] }'}
}Object.defineProperty(HTMLCanvasElement.prototype,'toBlob',{get : function (){return toBlob;}
})Object.defineProperty(HTMLCanvasElement.prototype,'toDataURL',{get : function (){return toDataURL;}
})window.CanvasRenderingContext2D = CanvasRenderingContext2D;
window.HTMLCanvasElement = HTMLCanvasElement;WebSocket = function(args)
{console.log('WebSocket ----------------------->', args);return {};
}window.WebSocket = WebSocket;webkitRequestFileSystem = _null;window.webkitRequestFileSystem = webkitRequestFileSystem;chrome = {};
window.chrome = chrome;//去浏览器里拿
location = {"ancestorOrigins": {},"href": "xxxxxxxx","origin": "xxxxxxxx","protocol": "https:","host": "xxxxxxxx","hostname": "xxxxxxxx","port": "","pathname": "xxxxxxxx","search": "xxxxxxxx","hash": ""
}window.top = window;
window.self = window;navigator = {appCodeName: "Mozilla",appName: "Netscape",appVersion: "5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",connection: {downlink: 2.4,effectiveType: "4g",onchange: null,rtt: 50,saveData: false},cookieEnabled: true,deprecatedRunAdAuctionEnforcesKAnonymity: true,deviceMemory: 8,doNotTrack: null,hardwareConcurrency: 22,languages: ["zh-CN", "en", "zh"],language: "zh-CN",onLine: true,platform: "Win32",product: "Gecko",productSub: '20030107',userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36",vendor: "Google Inc.",vendorSub: "",// webdriver: false,webkitPersistentStorage: {},getBattery: function() {return {then(){}}}
};function Naviator(){};Object.defineProperties(Naviator.prototype,{})function webdriver()
{console.log("webdriver--------------------->");return false;
}webdriver.toString = function () {return 'false'}Object.defineProperty(Naviator.prototype, 'webdriver',{[Symbol.toStringTag]: {value: 'webdriver',configurable: true},configurable:true,enumerable: true,get: webdriver
});navigator.__proto__ = Naviator.prototype;Object.defineProperties(navigator, {[Symbol.toStringTag]: {value: 'webdriver',configurable: true}
})window.navigator = navigator;window["clientInformation"] = navigator;window.location = location;window.history = {length: 2,state: null,scrollRestoration: "auto",replaceState: _null,
}screen = {availHeight: 1392,availLeft: 1536,availTop: 0,availWidth: 2560,colorDepth: 24,height: 1440,isExtended: true,onchange: null,orientation: {angle: 0, type: 'landscape-primary', onchange: null},pixelDepth: 24,width: 2560
}window.screen = screen;window.DOMParser = function ()
{debugger;return {};
}window.XMLHttpRequest = function () {debugger;return {}
}localStorage = {length: 0,removeItem: function () {console.log('localStorage->removeItem')console.log(arguments);},setItem: function () {console.log('localStorage->setItem');console.log(arguments);this[arguments[0]] = arguments[1];console.log(this);},getItem: function (args) {console.log('localStorage->getItem')console.log(arguments);return this[args];},
}
sessionStorage = {length: 0,removeItem: function () {console.log('localStorage->removeItem')console.log(arguments);},setItem: function () {console.log('localStorage->setItem');console.log(arguments);this[arguments[0]] = arguments[1];console.log(this);},getItem: function (args) {console.log('localStorage->getItem')console.log(arguments);console.log(this[args]);return this[args];},
}window.localStorage = localStorage;
window.sessionStorage = sessionStorage;
window.name = '$_YWTU=7nXC8M_ZRylQDpM8YlUdxPdHlh7M_t8lWHF71cWe4Q7&$_YVTX=Js3&vdFm='indexedDB = {open: function (args) {console.log('indexedDB->open---------------->');// return {};return indexedDB;}
}window.indexedDB = indexedDB;window.addEventListener = function (args)
{console.log('window->addEventListener: ', args)debugger;return {};
}window.attachEvent = undefined;window.Request = function (args)
{console.log('window->Request: ', args)debugger;return {};
}window.fetch  = function (args)
{console.log('window->fetch: ', args)debugger;return {};
}window.setInterval = _null;
window.setTimeout = _null;window.document = document;//$_ts=window['$_ts']内容
require('./ts')//外链js内容
require('./link')function get_cookie()
{return document.cookie;
}

http://www.xdnf.cn/news/197857.html

相关文章:

  • Qt开发:XML文件的写入与读取
  • AI与机器人外科手术:如何用智能化技术提升手术精度与安全性?
  • 【android bluetooth 协议分析 06】【l2cap详解 10】【通过avdtp连接流程,感受l2cap通道的生命周期变化】
  • [JavaScript]对象关联风格与行为委托模式
  • WSL释放空间
  • ‌wangEditor 所有菜单项分类、说明及隐藏方法
  • Java项目场景题深度解析
  • Termux - Android终端应用与Linux环境
  • Java读Excel:解析阿里云easyExcel导入文件的行号
  • vmare pro安装报错用户在命令行上发出了EULAS_AGREED=1,表示不接受许可协议的错误解决方法
  • 高压开关柜局部放电信号分析系统
  • C/C++链表的常用操作实现
  • three.js后处理原理及源码分析
  • HTML5好看的水果蔬菜在线商城网站源码系列模板7
  • 文档在线协同工具ONLYOFFICE教程:如何使用宏突出显示具有特定提示文本的空文本字段
  • window 图形显示驱动-在 WDDM 1.2 中提供无缝状态转换(下)
  • 系统架构师2025年论文《论面向对象的软件设计——UML 在面向对象软件架构中的应用》
  • leetcode 876. 链表的中间结点
  • Python 实现的运筹优化系统数学建模详解(动态规划模型)
  • 第二阶段:基础加强阶段总体介绍
  • 网络安全怎么入门?快速了解
  • 基于大模型的公安预审办案笔录分析的挑战与应对策略-3
  • 2025汽车制造企业数字化转型路径参考
  • TypeScript之基础知识
  • vue报错:Loading chunk * failed,vue-router懒加载出错问题。
  • C++复习补充 类型转换和RTTI
  • 人工智能与机器学习:Python从零实现K-Means 算法
  • RAG工程-基于LangChain 实现 Advanced RAG(预检索优化)
  • 驱动开发硬核特训 │ Regulator 子系统全解
  • 企业架构之旅(2):解锁TOGAF ADM