在进行网站加密算法逆向时,我们常常会遇到一个核心问题:浏览器环境与 Node.js 环境的差异。直接将网站的 JavaScript 加密代码复制到 Node.js 中运行,往往会报出 window is not defined、document is not defined 等错误。这篇文章将带你深入理解「补环境」的核心思路,并通过 Proxy 代理实现高效的环境伪装。
一、逆向的两种核心思路
在破解网站加密时,通常有两种主流方案:
1.重写加密逻辑
深度分析网站的加密算法原理(如参数加密、签名生成等),用 Python 或 JavaScript 从零实现一套等价逻辑,使其能在本地独立运行。
✅ 优势:彻底脱离浏览器环境,运行稳定,不受环境依赖影响;
❌ 劣势:对算法理解要求极高,逆向周期长,适合复杂加密场景。
2.复制加密代码(补环境)
直接将网站中扣取的加密代码片段,在 Node.js 环境中运行。无需深入理解加密算法细节,只需解决环境差异问题即可。
✅ 优势:开发速度快,上手门槛低,适合快速获取加密结果;
❌ 劣势:依赖浏览器环境,易出现环境缺失报错,需手动补全环境。
本文将重点讲解第二种方案——补环境。
二、环境差异:浏览器 vs Node.js
1. 浏览器环境
浏览器中的 JavaScript 可以访问大量专属 API,这些是网站加密代码的常见依赖:
BOM (Browser Object Model):操作浏览器窗口和自身信息,核心对象是
window。
window:全局对象,代表浏览器窗口。
navigator:提供浏览器信息(如 userAgent)。
navigator.userAgent; // 用户代理字符串
navigator.appName; // 浏览器名称
navigator.appVersion; // 浏览器版本信息
// 平台信息
navigator.platform; // 操作系统平台(如 "Win32", "MacIntel")
navigator.language; // 浏览器的主语言
navigator.languages; // 用户偏好语言数组
// 硬件和网络
navigator.onLine; // 布尔值,表示浏览器是否联网
navigator.hardwareConcurrency; // 逻辑处理器核心数
navigator.deviceMemory; // 设备内存(GB)
// 其他功能
navigator.cookieEnabled; // 浏览器是否启用cookie
navigator.geolocation; // 地理位置API接口
navigator.clipboard; // 剪贴板API接口
navigator.mediaDevices; // 媒体设备API接口(摄像头、麦克风)location:操作当前页面 URL。
location.href; // 完整的URL
location.protocol; // 协议(如 "https:")
location.host; // 主机名和端口(如 "www.example.com:8080")
location.hostname; // 主机名(如 "www.example.com")
location.port; // 端口号
location.pathname; // 路径部分(如 "/path/page.html")
location.search; // 查询字符串(如 "?id=123")history:浏览器历史记录。
history.length; // 会话历史中的记录数量
history.state; // 当前历史记录条关联的状态对象
history.back(); // 后退一页
history.forward(); // 前进一页
history.go(-2); // 前进/后退指定页数
history.pushState(state, title, url); // 向历史记录中添加一条新记录,但不刷新页面
history.replaceState(state, title, url); // 替换当前这一条历史记录,不产生新的记录
screen:显示器信息。
screen.width; // 屏幕的总宽度
screen.height; // 屏幕的总高度
screen.availWidth; // 屏幕的可用宽度(减去任务栏等)
screen.availHeight; // 屏幕的可用高度
screen.colorDepth; // 颜色深度(如 24)
screen.pixelDepth; // 像素深度(通常与colorDepth相同)DOM (Document Object Model): DOM 是浏览器解析 HTML 后生成的树形结构,JavaScript 可以通过 document 对象访问和操作 HTML 元素。
常用的属性如下:
2. Node.js 环境
Node.js 是一个服务端运行时,不包含任何浏览器专属的 BOM/DOM API。因此,当加密代码尝试访问 window、document 等对象时,就会抛出经典的 ReferenceError。
三、基础补环境:手动模拟浏览器对象
我们的目标是在 Node.js 中模拟出浏览器环境,让加密代码认为自己正运行在 Chrome 等浏览器中。下面通过一个完整案例来演示。
案例代码分析
假设我们有一段依赖浏览器环境的函数:
function getTime() {
if (typeof window == 'undefined') {
return window;
}
document.cookie;
document.createElement('div').tagName;
const canvas = document.createElement('canvas');
if (!canvas.getContext) return false;
if (!navigator.userAgent) {
return null;
}
if (screen.width === undefined || screen.width < 800) {
return '1925/10/26 01:22:52';
}
if (!location.href) {return false};
return "运行成功:" + new Date().toLocaleString();
}在 Node.js 中直接运行,会依次报出以下错误:
ReferenceError: window is not definedReferenceError: document is not definedTypeError: document.createElement is not a functionReferenceError: navigator is not definedReferenceError: screen is not definedReferenceError: location is not defined
逐步补全环境
我们需要手动创建这些缺失的全局对象:
1. 模拟 window 对象
在 Node.js 中,global 是全局对象,我们可以将其赋值给 window:
global.window = global;2. 模拟 document 对象
global.document = {
createElement: function(tagName) {
return {
tagName: tagName,
getContext: function() {} // 模拟 canvas.getContext 方法
};
}
};3. 模拟 navigator 对象
global.navigator = {
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
};4. 模拟 screen 对象
global.screen = {
width: 1920 // 模拟屏幕宽度,绕过小于 800 的检测
};5. 模拟 location 对象
global.location = {
href: 'https://www.example.com' // 模拟 URL,绕过空值检测
};完整可运行代码
将以上补全代码整合后,getTime() 函数就能在 Node.js 中正常运行并返回正确结果:
global.window = global;
global.document = {
createElement: function(tagName){
return {
tagName: tagName,
getContext:function(){}
}
}
};
global.navigator = {
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
};
global.screen = {
width: 1920
};
global.location = {
href: "https://www.example.com"
};
function getTime() {
// ... 原函数代码 ...
}
const result = getTime();
console.log(result); // 输出:运行成功:2026/03/25 17:00:00四、进阶补环境:用 Proxy 代理高效伪装
手动补环境虽然有效,但面对复杂的网站代码时,需要模拟的对象和方法会非常多,工作量巨大。这时,Proxy 代理就成了更高效的解决方案。
1. 什么是 Proxy?
Proxy 是 ES6 引入的特性,它可以拦截并自定义对象的基本操作(如读取属性、设置属性、调用方法等)。我们可以用它来批量代理 window、document 等对象,自动处理所有未定义的属性访问,避免报错。
2. Proxy 基本用法
const target = { name: '张三' };
const handler = {
get(target, property) {
console.log(`正在读取属性: ${property}`);
return target[property];
}
};
const proxy = new Proxy(target, handler);
console.log(proxy.name); // 输出:正在读取属性: name → 张三3. 批量代理环境对象
我们可以编写一个通用函数,批量代理所有需要的浏览器对象:
global.window = global;
function getEnv(proxy_array) {
for (let i = 0; i < proxy_array.length; i++) {
const objName = proxy_array[i];
// 定义代理处理器,拦截 get 和 set 操作
const handler = {
get(target, property, receiver) {
console.log(`[get] ${objName}.${property}`);
// 如果属性不存在,返回一个空函数,避免继续报错
return target[property] || function() {};
},
set(target, property, value, receiver) {
console.log(`[set] ${objName}.${property} = ${value}`);
return Reflect.set(...arguments);
}
};
try {
// 如果对象已存在,直接代理
global[objName] = new Proxy(global[objName] || {}, handler);
} catch (e) {
// 如果对象不存在,创建一个空对象再代理
global[objName] = new Proxy({}, handler);
}
}
}
// 需要代理的环境对象列表
const proxy_array = ['document', 'location', 'navigator', 'history', 'screen', 'Object', 'window'];
getEnv(proxy_array);4. 日志增强版(便于调试)
在逆向调试时,我们需要清晰地知道代码访问了哪些属性。可以将处理器改造为日志版:
function getEnv(proxy_array) {
for (let i = 0; i < proxy_array.length; i++) {
const handler = `{
get: function(target, property, receiver) {
console.log('方法:get',' 对象:${proxy_array[i]}',' 属性:',property);
return target[property] || function() {};
},
set: function(target, property, value, receiver){
console.log('方法:set',' 对象:${proxy_array[i]}',' 属性:',property,' 值:',value);
return Reflect.set(...arguments);
}
}`;
// 使用 eval 动态创建代理
eval(`
try {
${proxy_array[i]} = new Proxy(${proxy_array[i]} || {}, ${handler});
} catch (e) {
${proxy_array[i]} = new Proxy({}, ${handler});
}
`);
}
}运行后,访问任何对象属性都会输出详细日志,例如:
方法:get 对象:document 属性: createElement
方法:get 对象:navigator 属性: userAgent五、总结与实践建议
核心思想
补环境的本质是:在 Node.js 中模拟出网站代码所依赖的最小浏览器 API 集合,让代码误以为自己在浏览器中运行。
实践步骤
运行代码,定位缺失对象:根据报错信息,逐个补全
window、document等对象。模拟最小可用接口:不需要完整实现所有 API,只需模拟代码实际用到的属性和方法。
使用 Proxy 批量处理:面对复杂代码时,用 Proxy 代理所有对象,自动处理未定义属性,大幅提高效率。
结合日志调试:通过 Proxy 的日志功能,清晰追踪代码对环境的访问路径,快速定位问题。
注意事项
环境检测:很多网站会通过
navigator.userAgent、screen.width等信息检测运行环境,模拟时要尽量贴近真实浏览器。性能考量:过度模拟会增加运行开销,只模拟必要的部分即可。
版本兼容:不同网站的环境依赖不同,需要根据具体代码灵活调整。
通过以上方法,你可以高效地在 Node.js 中运行网站的加密代码,为后续的逆向分析和算法还原打下坚实基础。