node插件过document.all类型检测(补环境)

978 阅读5分钟

前言

之前一直想写这篇过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一套下来,相关的实现是一点没有,唯一告诉你的是这是一个历史遗留问题, 大意是:allIE浏览器拥有的,并不属于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吗? 测试案例中UndetectableObject是个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的类型检测吧。我更是把v8python结合,利用python的扩展,将v8嵌入了python的,实现了纯纯正正的python下的v8环境,摆脱了对node的调用,并且实现了V8Inspector协议,可以像node一样进行jsdebugger调试,比那啥bestV8可舒服多了。用这套补环境,可是爽歪歪,js实现不了的交给python实现,python的方法直接变成js的方法。缺点嘛,跨平台编译有点烦,环境依赖有点强,C++的通病。这个下次在分享吧。这才是我这趟下来的最大收获。

代码

请添加图片描述

这源码值根华子吗?

这源码值根华子吗?

这源码值根华子吗?

链接: pan.baidu.com/s/18LWFzWdS…

提取码: 8p5k

别忘了打赏啊大爷 来根华子啊

别忘了打赏啊大爷 来根华子啊

别忘了打赏啊大爷 来根华子啊