mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4mobile wallpaper 5mobile wallpaper 6
902 字
3 分钟
js逆向补环境入门
2026-06-01

补环境知识总结:对象类型与属性详解#

一、this 问题详解#

1.1 问题现象#

在 Proxy 的 get handler 中,如果直接返回函数,调用时的 this 会指向 Proxy 而不是原始对象:

const target = {
value: 42,
getData: function() {
return this.value; // this 应该指向 target
}
};
const proxy = new Proxy(target, {
get(target, property, receiver) {
const value = target[property];
return value; // 问题:函数被返回后,this 不再是 target
}
});
proxy.getData(); // this 指向 proxy,而不是 target
// this.value === undefined(proxy 没有 value 属性)

1.2 解决方案#

方案一:使用 bind 绑定 this#

get(target, property, receiver) {
const value = target[property];
if (typeof value === 'function') {
return value.bind(target); // 显式绑定 this 到 target
}
return value;
}

缺点:每次调用都会创建新函数,有性能开销

方案二:使用 Reflect.get(推荐)#

get(target, property, receiver) {
// Reflect.get 会自动处理 receiver,确保正确的 this
return Reflect.get(target, property, receiver);
}

优点

  • 自动处理 this 上下文
  • 保持 Proxy 的响应式特性
  • 性能更好

方案三:返回函数但不改变 this(当需要代理行为时)#

get(target, property, receiver) {
const value = target[property];
if (typeof value === 'function') {
// 使用闭包记录原始函数,但仍让 this 通过 receiver 传递
return function(...args) {
// 这里的 this 会正确传递到原函数
return value.apply(this, args);
};
}
return value;
}

1.3 set handler 的 this 问题#

set(target, property, newValue, receiver) {
// 必须使用 Reflect.set 保持 receiver 上下文
// 否则 this 的属性修改会作用在错误的对象上
return Reflect.set(target, property, newValue, receiver);
}

二、JavaScript 对象类型分类#

在补环境时,浏览器对象主要分为以下几种类型:

类型示例特征
Functionwindow.alertdocument.createElement可调用,有 name
Objectwindow.historynavigator普通对象,有原型链
SymbolSymbol.toStringTagSymbol.iterator唯一标识符,有 description
ConstructorWindowDocumentNavigator可 new 实例化,有 prototype

三、各类型对象的属性详解#

3.1 Function 类型#

3.1.1 核心属性#

属性说明示例
name函数名称window.alert.name === "alert"
length参数个数String.prototype.indexOf.length === 2
prototype原型对象用于 prototype 链继承
call调用函数func.call(thisArg, ...args)
apply调用函数func.apply(thisArg, [args])
bind绑定 thisfunc.bind(thisArg, ...args)

3.1.2 toString 输出#

浏览器原生函数

window.alert.toString();
// "function alert() { [native code] }"
document.createElement.toString();
// "function createElement() { [native code] }"

普通函数

function test() {}
test.toString();
// "function test() {}"

3.1.3 补环境要点#

// 模拟原生函数
const fakeCreateElement = function createElement(tagName) {
return { tagName: tagName.toUpperCase() };
};
// 设置 name 属性
Object.defineProperty(fakeCreateElement, 'name', {
value: 'createElement',
writable: false,
enumerable: false,
configurable: true
});
// 使用 safeFunction 处理 toString
safeFunction(fakeCreateElement);
// 设置 prototype
fakeCreateElement.prototype = {};

3.2 Object 类型#

3.2.1 普通属性#

属性说明
hasOwnProperty检查自身属性
propertyIsEnumerable检查是否可枚举
isPrototypeOf检查原型链
valueOf返回原始值
toString转换为字符串

3.2.2 Symbol 属性#

属性说明浏览器输出
Symbol.toStringTag对象类型标识Object.prototype.toString.call(obj)
Symbol.toPrimitive类型转换当对象参与运算时的转换
Symbol.iterator迭代器for...of 循环支持
Symbol.hasInstanceinstanceofinstanceof 检测

