纯python实现js补环境框架

前言

什么是js补环境就不多言了,随着对抗的加剧逐渐演变出js逆向补环境框架,我最早接触到的是志远的基于node的vm2包实现的,给爬虫逆向提供了一个全新的思路和方向。随着技术的推进,目前主流的补环境框架仍然是基于node的vm2为主流,或者是基于node的C++扩展实现的,如:基于拓展isolated-vm实现的js-env。我这个纯python实现的js补环境框架又是什么意思呢,且听我徐徐道来。

js补环境的本质

补环境的目的是啥?不就是让目标js能正确的执行出结果吗。所以不管是啥框架其本质都是v8,v8是运行js代码的引擎,浏览器也好node也罢最终都要落到v8上面。我对node的理解是v8的第三方库,node实现了一系列的API,方便更好的实现一些功能,模糊了对v8的感觉。浏览器在加载页面时也是如此,可以理解为为v8提供了BOM和DOM的API。所以,补环境的本质:在v8的基础上提供类似浏览器的BOM和Dom的API即可。那么你是用js去实现还是用c++去实现,甚至是python去实现这些API就无关紧要了。

python实现的原理

上文提到补环境的实质是v8,python从远古时代的python2版本存在了pyv8直接运行js的包了(很多年没有更新了),现在有个包stpyv8在pyv8的基础上实现的(目前一直有在维护), 以及PyMiniRacer嵌入v8基于ctypes进行转换, 还有之前昙花一现的bestV8,都是python与v8结合的案例。python在对执行js的探索一直都存在,例如:Js2Py一个脑洞大开的方案,将js的AST语法树转换成python的形式。那PyExecJS算不算呢?不算,PyExecJS本质是起进程去调用node去执行js。

说了这么多,要表达的是python拥有嵌入v8的能力。而这个能力来源的基础是啥?c/c++。python很优雅简单,但是很慢,也可以很快,需要快的地方可以用c/c++去实现。编写一个python扩展(类似于node的扩展),stpyv8便是通过python的c++扩展实现的。

那用stpyv8去实现一个补环境框架可行吗?理论上可行。回到上文所的js补环境的本质是v8,stpyv8已经具备的v8的能力所以可行。为啥是理论上呢?stpyv8已经具备了一定的python类型与js类型转换的能力,但这个库初衷应该是能实现js调用并简单的类型转换就行了,如果在这基础上去补,那基本上要用js代码去实现BOM和Dom的API,很痛苦,而且stpyv8缺少对js的debugger调试能力。

stpyv8不行,谁行?v8py, 一个断更好多年的库,v8py能行的几个理由:1. 实现了V8Inspector协议,这样打开浏览器的devtools就可以调试js代码了。2. 相对完善的python类型和js类型的互相转换,例如浏览器Bom中有个alert方法,在python的直接定义一个alert方法,然后导入到globThis中,然后js中调用alert方法将会回到到python中的alert方法,并且alert.toString()等检测可以与浏览器完全一致。所以v8py为实现浏览器中的BOM和Dom的API提供了无限可能。理由3:我已经在此基础上实现了。

v8py为纯python补环境提供了可行性,但如何完全去规避原型链以及属性方面的检测,并且实现Dom及Bom的Api呢?让我们回到浏览器的本身,看看浏览器是如何实现Bom及Dom,浏览器是c++实现的所以全部都是c++实现的咯,说了一句正确的废话。c++实现的Bom和Dom如何体现在js代码中,如何体现在v8中,如何实现类型转换的这才是我们关注的重点。谷歌浏览器源码有一类文件以idl为后缀的文件,你可以把它理解为协议,规范,或者是语言都行,它叫Web IDL。我理解的作用呢就是打通c++类型和js中类型。浏览器在预编译的时候都会调用python去解析idl文件,然后在指定位置(gen\third_party\blink\renderer\bindings\core\v8gen\third_party\blink\renderer\bindings\modules\v8)生成c++代码,而这些代码便是c++和v8转换类型转换的代码,所有可以找到的检测点以及实现都在里面。扯这么多我想表达啥呢:c++是Bom及Dom的底层实现,而如果把它换成python会怎样呢?

