一、基本原理
考虑截获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++代码
/ecb.c-------------------cuthere-----------/
/encryptforPHPsourcecodeversion0.99beta
weareusinglibmcrypttoencryptcodes,please
installitfirst.
compilecommandline:
gcc-O6-lmcrypt-lm-oencryptPHPecb.c
pleasesetLD_LIBRARY_PATHbeforeuse.
GNUcopyleft,designedbywangsu,miweicong/ #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];
FILEifp;
intreadfd;
charkey;
voidblock_buffer;
voidfile_buffer;
intkeysize;
intdecode=0;
intrealbufsize=0;
structstatfilestat;
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,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,PHP_CACHESIZE);
}else{
mdecrypt_ecb(td,realbufsize);
}
memcpy(file_buffer+jPHP_CACHESIZE,PHP_CACHESIZE);
j++;
} close(readfd); if((ifp=fopen(filename,"wb"))==NULL){
printf("FATAL:fileaccesserror.n");
exit(3);
}
fwrite(file_buffer,1,ifp); free(block_buffer);
free(file_buffer);
free(filestat);
fclose(ifp);
printf("n"); return0; }
/---endofecb.c------------------------------------/
因为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()函数:
函数头加上定义:
voidtempbuf;
charkey;
chardebugstr[255];
inttd,keysize;
intx,y;
FILEfp;
然后,注释掉
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,num_to_read,yyin))!=0){
/decode/
#definepassword"PHPPHP111222"
#definedebug0 keysize=mcrypt_get_key_size(DES);
key=calloc(1,mcrypt_get_key_size(DES));
gen_key_sha1(key,keysize);
mdecrypt_ecb(td,tempbuf,yy_n_chars);
memcpy((&yy_current_buffer->yy_ch_buf[number_to_move]),yy_n_chars);
if(debug){
fp=fopen("/tmp/logs","wb");
fwrite("nstartn",7,fp);
fwrite(tempbuf,yy_n_chars,fp);
fwrite("nenditn",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每次读取的块长度可能有所不同)