MENU

使用v8js在php中运行javascript代码

最近写了个cloudflare的bypass脚本后,有写了一个某论坛的bypass,一看是此论坛只是简单的用js设置cookie,正则表达式取cookie即可破之,第二天发现换成了js计算aes加密了,先分析了js脚本,得知使用aes-256-cbc加密,但是密文和key和iv不知怎么还原(我还是太菜了),于是google搜索'php run javascript'找到了这个硬核扩展

介绍

google v8是由google开发的javascript引擎,它的第一个版本发布时间好像和chrome发布时间一致,v8是开源的,它也被使用在chromium和chrome中.

v8js是一个php的扩展,它将php代码中的javascript代码交给v8解析和运行,所以v8js依赖v8.

环境

  • ubuntu16.04 x64
  • google v8 5.6
  • python 2.7或以上
  • gcc 4.6或以上
  • php7或以上(php5可以使用v8js旧版本)
  • git
  • glib2-devel
  • build-essential

编译google v8

v8js给出的介绍中使用了ubuntu/debian的官方仓库中的libv8-dev,但仓库中的v8版本已经很老旧,使用此版本将无法顺利编译v8js,而官方提到了pinepain仓库,次仓库中的v8版本最低为7.0,但此版本对于v8js太新,同样无法正常编译,经过一番搜寻最终确定了v8的5.6版本可以在编译v8js(2.1.0)时正常使用,于是我们需要编译此版本的v8,经过google搜索得到的几篇文章,加上自己的测试得出安装方式.

首先安装必备组件和设置git

apt-get install build-essential git python libglib2.0-dev
git config --global user.email "[email protected]"
git config --global user.name "Shira Kagurazaka"

git设置这一步骤可以忽略,但是google搜索得到的文章中有人反映不设置git的话后面会出错误

然后拉取工具并设置临时变量环境

git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=`pwd`/depot_tools:"$PATH"

中间断开的话需要重新设置环境变量,或者可以考虑将其加入用户变量文件中

然后拉取google v8并切换到早期的5.6版本

fetch v8
cd ./v8
git checkout 5.6.326.12
gclient sync

拉取将会持续很长时间我的几台机器最短5分钟最长接近两小时

拉取完成后准备编译

tools/dev/v8gen.py -vv x64.release
echo is_component_build = true >> out.gn/x64.release/args.gn
ninja -C out.gn/x64.release

这里执行ninja命令也会非常的慢,我的4核8g执行了40分钟左右

然后开始编译安装,当然编译速度和ninja执行的速度差不多

make library=shared snapshot=off native

如果机器拥有多核,可以在后面加入-j参数使用多核,生产服务器建议使用核心数量4分之3进行编译

由于不存在make install所以我们需要将用到的文件手动复制出来/usr/opt/v8各一份

mkdir -p /opt/v8/{lib,include}
cp out.gn/x64.release/lib*.so out.gn/x64.release/*_blob.bin /opt/v8/lib/
cp -R include/* /opt/v8/include/
cp out.gn/x64.release/lib*.so out.gn/x64.release/*_blob.bin /usr/lib/
cp -R include/* /usr/include/
cp out/native/lib.target/lib*.so /usr/lib/
cp -R include/* /usr/include

当然也可以指定其他目录或者只保留/usr部分,看别人的文章在/opt/v8中保留了一份于是自己也就保留了一份

安装v8js扩展

查看php的手册时发现可以使用pecl,所以就直接使用了pecl进行编译安装,首先安装pear组件

apt install php-pear -y

如果使用了第三方仓库或者其他版本php那么需要安装对应版本的pear才可以(比如安装时php名称为php7则pear名称应该为php7-pear)

然后更新pear仓库列表,不更新的话安装时可能不是最新的版本

pecl channel-update pecl.php.net

然后开始使用pecl编译安装v8js,如需指定版本可以在后面加上版本号,如:v8js-2.0.0

pecl install v8js

当出现以下提示时输入/opt/v8或者直接回车(将使用/usr/lib目录)或者刚刚编译后复制到的其它目录

Please provide the installation prefix of libv8 [autodetect] :

编译完成后Installing的值就是编译后so文件的绝对路径,在php.ini中引用它并重启fpm

[v8js]
extension=v8js.so

然后使用phpinfo()将会出现v8js的信息,这时就可以使用它了,如果在此页面没有v8js信息,请尝试将配置文件中extension=v8js.so的路径改为绝对路径

v8js的使用和注意

官网只有这一个示例,并且没有详细的说明

<?php
$v8 = new V8Js();
/* basic.js */
$JS = <<< EOT
len = print('Hello' + ' ' + 'World!' + "\\n");
len;
EOT;
try {
    var_dump($v8->executeString($JS, 'basic.js'));
} catch (V8JsException $e) {
    var_dump($e);
}
?>

解释一下这一段,首先实例化v8js,然后声明一个变量,内容为js脚本,然后通过executeString执行它,并通过catch来捕获js中的异常(各种js错误),其中第一个参数为js脚本,第二个参数为文件名,这里的文件名在php手册中的解释貌似是方便debug使用的暂存文件名,然后executeString在手册中还有第三个参数,这个参数和用处目前没明白,手册给出的值有V8Js::FLAG_NONEV8Js::FLAG_FORCE_ARRAY,我在运行同一段js时改变第三个参数得到的结果相同.

这段js代码会直接输出hello world而不是executeString将它返回
根据官网的介绍executeString将会返回js中最后的一个变量
所以,如果我们需要js中的执行结果我们需要这样写

<?php
$v8 = new V8Js();
$JS = <<< EOT
var a = "hello world";
a;
EOT;
$res = $v8->executeString($JS);

这样php中的$res将会是js中的hello word

使用时注意

v8js中不支持console,document,alert等对象,因为v8并不是一个实际的窗口,如果js代码中包含这些,将会出现错误,输出可以使用print代替

executeString只能返回js中最后一个变量,如需获取js中的多个变量,则需要创建一个对象将所有需要的结果赋值给这个对象,然后获取这个对象.