畢業設計之php RASP(一) hook函數


環境:5.5.9-1ubuntu4.24

創建擴展插件

php擴展開發為了方便,有一個代碼生成器ext_skelphp-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_existsmethod_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,等畢業了后再研究吧....


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM