平台开发 360云计算php
女主宣言html
今天小编为你们分享一篇关于PHP代码加密的文章,若是你们对代码加密有需求,欢迎学习交流。但愿能对你们有所帮助。linux
PS:丰富的一线技术、多元化的表现形式,尽在“360云计算”,点关注哦!git
在咱们开发的项目中,有一部分多是用于商业用途,会部署在客户提供的机器环境中。由于PHP自己是解释型语言,因此未进行处理的代码,就会有泄露或被修改的风险。那么咱们可能会想到最简单有效的方法就是进行加密混淆,而后配合一系列的校验,来保护咱们的代码。那么本篇文章中,咱们就采用了开源的PHP加密扩展 screw-plus 进行相关实践分析。github
目前市场上有多种加密方案,但基本都是收费的。咱们本次实践采用了开源的方案。screw-plus 是一款开源php加密运行扩展,基于screw二次开发,暂时只能运行在linux下。虽然有些不足,可是若是增强一下,也是能够知足一些场景。vim
在介绍实现以前,咱们先简单介绍下 PHP 扩展的相关知识以及 Hook 机制。安全
1php7
PHP扩展周期app
一款扩展的主要生命周期,就是上面的四个宏定义。ide
MINIT:扩展模块初始化时执行的动做
MSHUTDOWN:扩展模块结束时执行的动做
RINIT:请求初始化时的动做
RSHUTDOWN:请求结束时的动做
对应于 zend_module_entry 结构中的四个函数指针:
int (*module_startup_func)(INIT_FUNC_ARGS); /* MINIT() */
int (*module_shutdown_func)(SHUTDOWN_FUNC_ARGS); /* MSHUTDOWN() */
int (*request_startup_func)(INIT_FUNC_ARGS); /* RINIT() */
int (*request_shutdown_func)(SHUTDOWN_FUNC_ARGS); /* RSHUTDOWN() */
咱们在实现一款扩展时,基本都会用到上面四个周期宏定义。完整的PHP周期见下图:
本篇文章中咱们只是介绍下跟该扩展相关的信息,若是想深刻了解,能够参考底部连接,自行深刻学习。
2
PHP扩展钩子
在PHP内核中,各个执行周期几乎都有提供可重写的钩子,以下表所示。
// AST, Zend/zend_ast.h:
void (*zend_ast_process_t)(zend_ast *ast)
// Compiler, Zend/zend_compile.h:
zend_op_array *(*zend_compile_file)(zend_file_handle *file_handle, int type)
zend_op_array *(*zend_compile_string)(zval *source_string, char *filename)
// Executor, Zend/zend_execute.h:
void (*zend_execute_ex)(zend_execute_data *execute_data)
void (*zend_execute_internal)(zend_execute_data *execute_data, zval *return_value)
// GC, Zend/zend_gc.h:
int (*gc_collect_cycles)(void)
// TSRM, TSRM/TSRM.h:
void (*tsrm_thread_begin_func_t)(THREAD_T thread_id)
void (*tsrm_thread_end_func_t)(THREAD_T thread_id)
// Error, Zend/zend.h:
void (*zend_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args)
/ Exceptions, Zend/zend_exceptions.h:
void (*zend_throw_exception_hook)(zval *ex)
// Lifetime, Zend/zend.h:
void (*zend_on_timeout)(int seconds)
void (*zend_interrupt_function)(zend_execute_data *execute_data)
void (*zend_ticks_function)(int ticks)
咱们能够经过对函数指针进行重写,来在周期中执行咱们本身的代码。
本次咱们只关心 Compiler 中的 zend_compile_file 函数,该函数的功能就是用来编译php文件,或者咱们指定的输入文件。
3
screw-plus扩展
项目文件以下:
.
├── aes.c
├── aes_crypt.c
├── aes.h
├── b64.h
├── config.h
├── config.m4
├── decode.c
├── encode.c
├── include
├── LICENSE
├── Makefile.in
├── md5.c
├── md5.h
├── php_screw_plus.c
├── php_screw_plus.h
├── README.en
├── README.jp
├── README.md
└── tools
├── Makefile
└── screw.c
在 php_screw_plus.h 中第一行有定义宏 CAKEY,这个就是咱们用来混淆代码的秘钥。
#define CAKEY "FwWpZKxH7twCAG4JQMO"
tools 文件夹中,是用来加密PHP文件的脚本,咱们直接执行 make,就会生成 screw 文件,而后用 screw 来加密PHP文件。tools/screw.c 中也是引入了上面的头文件,使用了 CAKEY 这个宏,用来加密代码。
4
流程分析
文件操做流程分析
经过 tools/screw.c 文件咱们能够发现,在 main 函数中,主要是对文件或文件夹进行遍历递归,使用固定的加解密步骤执行加密或者解密操做。
screw_encrype 为加密函数,screw_decrypt 为解密函数。
加密函数首先是对上面定义的 CAKEY 宏的值进行 md5 运算,获得32位的md5值,而后将前16存入 enTag 中,后面直接写入文件的前16个字节。接着会获取文件的长度,而且写入16~32的字节位置中。后续的内容以32位的md5值为秘钥,以16字节长度为小段,采用 cbc 方式进行加密,而后返回加密以后的长度,根据长度把加密以后的内容写入到文件中。
解密为对应的逆操做,在此就不详细介绍了。
5
扩展分析
php_screw_plus.c 文件就是扩展的主要实现入口。咱们能够看到上面提到的 Compiler 时期的钩子函数定义。
ZEND_API zend_op_array *(*org_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
如下是 screw-plus 扩展模块初始及结束的代码,就是将系统的编译函数暂存,而后用本身定义的编译函数替换为系统编译函数。再结束时再还原回去。
PHP_MINIT_FUNCTION(php_screw_plus)
{
CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
org_compile_file = zend_compile_file;
zend_compile_file = pm9screw_compile_file;
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(php_screw_plus)
{
CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
zend_compile_file = org_compile_file;
return SUCCESS;
}
能够看到,在编译PHP文件的时候,是会用到自定义的 pm9screw_compile_file 函数来解析。
咱们能够看到该函数中就是作了一些校验,而后使用自定义的名字为 pm9screw_ext_fopen 的 handler 对文件进行打开操做,以后又将打开的文件转交回了系统的原始解析器。
从上面 handler 的实现可知,就是使用 CAKEY 对加密的文件内容进行了解密,而后使用 tmpfile() 建立了临时文件,把内容保存进去。
6
思路梳理
设定 CAKEY 秘钥
对CAKEY 取 md5 值,而且做为 aes 的 key。将 md5 值的前0~15字节,写入文件开始位置,获取文件长度,将长度写入前16~32字节。
以16字节为一段进行 cbc 方式加密,使用上面的 key 做为秘钥。
将加密以后的内容追加写入文件完成静态加密。
扩展在初始化的时候,使用自定义的 compile 函数;
在解析时,检测若是文件前16字节内容与md5值的前16字节相同,则使用解密函数对文件内容进行解密,以后写入临时文件中,返回给系统去执行。
以上就是整个加密解密过程。
7
优缺点
加解密简单,并且相对于上面收费的产品,该项目是开源免费。
安全木桶原理,最短板就是 CAKEY 若是泄露了,就能够解密出来。
用 vim 打开 .so 文件,而后使用 :%!xxd 使用hex方式显示,咱们就能够看到明文的秘钥 CAKEY。
或者咱们使用 gdb 对php进行跟踪调试,由于上面调用了md5函数,对 md5 进行设断点,能够直接追踪到秘钥。
8
思考及改进
一、若是咱们是对生成的 .so 文件进行一些加壳压缩混淆,好比加上 upx 壳,就会增长追踪难度。
二、能够基于该项目,进行一些改进,好比对秘钥值进行稍微复杂一些的合并计算,不让秘钥仅为一个字符串。再配合混淆,也是能够适用于部分场景。
总结
以上就是本次分享的内容~
若是有什么改进建议,也能够在咱们评论区留言,供你们参考学习。
GitHub参考:https://github.com/del-xiong/screw-plus
PHP生命周期及Hooks:http://www.phpinternalsbook.com/php7/extensions_design/php_lifecycle.html