探究PHP命令执行函数原理

0x01 前言

不知道总记得有这么一个问题,但是一直没去解决,于是自己下载php的源码进行分析。

0x02 PHP编译

参考
http://blog.csdn.net/earbao/article/details/53438029

https://wiki.php.net/internals/windows/stepbystepbuild

0x03 调试

php里面使用命令执行的函数很多,为了便于搜索,使用shell_exec函数搜索。(也可以进行静态分析,但是动态调试的话,能根据php返回的信息,更快定位shell_exec执行代码的位置。)

ext\standard\exec.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
PHP_FUNCTION(shell_exec)
{
FILE *in;
size_t total_readbytes;
char *command;
int command_len;
char *ret;
php_stream *stream;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &command, &command_len) == FAILURE) {
return;
}
#ifdef PHP_WIN32
if ((in=VCWD_POPEN(command, "rt"))==NULL) {
#else
if ((in=VCWD_POPEN(command, "r"))==NULL) {
#endif
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to execute '%s'", command);
RETURN_FALSE;
}
stream = php_stream_fopen_from_pipe(in, "rb");
total_readbytes = php_stream_copy_to_mem(stream, &ret, PHP_STREAM_COPY_ALL, 0);
php_stream_close(stream);
if (total_readbytes > 0) {
RETVAL_STRINGL_CHECK(ret, total_readbytes, 0);
}
}

既然找到了shell_exec源码的位置,接下来使用php.exe执行命令执行的代码。

1
2
3
4
<?php
exec('pause');
shell_exec("whoami");
?>

执行

1
php.exe test.php

接下进行使用vs进行附加

exec.c放入vs中,并且下断点进行一步一步跟进。

最后在exec.c文件找到命令执行的代码,使用CreateProcess进行创建cmd.exe进程,使用cmd.exe /c参数进行执行命令。

调用堆栈。

1
2
3
4
5
php5ts_debug.dll!popen_ex(const char * command, const char * type, const char * cwd, char * env, void * * * tsrm_ls) 行 545 C 已加载符号。
php5ts_debug.dll!virtual_popen(const char * command, const char * type, void * * * tsrm_ls) 行 1864 C 已加载符号。
php5ts_debug.dll!zif_shell_exec(int ht, _zval_struct * return_value, _zval_struct * * return_value_ptr, _zval_struct * this_ptr, int return_value_used, void * * * tsrm_ls) 行 541 C 已加载符号。

0x04 总结

一开始知道PHP是c语言写的,那命令执行的实现,也就是平时c语言那样实现,调试分析不过是确定一下,并且看下PHP的源码。

0x05 参考

http://blog.csdn.net/earbao/article/details/53438029

https://wiki.php.net/internals/windows/stepbystepbuild