内容简介:前段时间想写一个静态代码审计工具,需要对php扩展熟悉一些,那么自己从零开始接触这一块,如果有错误的地方,麻烦师傅们指正。另外呢网上虽然有一些文章,但是感觉都不是特别细,对于刚入门的我来说有些难以理解,因此详细的记录下自己的学习过程。我在mac环境上折腾了两天gdb,还是没折腾好,无奈选择docker,这里推荐一个
前段时间想写一个静态代码审计工具,需要对 php 扩展熟悉一些,那么自己从零开始接触这一块,如果有错误的地方,麻烦师傅们指正。
另外呢网上虽然有一些文章,但是感觉都不是特别细,对于刚入门的我来说有些难以理解,因此详细的记录下自己的学习过程。
我在mac环境上折腾了两天gdb,还是没折腾好,无奈选择docker,这里推荐一个
https://github.com/hxer/php-debug/blob/master/Dockerfile
这个dockerfile的vld和php版本不匹配,需要更换下低版本的vld。
启动命令
docker run -i -d --security-opt seccomp=unconfined -v /Users/p0desta/Desktop/code:/home php5-debug
编写最简单的php扩展
-
在ext目录下执行命令
./ext_skel --extname=p0desta
-
然后进入到扩展目录下,编辑config.m4文件
16 dnl PHP_ARG_ENABLE(foobar, whether to enable foobar support, 17 dnl Make sure that the comment is aligned: 18 dnl [ --enable-foobar Enable foobar support])
删除第16-18行的注释
-
然后去php_p0desta.h文件,添加函数声明
PHP_FUNCTION(confirm_foobar_compiled); PHP_FUNCTION(p0desta);
-
然后到p0desta.c中
const zend_function_entry p0desta_functions[] = { PHP_FE(p0desta, NULL) PHP_FE(confirm_p0desta_compiled, NULL) /* For testing, remove later. */ PHP_FE_END /* Must be the last line in p0desta_functions[] */ };添加如下
PHP_FE(p0desta, NULL) -
然后到最底下编写函数
PHP_FUNCTION(p0desta) { php_printf("hello world"); } -
然后在当前目录下执行命令
phpize ./configure --enable-p0desta --enable-debug make
然后会在modules文件夹下生存 so 文件,在php.ini中添加拓展
extension=p0desta.so
然后就可以调用自写的函数。
php代码的大致执行流程
开始 -> Scanning,将php代码转换为语言片段(Tokens) -> Parsing,将tokens转化为简单而有意义的表达式 -> Compilation,将表达式编译成opcode -> Execution,顺次执行opcodes,从而实现php脚本的功能。
hook最简单的opcode
关于一些宏的解释参考: https://github.com/pangudashu/php7-internal/blob/master/7/hook.md
这里我使用 zend_set_user_opcode_handler 函数来hook echo 函数
zend_set_user_opcode_handler(ZEND_ECHO, ppecho);
主要原理是将对应的Zend op的handler函数替换成我们自己定义的来实现HOOK
首先我在扩展.h中定义如下
#define ZEND_OPCODE_HANDLER_ARGS void PHP_FUNCTION(confirm_foobar_compiled); int ppecho(ZEND_OPCODE_HANDLER_ARGS);
扩展.c中
PHP_MINIT_FUNCTION(p_echo)
{
/* If you have INI entries, uncomment these lines
REGISTER_INI_ENTRIES();
*/
zend_set_user_opcode_handler(ZEND_ECHO, ppecho);
return SUCCESS;
}
int ppecho(ZEND_OPCODE_HANDLER_ARGS)
{
php_printf("hook success");
return ZEND_USER_OPCODE_RETURN;
}
如果打算放行继续执行的话 return ZEND_USER_OPCODE_DISPATCH ,如果不继续执行的话 return ZEND_USER_OPCODE_RETURN
编译完之后看一下效果
Webshell简单防御初探
关于一些PHP内核中的定义详情请参考 https://www.kancloud.cn/kancloud/php-internals/42755
这里我们暂时需要了解的有
-
全局变量
EG()、这个宏可以用来访问符号表,函数,资源信息和常量 CG() 用来访问核心全局变量 PG() PHP全局变量。我们知道php.ini会映射一个或者多个PHP全局结构。举几个使用这个宏的例子:PG(register_globals), PG(safe_mode), PG(memory_limit) FG() 文件全局变量。大多数文件I/O或相关的全局变量的数据流都塞进标准扩展出口结构。
-
函数类型
Zend引擎将函数分为以下几个类型
#define ZEND_INTERNAL_FUNCTION 1 #define ZEND_USER_FUNCTION 2 #define ZEND_OVERLOADED_FUNCTION 3 #define ZEND_EVAL_CODE 4 #define ZEND_OVERLOADED_FUNCTION_TEMPORARY 5
-
ZEND_USER_FUNCTION (用户函数:用户定义的函数)
<?php function test(){ } ?> -
ZEND_INTERNAL_FUNCTION (内部函数:由扩展、PHP内核、Zend引擎提供的内部函数)
-
变量函数
$func = 'print_r'; $func('i am print_r function.'); -
匿名函数
-
-
php7的_zend_execute_data
struct _zend_execute_data { const zend_op *opline; /* executed opline */ zend_execute_data *call; /* current call */ zval *return_value; zend_function *func; /* executed function */ zval This; /* this + call_info + num_args */ zend_execute_data *prev_execute_data; zend_array *symbol_table; #if ZEND_EX_USE_RUN_TIME_CACHE void **run_time_cache; /* cache op_array->run_time_cache */ #endif #if ZEND_EX_USE_LITERALS zval *literals; /* cache op_array->literals */ #endif };
我们看一下如下代码的opcode
<?php
eval("system('whoami');");
我们hook掉 INCLUDE_OR_EVAL
修改 php_hook_eval.h 增加
PHP_FUNCTION(confirm_foobar_compiled); static int HOOK_INCLUDE_OR_EVAL(ZEND_OPCODE_HANDLER_ARGS); # define ZEND_OPCODE_HANDLER_ARGS zend_execute_data *execute_data
修改 hook_eval.c 增加
static int HOOK_INCLUDE_OR_EVAL(ZEND_OPCODE_HANDLER_ARGS)
{
zend_execute_data *tmp = &execute_data;
zend_op *opline = execute_data->opline;
return ZEND_USER_OPCODE_DISPATCH;
}
直接在 execute_data 中往下找调用的函数 system
这个也就是操作数
string型变量比较特殊,因为内核在保存String型变量时,不仅保存了字符串的值,还保存了它的长度,所以它有对应的两种宏组合STRVAL和STRLEN,即:Z_STRVAL、Z_STRVAL_P、Z_STRVAL_PP与Z_STRLEN、Z_STRLEN_P、Z_STRLEN_PP。
编写 HOOK_INCLUDE_OR_EVAL 如下
static int HOOK_INCLUDE_OR_EVAL(ZEND_OPCODE_HANDLER_ARGS)
{
zend_op *opline = execute_data->opline;
zval *operands = opline->op1.zv;
char *cmd = Z_STRVAL_P(operands);
if(cmd){
if((strstr(cmd, "system")==NULL)&&(strstr(cmd, "exec")==NULL)&&(strstr(cmd, "shell_exec")==NULL)&&(strstr(cmd, "passthru")==NULL)&&(strstr(cmd, "roc_open")==NULL)){
return ZEND_USER_OPCODE_DISPATCH;
}else{
return ZEND_USER_OPCODE_RETURN;
}
}
return ZEND_USER_OPCODE_DISPATCH;
}
看下执行流程
当然,只hook掉 ZEND_INCLUDE_OR_EVAL 是很难防御的,比如说
<?php
eval('echo `whoami`;');
这种就必须再去hook DO_FCALL
为了不影响业务并且去做更好的防御,还需要更深入的研究。
参考:
http://drops.xmd5.com/static/drops/web-7333.html https://www.cnblogs.com/iamstudy/articles/php_code_rasp_1.html
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Persuasive Technology
B.J. Fogg / Morgan Kaufmann / 2002-12 / USD 39.95
Can computers change what you think and do? Can they motivate you to stop smoking, persuade you to buy insurance, or convince you to join the Army? "Yes, they can," says Dr. B.J. Fogg, directo......一起来看看 《Persuasive Technology》 这本书的介绍吧!