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

前言

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

代码

请添加图片描述

这源码值根华子吗?
这源码值根华子吗?
这源码值根华子吗?
链接: https://pan.baidu.com/s/18LWFzWdS6LEHMFdj-RicWA?pwd=8p5k
提取码: 8p5k
别忘了打赏啊大爷 来根华子啊
别忘了打赏啊大爷 来根华子啊
别忘了打赏啊大爷 来根华子啊

  • 49
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: 可以使用CSS选择器中的属性选择器来筛选符合特定属性条件的节点。例如: ``` document.querySelectorAll("[attributeName='attributeValue']") ``` 这将返回所有具有名为“attributeName”,值为“attributeValue”的属性的节点。 例如: ``` document.querySelectorAll("[class='my-class']") ``` 这将返回所有具有class='my-class'的节点 另外,还可以使用伪类选择器来筛选节点。例如: ``` document.querySelectorAll(":checked") ``` 这将返回所有被选中的复选框和单选按钮。 ### 回答2: 在使用document.querySelectorAll方法时,可以通过添加属性选择器来过滤所选节点的属性。 属性选择器是CSS选择器的一种,用于选择具有特定属性值的元素。可以使用以下几种方法过滤节点的属性: 1. 通过属性名选择元素: ```javascript const elements = document.querySelectorAll('[属性名]'); ``` 在属性名的位置,填入要过滤的属性名,该方法会选择所有拥有该属性的元素。 2. 通过属性值选择元素: ```javascript const elements = document.querySelectorAll('[属性名="属性值"]'); ``` 在属性名的位置,填入要过滤的属性名,然后使用等号和属性值来匹配拥有指定属性值的元素。 3. 通过属性值前缀选择元素: ```javascript const elements = document.querySelectorAll('[属性名^="属性前缀"]'); ``` 在属性名的位置,填入要过滤的属性名,然后使用^=和属性前缀来匹配属性值以指定前缀开始的元素。 4. 通过属性值后缀选择元素: ```javascript const elements = document.querySelectorAll('[属性名$="属性后缀"]'); ``` 在属性名的位置,填入要过滤的属性名,然后使用$=和属性后缀来匹配属性值以指定后缀结尾的元素。 5. 通过包含属性值选择元素: ```javascript const elements = document.querySelectorAll('[属性名*="属性值"]'); ``` 在属性名的位置,填入要过滤的属性名,然后使用*=和属性值来匹配属性值中包含指定值的元素。 通过上述方法,可以使用属性选择器来过滤所选节点的属性,获取符合条件的元素列表。 ### 回答3: document.querySelectorAll可以使用CSS选择器来过滤node的属性。通过在选择器中使用属性选择器,可以根据node元素的属性值来选择匹配的元素。 属性选择器有以下几种形式: 1. selector[attribute]:选择具有attribute属性的元素。 2. selector[attribute=value]:选择属性attribute的值为value的元素。 3. selector[attribute^=value]:选择属性attribute的值以value开头的元素。 4. selector[attribute$=value]:选择属性attribute的值以value结尾的元素。 5. selector[attribute*=value]:选择属性attribute的值包含value的元素。 例如,假设有以下HTML代码: ```html <div class="item">item1</div> <div class="item">item2</div> <div class="item" data-category="fruit">apple</div> <div class="item" data-category="fruit">banana</div> <div class="item" data-category="vegetable">carrot</div> <div class="item" data-category="vegetable">potato</div> ``` 可以使用示例代码来过滤node的属性: ```javascript // 选择所有包含data-category属性的元素 var items = document.querySelectorAll("[data-category]"); console.log(items); // 输出:[<div class="item" data-category="fruit">apple</div>, // <div class="item" data-category="fruit">banana</div>, // <div class="item" data-category="vegetable">carrot</div>, // <div class="item" data-category="vegetable">potato</div>] // 选择data-category属性值为"fruit"的元素 var fruits = document.querySelectorAll("[data-category='fruit']"); console.log(fruits); // 输出:[<div class="item" data-category="fruit">apple</div>, // <div class="item" data-category="fruit">banana</div>] // 选择class属性以"item"开头的元素 var startsWithItem = document.querySelectorAll("[class^='item']"); console.log(startsWithItem); // 输出:[<div class="item">item1</div>, // <div class="item">item2</div>, // <div class="item" data-category="fruit">apple</div>, // <div class="item" data-category="fruit">banana</div>, // <div class="item" data-category="vegetable">carrot</div>, // <div class="item" data-category="vegetable">potato</div>] ``` 通过在属性选择器中使用不同的符号和值,我们可以根据需要来过滤node的属性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值