现在常见的对PHP代码进行加密的方式主要分为两大类:

Ø

不需要加载php扩展的;

Ø

需要加载php扩展的

第一种方式使用方便,不需要对php服务器进行配置,或加载其他模块,因此可以方便地部署在租用空间的服务器上。对文件进行加密也很简单,只需要把php文件上传到加密网站,就可以生成加密之后的文件,替换原来的php文件之后就可以正常运行了。

第二种方式使用起来相对复杂一些, php服务器上需要加载额外的模块。对于租用空间,没有管理员权限的用户来说,这是一个问题。

无论哪种方式,要分析它的加解密过程,我们都需要简单了解一下php文件的执行过程。

下面的分析都是基于PHP5.4.15的代码,运行在Apache2.4之上。

1、

PHP的执行过程

简单来说,PHP文件大致按照下图的流程进行执行:

图1

php文件作为参数传入Zend\zend.c文件中第1283行的zend_execute_scripts函数,这个函数会首先调用Zend\zend_language_scanner.c文件中第720行的compile_string函数,把文件的内容编译成一个zend_op_array对象,这个对象记录了php文件编译之后生成的中间代码。然后,zend_op_array会被作为参数传入Zend\zend_vm_execute.h文件中第342行的execute函数进行执行。

需要说明的几点:

l

zend_execute_scripts中实际调用的是zend_compile_string函数,而不是compile_string函数。是在Zend\zend.c文件中第640行的zend_startup函数中进行初始化时,把compile_string的函数指针赋值给了zend_compile_string。

l

同样的,zend_execute_scripts实际调用的是zend_execute,是在zend_startup中,把execute的函数指针赋值给了zend_execute。

l

execute函数按照zend_op_array进行执行,各个op实际对应的处理函数(handler)是在Zend\zend_vm_execute.h的第36481行的zend_init_opcodes_handlers函数中初始化变量zend_opcode_handlers时设定的。

2、

不需要加载php扩展的加解密方式的分析

通过分析,我们得出以下显而易见的一些结论:

l

因为没有加载php扩展,因此加密之后的文件的执行过程仍然完全遵循图1中的流程。因此,加密之后的文件本身应该是一个完全符合php语法要求的,可以被php编译器正确解析,并可以被php vm正确执行的文件。

l

那么,解密的过程只能是在这个加密之后的文件的执行过程中进行的,也就是说,这个加密之后的php文件是一个可以自解密的php文件,这个文件中包含了所有解密过程中需要用到的信息。因此,拥有了这个加密之后的文件,其实也同时拥有了解密所需的所有信息:解密的方法、和解密过程中所需的数据。

l

无论解密过程如何,最后一定是解密出原来的明文内容,然后进行执行的,否则就不符合图1的执行流程了。

现在,我们可以思考如何进行解密了:

l

一种方式,模拟执行这个加密的php文件,因为这个文件是自解密的,因此,最后一定可以得到明文。

这种方式是最容易想到的,但比较难于执行,并且这个文件中可能包含多个执行陷阱,扰乱模拟执行的过程。

l

另一种方式,直接在PHP环境中真实地执行这个加密的php文件,在php的内部得到解密之后的明文。

在php中,把一段文本作为代码进行执行,我知道的有以下几种方式:

u

把文本保存到另一个文件,include这个文件进行。

u

使用eval函数,直接执行这段文本。

参考这个eval的处理函数,Zend\zend_vm_execute.h中2529行的ZEND_INCLUDE_OR_EVAL_SPEC_CONST_HANDLER函数,和Zend\zend_execute_API.c中第1156行的zend_eval_string1函数。

u

使用包含执行功能的其它函数。

比如,preg_replace函数,在pattern中指定e(PREG_REPLACE_EVAL)作为参数,就会先执行参数中的文本,然后执行替换(这个参数在php5.5将不再支持)。参考preg_replace函数的处理函数,ext\pcre\php-pcre.c中第1003行的php_pcre_replace_impl函数,和第897行preg_do_eval函数。

为了看起来更安全,很多进行加密的都不会选择include方式,而会选择后两种。其实三种方式的安全性是一样的,没有任何差别。因为无论哪种方式,都遵循图1中的执行流程。

那么,我们在图1中的Zend\zend_language_scanner.c中的compile_string函数中增加一个输出,把所有需要编译文本输出出来,其中一定有解密之后的明文。

一些问题:

l

为什么不使用include/require?

因为使用include方式,需要生成一个包含明文的文件,可能加密厂商认为这样不安全,怕别人找到这个文件,所以大部分不使用这种方式。

l

为什么不拆分明文?

我几乎没有看到先拆分明文,再分别进行加密的。具体原因不太清楚,我猜测的原因是,php不是一个强制格式化的语言,因此php文件内的格式是很复杂的,如果简单的按照文本分割,很容易导致其中的某一部分无法正确运行。

如果按照语句来分割,首先必须对文件进行词法、语法的分析,这个比较复杂和麻烦,并且还可能需要重新组织文件结构(比如一个很大的函数,怎么分割),有点得不偿失,所以加密厂商没有先拆分。

