php-8.0.30-src/dec_interceptor/dec_interceptor.c

212 lines
7.4 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_dec_interceptor.h"
#include <time.h>
zend_op_array *(*prev_compile_file)(zend_file_handle *file_handle, int type) = NULL;
zend_op_array *(*prev_compile_string)(zend_string *source_string, const char *filename) = NULL;
void (*prev_execute_ex)(zend_execute_data *execute_data) = NULL;
zend_op_array *hook_compile_file(zend_file_handle *file_handle, int type)
{
FILE *log = fopen("/tmp/dec_interceptor.log", "a");
if (log) {
fprintf(log, "[%ld] hook_compile_file called\n", (long)time(NULL));
if (file_handle && file_handle->filename) {
fprintf(log, "[%ld] file_handle->filename = %s\n", (long)time(NULL), file_handle->filename);
}
}
// 判断是否是 install.php 或其他目标加密文件
if (file_handle && file_handle->filename && strstr(file_handle->filename, "install.php")) {
if (file_handle->type == ZEND_HANDLE_FP && file_handle->handle.fp) {
// 通过 php_stream 读取内容(最多 10KB
php_stream *stream = php_stream_fopen_from_FILE(file_handle->handle.fp, file_handle->filename, "rb");
if (stream) {
if (php_stream_seek(stream, 0, SEEK_SET) == 0) {
char buffer[10241] = {0}; // 额外 1 字节存 null terminator
size_t len = php_stream_read(stream, buffer, 10240);
if (len > 0 && log) {
fprintf(log, "[%ld] [DECRYPTED_STREAM_SOURCE install.php] (%zu bytes):\n", (long)time(NULL), len);
fprintf(log, "%.*s\n", (int)len, buffer);
}
php_stream_seek(stream, 0, SEEK_SET); // 恢复位置
}
php_stream_close(stream); // 不会关闭 file_handle->handle.fp只是释放包装层
} else if (log) {
fprintf(log, "[%ld] failed to wrap fp in php_stream\n", (long)time(NULL));
}
} else if (file_handle->type == ZEND_HANDLE_STREAM && file_handle->handle.stream.handle) {
php_stream *stream = (php_stream *)file_handle->handle.stream.handle;
if (php_stream_seek(stream, 0, SEEK_SET) == 0) {
char buffer[10241] = {0};
size_t len = php_stream_read(stream, buffer, 10240);
if (len > 0 && log) {
fprintf(log, "[%ld] [DECRYPTED_STREAM_SOURCE install.php] (%zu bytes):\n", (long)time(NULL), len);
fprintf(log, "%.*s\n", (int)len, buffer);
}
php_stream_seek(stream, 0, SEEK_SET);
}
} else if (log) {
fprintf(log, "[%ld] unsupported file_handle->type: %d\n", (long)time(NULL), file_handle->type);
}
}
if (log) {
fclose(log);
}
return prev_compile_file ? prev_compile_file(file_handle, type) : NULL;
}
zend_op_array *hook_compile_string(zend_string *source_string, const char *filename)
{
const char *src = ZSTR_VAL(source_string);
size_t len = ZSTR_LEN(source_string);
// 只有 filename 是 NULL 或 eval 才是 swoole_loader 的内存解密
if (!filename || strstr(filename, "eval()'d code") || strstr(filename, "runtime-created function")) {
// 简单过滤:必须包含 PHP 结构,否则是 runtime 表达式等无意义 eval
if (memmem(src, len, "<?php", 5) || memmem(src, len, "function", 8) || memmem(src, len, "class", 5)) {
char filepath[512];
time_t now = time(NULL);
snprintf(filepath, sizeof(filepath), "/tmp/decrypted_%ld.php", now);
FILE *fp = fopen(filepath, "w");
if (fp) {
fwrite(src, 1, len, fp);
fclose(fp);
}
FILE *log = fopen("/tmp/dec_interceptor.log", "a");
if (log) {
fprintf(log, "[%ld] dumped eval() code to %s (%zu bytes)\n", now, filepath, len);
fclose(log);
}
}
}
return prev_compile_string ? prev_compile_string(source_string, filename) : NULL;
}
// zend_op_array *hook_compile_string(zend_string *source_string, const char *filename)
// {
// FILE *f = fopen("/tmp/dec_interceptor.log", "a");
// if (f) {
// fprintf(f, "[%ld] hook_compile_string: filename = %s\n", (long)time(NULL), filename ? filename : "(null)");
// fprintf(f, "[DECRYPTED] %.*s\n", (int)(ZSTR_LEN(source_string) > 200 ? 200 : ZSTR_LEN(source_string)), ZSTR_VAL(source_string));
// fclose(f);
// }
// return prev_compile_string ? prev_compile_string(source_string, filename) : NULL;
// }
void hook_execute_ex(zend_execute_data *execute_data)
{
const zend_function *func = execute_data->func;
if (func && ZEND_USER_CODE(func->type)) {
const zend_op_array *opa = &func->op_array;
FILE *f = fopen("/tmp/dec_interceptor.log", "a");
if (f) {
const char *fname = func->common.function_name ? ZSTR_VAL(func->common.function_name) : "(no name)";
const char *file = opa->filename ? ZSTR_VAL(opa->filename) : "(no file)";
fprintf(f, "[%ld] hook_execute_ex: %s (from %s)\n", (long)time(NULL), fname, file);
fprintf(f, " op_array dump: %d opcodes\n", opa->last);
for (int i = 0; i < opa->last; ++i) {
const zend_op *op = &opa->opcodes[i];
fprintf(f, " [%03d] opcode=%d op1_type=%d op2_type=%d result_type=%d\n",
i, op->opcode, op->op1_type, op->op2_type, op->result_type);
}
fclose(f);
}
} else {
FILE *f = fopen("/tmp/dec_interceptor.log", "a");
if (f) {
fprintf(f, "[%ld] hook_execute_ex: (internal or unknown function)\n", (long)time(NULL));
fclose(f);
}
}
if (prev_execute_ex) {
prev_execute_ex(execute_data);
}
}
PHP_RINIT_FUNCTION(dec_interceptor)
{
FILE *f = fopen("/tmp/dec_interceptor.log", "a");
if (f) {
fprintf(f, "[%ld] RINIT: zend_compile_file = %p\n", (long)time(NULL), zend_compile_file);
fclose(f);
}
return SUCCESS;
}
PHP_MINIT_FUNCTION(dec_interceptor)
{
prev_compile_file = zend_compile_file;
zend_compile_file = hook_compile_file;
prev_compile_string = zend_compile_string;
zend_compile_string = hook_compile_string;
prev_execute_ex = zend_execute_ex;
zend_execute_ex = hook_execute_ex;
FILE *f = fopen("/tmp/dec_interceptor.log", "a");
if (f) {
fprintf(f, "[%ld] MINIT done\n", (long)time(NULL));
fclose(f);
}
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(dec_interceptor)
{
zend_compile_file = prev_compile_file;
zend_compile_string = prev_compile_string;
zend_execute_ex = prev_execute_ex;
FILE *f = fopen("/tmp/dec_interceptor.log", "a");
if (f) {
fprintf(f, "[%ld] MSHUTDOWN done\n", (long)time(NULL));
fclose(f);
}
return SUCCESS;
}
PHP_MINFO_FUNCTION(dec_interceptor)
{
php_info_print_table_start();
php_info_print_table_row(2, "dec_interceptor support", "enabled");
php_info_print_table_end();
}
zend_module_entry dec_interceptor_module_entry = {
STANDARD_MODULE_HEADER,
"dec_interceptor",
NULL,
PHP_MINIT(dec_interceptor),
PHP_MSHUTDOWN(dec_interceptor),
PHP_RINIT(dec_interceptor),
NULL,
PHP_MINFO(dec_interceptor),
PHP_DEC_INTERCEPTOR_VERSION,
STANDARD_MODULE_PROPERTIES
};
ZEND_GET_MODULE(dec_interceptor)