一次不必要的调试PHP-SRC

和小伙伴一起折腾一个项目,小伙伴从一个子站利用tp5rce打到了shell,找到了数据库的密码,我的目标和子站共用一个数据库,他就把测试账户丢给我了。进到后台,发现可以修改上传文件类型。我寻思着,我这个是不是也得想想怎么办。

1568282700754

修改成允许php的类型上传,但是但是,上传关于<?php 就会失败

1569903731235

1569903804327

遇到这种,我第一时间想到的就是PHP短标签。可是有时候目标没开启短标签,怎么办?

还有一种PHP的词法

<script language="php"></script>

可是这种,PHP7就不支持了。

也尝试了图片马,但是不知道为什么没执行成功。脑阔疼。我去问大佬,大佬推荐我看一下PHP的词法解析和语法解析。反正那时候没有什么好办法,看就看吧。

由于目标是php7.2的,跑去下载了php7.2的源码包

1568289152788

解决过程

PHP的官网有一个T_SR的一些标识符,上面很明确了各个版本的一些标识符的作用。

1569903309059

T_OPEN_TAG是php-zend引擎解析php语法打的标签,(这个文档不知道是哪个版本的,我看php-src里面没用<% == 当时坑了我一下)

所以真正准确的T_OPEN_TAG在

php-src-php-7.2.13\php-src-php-7.2.13\Zend\zend_language_scanner.l

1569903497905

解释一下,只有<?= 和<?php 能使用,因为<? 这个需要开启short_tags 也就是大家熟悉的短标签。

现在找到了<?php的替代,去试试去。

POST :

<?= phpinfo();?>

1569903894129

成了。搞定就想休息,等过了一段时间,再去上传一句话的时候,发现。。目标已经转了阿里云资源服务器==

1569904164255

就想看看PHP的词法分析(下面是我收集资料理解的,可能理解的不对,还请大佬们斧正)

PHP

PHP是怎么运行的?

首先,PHP是一门解释型的语言。PHP执行的时候,需要把PHP代码翻译成机器语言来执行(所以需要一个解释器,但是为了效率不可能每一次都重新解释,所以PHP中就有各种的opcode)。

而翻译这个过程,就是在PHP的内核完成的,向其他语言一样,都会有自己的语法风格。所以继续进行词法分析(像<?php这些),然后再进行语法分析,判断语法是否符合规范。最后交给Zend 去执行。

那么问题来了,PHP内核怎么接受PHP代码呢?

SAPI 一切的开始

SAPI接口是负责对各个接入层的抽象

PHP在Apache模块里边的实现,Fast-CGI的实现,命令行CLI的实现

1569905303331

然后顺带注册一些全局变量,然后接着执行词法分析->语法分析->生成opcode

词法分析

[http://rapheal.sinaapp.com/2013/11/14/php_zend_lex/]:

跟着拉风大佬的分析方法,源码高亮

我们从cli命令行下追溯如何进入词法分析流程。

先来看看php -s 在 sapi\cli\php_cli.c

注意到这里还有一个行为 PHP_MODE_PROCESS_STDIN和PHP_MODE_CLI_DIRECT

查了相关资料知道:

PHP_MODE_PROCESS_STDIN 程序阻塞在一个循环中,执行每一行输入,直到用户中断或异常退出。

1570000603240

PHP_MODE_CLI_DIRECT 应该是cli下的一个模式,跟着进去就可以看到使用了zend_eval_string_ex去处理。

1570097546843

追踪到zend_eval_string_ex上,可以看到调用了zend_eval_stringl_ex,继续就是转给了zend_eval_stringl处理

1570097739123

关于zend_eval_stringl就是函数编译代码生成字节码 new_op_array,再调用 zend_execute 函数执行生成的字节码

1570099585882

看一下另外一个PHP_MODE_PROCESS_STDIN

1570000822511

在这个模式下, 程序阻塞在一个循环中,执行每一行输入,直到用户中断或异常退出。所以这个应该是切割每一行的。

而执行的话应该是这两个,

php_execute_script
zend_eval_string_ex和zend_execute_scripts类似,不过前者编译的是一个串,后者编译的是一个PHP文件。

php_execute_script在其定义下,op_array是通过zend_compile_file获取,然后就用zend_execute去执行

1570003553580

先找这个zend_compile_file,发现在PHP启动Zend时候,就已经改成了compile_file。

在\php-src-php-7.2.13\Zend\zend.c

1570003694460

compile_file 在\php-src-php-7.2.13\Zend\zend_language_scanner.c 下

1570003831049

可以发现op_array 又从zend_compile这里拿,(zend_compile的函数实现)

现在拿到了op_array,我们回去zend.c的zend_execute_scripts中,发现最后执行op_array的是zend_excute

1570004969563

这两个模式大致就是检查是否文件运行,因为我们是在cli命令行下去执行,如果这两个没通过就会抛出异常。

下面去看看PHP_MODE_HIGHLIGHT

1569907732987

然后就调用了

1569907751230

在 php-src-php-7.2.13\Zend\zend_highlight.c

就进入了lex_scan词法分析,获取Token,然后加入对应的颜色

1569907841853

lex_scan则在 php-src-php-7.2.13\Zend\zend_language_scanner.c 下

在php-src下有三个相关的文件

zend_language_scanner.c  //主要的代码
zend_language_scanner.l  //基于re2c生成的语法规则
zend_language_scanner.h  //头文件

然后将源代码按照词法规则切分一个一个的标记(token)

1570098886432

这是词法分析注释

1570098908944

那么词法分析之后,怎么去执行呢?给大家一看就知道了,-s 仅仅是对代码进行高亮,没有去执行

1570098583541

总结

我捣鼓了那么久,总算把PHP的词法分析给弄明白点了。(这个还是拉风大佬的图)

词法语法分析就是通过lex_scan去按照规则语法去打标签,然后匹配某一条规则语法。php_execute_script这个PHP执行的入口,调了zend_execute_scripts。而zend_execute_scripts这个函数里面,又调用了zend_compile_file和zend_execute,zend_compile_file在初始时候就变成了compile_file它用于生成opcode中间代码,然后交给zend_execute去执行。

1570100454835

1570101347410

大概是这个意思。这里我感觉,有一些时候,网上的套路总要用源代码去解释,追根溯源。这次的瞎折腾,感觉还是有点没看透,寻思着在分析分析。得看看函数实现等等

参考资料:

https://www.cnblogs.com/yjf512/category/272034.html

https://www.notee.cc/PHP/engine_general_lifetime_of_php_code_1/#php_mode_process_stdin

https://www.kancloud.cn/kancloud/php-internals/42752

https://www.php.net/manual/zh/internals2.ze1.zendapi.php

http://rapheal.sinaapp.com/2013/11/

标签: PHP

添加新评论