前言
之前一直想写这篇过document.all
的检测的文章,但是因为在搞app,所有一直没得闲。今天抽空,梳理一下之前我之前是如何实现过document.all
的检测。
document.all类型检测是啥?
在补环境中,document.all最难的是他的类型, 在浏览器控制台中你敲下面这段代码,
console.log("document.all.toString() -> ", document.all.toString());
console.log("typeof document.all -> ", typeof document.all);
// document.all.toString() -> [object HTMLAllCollection]
// typeof document.all -> undefined
明明是个HTMLAllCollection
对象怎么又undefined
了呢?如何在node
或纯v8
中实现一个对象既是object
又是undefined
类型?
为此我走上了漫长的不归路…
探究的心酸历程
下面记录我一步步的探究历程
0x0 搜索资料视频
说不定有大佬分享过呢,网上找咯。谷歌百度ChatGpt一套下来,相关的实现是一点没有,唯一告诉你的是这是一个历史遗留问题
, 大意是:all
是IE
浏览器拥有的,并不属于w3c
规范, 大家最好都不要用。
0x1 请教别人
那些补环境的大佬不是都实现过嘛,去问问。这都是别人吃饭卖课的技能啊,咋就能轻易告诉你呢,都是实现好的,你用就是了。无果,还得靠自己哟。
0x2 浏览器源码
因为之前有编译过浏览器实现随机指纹之类的经验,所有直接去浏览器源码里面找一找,碰碰运气,看看能不能找到相关实现。chromium
源码之庞大,无异于大海捞针,而我这扣脚的C++
更是雪上加霜啊。依旧无果。
0x3 加强C/C++
想要知道答案,肯定还是要去源码入手的。奈何浏览器和v8
都是C++
实现的。那就加强一下C++
吧,开始了漫长的学习之旅,2 Months Later…,略有小成。
0x4 再战v8源码
不去chromium
源码里面找了 太大了 受不了,转战v8
源码,拉取源码编译v8
,跑通demo。结合案例理解v8的思想和里面的概念, 尽管之前突击了一下C++, 但依旧吃力。耐心,耐心,保持耐心…
0x5 突破
day by day,念念不忘必有回响,在捋顺了v8的基础知识后,直接去查找和翻阅v8中实现typeof
相关的代码和资料,发现这篇文章<<typeof 与 Javascript 类型源码分析>>,顺着他的思路,关注is_undetectable
类型相关的代码,终于我发现了这个方法void ObjectTemplate::MarkAsUndetectable()
, MarkAsUndetectable
直译过来标记为不可检测
, 并找到了他的测试demo如下:
THREADED_TEST(UndetectableObject) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
Local<v8::FunctionTemplate> desc =
v8::FunctionTemplate::New(env->GetIsolate());
desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable
Local<v8::Object> obj = desc->GetFunction(env.local())
.ToLocalChecked()
->NewInstance(env.local())
.ToLocalChecked();
CHECK(obj->IsUndetectable());
CHECK(
env->Global()->Set(env.local(), v8_str("undetectable"), obj).FromJust());
ExpectString("undetectable.toString()", "[object Object]");
ExpectString("typeof undetectable", "undefined");
ExpectString("typeof(undetectable)", "undefined");
ExpectBoolean("typeof undetectable == 'undefined'", true);
ExpectBoolean("typeof undetectable == 'object'", false);
ExpectBoolean("if (undetectable) { true; } else { false; }", false);
ExpectBoolean("!undetectable", true);
...
宝子们,这不就是心心念念的document.all
吗? 测试案例中创建的obj
实例,是个Object
对象,通过调用MarkAsUndetectable
方法,标记成了undefined
。这可是deom都给了啊,直接node插件搞起来!
0x6 node插件
之前没写过node插件,但略有了解,node插件本质也就是使用C++
操作v8
的方法和对象,达到实现增加功能的目的。这也是我愿意耗费时间和精力在C++
和v8
的原因之一。 找到相关文档和使用案例,不出半日。已成。
成果源码展示
#include <node.h>
namespace faker {
using v8::FunctionCallbackInfo;/src
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::Value;
using v8::Context;
using v8::FunctionTemplate;
static void ReturnThis(const FunctionCallbackInfo<Value>& args) {
args.GetReturnValue().Set(args.This());
}
void DocumentAll(const FunctionCallbackInfo<Value>& args) {
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
Local<FunctionTemplate> desc = FunctionTemplate::New(isolate);
desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable
Local<Object> obj = desc->GetFunction(context).ToLocalChecked()
->NewInstance(context).ToLocalChecked();
// printf("Create <document.all> Object From faker.node\n");
args.GetReturnValue().Set(obj);
}
void Initialize(Local<Object> exports) {
NODE_SET_METHOD(exports, "DocumentAll", DocumentAll);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
}
没错, 就这么几行, 为了实现这几行代码,付出了大把时间。
成果测试
写一个简单的js测试demo,实现一些类型检测
const faker = require('./build/Release/faker');
document = {};
document.all = faker.DocumentAll();
HTMLAllCollection = function HTMLAllCollection() {};
document.all.__proto__ = HTMLAllCollection
console.log("document.all.toString() -> ", document.all.toString());
console.log("typeof document.all -> ", typeof document.all);
老铁跑得没毛病。
写在最后
动手能力强的小伙伴已经能自己实现了吧。这一趟下来收获还是挺大的,尤其体现在对v8的使用。不会以为这段时间只是搞出个这个document.all
的类型检测吧。我更是把v8
和python
结合,利用python的扩展,将v8嵌入了python
的,实现了纯纯正正的python
下的v8
环境,摆脱了对node
的调用,并且实现了V8
的Inspector
协议,可以像node
一样进行js
的debugger
调试,比那啥bestV8
可舒服多了。用这套补环境,可是爽歪歪,js实现不了的交给python实现,python的方法直接变成js的方法。缺点嘛,跨平台编译有点烦,环境依赖有点强,C++的通病。这个下次在分享吧。这才是我这趟下来的最大收获。
代码
这源码值根华子吗?
这源码值根华子吗?
这源码值根华子吗?
链接: https://pan.baidu.com/s/18LWFzWdS6LEHMFdj-RicWA?pwd=8p5k
提取码: 8p5k
别忘了打赏啊大爷 来根华子啊
别忘了打赏啊大爷 来根华子啊
别忘了打赏啊大爷 来根华子啊