QCon全球软件开发大会8折优惠倒计时,购票立减¥1760!了解详情 >>> 了解详情
写点什么

PHP 代码加密实践

2019 年 11 月 14 日

PHP代码加密实践

今天为大家分享一篇关于 PHP 代码加密的文章,如果大家对代码加密有需求,欢迎学习交流。希望能对大家有所帮助。


在我们开发的项目中,有一部分可能是用于商业用途,会部署在客户提供的机器环境中。因为 PHP 本身是解释型语言,所以未进行处理的代码,就会有泄露或被修改的风险。那么我们可能会想到最简单有效的方法就是进行加密混淆,然后配合一系列的校验,来保护我们的代码。那么本篇文章中,我们就采用了开源的 PHP 加密扩展 screw-plus 进行相关实践分析。


目前市场上有多种加密方案,但基本都是收费的。我们本次实践采用了开源的方案。screw-plus 是一款开源 php 加密运行扩展,基于 screw 二次开发,暂时只能运行在 linux 下。虽然有些不足,但是如果加强一下,也是可以满足一些场景。



在介绍实现之前,我们先简单介绍下 PHP 扩展的相关知识以及 Hook 机制。


1 PHP 扩展周期


一款扩展的主要生命周期,就是上面的四个宏定义。


  • 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 思路梳理

1.设定 CAKEY 秘钥


2.对 CAKEY 取 md5 值,并且作为 aes 的 key。将 md5 值的前 0~15 字节,写入文件开始位置,获取文件长度,将长度写入前 16~32 字节。


3.以 16 字节为一段进行 cbc 方式加密,使用上面的 key 作为秘钥。


4.将加密之后的内容追加写入文件完成静态加密。


5.扩展在初始化的时候,使用自定义的 compile 函数;


6.在解析时,检测如果文件前 16 字节内容与 md5 值的前 16 字节相同,则使用解密函数对文件内容进行解密,之后写入临时文件中,返回给系统去执行。


以上就是整个加密解密过程。


7 优缺点

1.加解密简单,而且相对于上面收费的产品,该项目是开源免费。


2.安全木桶原理,最短板就是 CAKEY 如果泄露了,就可以解密出来。


用 vim 打开 .so 文件,然后使用 :%!xxd 使用 hex 方式显示,我们就可以看到明文的秘钥 CAKEY。



或者我们使用 gdb 对 php 进行跟踪调试,因为上面调用了 md5 函数,对 md5 进行设断点,可以直接追踪到秘钥。



8 思考及改进

1、如果我们是对生成的 .so 文件进行一些加壳压缩混淆,比如加上 upx 壳,就会增加追踪难度。


2、可以基于该项目,进行一些改进,比如对秘钥值进行稍微复杂一些的合并计算,不让秘钥仅为一个字符串。再配合混淆,也是可以适用于部分场景。


总结

以上就是本次分享的内容~


如果有什么改进建议,也可以在我们评论区留言,供大家参考学习。


本文转载自公众号 360 云计算(ID:hulktalk)。


原文链接:


https://mp.weixin.qq.com/s/mVTAbm-qSSuDE7JWk0hMow


2019 年 11 月 14 日 15:301086

评论

发布
暂无评论
发现更多内容

螺旋矩阵算法,臭代码解析,微服务架构 Service Mesh 服务网格 RPC 协议实现原理 Dubbo 通讯协议,John 易筋 ARTS 打卡 Week 13

John(易筋)

ARTS 打卡计划

16张图入门Nginx——(前端够用,运维入门)

执鸢者

nginx 运维 前端

python必备知识总结

我是程序员小贱

SpringBoot系列(八):SpringBoot 中的事务处理

xcbeyond

Java 微服务 事务 springboot

Bash 脚本的单元测试

柴锋

bash Linux DevOps Unit Test Shell

JDK中居然也有反模式接口常量

看山

Java 源码阅读

[python基础]3 python数据类型下篇(不得不看的字典,列表大总结)

我是程序员小贱

ARTS 07 - 使用 supervisor 配置 ngrok 内网穿透为守护进程

jerry.mei

算法 练习 ARTS 打卡计划 ARTS活动 内网穿透

ARTS Week12

时之虫

ARTS 打卡计划 arts

目前数字人民币试点仍是“4+1” 别误读了

CECBC区块链专委会

数字货币 央行 人民币

全面了解CGI、FastCGI、PHP-FPM

书旅

CGI PHP-FPM Fast-CGI

[python基础]2 python数据类型上篇

我是程序员小贱

SpringBoot系列(七):SpringBoot 中使用Redis缓存

xcbeyond

Java redis 微服务 springboot

蓝绿部署、金丝雀发布(灰度发布)、AB测试

看山

微服务 持续集成

这样看mybatis,谁都会分析源码!

诸葛小猿

源码 mybatis mybatis源码

这些年看过的Linux相关书籍推荐

我是程序员小贱

spark学习之IDEA配置spark并wordcount提交集群

我是程序员小贱

SICP,我的函数式编程启蒙书

Kurtis Moxley

读书 函数式编程

年轻的樵夫哟,你掉的是这个免费 8 核 4G 公网服务器,还是这个随时可用的 Docker 实验平台?

newbe36524

Docker 微服务 微服务架构 .net core ASP.NET Core

一次由默认参数引起的思考

Lart

编程 思考

正则表达式位置匹配——匹配两个特殊符号中间的内容

jerry.mei

Java 正则表达式 前端 字符串匹配

Nginx之反向代理

xcbeyond

nginx 反向代理 代理

IT人为什么难以拿高薪?

看山

成长 随笔杂谈 薪资 心灵鸡汤

ARTS打卡 第12周

引花眠

微服务 ARTS 打卡计划

区块链技术--公证人机制

CECBC区块链专委会

区块链 数字货币 公证人

结算场景下的跳坑记

Lin

Java统一异常处理(配置文件集中化定义)

xcbeyond

Java 架构 后端 统一异常

异常处理的那些事儿

松花皮蛋me

Java 设计模式

准时下班的秘密:集成 GitLab && JIRA 实现自动化工作流

Phoenix

团队协作 研发效能

如何选择:Bootstrap Or Layui

引花眠

bootstrap layui

全面剖析PHP-FPM+Nginx通信原理

书旅

nginx 正向代理与反向代理 PHP-FPM

移动应用开发的下一站

移动应用开发的下一站

PHP代码加密实践-InfoQ