PHP扩展开发 - 替换PHP底层函数实现

最近在看php的底层源码,虽说有点自闭,但也搞出了一点东西

思路

自己新建一个扩展,然后,在扩展中找到对应的函数实现,然后把这个函数实现替换成自己的

几个关键函数或定义

INTERNAL_FUNCTION_PARAME

这个宏来表示整个函数的参数列表

CG(function_table)

在php中的所有的函数,都会在执行时存放在一个大的HashTable中,这个HashTable就是function_table,在PHP扩展中,可以通过宏CG(function_table)去获取这个HashTable

zend_hash_tr_find_ptr

https://phpinternals.net/docs/zend_hash_str_find_ptr

实现

step1

创建一个新扩展
php ext_skel.php --ext fake_phpinfo

step2

进入fake_phpinfo
添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
typedef void (*php_func)(INTERNAL_FUNCTION_PARAMETERS);
// PHP 5版本
static void php_override_func(const char *name, size_t len, php_func handler, php_func *stash TSRMLS_DC)
{
zend_function *func;
if (zend_hash_find(CG(function_table), name, len, (void **)&func) == SUCCESS) {
if (stash) {
*stash = func->internal_function.handler;
}
func->internal_function.handler = handler;
}
}

// PHP 7版本
static void php_override_func(const char *name, size_t len, php_func handler, php_func *stash)
{
zend_function *func;
if ((func = zend_hash_str_find_ptr(CG(function_table), name, strlen(name))) != NULL) {
if (stash) {
*stash = func->internal_function.handler;
}
func->internal_function.handler = handler;
}
}

php_override_func就是拿来hook函数的,同时,还可以根据需要,把原有的实现保留下来,用作它用

step3

创建一个假函数,并在模块初始化时调用

1
2
3
4
5
6
7
8
9
PHP_FUNCTION(fake_phpinfo)
{
ZEND_PARSE_PARAMETERS_NONE();
RETURN_STRING("Success");
}
PHP_MINIT_FUNCTION(fake_phpinfo)
{
php_override_func("phpinfo", sizeof("phpinfo"), PHP_FN(fake_phpinfo), NULL TSRMLS_CC);
}

step 4

编译成.so

1
2
3
4
phpize
./configure
make
make install

code

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/* fake_phpinfo extension for PHP */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "php.h"
#include "ext/standard/info.h"
#include "php_fake_phpinfo.h"

/* For compatibility with older PHP versions */
#ifndef ZEND_PARSE_PARAMETERS_NONE
#define ZEND_PARSE_PARAMETERS_NONE() \
ZEND_PARSE_PARAMETERS_START(0, 0) \
ZEND_PARSE_PARAMETERS_END()
#endif

typedef void (*php_func)(INTERNAL_FUNCTION_PARAMETERS);
static void php_override_func(const char *name, size_t len, php_func handler, php_func *stash)
{
zend_function *func;
if ((func = zend_hash_str_find_ptr(CG(function_table), name, strlen(name))) != NULL) {
if (stash) {
*stash = func->internal_function.handler;
}
func->internal_function.handler = handler;
}
}
PHP_FUNCTION(fake_phpinfo)
{
ZEND_PARSE_PARAMETERS_NONE();
php_printf("success");
}
PHP_MINIT_FUNCTION(fake_phpinfo)
{
php_override_func("phpinfo", sizeof("phpinfo"), PHP_FN(fake_phpinfo), NULL TSRMLS_CC);
}

ZEND_BEGIN_ARG_INFO(arginfo_fake_phpinfo, 0)
ZEND_END_ARG_INFO()

static const zend_function_entry fake_phpinfo_functions[] = {
PHP_FE(fake_phpinfo, arginfo_fake_phpinfo)
PHP_FE_END
};
/* }}} */

/* {{{ fake_phpinfo_module_entry
*/
zend_module_entry fake_phpinfo_module_entry = {
STANDARD_MODULE_HEADER,
"fake_phpinfo", /* Extension name */
fake_phpinfo_functions, /* zend_function_entry */
PHP_MINIT(fake_phpinfo), /* PHP_MINIT - Module initialization */
NULL, /* PHP_MSHUTDOWN - Module shutdown */
NULL, /* PHP_RINIT - Request initialization */
NULL, /* PHP_RSHUTDOWN - Request shutdown */
NULL, /* PHP_MINFO - Module info */
PHP_FAKE_PHPINFO_VERSION, /* Version */
STANDARD_MODULE_PROPERTIES
};
/* }}} */

#ifdef COMPILE_DL_FAKE_PHPINFO
# ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
# endif
ZEND_GET_MODULE(fake_phpinfo)
#endif

结果

参考

https://www.ichenfu.com/2015/03/01/override-php-function/