网站用来防止逆向工程的一个常见技巧是在开发人员工具打开时阻止 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));
如何使代理对象看起来与其目标相同?
[此站点] 检测您是否已代理或以其他方式弄乱了本机功能。在获取原始对象的副本之前,该对象将被代理,因此身份并不重要。
如果您可以在他们的代码之前注入您的代码,您就可以通过足够令人信服的模拟覆盖所有本机对象。您甚至不需要使用代理。不过,根据它们的复杂程度和您的需求,您可能需要构建一个全局变量和内置变量的整个虚拟环境。
对于替换console
及其方法,就更简单了。为了令人信服地模拟一个函数,您需要考虑它的
.name
、
.length
(数量)、
.prototype
、它的代码(通过
.toString()
)及其行为(副作用和返回值)。所以你首先需要替换
Function.prototype.toString
本身:
{
const originals = new WeakMap();
const replace = (target, methods) => {
for (const name in methods) {
originals.set(methods[name], target[name]);
// assert(typeof target[name] === typeof methods[name]);
// assert(target[name].name === methods[name].name);
// assert(target[name].length === methods[name].length);
// assert(target[name].prototype === methods[name].prototype);
// assert(Object.getPrototypeOf(target[name]) === Object.getPrototypeOf(methods[name]));
target[name] = methods[name];
}
};
const toString = Function.prototype.call.bind(Function.prototype.toString);
replace(Function.prototype, {
toString() {
const orig = originals.get(this);
return toString(orig ?? this);
},
});
replace(console, {
log() {
// do nothing
},
});
}