日期:2014-05-17  浏览次数:20687 次

windows下用c写php扩展(转)

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


首先用hello world试手一下。
下载php源码包,ext目录就是扩展目录了,里面有2个重要的文件是ext_skel以及ext_skel_win32.php。
下载cygwin,有了这个就可以方便的在windows下创建php扩展了。
下载中……
下载完后用php ext_skel_win32.php --extname=hello来编译生成我们的扩展开发目录hello,然后开始写测试程序hello world。在hello.c文件里添加函数定义以及函数注册语句:
函数注册语句:

const zend_function_entry hello_functions[] = {
 PHP_FE(confirm_hello_compiled, NULL)  
    PHP_FE(sayHello,NULL)//这句是我们手动添加的
 {NULL, NULL, NULL} 
};

?

函数定义:

PHP_FUNCTION(sayHello){
 php_printf("Hello C extension");
}

?

好像说是一定要以PHP_FUNCTION出现的宏形式,因为如果直接裸写c代码可能会发生命名冲突或是其他的冲突。然后在php_hello.h里面添加函数声明语句:
PHP_FUNCTION(sayHello);
写好测试程序,编译结果出现:
../main/config.w32.h': No such file or directory
网上查了一下,好像是要下载额外的包。。。麻烦啊!
http://www.php.net/extra/bindlib_w32.zip
http://www.php.net/extra/win32build.zip
将这两个包放在一起,我把它们放在win32/build目录下,然后执行php源码包根目录下的buildconf.bat(最好在命令行运行,不然显示结果会一闪而过)。
然后把bison.exe(在刚下载的包里面)所在的目录设置为环境变量,再运行configure.bat。完后就生成/main/config.w32.h这个文件了。
然后再次编译刚才hello项目结果出现一大推错误。
形如:
..\..\main\streams/php_stream_transport.h(85) : error C2143: syntax error : missing ')' before '*'
..\..\main\streams/php_stream_transport.h(85) : error C2081: 'socklen_t' : name in formal parameter list illegal

网上说是由于找不到宏定义才会这样,那应该是socklen_t这个宏没定义了,但是它具体的宏定义应该是怎么样的呢,总不能随便写一个吧,所以打开
\main\streams/php_stream_transport.h
发现应该是个类型别名,而且是个int ,因为有socklen_t addrlen; addrlen 按字面应该是存储长度的值。
所以在这个文件中添加
typedef int socklen_t;
并保存,再编译刚才的项目,错误少了很多,但是还有7个,经检查发现是输入了中文符号。改正再编译。。。还有一个错误:
LINK : fatal error LNK1181: cannot open input file "php5ts.lib"
于是将寻找php5ts.lib这个文件并将它放到项目目录下或是VC6 lib文件默认搜索目录下也可以。找啊找啊找。。。
tmd,用windows搜索找了好久都没找到。百度是说在php二进制代码包里面。所以先下个同版本的二进制代码包(应该就是平时写php所必须下载的那个包吧)
这里先说下环境吧windows+vc6+php5.3.5(二进制代码包和源码包)+cygwin
下载完毕,找到,复制 ,编译,成功!
但是出现了一个很严重的问题,dll文件没出来,,,我哭!创建出来的是php_hello.exp和php_hello.lib。怎么会是静态的呢??
其实已经生成了,只是不在本目录下的Release_TS目录下,而是在ext上级的Release_TS目录下。
然后测试。哈哈,说是未定义函数,有没可能是测试的php版本和我扩展的php版本不一样的关系呢?
测试了一下也不是,只要在ini里一设置加载php_hello.dll重启apache就会出现内存不能读的错误。
总觉得代码没有问题,应该还是前面配置编译的时候有问题。
经过一番又一番的测试,发现时php二进制包下错了我下了vc9的应该下vc6的。

接下来要开始写加密和解密了。加密解密算法本身不是这里的重点,重点是如何在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脚本的功能。
-------------
所以我们应该要在这个四个步骤之前将文件解密。
想法是重写一个函数 myCompile 判断在文件被compile之前先将它解密,然后再调用默认的complie函数。定义好 myCompile 后,应该在请求初始化的时候将 myCompile 传递给函数指针zend_compile_file

PHP_MINIT_FUNCTION(dencrypt){
 old_comlie_file = zend_complie_file;//保留默认的compile,以便等一下调用
 zend_complie_file = myCompile;
 return SUCCESS;
}
ZEND_API zend_op_array *myCompile(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的结构体定义语句也就知道了)。但是