JavaScript - 使代理无法检测

2023-12-19

据我了解,ES 规范是这么说的Proxy(用于代理对象、函数和类的全局构造函数)不可检测。这意味着如果我代理一个函数,使用该代理函数的任何人都无法检测到我使用了代理。然而,显然我误解了它,因为代理功能是可以检测到的。

例如,new Proxy(a=>a,{apply:a=>a})+''抛出错误。它说

Uncaught TypeError: Function.prototype.toString requires that 'this' be a Function

然而,typeof new Proxy(a=>a,{apply:a=>a})确实是"function",但它以某种方式无法对代理进行字符串化。因此,显然,在这种情况下,代理函数的行为与非代理函数的行为不同。Function.prototype.toString能够区分代理函数和非代理函数。

我的目标是代理一个函数,使其变得不可检测。我的第一个想法是像这样真正代理代理:

Proxy.toString = (a => () => a)(Proxy + '');
Proxy = new Proxy(Proxy, {
  construct: (f, args) => {
    if(typeof args[0] == 'function'){
      var a = args[0] + '';
      args[0].toString = () => a;
    }
    return new f(...args);
  }
});

但遗憾的是,这是可以检测到的。如果有人打电话Function.prototype.toString绑定到我的代理函数,就会发生错误,因此他可以检测到我的函数实际上是代理。所以,我尝试代理Function and Function.prototype并且Function.prototype.toString,但后来我意识到我无法代理Function因为即使我覆盖全局属性Function,有人可以使用它来访问它(a=>a).constructor.

所以,这就是我在这里问这个问题的原因,因为我没有想法了。如何代理一个函数以使其完全无法检测到?在 ES 规范中明确指出“代理无法检测到”,那么作为一个附带问题,为什么代理一个函数可以被检测到呢?

Edit

我试图实现这一目标的原因是因为我正在为 Chrome 开发增强型广告拦截扩展。我正在处理非常激进的网站,该网站利用大量 JavaScript 技巧来检测我是否正在查看广告。所以,基本上,我删除了一个广告,然后他们的脚本检查是否有特定元素,如果没有,那么我无法访问该网站。所以,我尝试代理document.getElementById,但他们会检查它是否被代理,如果是,我将无法访问该网站,因此我必须使其无法检测到。


我对这个问题有几个部分答案/评论:

1.首先:浏览器的行为似乎同时发生了变化。 Chrome 106 和 Firefox 105 都不再抛出初始示例中描述的类型错误,但它们仍然为toString()方法比原始对象:

 new Proxy(a=>a, {apply:a=>a}) + ''

导致以下结果toString()返回值:

'function () { [native code] }'

而原始 lambda 表达式(a=>a) + ''简单地产生

'a=>a'

2. OP 试图改变toString代理对象的实际上正在改变toString- 目标方法args[0](这是a=>a)通过始终对其自身进行评估args[0] + ''(从而消除任何this)。因此,当重新定义toString使用代理调用目标的函数,如下所示this争论,已经没有什么区别了。

对于任何在搜索如何分配自定义的问题时绊倒的人toString对于代理(不要求不可检测),也许应该提及在不更改目标的情况下执行此操作的标准方法: 可以用以下方法完成get https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/get- 处理程序的陷阱,因此将处理程序扩展为:

{
    apply: a=>a,
    get(target, prop, receiver) {
       if(prop == "toString") {
          return target.toString.bind(target);
       } else {
          return Reflect.get(target, prop, receiver); // or your favorite forwarding
       }
    }
}

这也产生相同的结果toString()代理和目标的答案。通过将其他函数属性一般绑定到目标,可以使“else”情况变得更有用,至少如果它们没有显式绑定到不同的接收器(请参阅私有财产转发 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#no_private_property_forwarding).

3. OP的全局替换问题Function不过,这是可以克服的。至少我是这么认为的。确实有参考原著的Function其实例的构造函数:

(a=>a).constructor === Function; // true

但我们不需要替换 Function 本身,只需替换它的属性,或者更准确地说,甚至不需要替换这些属性,而只需替换其属性的属性之一。考虑以下简单的重新定义。它不是很复杂(仅针对一个代理),但至少可以完成这项工作toString该代理的属性:

const proxy = new Proxy(a=>a,{apply:a=>a});
{
    const nativeToString = Function.prototype.toString;
    Function.prototype.toString = function() {
        return this === proxy? "a=>a" : nativeToString.call(this);
    }
}

现在至少这个测试是成功的:

 (x=>x).constructor.prototype.toString.call(proxy); // returns "a=>a" 

4.然而,作为@jmrk 在他的回答中指出 https://stackoverflow.com/questions/45688944/javascript-make-proxy-undetectable/45698826#45698826,代理可检测的原因是存在一些特殊的内部槽,这些槽无法通过代理处理程序陷阱拦截。后者仅拦截标准对象内部方法 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#object_internal_methods。因此,任务是找到通过外部 javascript 调用触发这些内部槽的所有方法,然后覆盖相应的全局可访问函数,就像使用toString()多于。我不确定触发内部插槽的进一步方法Function,但是当尝试使用代理来替代代理时也会出现同样的问题HTMLElement https://stackoverflow.com/questions/56008415/how-to-create-a-proxy-for-htmlelement,我们称之为htmlProxy。在这种情况下调用getComputedStyle(htmlProxy)抛出类型错误,即使htmlProxy标识为 HTMLElement。相似地element.insertNode(htmlElementProxy)抛出异常。似乎需要重新定义所有这些全局可访问的触发函数,以使代理无法检测到。

内部槽可以看作是内置对象的私有属性。因此,非内置类/对象的私有属性出现同样的问题也就不足为奇了。可以使用上面(第 2 点)提到的私有财产转发 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy#no_private_property_forwarding当通过代理调用访问私有属性的方法时,获得与目标对象相同的代理行为。假设我们有someObject类型的SomeClass与私人成员#member和一个公共吸气剂getMember。然后我们可以做代理someObject通过转发也成功调用此函数this到目标,这样proxy.getMember()只会给成员someObject。但是,如果有人打电话SomeClass.prototype.getMember.call(proxy),又会出现异常,唯一的办法就是重新定义SomeClass.prototype.getMember.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JavaScript - 使代理无法检测 的相关文章

随机推荐