毕业设计之php RASP(一) hook函数
环境:5.5.9-1ubuntu4.24
创建扩展插件
php扩展开发为了方便,有一个代码生成器ext_skel
,php-src-php-5.5.9/ext/
便存在此程序
wget https://github.com/php/php-src/archive/php-5.5.9.zip
新建一个phpext.skel
,内容如下
string lemon_tree(string str)
生成文件: ./ext_skel --extname=phpext --proto=./phpext.skel
,其中--extname
为扩展名
这样将会在php-src-php-5.5.9/ext/phpext
目录中生成一些文件
1、修改config.m4
PHP_ARG_WITH或PHP_ARG_ENABLE,二选择一,第一种是指扩展需第三方库支持
2、php_phpext.h文件
其中confirm_phpext_compiled
函数只是为了测试用的,可以去除,也可以在此添加一些自定义的函数名
PHP_FUNCTION(confirm_phpext_compiled); /* For testing, remove later. */
PHP_FUNCTION(lemon_tree);
3、phpext.c
定位到自定义函数这块,可以写一些自己想实现的功能。
PHP_FUNCTION(lemon_tree)
{
char *str = NULL;
int argc = ZEND_NUM_ARGS();
int str_len;
if (zend_parse_parameters(argc TSRMLS_CC, "s", &str, &str_len) == FAILURE)
return;
php_error(E_WARNING, "lemon_tree: not yet implemented");
}
4、编译安装phpext
首先安装一下phpize
apt-get install php5-dev
phpize
./configure --with-php-config=/usr/bin/php-config
./configure
编译并安装phpext扩展到php库中
make
make test
make install clean
这样就会多出一个扩展
root@ubuntu:/usr# find / -name "phpext.so"
/usr/lib/php5/20121212/phpext.so
5、加载扩展phpext.so
编辑/etc/php5/apache2/php.ini
添加: extension = phpext.so
重启apache2
GDB调试
下载php版本: http://mirrors.sohu.com/php/
1、重新编译一下php,需要开启--enable-debug
./configure \
--prefix=/opt/php_debug/ \
--enable-debug \
--enable-cli \
--without-pear \
--enable-embed \
--enable-inline-optimization \
--enable-shared \
--enable-opcache \
--enable-fpm \
--with-gettext \
--enable-mbstring \
--with-iconv \
make
make install
mkdir /opt/php_debug/conf/
cp php.ini-development /opt/php_debug/conf/php.ini
创建插件如上,但是需要在config.m4最后加入以下内容
if test -z "$PHP_DEBUG"; then
AC_ARG_ENABLE(debug,
[--enable-debug compile with debugging system],
[PHP_DEBUG=$enableval], [PHP_DEBUG=no]
)
fi
新建一个bash,为了后面修改好调试
/opt/php_debug/bin/phpize
./configure --with-php-config=/opt/php_debug/bin/php-config --enable-debug
make
make test
make install clean
安装后lib的路径为:
/opt/php_debug/lib/php/extensions/debug-non-zts-20121212/
2、命令行下加载php.ini,可以看到加载了phpext模块
/opt/php_debug/bin/php -c /opt/php_debug/conf/php.ini -m
当然也可以为了方便:/opt/php_debug/bin/php --ini
,看会扫描ini的目录是哪个,然后放入php.ini即可
3、设置生成core文件
为0的时候不会生成
ulimit -c unlimited
4、gdb调试错误
gdb /opt/php_debug/bin/php -c core
source /php-src-php-5.5.9/.gdbinit
backtrace
5、如果想动态调,可以先设置一个断点再进行调试
调试前决定一下调试哪个函数,可以选择一个
nm phpext.so
gdb /opt/php_debug/bin/php
由于扩展名加载的so是第三方共享库,事先并未加载,直接下断点是找不到函数,可以在php的`php_execute_script`函数下断点,这里是属于运行阶段,前面扩展加载是属于启动初始化阶段,这个时候就可以对扩展函数进行下断点
b php_execute_script
run /var/www/html/bishe/1.php
b lemon_php_fcall_handler
run /var/www/html/bishe/1.php
Hook函数
如果想要Hook函数,隐形人师傅这里写了一些方法
php内核中有几个常见的变量操作宏
CG -> Complier Global 编译时信息,包括函数表等(zend_globals_macros.h:32)
EG -> Executor Global 执行时信息(zend_globals_macros.h:43)
PG -> PHP Core Global 主要存储php.ini中的信息
SG -> SAPI Global SAPI信息
Zend引擎将php函数分为5种类型
#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_INTERNAL_FUNCTION为内置函数,比如count,strpos
ZEND_USER_FUNCTION为用户自己定义的函数,平时写代码定义的function就是这类
php运行分为两个部分,一个是启动,一个是运行,启动的时候会所有的东西初始化,比如插件加载,php.ini加载等,运行的时候就是执行php代码,所以我们也可以从启动的时候进行入手,比如MINIT部分。
php5与php7结构的一些变化
char -> zend_string
zend_execute_data
下面一些代码可参考php内部函数实现过程:function_exists
、method_exists
1、函数替换:
php5下从CG表中找到函数,然后hook。
static struct lemon_overridden_fucs /* {{{ */ {
php_func ini_get;
} lemon_origin_funcs;
#define LEMON_O_FUNC(m) (lemon_origin_funcs.m)
/* }}} */
static void lemon_php_override_func(const char *name, php_func handler, php_func *stash){
zend_function *func;
if(zend_hash_find(CG(function_table), name, strlen(name) + 1, (void **)&func) == SUCCESS){
if (stash) {
*stash = func->internal_function.handler;
}
func->internal_function.handler = handler;
}
}
static void lemon_php_override_functions(){
const char *f_ini_get = "ini_get";
lemon_php_override_func(f_ini_get, PHP_FN(lemon_ini_get), &LEMON_O_FUNC(ini_get));
}
PHP_FUNCTION(lemon_ini_get)
{
char *varname, *str;
int varname_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &varname, &varname_len) == FAILURE) {
return;
}
zend_error(E_WARNING, "ini_get Hook success");
LEMON_O_FUNC(ini_get)(INTERNAL_FUNCTION_PARAM_PASSTHRU);
}
遇上一个函数的坑点导致出现部分函数不能覆盖bug。开始用的sizeof
,这个还会算上\0
的后面的字符
strlen()函数求出的字符串长度为有效长度,既不包含字符串末尾结束符 '\0'
sizeof()操作符求出的长度包含字符串末尾的结束符 '\0'
2、
hook OPCODE
ZEND_INCLUDE_OR_EVAL — eval、require等
ZEND_DO_FCALL — 函数执行system等
ZEND_DO_FCALL_BY_NAME — 变量函数执行 $func = “system”;$func();
php5与php7的_zend_execute_data变化也挺大的
php7
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
};
php5
/php-src-php-5.5.9/Zend/zend_compile.h
pedef union _zend_function {
zend_uchar type; /* MUST be the first element of this struct! */
struct {
zend_uchar type; /* never used */
const char *function_name;
zend_class_entry *scope;
zend_uint fn_flags;
union _zend_function *prototype;
zend_uint num_args;
zend_uint required_num_args;
zend_arg_info *arg_info;
} common;
zend_op_array op_array;
zend_internal_function internal_function;
} zend_function;
typedef struct _zend_function_state {
zend_function *function;
void **arguments;
} zend_function_state;
typedef struct _call_slot {
zend_function *fbc;
zval *object;
zend_class_entry *called_scope;
zend_bool is_ctor_call;
zend_bool is_ctor_result_used;
} call_slot;
struct _zend_execute_data {
struct _zend_op *opline;
zend_function_state function_state;
zend_op_array *op_array;
zval *object;
HashTable *symbol_table;
struct _zend_execute_data *prev_execute_data;
zval *old_error_reporting;
zend_bool nested;
zval **original_return_value;
zend_class_entry *current_scope;
zend_class_entry *current_called_scope;
zval *current_this;
struct _zend_op *fast_ret; /* used by FAST_CALL/FAST_RET (finally keyword) */
call_slot *call_slots;
call_slot *call;
};
如下图所示:
搞了很久,比如获取当前调用的函数名EG(current_execute_data)->function_state.function
,当然还看到有这样获取的execute_data->call->fbc
,但是不知道为啥一直获取不到
毕业要紧,换个php5.3.28版本,只能换成从opline
中来获取函数名
# define LEMON_OP1_CONSTANT_PTR(n) (&(n)->op1.u.constant)
static int lemon_php_fcall_handler(ZEND_OPCODE_HANDLER_ARGS){
zend_op *opline = execute_data->opline;
zval *fname = LEMON_OP1_CONSTANT_PTR(opline);
char *funcname = Z_STRVAL_P(fname);
int len = strlen(funcname);
if (fname) {
if (strncmp("passthru", funcname, len) == 0
|| strncmp("system", funcname, len) == 0
|| strncmp("exec", funcname, len) == 0
|| strncmp("shell_exec", funcname, len) == 0
|| strncmp("proc_open", funcname, len) == 0 ) {
zend_error(E_WARNING, "Hook success");
}
}
return ZEND_USER_OPCODE_DISPATCH;
}
static void lemon_php_register_handlers(){
zend_set_user_opcode_handler(ZEND_DO_FCALL, lemon_php_fcall_handler);
zend_set_user_opcode_handler(ZEND_DO_FCALL_BY_NAME, lemon_php_fcall_handler);
}
还有hook一些语言结构,比如echo、eval、include,等毕业了后再研究吧....