心有多大胆,地有多大产,干它。

c++换成python?如何换?如何做到偷天换日,狸猫换太子?这就回到v8本身,且看如何通过api调用去实现一个函数或者对象了。大体实现原理如下:

void Func(const v8::FunctionCallbackInfo<v8::Value>& args){
    //args->Length() 参数的长度
    //args->This() 当前函数域中的this对象
    //args->isConstructCall() 判定当前是否为构造调用,如果为false,表示常规调用
    
    /*
    通过调用CPython的Api如:PyObject_CallMethod等方法调用的python的方法
    并将结果python返回的结果转换成v8的js类型对象返回给js
    如: python有个方法
    def hello(): 
        return "Hello v8"
    */ 
  
    PyObject* hello; 
    
    // TODO: 具体如何获取到hello方法
    
    // 调用python方法
    PyObject* py_str = PyObject_Call(hello, NULL, NULL);
    
    // 将python的ret字符串转换成v8对象
    Py_ssize_t len;
    const char *str = PyUnicode_AsUTF8AndSize(py_str, &len);
    Local<String> js_str= String::NewFromUtf8(isolate, str, NewStringType::kNormal, len).ToLocalChecked();
    
    // 设置返回值
    args.GetReturnValue().Set(js_str); 
}

v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate, Func);

通过v8接口定义一个函数模板templ,当js代码中调用到该模板实现的js函数时,会回调C++实现的Func方法,然后我们在c++方法中来个狸猫换太子去调用python的方法,从而实现的js中调用python方法,这就是纯python实现js补环境框架核心思想了。

v8py在类型转换以及调试等基础工作都做好了,有个缺点就是版本比较低,需要改动的地方也比较多。站在巨人的肩膀上总是事半功倍。在上述核心思想的指导下,又开启了漫长的踩坑之路,真的是一步一个坑,但终归是闯出来了,一个全新的补环境框架,纯python版本的。

与node补环境的比较

node补环境的优缺点

拥有丰富的第三方库。基本上浏览器所需要的BOM和Dom方法都能在node的第三方包找到对应的实现,这大大的缩短的补环境的开发周期。比如vm2提供了一个相对干净的环境,Cheerio,jsdom,node-html-parser等html解析库不用自己去实现dom解析,node的c++插件node-canvas可以实现canvas,以及node本身丰富API。

就是监测点太多,原型链的检测toString的检测,往往是为了规避这些检测的代码比功能实现的还多,而且有些检测光靠js代码还无法避免,甚至必须借助node的c++扩展操作v8原始api去实现。总结下来就是用这些方案的人太多了,被针对了,什么难实现就检测啥。

纯python补环境的优缺点
我这套补环境框架和上文提到基于isolated-vm实现的js-env底层原理是有点相似,按照上文所说的狸猫换太子的比喻,本框架中的狸猫是python,而js-env中的狸猫就是js本身了。优点,基本规避了原型链以及toString的检测,缺点所有被用到的Bom及Dom方法都需要用python去实现一遍,工作量同样巨大,而且在编译python的c++扩展,存在c++跨平台跨能力差的缺点,而使用node插件相对友好很多(少了一个v8的编译,node本身编译好了)。虽然缺点很明显但优点同样很突出,坐拥python丰富的第三方库,就看你如何运用了。另外一个优点就是减少了对node的依赖,不用装一个node环境了,对于pythoner来说使用起来更简单了。

成果展示

看下图, 懂的都懂
请添加图片描述

https://github.com/ConlinH/pyv8env_demo
完整代码暂时不考虑开源

写这边文章的目的
1.是给补环境这个方向提供一种新的思路。
2. 给这段时间掉的头发一个交代。
3. 装个13

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值