修改Zend引擎实现PHP源码加密的原理及实践

时间:2021-05-25

一、基本原理
  考虑截获PHP读取源文件的接口。一开始,我考虑从Apache和PHP之间的接口处处理,参见apache的src/modules/php4/mod_php4.c(这个是PHP用static方式编译进apache,makeinstall后的文件),在send_php()函数中截获文件指针,采用临时文件的方式,解密后替换文件指针。这种方法经过测试实践,证明是可行的。但是,必须使用两次文件操作,效率低下,而且对于DSO方式不可采用。双缘敬老院
  由此,重新考虑截获PHP读取文件并装载至缓存的过程,经过费力的寻找,发现在Zend引擎中zend-scanner.c是做此处理的。开始对此文件修改。照明工程

二、实现方法示意

  采用libmcrypt作为加密模块,现在采用的是DES方法ECB模式加密,

下面是文件加密的源代码:

C++代码



#defineMCRYPT_BACKWARDS_COMPATIBLE1
#definePHP_CACHESIZE8192
#include<mcrypt.h>
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>


main(intargc,char**argv)
{

inttd,i,j,inputfilesize,filelength;
charfilename[255];
charpassword[12];
FILE*ifp;
intreadfd;
char*key;
void*block_buffer;
void*file_buffer;
intkeysize;
intdecode=0;
intrealbufsize=0;
structstat*filestat;


if(argc==3){
strcpy(password,argv[1]);
strcpy(filename,argv[2]);
}elseif(argc==4&&!strcmp(argv[1],"-d")){
strcpy(password,argv[2]);
strcpy(filename,argv[3]);
decode=1;
printf("Enteringdecodemode...n");
}else{
printf("Usage:encryptphp[-d]passwordfilenamen");
exit(1);
}


keysize=mcrypt_get_key_size(DES);
key=calloc(1,mcrypt_get_key_size(DES));

gen_key_sha1(key,NULL,0,keysize,password,strlen(password));
td=init_mcrypt_ecb(DES,key,keysize);

if((readfd=open(filename,O_RDONLY,S_IRUSR|S_IWUSR|S_IRGRP))==-1){
printf("FATAL:Can'topenfiletoread");
exit(3);
}

filestat=malloc(sizeof(stat));

fstat(readfd,filestat);
inputfilesize=filestat->st_size;
printf("filesizeis%dn",inputfilesize);
filelength=inputfilesize;

inputfilesize=((int)(floor(inputfilesize/PHP_CACHESIZE))+1)*PHP_CACHESIZE;

if((file_buffer=malloc(inputfilesize))==NULL){
printf("FATAL:can'tmallocfilebuffer.n");
exit(2);
}
if((block_buffer=malloc(PHP_CACHESIZE))==NULL){
printf("FATAL:can'tmallocencryptblockbuffer.n");
exit(2);
}

j=0;
while(realbufsize=read(readfd,block_buffer,PHP_CACHESIZE)){
printf(".");
if(!decode){
if(realbufsize<PHP_CACHESIZE){
for(i=realbufsize;i<PHP_CACHESIZE;i++){
((char*)block_buffer)[i]='';
}
}
mcrypt_ecb(td,block_buffer,PHP_CACHESIZE);
}else{
mdecrypt_ecb(td,block_buffer,realbufsize);
}
memcpy(file_buffer+j*PHP_CACHESIZE,block_buffer,PHP_CACHESIZE);
j++;
}

close(readfd);

if((ifp=fopen(filename,"wb"))==NULL){
printf("FATAL:fileaccesserror.n");
exit(3);
}
fwrite(file_buffer,inputfilesize,1,ifp);

free(block_buffer);
free(file_buffer);
free(filestat);
fclose(ifp);
printf("n");

return0;

}

因为ECB模式是块长度确定的块加密,这里填充了一些空字符。国际展览

  然后,修改php代码中Zend/zend-scanner.c如下:

(我的php版本是4.01pl2,SUNsparc/solaris2.7,gcc2.95;)

文件前加入:

#defineMCRYPT_BACKWARDS_COMPATIBLE1
#include<mcrypt.h>

  然后,注释掉大约3510行前后的YY_INPUT的定义。

  然后,修改大约5150行前后的yy_get_next_buffer()函数:
函数头加上定义:
void*tempbuf;
char*key;
chardebugstr[255];
inttd,keysize;
intx,y;
FILE*fp;
然后,注释掉
YY_INPUT((&yy_current_buffer->yy_ch_buf[number_to_move]),
yy_n_chars,num_to_read);
这一句。
改为:

tempbuf=malloc(num_to_read);
if((yy_n_chars=fread(tempbuf,1,num_to_read,yyin))!=0){

#definepassword"PHPphp111222"
#definedebug0

keysize=mcrypt_get_key_size(DES);
key=calloc(1,mcrypt_get_key_size(DES));
gen_key_sha1(key,NULL,0,keysize,password,strlen(password));
td=init_mcrypt_ecb(DES,key,keysize);
mdecrypt_ecb(td,tempbuf,yy_n_chars);
memcpy((&yy_current_buffer->yy_ch_buf[number_to_move]),tempbuf,yy_n_chars);
if(debug){
fp=fopen("/tmp/logs","wb");
fwrite("nstartn",7,1,fp);
fwrite(tempbuf,1,yy_n_chars,fp);
fwrite("nenditn",7,1,fp);
fclose(fp);
}
}
free(tempbuf);

  然后,编译php,按正常方法安装即可,因为我对于libtool不太熟悉,因此我选择static方式,并在configure时加入了--with-mcrypt,这样我就不用自己手工修改Makefile电缆桥架

三、测试及结果

  编译php,apache后,用ecb.c编译出来的encryptphp加密了几个文件,分别为<1K,10K+,和40K+,在处理40K大小文件时出错,别的文件均正常。塑胶地板
  这是因为块的ECB加密方式决定了必须使用定长块,所以,请诸位同好指点采用何种流加密方式可以兼顾到zend每次读取8192字节的缓存处理方式。(其他平台上zend每次读取的块长度可能有所不同)

声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。

相关文章