windows下用c写php扩展(加密解密php源代码)(2)

接下来要开始写加密和解密了。加密解密算法本身不是这里的重点,重点是如何在zend层用zend本身的接口结合c来编程,在zend编译源文件之前将文件解密(当然文件之前要是有加过密的)。为了使用的方便。我的想法是像php_screw一样生成dll的同时,生成一个加密的可执行文件,这加密的可执行文件由我们手动执行,传入目录参数,能够对该目录下的所有文件进行加密。
在网上找了一堆资料后,再看看php_screw的代码,还有有些吃力,所以决定根据自己的思路来写。当然有些地方时会借鉴php_screw的代码
首先是先写一个对文件进行解密的函数。这个函数利用我们现成的解密算法对文件内容进行解密。
这个函数应该有一个参数用来接收当前请求文件的句柄(貌似是zend_compile_file这个东西,百度之,确认一下,几篇文章说是函数指针,我看了下源代码确实是函数指针)extern ZEND_API zend_op_array *(*zend_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
那么要如何得到当前请求文件的文件指针呢?或许和zend_compile_file函数指针所指函数的作用有关。在源代码中发现了zend_compile_file = compile_file;该函数的定义在zend_language_scanner.c,不过要看懂该函数有点难度,网上的说法是:
-------------
zend_compile_file负责将要执行的脚本文件编译成由ZE的基本指令序列构成的op codes 。
PHP执行这段代码会经过如下4个步骤:
1. Scanning (Lexing) ,将PHP代码转换为语言片段(Tokens)
2. Parsing , 将Tokens转换成简单而有意义的表达式
3. Compilation , 将表达式编译成Opocdes
4. Execution , 顺次执行Opcodes,每次一条,从而实现PHP脚本的功能。
-------------
所以我们应该要在这个四个步骤之前将文件解密。
想法是重写一个函数a判断在文件被compile之前先将它解密,然后再调用默认的complie函数。定义好函数a后,应该在请求初始化的时候将函数a传递给函数指针zend_compile_file
PHP_MINIT_FUNCTION(dencrypt){
 old_comlie_file = zend_complie_file;//保留默认的compile,以便等一下调用
 zend_complie_file = 函数a;
 return SUCCESS;
}
ZEND_API zend_op_array *a(zend_file_handle *file_handle,int type TSRMLS_DC){//这里的TSRMLS_DC是一个宏类似于,...(宏的定义暂时找不到)总之是跟多线程环境下全局变量的线程安全有关系的,以后再深究
 解密代码。。。
 old_comlie_file(file_handle);
 ....
}
但是问题还是没有解决,因为我们还是不知道如何获取到文件指针。我看php_screw里面的解密步骤挺长的,参考之。。。找到了fp = fopen(file_handle->filename, "r");原来file_handle里面有文件名的信息(其实如果找到file_handle的结构体定义语句也就知道了)。
但是php_screw里面还有这么一段
char fname[32];

 memset(fname, 0, sizeof fname);
 if (zend_is_executing(TSRMLS_C)) {//TSRMLS_C获得全局变量
  if (get_active_function_name(TSRMLS_C)) {//获取当前调用函数的名字(当前调用函数?哪里的函数?php函数?想想当然是zend里面的函数。而不是php层的函数,因为现在都还没编译,更没执行呢)
   strncpy(fname, get_active_function_name(TSRMLS_C), sizeof fname - 2);
  }
 }
 if (fname[0]) {
  if ( strcasecmp(fname, "show_source") == 0//也就是说如果当前是这两个函数则不解密也不编译了。恩,看样子没错。
    || strcasecmp(fname, "highlight_file") == 0) {
   return NULL;
  }
 所以这段还是必要的,不然遇到以上两个函数就没法实现了,compile_file函数里面应该也有这步才对。所以这里主要是不要让它被解密,而是直接显示密文。
还有这么一段
 fp = fopen(file_handle->filename, "r");
 if (!fp) {//如果打开失败则直接调用默认compile函数
  return org_compile_file(file_handle, type);
 }

 fread(buf, PM9SCREW_LEN, 1, fp);//一下5句的作用是:如果发现时未加密的文件则不进行解密。
 if (memcmp(buf, PM9SCREW, PM9SCREW_LEN) != 0) {
  fclose(fp);
  return org_compile_file(file_handle, type);
 }

 if (file_handle->type == ZEND_HANDLE_FP) fclose(file_handle->handle.fp);//判断文件句柄类型,应用相应的关闭函数。
 if (file_handle->type == ZEND_HANDLE_FD) close(file_handle->handle.fd);
 
 file_handle->handle.fp = pm9screw_ext_fopen(fp);//调用解密文件的函数,并用file_handle里面的fp接收函数返回结果
 file_handle->type = ZEND_HANDLE_FP;//将句柄类型设置为文件指针类型
 file_handle->opened_path = expand_filepath(file_handle->filename, NULL TSRMLS_CC);//这里有点不懂,猜想是不是接受当前文件的路径呢?一路追踪找到了expand_filepath_ex这个函数这个函数的最后一句是return real_path;看样子应该就是返回当前要编译文件的路径吧。但是为什么要有上面那两步呢?就是设置类型和路径这两部。如果我们不做解密操作就不用这两步,我的猜想是因为fclose(file_handle->handle.fp)这里改变了file_handle的状态,所以才需要重新设置吧!
//(但是有一个疑问是,这样做是Compilation之前解密,还是在Scanning之前解密?我初步认为从语义上来讲是在Compilation之前解密,但是从实际情况来看应该是要在Scanning之前解密,这边后期可以验证,事实证明后者是对的,因为compile_file函数里有对open_file_for_scanning函数的调用,也就是说在compile_file函数里执行了前面所说的123步)
入口点有了(还有4分之3的问题需要解决)。
接下来开始写解密文件的函数
需要考虑的问题有,解密文件后的明文代码,并不需要写入文件,那么如何取得这些明文的文件指针呢?用临时文件来做?但是如果每个请求都用不同的临时文件那会生成很多临时文件(显然不可行),如果对同一个php文件的不同请求只用一个临时文件,也会出现资源等待的问题(显然也不可行)。能不能直接在内存中指定一个文件指针呢?先看看php_screw的做法,调用tmpfile()产生临时文件,但是在程序退出时便会自动删除!其实我认为的在内存中的文件也是这样实现的。
直接下来把之前用php写的可逆加解密算法用c来实现,可是c并没有现成了md5和base64函数,看来只能抛弃这2个了。
写好后就开始编译了,遇到了一些错误也都改过来了。
然后链接的时候出现了错误:error LNK2001: unresolved external symbol _zend_compile_file
搜索了一下,貌似是因为函数编译方式的不一样所导致的找不到函数加入
BEGIN_EXTERN_C()
ZEND_API zend_op_array *(*zend_compile_file)(zend_file_handle *file_handle, int type TSRMLS_DC);
END_EXTERN_C()
即可
生成dll文件成功,但是不知道如何生成用于加密的可执行文件,当然最简单的解决方法就是另外建一个项目。看来只好先这样了,因为对vc的一些编译机制还有参数设置也不是很熟悉(以后再研究程序员的自我修炼好像有讲这个的)
之后又遇到c的指针传递问题(很久没弄,一些基础的都忘了,最后用二级指针来解决指针作为参数回传的问题)
又遇到了链接时找不到_zend_compile_file符号的问题(跟zend_api宏有关dllimport\dllexport)
然后又遇到读写文件的问题,其

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值