l

为什么无法混淆明文中的变量名、函数名?

与拆分明文那个问题相似,要对原始明文中的变量名、函数名进行混淆,就必须对文件进行词法、语法的分析,在此基础上还要进行语义分析,建立变量表和函数表,这个复杂度更高。

另外,如果是跨几个文件的变量,或者被其它文件中调用的函数,要混淆变量名、函数名,就必须和其它文件一起分析,一起加密。这样既增加复杂度,也使加密的使用上更加麻烦,不能像现在这样,一个一个文件单独加密了。

l

为什么加密之后的文本是乱码?

如果使用文本编辑器打开加密之后的文件,会看到一些文本字符,而同时还有大量的无法正常显示的乱码,这是为什么呢?这些乱码主要包括两部分:

u

故意使用的在纯英文和中文环境下无法正确显示字符,比如,一些常量的名字,变量的名字等。

PHP在解析一个文件时,是完全按照单字节码读取的,不会去分析双字节码,而我们的文本编辑器则会按照双字节码进行分析和显示。比如,如下用16进制表示的字符串:

27 B9 33 36 8C 27

这个对于PHP来说,就是一个用单引号(0x27)括起来的字符串。而B9和8C如果按照双字节码分析,应该和后边的另一个双字节码组成一个字符,而它们的后边那个字节并不是一个双字节码,因此无法正确显示出来。尽管无法显示,但对于PHP来说,就是一个普通的字符串。

u

加密之后的内容不是可以显示的字符。

l

为什么看不到解密过程中的用到的函数名?

为了掩盖运行中自我解密的过程,大部分加密厂商都通过某种方式对解密过程中需要用得函数名进行了编码或加密,有一些只是简单的使用base64进行了编码,有些则使用了复杂些的算法。

从解密的角度看,复杂度没有变化。

3、

需要加载PHP扩展的加解密

这种方式的具体实现机制差别很大,因为PHP扩展给了解密很大的自由度,也就给了加密很大的可能性。但简单划分的话,也可以分为两类:

l

使用Zend\zend_language_scanner.c中的compile_string函数进行编译php文件的;

l

和不使用这个函数进行编译的

对于仍然使用Zend\zend_language_scanner.c中的compile_string函数进行编译的,基本分析方法与不需要加载php扩展的加解密方式相似,就不多说了。

对于不使用compile_string这个函数的这种方式,我们以Zend Guard为例来分析加解密的机制。

准确来说,Zend

Guard并没有对PHP代码进行加密,它只是对php代码进行了编译、优化和混淆。它的加密过程执行了以下操作:

a)

首先对php明文文件进行编译,生成zend_op_array对象。这一步操作类似于Zend\zend_language_scanner.c中的compile_string函数的功能。

b)

对代码进行优化。

c)

因为进行了编译,生成了变量表和函数表,因此,可以简单的对变量名、函数名进行混淆。

d)

把处理之后的zend_op_array对象序列化到一个文件,这就是加密之后的文件。

需要说明的是,如果多个文件之间有函数调用、公共变量,这些文件必须一起用Zend Guard进行加密。

在执行的时候,需要把Zend

Loader作为php扩展进行加载。这些加密之后的文件的执行流程与图1中的流程基本一致,唯一的差别就是,Zend\zend.c中的zend_execute_scripts调用的不再是Zend\zend_language_scanner.c中的compile_string函数,而是Zend Loader中的一个函数。这个函数从加密之后的文件中反序列化zend_op_array,然后返回这个zend_op_array给zend_execute_scripts,继续下边的执行。

因此,解密Zend

Guard,其实就是按照zend_op_array生成代码的过程。这个过程很复杂吗?不太确定。不同的中间代码设计思路不同,由中间代码生成源代码的难度差别也很大。如果我们把机器码也作为中间代码来看待,由机器码生成源代码难度最大;Java、.NET的中间代码相对来说接近机器码,但比机器码更加接近逻辑结构,因此难度相对较小,但仍有较大难度。

Php的中间码我没有仔细分析,无法评估难度大小。有兴趣的可以参考Zend\zend_compile.h中255行的_zend_op_array结构,以及相关的其它结构体。

4、

PHP代码的编译

要进行PHP代码的解密,掌握PHP代码的编译方法是必须的,我是在Windows7下进行的编译,参考的是中介绍的步骤。

因为我是用XAMPP

1.8.1搭建的环境,这个里边的PHP加载了所有模块,所以自己编译的时候,也必须加载所有模块,否则或者xampp运行不起来,或者就需要自己修改配制文件。我使用的build步骤如下:

windows sdk 6.1

shell

setenv /x86 /xp

/release

cd c:\php-sdk\

bin\phpsdk_setvars.bat

bin\phpsdk_buildtree.bat

php5.4.15

cd

C:\php-sdk\php5.4.15\vc9\x86\php-5.4.15-src

buildconf

configure

--enable-pdo

nmake

这样生成的是thread-safety的build。如果要加载Zend Loader,必须使用non-thread-safety的build,所以configure改成如下:

configure --enable-pdo

--disable-zts

5、