3.2.3 补环境要点#

const fakeNavigator = {
userAgent: 'Mozilla/5.0...',
appName: 'Netscape',
};
// 必须设置 Symbol.toStringTag
Object.defineProperty(fakeNavigator, Symbol.toStringTag, {
value: 'Navigator',
writable: false,
enumerable: false,
configurable: false
});
// 可选:设置 Symbol.toPrimitive
Object.defineProperty(fakeNavigator, Symbol.toPrimitive, {
value: function(hint) {
if (hint === 'string') return '[object Navigator]';
return null;
},
writable: false,
enumerable: false,
configurable: false
});

3.3 Symbol 类型#

3.3.1 Symbol 的特性#

// Symbol 是唯一标识符
const s1 = Symbol('test');
const s2 = Symbol('test');
s1 === s2 // false
// Symbol 有 description
s1.description // "test"
// Symbol 不能被 for...in 枚举
// Symbol 作为属性键时,需要用 Object.getOwnPropertySymbols 获取

3.3.2 常见的浏览器 Symbol#

Symbol用途示例
Symbol.toStringTagObject.prototype.toString.call() 的返回值'[object Window]'
Symbol.toPrimitive类型转换Number(obj) 时调用
Symbol.iterator迭代协议for...of... 展开
Symbol.hasInstanceinstanceofobj instanceof Class
Symbol.isConcatSpreadableconcat 展开[].concat(obj)
Symbol.unscopableswith 语句with(obj) { ... }

3.3.3 补环境要点#

// 模拟 Symbol.toStringTag
const obj = {};
Object.defineProperty(obj, Symbol.toStringTag, {
value: 'HTMLDocument',
enumerable: false,
configurable: false
});
Object.prototype.toString.call(obj);
// "[object HTMLDocument]"
// 模拟 Symbol.iterator
const arrLike = { 0: 'a', 1: 'b', length: 2 };
Object.defineProperty(arrLike, Symbol.iterator, {
value: function*() {
for (let i = 0; i < this.length; i++) {
yield this[i];
}
},
enumerable: false,
configurable: false
});
// 现在可以 for...of
for (const item of arrLike) {
console.log(item); // 'a', 'b'
}

3.4 Constructor 类型#

3.4.1 构造函数的结构#

function Window() {}
Window.prototype = {
constructor: Window,
// 原型方法
alert: function() {},
document: null,
};
// 实例通过 prototype 链访问方法
const win = new Window();
win.alert(); // 通过 prototype 链找到

3.4.2 构造函数的属性#

属性说明
name构造函数名称
length参数个数
prototype原型对象
prototype.constructor指回构造函数

3.4.3 补环境要点#

// 创建构造函数
function createConstructor(name, prototypeMethods) {
const Constructor = function() {};
// 设置 name
Object.defineProperty(Constructor, 'name', {
value: name,
writable: false,
enumerable: false,
configurable: true
});
// 设置 prototype
Constructor.prototype = prototypeMethods || {};
// 确保 constructor 属性正确
Object.defineProperty(Constructor.prototype, 'constructor', {
value: Constructor,
writable: false,
enumerable: false,
configurable: false
});
// 设置 toStringTag
Object.defineProperty(Constructor, Symbol.toStringTag, {
value: name,
writable: false,
enumerable: false,
configurable: false
});
return Constructor;
}

四、属性描述符详解#

4.1 数据描述符 vs 访问器描述符#

// 数据描述符:有 value 的属性
Object.defineProperty(obj, 'prop1', {
value: 123,
writable: true, // 可写
enumerable: true, // 可枚举
configurable: true // 可配置
});
// 访问器描述符:有 get/set 的属性
Object.defineProperty(obj, 'prop2', {
get: function() { return 456; },
set: function(v) { console.log('set:', v); },
enumerable: true,
configurable: true
});

4.2 描述符属性说明#

