PHP语言作为脚本语言的一种,由于不需要进行编译,所以通常PHP程序的分发都是直接发布源代码。对于一些开源软件来说,这并没有什么问题,因为它本来就希望有更多的人阅读代码,希望有更多的人参与进来,而对于商业代码来说,这却是一个不太好的消息,不管是从商业秘密,还是从对公司产权的保护来说却是一个问题,基于此,从而引出了对PHP代码的加密和解密的议题。例如国内的Discuz论坛程序在开源之前要运行是必须安装Zend Optimizer的,Zend官方的代码加密软件是[Zend Guard](http://www.zend.com/en/products/guard/),可以用来加密和混淆PHP代码,这样分发出去的代码就可以避免直接分发源代码,不过加密后的代码是无法直接运行的,在运行时还需要一个解密的模块来运行加密后的程序,要运行Zend Guard加密后的代码需要安装Zend Optimizer(PHP5.2之前的版本),或者安装Zend Guard Loader(PHP5.3版本)扩展才能运行。
## 加密的本质[]()
本质上程序在运行时都是在执行机器码,而基于虚拟机的语言的加密通常也是加密到这个级别,也就是说PHP加密后的程序在执行之前都会解密成opcode来执行。
PHP在执行之前有一个编译的环节,编译的结果是opcode,然后由Zend虚拟机执行,从这里看如果只要将源代码加密,然后在执行之前将代码解密即可。
从这里看,只要代码能被解密为opcode,那么总有可能反编译出来源代码,其他的语言中也是类似,比如objdump程序能将二进制程序反汇编出来,.NET、Java的程序也是一样,都有一些反编译的程序,不过通常这些厂商同时还会附带代码混淆的工具,经过混淆的代码可读性极差,很多人都留意过Gmail等网站经过混淆的JS代码吧,他们阅读起来非常困难,经过混淆的代码即使反编译出来,读者也很难通过代码分析出代码中的逻辑,这样也就极大的增加了应用的安全性。
## 简单的代码加密解密实战[]()
根据前文的介绍,作为实例,本文将编写一个简单的代码加密扩展用于对PHP代码的加密,我们只需要能把源码加密,简单通过浏览源代码的方法无法获取到源代码那我们的目标就达到了,为了能正确执行加密后的代码,我们还需要另一个模块:解密模块。
简单的思路是把所有的PHP文件代码进行加密,同时另存为同名的PHP文件,这是一种很简单的做法,只是为了防止源代码赤裸裸的暴露在代码中。
加密也有很多种做法,第一种简单的方法可以简单的把源码本身进行一些可逆加密,这样我们可以在运行之前把真实的源码反解出来执行,不过这种方式存在一种问题,只要知道了加密算法我们就可以把代码给解出来,采用这种方式唯一能做的就是尽量增加加密的复杂度,既然正式的代码在运行之前会被转化成PHP源代码,通过hack的方式是可以完完整整的获得PHP源码的,保密的效果就很有限了。
因为Zend引擎最终执行的是opcode,那么我们只要保证能解密出opcode则能满足需求,我们只要简单的将opcode进行简单的序列化或者像Zend Guard那样进行混淆,在运行之前将opcode还原,那么源代码的信息就不存在了,这样我们就能保证源代码的安全,而不至于泄露。
### 加密[]()
前面提到加密的目的就是为了防止轻易获取程序源码的一种手段,对于PHP来说,将源码编译为opcode已经能达到目的了,因为PHP引擎最终都是需要执行opcode的。虽然可以将加密进一步,但是如果需要修改Zend引擎,那么成本就有点大了,因为需要修改Zend引擎了,而这是无法通过简单的扩展机制来实现了,所以解密的成本也会变的太大,也就没有实际意义了。
在本例中为了方便,代码的加密和解密实现均实现在同一个模块中。
熟悉PHP的同学可能会发现,这种加密方式和opcode缓存本质上没有太大差别,opcode缓存的工作是将源码编译为opcode然后缓存起来,在执行的时候绕过编译直接执行opcode,的确是没错的。这里唯一的区别是:opcode缓存是动态透明的,而加密后我们要做的是分发加密后的代码。这么说我们是不是可以直接将APC之类的缓存扩展进行改造就可以了,其实理论上是可以的。不过这两者的定位还是有差别的:加密的目的是为了减少源码被分析破解的可能,而缓存只是为了提高程序运行的速度。
### 解密[]()
本例中的代码其实并没有进行加密,相对源代码来说,opcode编译本身也可以算做一种加密了,因为毕竟通过阅读opcode来理解程序的逻辑还是比较困难的。
### 实现[]()
TODO
- 第一章 准备工作和背景知识
- 第一节 环境搭建
- 第二节 源码结构、阅读代码方法
- 第三节 常用代码
- 第四节 小结
- 第二章 用户代码的执行
- 第一节 生命周期和Zend引擎
- 第二节 SAPI概述
- Apache模块
- 嵌入式
- FastCGI
- 第三节 PHP脚本的执行
- 词法分析和语法分析
- opcode
- opcode处理函数查找
- 第四节 小结
- 第三章 变量及数据类型
- 第一节 变量的结构和类型
- 哈希表(HashTable)
- PHP的哈希表实现
- 链表简介
- 第二节 常量
- 第三节 预定义变量
- 第四节 静态变量
- 第五节 类型提示的实现
- 第六节 变量的生命周期
- 变量的赋值和销毁
- 变量的作用域
- global语句
- 第七节 数据类型转换
- 第八节 小结
- 第四章 函数的实现
- 第一节 函数的内部结构
- 函数的内部结构
- 函数间的转换
- 第二节 函数的定义,传参及返回值
- 函数的定义
- 函数的参数
- 函数的返回值
- 第三节 函数的调用和执行
- 第四节 匿名函数及闭包
- 第五节 小结
- 第五章 类和面向对象
- 第一节 类的结构和实现
- 第二节 类的成员变量及方法
- 第三节 访问控制的实现
- 第四节 类的继承,多态及抽象类
- 第五节 魔术方法,延迟绑定及静态成员
- 第六节 PHP保留类及特殊类
- 第七节 对象
- 第八节 命名空间
- 第九节 标准类
- 第十节 小结
- 第六章 内存管理
- 第一节 内存管理概述
- 第二节 PHP中的内存管理
- 第三节 内存使用:申请和销毁
- 第四节 垃圾回收
- 新的垃圾回收
- 第五节 内存管理中的缓存
- 第六节 写时复制(Copy On Write)
- 第七节 内存泄漏
- 第八节 小结
- 第七章 Zend虚拟机
- 第一节 Zend虚拟机概述
- 第二节 语法的实现
- 词法解析
- 语法分析
- 实现自己的语法
- 第三节 中间代码的执行
- 第四节 PHP代码的加密解密
- 第五节 小结
- 第八章 线程安全
- 第二节 线程,进程和并发
- 第三节 PHP中的线程安全
- 第九章 错误和异常处理
- 第十章 输出缓冲
- 第十六章 PHP语言特性的实现
- 第一节 循环语句
- foreach的实现
- 第二十章 怎么样系列(how to)
- 附录
- 附录A PHP及Zend API
- 附录B PHP的历史
- 附录C VLD扩展使用指南
- 附录D 怎样为PHP贡献
- 附录E phpt测试文件说明
- 附录F PHP5.4新功能升级解析
- 附录G:re2c中文手册