JS逆向环境自吐法实战:Proxy与Reflect拦截5类关键对象(附代码)

JS逆向环境自吐法实战:Proxy与Reflect拦截5类关键对象(附代码) JS逆向环境自吐法实战Proxy与Reflect拦截5类关键对象1. 环境自吐法的核心原理在JS逆向工程中环境自吐法是一种通过拦截和记录目标代码对浏览器环境的访问行为从而逆向分析出完整环境检测逻辑的技术。这种方法的核心在于利用JavaScript的元编程能力通过Proxy和Reflect API实现对关键对象的透明拦截。Proxy对象可以创建一个对象的代理从而拦截和自定义该对象的基本操作。Reflect则提供了一组与Proxy traps对应的方法用于执行对象的默认行为。两者结合使用可以实现对对象操作的完全控制const handler { get(target, prop, receiver) { console.log(访问属性: ${prop}); return Reflect.get(...arguments); }, set(target, prop, value, receiver) { console.log(设置属性: ${prop} ${value}); return Reflect.set(...arguments); } }; const proxy new Proxy(targetObject, handler);这种技术相比传统的Object.defineProperty方法有显著优势拦截全面性可以拦截对象的所有操作包括属性访问、属性设置、in操作符检查等非侵入性不需要修改原始对象的属性描述符性能更好Proxy是专门为这种用例设计的性能优于defineProperty更丰富的拦截点支持13种不同的trap类型覆盖所有对象操作2. 基础拦截器实现2.1 通用拦截器模板下面是一个功能完整的通用拦截器实现可以记录所有对象操作function createInterceptor(obj, name) { return new Proxy(obj, { get(target, prop, receiver) { const value Reflect.get(...arguments); console.groupCollapsed([GET] ${name}.${prop.toString()}); console.log(类型:, typeof value); console.log(值:, value); console.groupEnd(); return value; }, set(target, prop, value, receiver) { console.log([SET] ${name}.${prop.toString()} , value); return Reflect.set(...arguments); }, apply(target, thisArg, argumentsList) { console.groupCollapsed([CALL] ${name}()); console.log(参数:, argumentsList); const result Reflect.apply(...arguments); console.log(返回值:, result); console.groupEnd(); return result; }, // 其他trap可以根据需要添加 }); }2.2 拦截器使用示例// 拦截navigator对象 window.navigator createInterceptor(window.navigator, navigator); // 拦截document对象 document createInterceptor(document, document); // 拦截特定函数 window.alert createInterceptor(window.alert, alert);3. 五类关键对象的拦截方案3.1 XMLHttpRequest拦截XMLHttpRequest是网站进行网络请求的核心对象许多反爬机制会在其中植入检测逻辑const nativeXHR window.XMLHttpRequest; window.XMLHttpRequest class XHRInterceptor { constructor() { this.xhr new nativeXHR(); return createInterceptor(this.xhr, XMLHttpRequest); } static get prototype() { return nativeXHR.prototype; } static get name() { return XMLHttpRequest; } };关键拦截点open()方法记录请求方法和URLsetRequestHeader()记录请求头设置send()记录请求体各种事件监听onreadystatechange等3.2 Document对象拦截Document对象包含大量与DOM操作相关的属性和方法是环境检测的重点const documentInterceptor { get(target, prop) { // 特殊处理querySelector等DOM查询方法 if ([querySelector, querySelectorAll, getElementById].includes(prop)) { return function(...args) { console.log([DOCUMENT] 查询元素: ${prop}(${args.join(, )})); return target[prop](...args); }; } return createInterceptor(target[prop], document.${prop}); } }; document new Proxy(document, documentInterceptor);需要特别关注的属性cookie网站常用cookie进行身份验证referrer检测页面来源documentElement检测DOM结构hidden检测页面可见性3.3 Storage拦截localStorage和sessionStorage常用于存储客户端状态[localStorage, sessionStorage].forEach(type { const storage window[type]; window[type] new Proxy(storage, { get(target, prop) { if (prop setItem || prop getItem || prop removeItem) { return function(...args) { console.log([${type.toUpperCase()}] ${prop} ${args[0]}); return storage[prop](...args); }; } return storage[prop]; } }); });3.4 Window对象拦截Window对象是最顶层的环境检测目标const windowInterceptor { get(target, prop) { // 过滤掉Symbol属性和一些常见但无用的属性 if (typeof prop symbol || [window, self, top].includes(prop)) { return Reflect.get(...arguments); } const value target[prop]; // 特殊处理函数调用 if (typeof value function) { return function(...args) { console.groupCollapsed([WINDOW] 调用 ${prop}); console.log(参数:, args); const result value.apply(this, args); console.log(返回值:, result); console.groupEnd(); return result; }; } console.log([WINDOW] 访问属性 ${prop}:, value); return value; } }; window new Proxy(window, windowInterceptor);3.5 Canvas指纹拦截Canvas指纹是高级反爬常用的技术HTMLCanvasElement.prototype.getContext new Proxy(HTMLCanvasElement.prototype.getContext, { apply(target, thisArg, args) { const [contextType] args; if (contextType 2d) { const ctx Reflect.apply(target, thisArg, args); return new Proxy(ctx, { get(target, prop) { if ([fillText, measureText, getImageData].includes(prop)) { return function(...args) { console.log([CANVAS] 调用 ${prop}:, args); return target[prop](...args); }; } return target[prop]; } }); } return Reflect.apply(target, thisArg, args); } });4. 实战完整环境自吐系统4.1 系统架构设计一个完整的环境自吐系统应该包含以下组件核心拦截器负责拦截和记录所有环境访问过滤器过滤掉无关紧要的访问减少噪音日志系统结构化记录所有拦截到的操作分析工具帮助分析记录的日志找出关键检测点4.2 完整实现代码class EnvironmentLogger { constructor() { this.logs []; this.filters [ toString, valueOf, constructor, Symbol.iterator, Symbol.toStringTag ]; } log(type, target, prop, value) { if (this.shouldFilter(prop)) return; const entry { timestamp: Date.now(), type, target: target.constructor.name, prop: prop.toString(), value, stack: new Error().stack.split(\n).slice(2).join(\n) }; this.logs.push(entry); this.printEntry(entry); } shouldFilter(prop) { return this.filters.includes(prop) || (typeof prop string prop.startsWith(_)); } printEntry(entry) { const {type, target, prop, value} entry; console.groupCollapsed([${type}] ${target}.${prop}); console.log(值:, value); console.log(调用栈:, entry.stack); console.groupEnd(); } createInterceptor(target, name) { const logger this; return new Proxy(target, { get(t, prop, receiver) { const value Reflect.get(t, prop, receiver); logger.log(GET, {constructor: {name}}, prop, value); if (typeof value function value ! t) { return function(...args) { logger.log(CALL, {constructor: {name}}, prop, args); return value.apply(this, args); }; } return value; }, set(t, prop, value, receiver) { logger.log(SET, {constructor: {name}}, prop, value); return Reflect.set(t, prop, value, receiver); }, apply(target, thisArg, args) { logger.log(APPLY, {constructor: {name}}, , args); return Reflect.apply(target, thisArg, args); } }); } install() { const logger this; // 拦截window window logger.createInterceptor(window, Window); // 拦截document document logger.createInterceptor(document, Document); // 拦截navigator window.navigator logger.createInterceptor(navigator, Navigator); // 拦截XMLHttpRequest const nativeXHR window.XMLHttpRequest; window.XMLHttpRequest class extends nativeXHR { constructor() { super(); return logger.createInterceptor(this, XMLHttpRequest); } }; // 拦截Canvas const nativeGetContext HTMLCanvasElement.prototype.getContext; HTMLCanvasElement.prototype.getContext function(type) { const ctx nativeGetContext.call(this, type); if (type 2d) { return logger.createInterceptor(ctx, CanvasRenderingContext2D); } return ctx; }; } } // 使用示例 const logger new EnvironmentLogger(); logger.install();4.3 日志分析技巧收集到的日志需要进行有效分析按频率排序高频访问的属性通常是关键检测点关注异常值返回undefined或null的属性可能需要补全跟踪调用链分析检测逻辑的执行路径对比不同环境在不同浏览器中运行找出差异点5. 高级技巧与优化5.1 性能优化策略环境拦截会带来性能开销可以采取以下优化措施选择性拦截只拦截已知的关键对象和属性采样记录对高频操作进行采样而非全量记录延迟记录将日志写入队列批量处理过滤噪音忽略toString等常见但无意义的访问// 性能优化示例 const optimizedHandler { get(target, prop) { // 跳过常见但无意义的属性 if ([toString, valueOf, constructor].includes(prop)) { return Reflect.get(...arguments); } // 对高频属性进行采样 if (prop length Math.random() 0.1) { return Reflect.get(...arguments); } // 正常记录 console.log([GET] ${prop}); return Reflect.get(...arguments); } };5.2 反检测技术一些网站会检测Proxy的使用需要采取反检测措施隐藏Proxy痕迹// 隐藏Proxy痕迹 const proxy new Proxy(target, handler); proxy[Symbol.toStringTag] target.constructor.name;伪装原生行为// 保持原型链不变 function createStealthProxy(target) { const proxy new Proxy(target, handler); Object.setPrototypeOf(proxy, Object.getPrototypeOf(target)); return proxy; }避免无限递归// 避免无限递归 const handler { get(target, prop) { if (prop window) return window; // 避免循环引用 return Reflect.get(...arguments); } };5.3 自动化补环境基于拦截结果可以自动生成补环境代码function generateEnvCode(logs) { const env {}; logs.forEach(log { if (log.type GET log.value ! undefined) { const path ${log.target}.${log.prop}; env[path] log.value; } }); return const env ${JSON.stringify(env, null, 2)};; }6. 实战案例分析6.1 案例一用户代理检测通过拦截navigator.userAgent的访问可以发现网站如何检测浏览器类型// 拦截navigator.userAgent Object.defineProperty(navigator, userAgent, { get() { console.trace(userAgent访问堆栈); return Mozilla/5.0 (Windows NT 10.0; Win64; x64); }, configurable: true });6.2 案例二屏幕分辨率检测拦截screen对象的访问分析网站如何检测屏幕属性// 拦截screen对象 window.screen new Proxy(screen, { get(target, prop) { console.log([SCREEN] 访问 ${prop}); return target[prop]; } });6.3 案例三时区检测拦截Date和Intl相关API分析时区检测逻辑// 拦截时区相关API const oldGetTimezoneOffset Date.prototype.getTimezoneOffset; Date.prototype.getTimezoneOffset function() { const offset oldGetTimezoneOffset.call(this); console.log([DATE] 获取时区偏移: ${offset}); return offset; }; // 拦截Intl.DateTimeFormat const oldDateTimeFormat Intl.DateTimeFormat; Intl.DateTimeFormat function(...args) { console.log([INTL] 创建DateTimeFormat:, args); return new oldDateTimeFormat(...args); };7. 常见问题与解决方案7.1 问题一Proxy导致无限递归现象拦截器自身被拦截导致无限递归解决方案使用WeakMap存储原始对象const rawObjects new WeakMap(); function createSafeProxy(obj) { if (rawObjects.has(obj)) return obj; const proxy new Proxy(obj, handler); rawObjects.set(proxy, obj); return proxy; }7.2 问题二性能瓶颈现象拦截导致页面响应缓慢解决方案使用节流和批量处理let logQueue []; let isProcessing false; function scheduleLog(entry) { logQueue.push(entry); if (!isProcessing) { isProcessing true; setTimeout(processLogs, 100); } } function processLogs() { // 批量处理日志 console.log(批量处理日志:, logQueue.length); logQueue []; isProcessing false; }7.3 问题三遗漏关键检测点现象某些环境检测未被拦截到解决方案扩展拦截范围包括WebGL API拦截WebGLRenderingContextAudioContext拦截音频相关APIPerformance API拦截性能相关检测WebRTC API拦截IP地址检测// 拦截WebGL const canvas document.createElement(canvas); const gl canvas.getContext(webgl); const glInterceptor createInterceptor(gl, WebGLRenderingContext); WebGLRenderingContext.prototype glInterceptor.__proto__;