属性说明默认值
value属性值undefined
writable是否可写false
enumerable是否可枚举false
configurable是否可删除/修改描述符false
getgetter 函数undefined
setsetter 函数undefined

4.3 检测手段与对策#

// 检测方法1:直接访问
obj.prop1; // 返回 value
obj.prop2; // 调用 get
// 检测方法2:Object.getOwnPropertyDescriptor
const desc = Object.getOwnPropertyDescriptor(obj, 'prop');
if (desc) {
console.log('value' in desc); // 数据描述符有 value
console.log('get' in desc); // 访问器描述符有 get
console.log('writable:', desc.writable);
console.log('enumerable:', desc.enumerable);
console.log('configurable:', desc.configurable);
}
// 检测方法3:Object.keys / for...in
Object.keys(obj); // 只返回 enumerable:true 的属性
for (const k in obj) {} // 遍历 enumerable:true 的属性
// 检测方法4:JSON.stringify
JSON.stringify(obj); // 只序列化 enumerable:true 的属性

4.4 浏览器对象的描述符特征#

// 浏览器中,navigator.userAgent 是访问器描述符
const desc = Object.getOwnPropertyDescriptor(navigator, 'userAgent');
// { get: [Function], set: undefined, enumerable: true, configurable: true }
// 而我们自己定义的属性可能是数据描述符
const fakeNav = { userAgent: '...' };
// { value: '...', writable: true, enumerable: true, configurable: true }
// 检测代码可能区分这两种
if (desc.get) {
// 浏览器原生的
} else {
// 可能是伪造的
}

4.5 补环境时的描述符设置#

// 模拟浏览器使用 getter
Object.defineProperty(fakeNavigator, 'userAgent', {
get: function() { return 'Mozilla/5.0...'; },
enumerable: true,
configurable: true
});
// 模拟只读属性
Object.defineProperty(fakeNavigator, 'appName', {
value: 'Netscape',
writable: false, // 不可写
enumerable: true,
configurable: false // 不可删除
});
// 模拟 Symbol 属性
Object.defineProperty(fakeNavigator, Symbol.toStringTag, {
value: 'Navigator',
writable: false,
enumerable: false, // Symbol 属性默认不可枚举
configurable: false
});

五、环境检测与对策总结#

5.1 检测类型一览#

检测类型检测代码对策
全局对象typeof window === 'undefined'定义 window 对象
process'process' in windowdelete global.process
Buffertypeof Buffer !== 'undefined'delete global.Buffer
toStringObject.prototype.toString.call(w)设置 Symbol.toStringTag
Function.toStringfn.toString() 检查 [native code]使用 safeFunction
属性描述符Object.getOwnPropertyDescriptor使用 defineProperty
原型链window instanceof WindowsetPrototypeOf
webdrivernavigator.webdriver设为 false
pluginsnavigator.plugins.length提供 PluginArray

5.2 Proxy 拦截器与检测对应#

Proxy Handler检测代码说明
getobj.prop属性读取
setobj.prop = value属性设置
hasprop in obj属性存在检测
ownKeysObject.keys(obj)属性枚举
deletePropertydelete obj.prop属性删除
getOwnPropertyDescriptorObject.getOwnPropertyDescriptor描述符查询
applyproxy(...args)函数调用

六、完整补环境清单#

6.1 基础对象清单#

