You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Chrome中如何让JavaScript Proxy与原生目标对象表现完全一致?

问题:如何让被代理的原生对象在Chrome中表现得与原对象完全一致?

网站常用的反逆向工程手段之一,是在开发者工具打开时阻止JavaScript代码执行——这类手段通常依赖于向控制台输出带有getter或自定义toString函数的对象,这类函数仅在控制台可见时才会被调用。简单的应对方案是代理console.*系列函数并丢弃问题对象。

但近期遇到一个更棘手的网站,它能够检测用户是否代理或篡改了原生函数,且该网站的脚本经过重度混淆,无法确定具体检测逻辑。已知Chrome浏览器中存在一种可靠的检测原生函数是否被代理的方法,示例代码如下:

function isProxied(func) {
  return func.toString().length === 29;
}

console.log(isProxied(eval));

eval = new Proxy(eval, {});

console.log(isProxied(eval));

现需解决:如何让被代理的对象在Chrome中表现得与目标原生对象完全一致?


解决方案

核心思路

这类检测的核心通常是验证原生函数的toString输出、原型链、属性描述符等特性是否符合原生标准。我们需要通过Proxy的陷阱拦截,让代理对象在所有可检测维度上与原对象完全对齐。

1. 拦截toString方法,匹配原生输出

Chrome中原生函数的toString()返回固定格式的字符串(比如eval的返回是"function eval() { [native code] }",长度为29)。我们需要在Proxy的get陷阱中拦截toString访问,返回原函数的toString结果:

const originalEval = eval;
eval = new Proxy(originalEval, {
  get(target, prop) {
    if (prop === 'toString') {
      // 绑定原函数上下文,确保toString输出原生内容
      return target.toString.bind(target);
    }
    return Reflect.get(target, prop);
  },
  // 拦截函数调用,确保执行逻辑和原函数一致
  apply(target, thisArg, args) {
    return Reflect.apply(target, thisArg, args);
  }
});

// 测试验证
function isProxied(func) {
  return func.toString().length === 29;
}
console.log(isProxied(eval)); // 输出false,与原生eval表现一致

2. 覆盖全维度检测点

部分网站会通过原型链、属性描述符、ownKeys等方式检测代理,需要补充更多陷阱拦截:

const originalEval = eval;
const evalProxy = new Proxy(originalEval, {
  get(target, prop) {
    if (prop === 'toString') {
      return target.toString.bind(target);
    }
    const value = Reflect.get(target, prop);
    // 若属性是函数,绑定原上下文避免执行异常
    if (typeof value === 'function') {
      return value.bind(target);
    }
    return value;
  },
  apply(target, thisArg, args) {
    return Reflect.apply(target, thisArg, args);
  },
  // 拦截属性存在性检测
  has(target, prop) {
    return Reflect.has(target, prop);
  },
  // 返回原函数的属性描述符
  getOwnPropertyDescriptor(target, prop) {
    return Reflect.getOwnPropertyDescriptor(target, prop);
  },
  // 返回原函数的所有键
  ownKeys(target) {
    return Reflect.ownKeys(target);
  }
});

// 同步原型链
Object.setPrototypeOf(evalProxy, Object.getPrototypeOf(originalEval));
// 复制原函数的所有属性描述符
Object.defineProperties(evalProxy, Object.getOwnPropertyDescriptors(originalEval));

eval = evalProxy;

3. 应对全局Function.prototype.toString检测

如果网站通过Function.prototype.toString.call(eval)来检测,需要全局拦截该方法,针对代理的原生函数返回正确结果:

const originalFunctionToString = Function.prototype.toString;
const targetProxies = new WeakSet([evalProxy]);

Function.prototype.toString = function() {
  if (targetProxies.has(this)) {
    return originalEval.toString();
  }
  return originalFunctionToString.call(this);
};

内容的提问来源于stack exchange,提问作者user2248702

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 23:26:18