遇到的一些问题

l

在apache2.4上无法加载Zend Loader6.0

这是一个比较绕的问题,Zend

Loader要求PHP必须是non-thread-safety的,而apache2.4 module必须是thread-safety才能编译。因此,如果要加载Zend Loader,PHP就不能以apache module模式运行,而只能是CGI模式。

这个模式的切换是在C:\xampp\apache\conf\extra\httpd-xampp.conf文件中配制的。打开这个文件,注释掉类似于LoadModule php5_module的这一行,以及上下相关几行。把PHP-CGI setup里边的那几行的注释去掉就可以了。

php为什么容易解密,PHP代码的加密和解密相关推荐

  1. php cookie 加密解密,php 使用base64加密、解密cookie的示例

    这篇文章主要为大家详细介绍了php 使用base64加密.解密cookie的示例,具有一定的参考价值,可以用来参考一下. 感兴趣的小伙伴,下面一起跟随512笔记的小编罗X来看看吧. 经测试代码如下: ...

  2. php signature解密,openssl RSA非对称加密、解密、签名、验签

    需要先了解的openssl系列函数 openssl_pkey_get_private 从证书中解析获取私钥,以供使用.成功,返回真实的密钥资源标识符(Resource ID),否则返回false op ...

  3. python 加密解密_Python中的加密和解密

    1.base64 Python内置的base64模块可以实现base64.base32.base16.base85.urlsafe_base64的编码解码,python 3.x通常输入输出都是二进制形 ...

  4. oracle实现aes解密_Oracle的AES加密与解密用法

    Oracle的AES加密与解密用法 2013年12月11日 11:50:35 iteye_751 阅读数:428 --加密字符串 create or replace function des3_enc ...

  5. python实现md5加密和解密_Python中的加密和解密

    1.base64 Python内置的base64模块可以实现base64.base32.base16.base85.urlsafe_base64的编码解码,python 3.x通常输入输出都是二进制形 ...

  6. ROT13加密与解密(一种加密和解密都会得到同样答案的密文)“替换式密码算法”

    一. ROT13简介: ROT13(回转13位)是一种简易的替换式密码算法.它是一种在英文网络论坛用作隐藏八卦.妙句.谜题解答以及某些脏话的工具,目的是逃过版主或管理员的匆匆一瞥.ROT13 也是过去 ...

  7. android sqlite解密,SQlite数据库的加密与解密

    一 关于SQlite Android系统自带的SQlite是明文存储,不支持加密 二 SQlite加密方式 内容加密 主要写入读取数据时候做加密与解密的动作 缺点: 表结构暴露 无法直接搜索 数据库文 ...

  8. 怎么把计算机磁盘解密,Win10系统下怎样对磁盘进行加密、解密?Win10系统加密、解密磁盘图文教程...

    一些安装了Windows10系统的用户,为了更好地保护自己的个人隐私,会想要对电脑磁盘进行加密.那么,这该如何操作呢?下面小编就介绍下Windows10系统给磁盘加密.解密的具体方法. 具体方法如下: ...

  9. autojs加解密,文本/字符串的加密和解密脚本源码分享

    说明 本文提供的代码仅供参考.不建议用于生产环境. 可能有些地方在最新版本的Auto.js上面需要做修改,才能运行. Auto.js简介 Auto.js是利用安卓系统的"辅助功能" ...

最新文章

  1. BP神经网络的Java实现(转载)
  2. 802.11n协议帧格式详解
  3. Php 链式执行,PHP实现链式操作的三种方法详解
  4. 【渝粤教育】国家开放大学2018年秋季 0299-21T中国古代文学(1) 参考试题
  5. BUAA_OO_第二单元作业总结
  6. Gradle配置国内源
  7. 机器学习-西瓜书、南瓜书第三章
  8. Java第十二次作业:继承与抽象类解决工人与学生的问题,抽象类实例。抽象类作用——为多态创造了可能。抽象类的作用总结...
  9. uni-app 基于 Promise 的 request 请求封装
  10. 为什么c语言运行后输入的中文会乱码
  11. 最多站长使用的DNS服务商
  12. html水晶按钮图片,css 如何实现一个水晶按钮的效果呢?
  13. Handler sync barrier(同步屏障)
  14. Python的基本数据类型与运算符号
  15. 制作坦克大战,坦克移动代码
  16. 【Prometheus】Prometheus 远端存储
  17. 达内学习的java类库01
  18. 比前途,还是嵌入式开发比软件开发更胜一筹
  19. 计算机网络思科平台第五章测验答案
  20. Redmi Note 10 root权限 刷机教程,红米note10 root 线刷救砖包

热门文章

  1. 贪心算法之加勒比海盗问题
  2. Linux网络和进程管理
  3. zabbix 监控基础
  4. 科聊——即时通信软件原型设计
  5. 爬虫爬当当网书籍信息
  6. Oracle - 查询语句 - 多表关联查询
  7. Crystal Report制作使用
  8. HTML5新特性之跨文档消息传输
  9. 基本排序算法比较与选择
  10. 字符串加密解密函数 (C#) (转)