C を使用して Windows 上で PHP 拡張機能を作成する (転送)

WBOY
リリース: 2016-06-13 13:17:42
オリジナル
910 人が閲覧しました

c を使用して Windows で php 拡張機能を記述します (転送)

Windows で C に php 拡張機能を書き込みます (php ソース コードを暗号化および復号化します)


まずは hello world を使ってみます。
php ソース コード パッケージをダウンロードします。ext ディレクトリには、ext_skel と ext_skel_win32.php という 2 つの重要なファイルがあります。
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");
}
ログイン後にコピー

?

裸の C コードを直接記述すると、名前の競合などが発生する可能性があるため、PHP_FUNCTION のマクロ形式で指定する必要があるようです。次に、php_hello.h に関数宣言ステートメントを追加します:
PHP_FUNCTION(sayHello);
テスト プログラムを作成すると、コンパイル結果が表示されます:
../main/config.w32.h': No suchファイルまたはディレクトリ
オンラインで確認したところ、追加のパッケージをダウンロードする必要があるようです。 。 。トラブル!
http://www.php.net/extra/bindlib_w32.zip
http://www.php.net/extra/win32build.zip
これら 2 つのパッケージをまとめて、次のように配置します。 win32/build ディレクトリに移動し、PHP ソース パッケージのルート ディレクトリで buildconf.bat を実行します (コマンド ラインから実行するのが最善です。そうしないと、表示される結果が点滅します)。
次に、bison.exe (ダウンロードしたパッケージ内) が配置されているディレクトリを環境変数として設定し、configure.bat を実行します。完了すると、ファイル /main/config.w32.h が生成されます。
その後、Hello プロジェクトを再度コンパイルすると、大きなエラーが表示されます。
次のような形式:
....mainstreams/php_stream_transport.h(85) : エラー C2143: 構文エラー : '*' の前に ')' がありません
....mainstreams/php_stream_transport.h(85 ) : エラー C2081: 'socklen_t' : 仮パラメータ リストの名前が不正です

ネット上では、マクロ定義が見つからないために起こると言われていますが、socklen_t マクロが定義されていないはずですが、マクロ定義をそのまま書くことはできないので、開いてください。
mainstreams/php_stream_transport.h
これは型エイリアスである必要があり、socklen_t addrlen があるため int であることがわかりました。 addrlen は文字通りストレージの長さの値である必要があります。
それで、このファイルに
typedef int socklen_t;
を追加して保存します。その後、プロジェクトをコンパイルします。エラーはかなり減りましたが、まだ 7 つあります。確認したところ、中国語であることがわかりました。記号が入力されました。修正して再度コンパイルします。 。 。別のエラーがあります:
LINK: 致命的なエラー LNK1181: 入力ファイル "php5ts.lib" を開けません
そのため、ファイル php5ts.lib を検索し、それをプロジェクト ディレクトリまたは VC6 のデフォルトの検索ディレクトリに置きます。 lib ファイル ダウンしても大丈夫です。検索して検索してください。 。 。
tmd、Windowsで長い間探しましたが見つかりませんでした。 Baidu によると、これは 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 を再起動すると、メモリが読み取れないというエラーが発生します。
コードには何も問題はないといつも感じていますが、前の設定とコンパイルに問題があるはずです。
何度もテストした結果、vc6 ではなく vc9 をダウンロードする必要があった php バイナリ パッケージが間違ってダウンロードされたことがわかりました。

次のステップは、暗号化と復号化の作成を開始することです。ここでの焦点は、暗号化および復号化のアルゴリズム自体ではありません。zend 独自のインターフェイスを C と組み合わせて使用​​して、zend 層でプログラムする方法と、zend がソース ファイルをコンパイルする前にファイルを復号化する方法にあります (もちろん、ファイルは暗号化されている必要があります)。以前は暗号化されていました)。
使いやすさのため。私のアイデアは、php_screw のような dll を生成するときに、暗号化された実行可能ファイルを生成することです。この暗号化された実行可能ファイルは、ディレクトリ パラメータを渡すと、ディレクトリ内のすべてのファイルを暗号化できます。
ネットで色々調べた結果、php_screwのコードを見てみましたが、やはり少し難しかったので、自分なりの考えで書いてみることにしました。もちろん、所々 php_screw のコードから学びます
まず、ファイルを復号化する関数を作成します。この関数は、既存の復号化アルゴリズムを使用してファイルの内容を復号化します。
この関数には、現在リクエストされているファイルのハンドルを受け取るためのパラメータが必要です (zend_compile_file、Baidu のようです、確認してください。いくつかの記事ではこれが関数ポインタであると書かれていました。ソース コードを確認したところ、確かに関数です)ポインタ)

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的结构体定义语句也就知道了)。但是php_screw里面还有这么一段:

char fname[32];
memset(fname, 0, sizeof fname);
if (zend_is_executing(TSRMLS_C)) {
  //TSRMLS_C获得全局变量
  
  //获取当前调用函数的名字(当前调用函数?哪里的函数?php函数?想想当然是zend里面的函数。而不是php层的函数,因为现在都还没编译,更没执行呢)
  if (get_active_function_name(TSRMLS_C)) {
    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 でのポインターの受け渡しの問題に遭遇しました (長い間やっていなかったため、基本的なことをいくつか忘れていました。最後に、ポインターをパラメーターとして返す問題を解決するために 2 次ポインターを使用しました)。
リンク時に _zend_compile_file シンボルが見つからないという問題にも遭遇しました (これは zend_api マクロ dllimportdllexport に関連しています)
その後、ファイルの読み取りと書き込みの問題にも遭遇しました。ただし、暗号文を手動でファイルに書き込んでテストしました。ファイルを直接コピーしたり、元のファイルを直接操作すると、結果は正しくなりません。
ついにメモリが読み込めないというエラーに遭遇しました。 。 。
最終的に、ファイルの読み込み方法に問題があることが判明しました。テキストモードで読み書きすると、一部の特殊文字が処理されます。
...結局のところ、テスト機能は問題ありませんが、実際に適用したい場合は、改善する必要があります。たとえば、暗号化したときに、暗号化されたファイルを元のディレクトリ A に直接生成しました。管理を容易にするために、元の PHP ファイルをパッケージ化することをお勧めします。
その後、zf フレームワークに取り組み始めましたが、ほとんど忘れていましたが、まだ使用していないものがたくさんありました。

?

?

================================

元のアドレス:

http://blog.sina.com.cn/s/blog_4d06da1f0100pgmj.html

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート