Ива*_*хин 14 javascript function tostring console.log
我决定为 YouTube 实时聊天创建一个用户脚本。这是代码:
const toString = Function.prototype.toString
unsafeWindow.setTimeout = function (fn, t, ...args) {
unsafeWindow.console.log(fn, fn.toString(), toString.call(fn))
unsafeWindow.fns = (unsafeWindow.fns ?? []).concat(fn)
return setTimeout(fn, t, ...args)
}
Run Code Online (Sandbox Code Playgroud)
现在看看输出的样子:
一些函数的输出是可以预测的,但看看其他函数!当你这样做console.log时,你会看到函数体,但如果你调用fn.toString(),你会看到function () { [native code] }。
但为什么?脚本在页面之前加载,因此 YouTube 的脚本无法替换这些方法。
use*_*170 13
这是因为这些函数已传递给Function.prototype.bind.
> (function () { return 42; }).toString()
'function () { return 42; }'
> (function () { return 42; }).bind(this).toString()
'function () { [native code] }'
Run Code Online (Sandbox Code Playgroud)
该bind方法将任意函数对象转换为所谓的绑定函数。调用绑定函数与调用原始函数的效果相同,只是this参数和一定数量的初始位置参数(可能为零)在创建绑定函数时将具有固定的值。在功能上,bind主要相当于:
Function.prototype.bind = function (boundThis, ...boundArgs) {
return (...args) => this.call(boundThis, ...boundArgs, ...args);
};
Run Code Online (Sandbox Code Playgroud)
除了以上当然会在字符串转换后产生不同的值。根据ECMA-262 第 11 版第 19.2.3.5 节 ¶2规定,绑定函数被指定为具有与本机函数相同的字符串转换行为:
2. 如果func是绑定函数外来对象或内置函数对象,则返回func的依赖于实现的 String 源代码表示。该表示必须具有NativeFunction的语法。[…]
[…]
本机功能:
function PropertyName [~Yield, ~ Await ] opt ( FormalParameters [~Yield, ~ Await ] ) { [本机代码] }
当直接将函数打印到控制台(而不是字符串化)时,实现不受任何规范的约束:它可以以任何方式在控制台中显示函数。Chromium 的控制台在要求打印绑定函数时,为了方便起见,只显示原始未绑定函数的源代码。
证明这确实发生在 YouTube 的案例中有点麻烦,因为 YouTube 的 JavaScript 被混淆了,但并不是特别困难。我们可以打开 YouTube 的主站点,然后进入开发者控制台并安装我们的陷阱:
window.setTimeout = ((oldSetTimeout) => {
return function (...args) {
if (/native code/.test(String(args[0])))
debugger;
return oldSetTimeout.call(this, ...args);
};
})(window.setTimeout);
Run Code Online (Sandbox Code Playgroud)
我们应该debugger很快就会对声明产生影响。我在这个函数中击中了它:
Function.prototype.bind = function (boundThis, ...boundArgs) {
return (...args) => this.call(boundThis, ...boundArgs, ...args);
};
Run Code Online (Sandbox Code Playgroud)
该g.D函数看起来特别有趣:它似乎是用第一个参数 调用的a,这大概是一个函数。看起来它可能会bind在幕后调用。当我要求控制台检查它时,我得到了这个:
window.setTimeout = ((oldSetTimeout) => {
return function (...args) {
if (/native code/.test(String(args[0])))
debugger;
return oldSetTimeout.call(this, ...args);
};
})(window.setTimeout);
Run Code Online (Sandbox Code Playgroud)
所以虽然这个过程有点复杂,但我们可以清楚地看到这确实发生了。
| 归档时间: |
|
| 查看次数: |
339 次 |
| 最近记录: |