window
├── document
│ ├── createElement()
│ ├── createEvent()
│ ├── createTextNode()
│ ├── getElementById()
│ ├── getElementsByTagName()
│ ├── querySelector()
│ ├── querySelectorAll()
│ ├── head (Element)
│ ├── body (Element)
│ └── cookie (String)
├── navigator
│ ├── userAgent (String)
│ ├── platform (String)
│ ├── language (String)
│ ├── languages (Array)
│ ├── appName (String)
│ ├── appVersion (String)
│ ├── hardwareConcurrency (Number)
│ ├── deviceMemory (Number)
│ ├── webdriver (Boolean)
│ ├── plugins (PluginArray)
│ └── connection (NetworkInfo)
├── location
│ ├── href (String)
│ ├── protocol (String)
│ ├── host (String)
│ ├── hostname (String)
│ ├── pathname (String)
│ ├── search (String)
│ ├── hash (String)
│ ├── assign()
│ ├── replace()
│ └── reload()
├── screen
│ ├── width (Number)
│ ├── height (Number)
│ ├── availWidth (Number)
│ ├── availHeight (Number)
│ ├── colorDepth (Number)
│ └── pixelDepth (Number)
├── history
│ ├── length (Number)
│ ├── back()
│ ├── forward()
│ └── go()
├── localStorage
│ ├── getItem()
│ ├── setItem()
│ ├── removeItem()
│ ├── clear()
│ └── key()
├── sessionStorage (同 localStorage)
└── chrome (Object)
└── runtime (Object)

6.2 Symbol 清单#

Window
├── Symbol.toStringTag = 'Window'
└── Symbol.toPrimitive
Navigator
├── Symbol.toStringTag = 'Navigator'
└── Symbol.toPrimitive
Document / HTMLDocument
├── Symbol.toStringTag = 'HTMLDocument'
└── Symbol.toPrimitive
Element / HTMLElement
├── Symbol.toStringTag = 'HTMLDivElement' (等)
└── Symbol.iterator (for...of 支持)
Location
├── Symbol.toStringTag = 'Location'
└── Symbol.toPrimitive
Array
├── Symbol.toStringTag = 'Array'
├── Symbol.iterator
└── Symbol.isConcatSpreadable
Object
├── Symbol.toString
└── Symbol.hasInstance

6.3 构造函数清单#

EventTarget
└── prototype
├── addEventListener()
├── removeEventListener()
└── dispatchEvent()
Node (extends EventTarget)
└── prototype
├── appendChild()
├── removeChild()
├── insertBefore()
├── getAttribute()
├── setAttribute()
└── textContent
Window (extends WindowProperties -> EventTarget)
└── prototype
├── alert()
├── confirm()
├── setTimeout()
├── setInterval()
├── eval()
├── document
├── navigator
├── location
└── ...
HTMLDocument
└── prototype
├── getElementById()
├── getElementsByTagName()
├── querySelector()
└── ...
HTMLElement
└── prototype
├── style
├── innerHTML
├── textContent
├── appendChild()
└── ...

6.4 需要 delete 的 Node.js 特征#

// 必须在加载目标代码前删除
delete global.Buffer;
delete global.process;
delete global.module;
delete global.exports;
delete global.require;
delete global.__dirname;
delete global.__filename;
delete Error.prepareStackTrace;

七、总结#

7.1 对象类型与关键属性#

类型关键属性防检测要点
Functionname, length, prototype, toStringsafeFunction 处理
ObjecthasOwnProperty, toString, valueOfSymbol.toStringTag
Symboldescription, 各种 well-known Symbol正确设置值
Constructorname, prototype, constructorprototype 链

7.2 代理 this 问题解决#

// 关键:get 时绑定 this
get(target, property, receiver) {
const value = target[property];
if (typeof value === 'function') {
return value.bind(target); // 或使用 Reflect.get
}
return value;
}
// 关键:set 时保持 receiver
set(target, property, newValue, receiver) {
return Reflect.set(target, property, newValue, receiver);
}

7.3 补环境三大原则#

  1. 类型正确:每种类型的属性要完整(Function 要有 name,Symbol 要有 description)
  2. 描述符正确:使用 defineProperty 设置正确的 enumerable/writable/configurable
  3. 原型链正确:使用 setPrototypeOf 设置正确的继承关系
分享

如果这篇文章对你有帮助,欢迎分享给更多人!

js逆向补环境入门
https://fatdog.20060113.xyz/posts/buhuanjing/
作者
神秘大胖狗
发布于
2026-06-01
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时

封面
Sample Song
Sample Artist
封面
Sample Song
Sample Artist
0:00 / 0:00