众所周知,Sentry 在运行的时候,会改写原生的 console
API,用于记录上下文相关的一些信息。然而,在一个有 Sentry 的页面上输入 console.log
并回车,会看到输出的内容是:
ƒ log() { [native code] }
但是如果真的输入 console.log('hello world')
执行一下,又会看到输出的文件来自于 breadcrumbs.ts
而不是常见的 VMxxx
。
这里,Sentry 确实重写了 console
的 API,而之所以会输出 ƒ log() { [native code] }
是因为 Sentry 通过改写 Function.prototype.toString
函数,再一次改写了输出结果,从而达到了迷惑的作用(有些代码会通过判断 toString
是否包含 native code
来判断当前的 API 是否被改写了)。
具体的代码可以在这里找到,大体如下:
originalFunctionToString = Function.prototype.toString;
Function.prototype.toString =
function(this: WrappedFunction, ...args: any[]): string {
const context = this.__sentry_original__ || this;
// tslint:disable-next-line:no-unsafe-any
return originalFunctionToString.apply(context, args);
};
如果需要判断当前的 console.log
是否被改写了,针对 Sentry 的话只需要判断 console.log__sentry_original__
是否存在就可以了。或者,看一下 console.log.toString.toString()
的值也是可以的,因为 Sentry 并没有对 Function.prototype.toString
也做一样的 toString
改写。
如果希望可以做更好的隐藏,那么可以考虑把 Function.prototype.toString
也改写掉:
function wrap(obj, api, f) {
const original = obj[api];
obj[api] = f;
obj[api].__wrapped__ = true;
obj[api].__wrapped_original__ = original;
}
wrap(Function.prototype, 'toString', function (...args) {
const context = this.__wrapped__ ? this.__wrapped_original__ : this;
return Function.prototype.toString.__wrapped_original__.apply(context, args);
});
wrap(console, 'log', function (...args) {
return console.log.__wrapped_original__.apply(this, ['extra: '].concat(args));
});