Zend Engine (ZE) 執行腳本經歷兩階段:
zend_compile_file() 將 PHP 源碼編譯為 Opcode 數組 (zend_op_array)zend_execute() 執行 Opcode默認每次請求都重新編譯,無緩存機制。
ZEND_API zend_op_array *(*zend_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
ZEND_API void (*zend_execute)(zend_op_array *op_array TSRMLS_DC);
通過 PHP 擴展在模塊初始化 (MINIT) 階段替換這兩個函數指針:
PHP_MINIT_FUNCTION(sample) {
old_compile_file = zend_compile_file; // 保存原始指針
old_execute = zend_execute;
zend_compile_file = my_compile_file; // 替換為自定義實現
zend_execute = my_execute;
return SUCCESS;
}
my_compile_filestatic zend_op_array *my_compile_file(zend_file_handle *file_handle, int type TSRMLS_DC) {
// 判斷是否為預編譯文件(約定後綴 .ze)
if (strstr(file_handle->filename, ".ze") != NULL) {
// 直接讀取並反序列化 Opcode 返回
return load_precompiled_opcode(file_handle->filename);
}
// 否則調用原始編譯器
zend_op_array *op_array = old_compile_file(file_handle, type TSRMLS_CC);
// 攔截編譯結果,序列化保存為 .ze 文件
if (op_array) {
save_opcode_to_file(op_array, file_handle->filename . ".ze");
}
return op_array;
}
my_execute(可選)static void my_execute(zend_op_array *op_array TSRMLS_DC) {
// 可在此注入:性能分析、權限檢查、代碼混淆還原等
old_execute(op_array TSRMLS_CC);
}
.ze Opcode 文件.ze 文件加密(AES/自定義算法).ze + 解密擴展.ze → 反序列化 Opcode → zend_execute| 難點 | 解決方向 |
|---|---|
| Opcode 序列化/反序列化 | 需處理指針、資源、閉包等複雜結構 |
| 跨 PHP 版本兼容 | Opcode 格式版本強綁定,需針對特定版本構建 |
| 動態特性支持 | eval()、include 變量、運行時定義類/函數 |
| 調試體驗 | 行號映射、變量檢查、錯誤堆棧還原 |
| 性能開銷 | 解密延遲需極小,不可成為新瓶頸 |
警示:源碼加密屬於「事後補救」,真正的安全應建立在架構設計、權限控制、審計日志等體系之上。加密僅提高逆向成本,絕非銀彈。