title: 初识PHP-Parser
date: 2020-9-13 03:11:21
categories:
- PHP-Parser
tags: - PHP-Parser
- web
-
php
toc: true
PHP-Parser 用于代码混淆的代码
learn from
http://j0k3r.top/2020/03/24/php-Deobfuscator/#0x01-php-parser
下载
curl -s http://getcomposer.org/installer | php
遇到权限问题时,可以输入一下命令
sudo chown -R 'user-name' /home/'user-name'/.composer
之后再在所需项目中php composer.phar require nikic/php-parser
为了更方便调用composer
,可以输入一下命令
mv composer.phar /usr/local/bin/composer
如果composer require nikic/php-parse
遇到长时间卡住,可以尝试更新源
composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
项目: https://github.com/nikic/PHP-Parser
runoob 再次好用
生成AST
首先介绍一下PHP-Parser中定义的一些节点类型:
(1)PhpParser\Node\Stmt是语句节点,不带任何返回信息(return)的结构,如赋值语句”$a = $b” ;
(2)PhpParser\Node\Expr是表达式节点,可以返回一个值的语言结构,如$var和func()。
(3)PhpParser\Node\Scalar是常量节点,可以用来表示任何常量值。如’string’,0,以及常量表达式。
(4)还有一些节点没有包括进去,如参数节点(PhpParser\Node\Arg)。
一些节点类的名称使用了下划线,这是为了避免和PHP关键字冲突。
PHP-parser的test.php下,该代码片段会生成AST
<?php
use PhpParser\Error;
use PhpParser\ParserFactory;
require dirname(__FILE__).'/vendor/autoload.php'; #composer require nikic/php-parse 成功后会生成这个文件,当然还有其他的文件
$code = <<<'CODE'
<?php
echo 'Hello PHP';
CODE;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);// 优先解析 PHP7 代码
try {
$stmts = $parser->parse($code);//使用 ParserFactory 的 parse 方法解析代码,得到一个 statement 节点数组
var_dump($stmts);
} catch (Error $e) {
echo 'Parse Error: ', $e->getMessage();//语法错误可以通过 PhpParser\Error 来捕获
}
output:
/home/fe1w0/babyshop/ttt.php:15:
array(1) {
[0] =>
class PhpParser\Node\Stmt\Echo_#1131 (2) {
public $exprs =>
array(1) {
[0] =>
class PhpParser\Node\Scalar\String_#1130 (2) {
...
}
}
protected $attributes =>
array(2) {
'startLine' =>
int(3)
'endLine' =>
int(3)
}
}
}
使用 NodeDumper
来查看
<?php
use PhpParser\Error;
use PhpParser\ParserFactory;
use PhpParser\NodeDumper;
require dirname(__FILE__).'/vendor/autoload.php'; #composer require nikic/php-parse 成功后会生成这个文件,当然还有其他的文件
$code = <<<'CODE'
<?php
echo 'Hello PHP';
CODE;
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);// 优先解析 PHP7 代码
try {
$stmts = $parser->parse($code);//使用 ParserFactory 的 parse 方法解析代码,得到一个 statement 节点数组
$nodeDumper = new NodeDumper;
echo $nodeDumper->dump($stmts), "\n";
#var_dump($stmts);
} catch (Error $e) {
echo 'Parse Error: ', $e->getMessage();//语法错误可以通过 PhpParser\Error 来捕获
}
output:
array(
0: Stmt_Echo(
exprs: array(
0: Scalar_String(
value: Hello PHP
)
)
)
)
修改节点值 将Hello PHP
改为Hello world
<?php
use PhpParser\Error;
use PhpParser\ParserFactory;
use PhpParser\NodeDumper;
use PhpParser\NodeTraverser;
use PhpParser\Node;
use PhpParser\NodeVisitorAbstract;
use PhpParser\PrettyPrinter;
require dirname(__FILE__).'/vendor/autoload.php';
$code = <<<'CODE'
<?php
echo 'Hello PHP';
CODE;
class MyNodeVisitor extends NodeVisitorAbstract
{
public function leaveNode(Node $node) {
if ($node instanceof Node\Scalar\String_) {
$node->value = 'Hello Word';
}
}
}
class PrintNodeVisitor extends NodeVisitorAbstract
{
public function leaveNode(Node $node) {
if ($node instanceof Node\Stmt\Echo_) {
return new PhpParser\Node\Stmt\Expression( new Node\Expr\Print_(new Node\Scalar\String_(($node->exprs)[0]->value)) );
}
}
}
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);
try {
$stmts = $parser->parse($code);
$traverser = new NodeTraverser;
// 添加 node visitors
$traverser->addVisitor(new MyNodeVisitor);
$traverser->addVisitor(new PrintNodeVisitor);
// 遍历 AST
$new_stmts = $traverser->traverse($stmts);
// 将 AST 转为 PHP 代码
$prettyPrinter = new PrettyPrinter\Standard;
$new_code = $prettyPrinter->prettyPrintFile($new_stmts);
echo $code.PHP_EOL;
echo "--After parser:--\n\n".$new_code;
} catch (Error $e) {
echo 'Parse Error: ', $e->getMessage();
}