417 12
发新话题
打印

[Zend Other] dezend 实战 - ZEND反编译之路

本主题由 cfan 于 2008-3-17 10:39 设置高亮

dezend 实战 - ZEND反编译之路

Johnny 写的文章,因为没有整理,所以比较乱。

参考文章

1.现在的几家dezend系统都基于VLD制作
#tar -xzf vld-0.8.0.tgz
#mv vld-0.8.0 vld
#cd -R vld ../php-4.3.8/ext //复制vld目录到php的源码下的ext中
#cd php-4.3.8
#rm configure
#./buildconf
#./configure –with-mysql –with-apxs2=/usr/www/bin/apxs –enable-vld
#make
#make install
据说运行zend过的文件,查看源代码就可以看到了

2.Vulcan Logic Disassembler
http://derickrethans.nl/vld.php


The Vulcan Logic Disassembler hooks into the Zend Engine and dumps all the opcodes (execution units) of a script. It was written as as a beginning of an encoder, but I never got the time for that. It can be used to see what is going on in the Zend Engine.

It's not hard to use this extension, but it might not work with all PHP versions. Here are the instructions to get it to work:

1. Unpack the tarball: tar -xzf vld-0.8.0.tgz.
2. cd into the newly created directory.
3. Create the configure script: phpize
4. Now run "./configure" followed by "make install".

That's it, if you now run PHP from the command line and add the -dvld.active=1 parameter VLD will spit out the opcodes:

php -dvld.active=1 program.php

注意:
1)debian 下要安装php4-dev或者php5-dev才有phpize
2)PHP 5.1 有一个宏定义是取消了的,要将其注释掉, 网上有补丁
这里给出我修改的
复制内容到剪贴板
代码:
diff -u srm_oparray.c vld-0.8.0/srm_oparray.c
--- srm_oparray.c      2005-01-19 19:59:54.000000000 +0800
+++ vld-0.8.0/srm_oparray.c    2006-10-29 09:47:04.000000000 +0800
@@ -351,6 +351,7 @@
                        op->op2.op_type = VLD_IS_OPLINE;
                        break;

+#ifdef ZEND_JMP_NO_CTOR
                case ZEND_JMP_NO_CTOR:
                        flags = OP2_USED;
                        if (op->op1.op_type != IS_UNUSED) {
@@ -361,6 +362,7 @@
#endif
                        op->op2.op_type = VLD_IS_OPLINE;
                        break;
+#endif

#ifdef ZEND_ENGINE_2
                case ZEND_FETCH_CLASS:<!--code2-->
3.截获PHP读取文件并装载至缓存的过程
zend/zend_ini_scanner.c

4. xfocus.net 2006大会
先说一段题外话

目前国内掌握了dezend的似乎有

lkq 此人应该是国内最早完整掌握了该技术的人

http://www.qinvent.com/
===============================
airsupply/segfault.cn
此人是
https://0x557.org
成员,在安全焦点2006大会做了dezend的报告
他应该是受了 qinvent的启发,才开始着手做这个事情的
===============================
3 dot
http://3.999ye.com/

他也认为基于vld可以做出来
===============================


看看他们之间的一段争论
lkq (lkq@kali.com.cn)

稍微读一下vld的源代码,就可以知道它什么事也没干。无非是从ram中的一点点信息打印出来而已。充其量是一个极烂的debug程序而已。(只能打印最低版encoder编译的一小部分opcode而已)

就和在dos下用debug显示汇编指令一样,那跟反编译或源代码没有一点关系。



segfault (airsupply@segfault.cn) 2006-8-31

事实上 vld 甚至可以反汇编 Zend Guard 4.01 加密的 php,注意是"反汇编"。

当然需要稍稍修改几行代码。 要做到这一步花几个小时就够了。

需要的只是一个在这基础上的反编译引擎。 这个比较复杂,算是大量体力劳动。




lkq:

vld只是从内存中dump一小部分opcode的一小部分信息(变量名、数组、类成员等信息丢失),function和class中的opcode无法dump。对于3.x以上由于opcode是加密的,所以dump出来的opcode是完全错误的。

所以vld离反汇编器还差的远了,充其量只是一个baby级的dump工具。


segfault:

vld 可以完全 dump 出 op_array 中所有的 opcode,也包括 function 和 class 中的 opcode.
可能你用的版本老?

我说了,对 4.01 的 Zend Guard, vld 修改几行代码就能 dump 了。


lkq(2006-8-31 20:43)

呵呵,很多版本zend的function和class中的opcode无法dump。对于加密后的opcode也无法dump,各种变量名、函数名、类名也无法dump,zend_op中很多信息也无法dump。

对于一般情况,要打印出opcode其实非常简单,随便写几十行代码就可以。但各种版本各种选项以及各种php语法有数不清的情况,不经过特殊处理根本无法获得所需的全部op信息。得到了包含完整信息的op也离获得PHP源码差的远了。这就是为什么除了deZender,到目前还没有出现第2个真正可用的 PHP反编译器。


segfault(2006-9-1 09:45)

简单测了 Zend Encoder 3.01 和 Zend Guard 4.01 加密后 php, vld 都几乎能完整 dump 出所有 opcode. 除了对 3.01 的 DO_FCALL 识别不出来。
当然,没有做大量的测试。

所以,对写反编译器,vld 还是有很大价值。只需添加一行代码就能 dump Zend Encoder 3.01 和 Zend Guard 4.01加密的 opcode.
它提供了原始的思路,方法和代码基础。而不是像楼上一些人说的是“垃圾”或一无用处。

确实,对不同版本的 Encoder 必然在细节上要做不同的修正,这个工作比较琐碎。从 opcode 到 php source,工作量也比较浩大。



lkq:

对于要想获得PHP源码的人,vld就是垃圾。vld做的事就是从内存中dump少部分 opcode。对于少数版本的简单PHP文件可以dump出完整的opcode,但一个op除了opcode以外,还包含了很多信息,vld遗漏了许多这样的信息。而对于opcode加密的情况,绝非添加一行或几行代码就能实现解密的,何况不同版本的Zend加密算法有所不同,不对zend所采用的加密算法进行透彻的研究怎么可能破解他的加密算法呢。

我们知道,一个反汇编器,需要针对各种情况得出完整的汇编代码(对于zend可以理解为op),仅仅能得到一部分opcode(不是op)和一些简单的参数信息,并且对于很多情况连opcode也无法得出。这能叫反编译器吗?在开发人员看来,vld只是一个dump工具,并且是一个玩具级的dump工具。

vld做的事情是用几十行代码就能完成的事情,编写一个真正能用的PHP反编译器需要几万行代码并且需要对各种数不尽的特殊情况进行识别和大量的测试。vld做的事情不到整个PHP反编译器工作的1%。

对于普通的PHP程序员,不可能用vld得到一行PHP源码,花大量时间精力去学习如何安装vld和编译PHP源码是不值得的;
对于专业人士(精通C和PHP并立志要写一个PHP反编译器),正确的第1步是研究编译原理和Zend Engine。如此简单的vld根本无任何参考价值。相反对于准备写PHP反编译器的人来说有误入歧途之嫌。

对于介于上述2种人之间的,有大量时间并懂linux和会编译PHP源码的人,体验一下vld到底能干什么也无可厚非。



segfault:

对 3.01 和 4.01 Zend Encoder 加密过的 opcode (我手头只有这两个版本), 确实可以用添加同样的一行代码来解密。

写反编译器,Zend 引擎和编译原理当然需要熟悉。

另外,其实 vld 使用很简单,根本不需要编译 php 源码。




lkq:

对于解密的代码,可否麻烦把这行代码贴出来?不在透彻研究加密算法的基础上做解密,是不专业和不严谨的,并没有实际运用价值,只能作为业余玩玩。
写反编译器,要研究透Zend 引擎和编译原理,而不是简单的熟悉就可以了。而且这也只是写反编译器的准备工作而已。
对于绝大多数甚至连linux都没有使用过的PHP程序员,要成功安装使用vld并不是件容易的工作。一个成熟的软件,应该能让绝大多数没有相关专业知识的人方便使用。


另外,我所知道的vld是源码,需要编译到php代码中。可能你已经找到了一个全新的vld版本,可否在此共享你的安装方法和程序。以让广大非专业人士体验一下。


segfault:

我用的就是 vld 0.8.0,不需要编译到 php 中,作为一个 php 扩展单独编译。
参考: http://derickrethans.nl/vld.php

解密代码暂时不打算公开。我想看看我能在反编译方面走多远,纯粹作为技术挑战,呵呵。
如果将来觉得难度太大,或者没兴趣了,再公开所有成果。

我不知道 Dezender 是怎么做的。如果是独立读入加密过的 php,解压,再构造 opcode array,
然后再解密,那么这时候解密就比较复杂了,不是一行代码能完成的。我估计是这么做的。

还有其它的思路,可以完全绕过上面的解压,构造 opcode array,和解密三个过程。这就是
vld 提供的思路。显然能减少不少工作量,代码量和不同版本 Encoder 兼容性问题。

总之,使用 vld 只花了两个晚上就能完成 opcode 解密和 dump (事实上我到目前位置只做了
这么久,本周才开始对这方面感兴趣,对 php 也只了解一点点,zend 引擎完全没接触过)。
如果不用 vld, 才用上述独立读入解压解密方式做,我估计需要 1-2 周才行。所以,vld 有没有
价值很显然。



lkq:
有多少PHP程序员懂如何编译vld 0.8.0?

显然老兄对vld或zend加密编译过程了解的不是太多。我们之所以最终认为vld是垃圾,是基于我们前期对zend engine和vld深入研究基础之上得出的。对于一个想要写出有实用性的PHP反编译器的人,以vld的思路走下去是完全行不通的,举个例子:很多 Zend加密后的文件都有时间、IP或域名等限制,以PHP扩展库的方式根本无法解决对这些文件的反编译。当你着手编写反编译器的时候,有许多问题用 vld的思路是无法解决的,你会发现vld不但无用,更让人误入歧途,浪费时间。当你研究完Zend Engine后再回过头来看vld,你甚至还会发现vld太小儿科,并且存在一些严重的理解错误。如果你纯粹为了好玩或想挑战一下自己,浪费点时间倒也无妨。

deZender已经推出了近1年了,期间不断地进行完善。在这一年中,有不少人都想挑战自己编写一个类似的PHP反编译器,包括我们在MSN上也遇到过许多俄国和美国的个人和组织,也表达了他们的决心和要求我们提供一些支持。但到目前为止第2个真正能用的PHP反编译器还没有出来。可想要做一个PHP反编译器是技术和毅力上的双重考验。

另外,vld并没有解决加密PHP文件的加载读入问题,你只需要花半小时时间仔细读一下vld就知道它所干的那些事情只是将内存中的最容易得到的 opcode打印出来而已。如果你了解了Zend engine后,在Zend engine的适当位置加十几行代码就能实现同样的事情,并且还能比vld干的更好。



segfault:

不见得是浪费时间,毕竟过程中能学到不少东西。 比如 Zend Encoder 对加密后 php 文件格式的组织,使用自定义字典做
压缩,都很新颖。其它如 Zend 引擎生成和解释执行 opcode 数组的过程,对我的工作本身很有参考价值。

不过最大的挑战是反编译引擎的组织。没有一个合适的框架,那么多类 opcode 处理起来会一团糟。



lkq:

是的,任何经历都可以认为是学到了东西,哪怕走错了路也一样可以学到东西。但对于不是要挑战自己而是要用最经济最可行的方法得到PHP源码的人来说,花时间在vld上就是浪费时间。何况对于一个真正要写PHP反编译器的人来说,阅读zend engine比从vld上学到的东西要多得多,做技术的人都懂得:在前人的经验上做事情是非常重要的。以我们的经验,如果你真的要测试一下自己的能力,想写一个PHP反编译器证明一下,与其在vld上浪费时间,不如把精力花在研究zend engine上,你的收获会更多。从zend engine的代码中,你才能真正体会到专业严谨的思维方式和技术方法。希望你能坚持不懈达到自己的目标。



segfault:
在 vld 上只花了不到一晚上时间。其实主要时间花在跟踪 opcode 解密上了。vld 就那么一点代码,不需要花费多少时间。

个人并不喜欢 Zend 引擎代码。从软件工程观点看,它的代码不好。首先,注释很少;其次,宏使用太多。对其它人阅读和维护代码很不利。



lkq:

不知你读过其他开源项目的源码没有,Zend Engine在开源项目中在软件工程化方面算是不错了。而且代码逻辑清晰、命名规范、结构关联较少。大量使用宏定义正是为了增强代码的可读性和可维护性,这些都是软件开发工程化的必备因素,比起很多开源项目,甚至商业程序的工程化程度都高。




segfault:

类似 EG,EX 这样的宏命名能增加可读性?

打开一个文件,满眼都是 EG,EX 这样不知道什么东西,立刻就晕了



lkq2006-9-1 17:55)

之所以定义EG, EX宏,是为了针对各种不同情况使代码体现一致性,并简化代码,增强可读性。而不把它定义成函数,是为了提升zend engine的性能。至于在命名上为何和其他宏相比显得过于简单,我想可能是Zend考虑要和低版本源码保持一致,或其他一些原因。

Zend engine中的绝大部分变量、宏、函数、文件等的命名都是规范化的。我想我们不应该对任何软件太吹毛求疵。目前,任何软件在工程化方面远未达到完美的程度,比起很多开源项目和商业程序,Zend engine算是比较符合当今软件工程的要求的。一个多人参与的大项目,不走工程化道路是很难保证质量和进度要求的。

实际上软件可以说是工程和艺术的结合体,即需要集体的协作也少不了个人的创造。之所以当今世界如此突出软件工程的作用,那是因为以前软件开发过程太过艺术化、太讲究个人英雄主义的创造和发挥,我们不能矫枉过正。我想,也许软件开发永远也无法实现如同在生产线上按部就班的生产一辆汽车那样的高度工程化。软件同样离不开个人的创新和创造。

[ 本帖最后由 flyindance 于 2007-6-19 18:50 编辑 ]
一個偽裝成白癡的天纔!

TOP

另一段讨论 2005-10-21 12:02

1. 已经过期或者需要但是没有licence的能反么?
2. for 循环反编译之后得到什么效果?
3. list($a, list($b), $c) = $array; 支持么?
4. @func(); @$a = $b; func($@c, $a, $@d); 的@能反么?


1. 已经过期或者需要但是没有licence的能反么?
>过期的可以反,没有license的目前还不能

2. for 循环反编译之后得到什么效果?
>就是for,不会变成while或do。

3. list($a, list($b), $c) = $array; 支持么?
>完全支持。(这上面我们费了不少功夫)

4. @func(); @$a = $b; func($@c, $a, $@d); 的@能反么?
>完全支持@


1. 已经过期或者需要但是没有licence的能反么?
>过期的可以反,没有license的目前还不能
看来我也要找个过期的来测测才知道

2. for 循环反编译之后得到什么效果?
>就是for,不会变成while或do。
for (init; cond; incr) {
body
}
cond/incr 都不难, 其中init部分据我所知是不好判断的他是for之前还是for的init
比如 for (init; cond; incr) ..;
跟 init; for ( ; cond; incr) ..;
编译后效果一样, 不知道dezender是否手工解决

3. list($a, list($b), $c) = $array; 支持么?
>完全支持。(这上面我们费了不少功夫)
me too, 不过依赖强大的***, 一个晚上(好几个小时)就搞定了
做之前我甚至还不知道可以嵌套list()

4. @func(); @$a = $b; func($@c, $a, $@d); 的@能反么?
>完全支持@
是@$c $@d, 我打错了 keke
能支持不错

看过别人反c编译的理论, 流程图也是个难点(现在我也卡在这里)
做个范性的反编译很复杂
针对性的话判断相对简单, 但是编译器的优化器稍微改改可能就反不出, 加上混淆器就更难. 请问有何妙方?

我做做只是学习/研究, 不便透露的地方就算了, 如果不合适这里讨论 请挪一下帖子到反编译版块

你以上说的并不是反编译中最困难的地方,还有很多比list的困难得多的情况需要处理。list嵌套本身的处理并不难,list嵌套和数组元素以及while的组合也算一个较花时间的问题(还有一些特殊情况要考虑)。


对 不是难点 是花时间
我只剩下流程了 我认为是难点
(这个只字不轻松)
如果你认为难的是变量名/函数名的话 我也已经搞定了 而且测试通过 的确花了我好久时间找出数据在哪 因为工具不够强 自己写的脚本分析. 否则理论上几分钟就能搞定
and/or 我也知道怎么回事 但是目前还没搞下去 因为能想到的办法也不够完美 跟流程一样


流程处理应该是整个反编译引擎中最困难的地方,有很多特殊情况很可能会疏忽
一個偽裝成白癡的天纔!

TOP

事实证明 PHP Encode过后的代码执行速度比 源码运行慢一些

测试代码
复制内容到剪贴板
代码:
/*
  Google PageRank Checksum Algorithm (Toolbar V3.x/4.x) V1.3
  compatible with PHP 4.4.x/PHP 5.x, supported X86_64 CPU

  Author's webpage:
      http://pagerank.gamesaga.net/
  ChangeLog:
      2005-09-13  V0.1 first release
      2005-10-21  V1.1 fix a bug for the final character
      2006-09-21  V1.2 compatible with PHP 4.4/PHP 5.x
      2006-09-29  V1.3 X86_64 CPU supported
*/

error_reporting(E_ALL);

/*
* convert a string to a 32-bit integer
*/
function StrToNum($Str, $Check, $Magic)
{
    $Int32Unit = 4294967296;  // 2^32

    $length = strlen($Str);
    for ($i = 0; $i < $length; $i++) {
        $Check *= $Magic;  
        //If the float is beyond the boundaries of integer (usually +/- 2.15e+9 = 2^31),
        //  the result of converting to integer is undefined
        //  refer to http://www.php.net/manual/en/language.types.integer.php
        if ($Check >= $Int32Unit) {
            $Check = ($Check - $Int32Unit * (int) ($Check / $Int32Unit));
            //if the check less than -2^31
            $Check = ($Check < -2147483648) ? ($Check + $Int32Unit) : $Check;
        }
        $Check += ord($Str{$i});
    }
    return $Check;
}

/*
* Genearate a hash for a url
*/
function HashURL($String)
{
    $Check1 = StrToNum($String, 0x1505, 0x21);
    $Check2 = StrToNum($String, 0, 0x1003F);

    $Check1 >>= 2;  
    $Check1 = (($Check1 >> 4) & 0x3FFFFC0 ) | ($Check1 & 0x3F);
    $Check1 = (($Check1 >> 4) & 0x3FFC00 ) | ($Check1 & 0x3FF);
    $Check1 = (($Check1 >> 4) & 0x3C000 ) | ($Check1 & 0x3FFF);  
  
    $T1 = (((($Check1 & 0x3C0) << 4) | ($Check1 & 0x3C)) <<2 ) | ($Check2 & 0xF0F );
    $T2 = (((($Check1 & 0xFFFFC000) << 4) | ($Check1 & 0x3C00)) << 0xA) | ($Check2 & 0xF0F0000 );
  
    return ($T1 | $T2);
}

/*
* genearate a checksum for the hash string
*/
function CheckHash($Hashnum)
{
    $CheckByte = 0;
    $Flag = 0;

    $HashStr = sprintf('%u', $Hashnum);
    $length = strlen($HashStr);
  
    for ($i = $length - 1;  $i >= 0;  $i --) {
        $Re = $HashStr{$i};
        if (1 === ($Flag % 2)) {               
            $Re += $Re;   
            $Re = (int)($Re / 10) + ($Re % 10);
        }
        $CheckByte += $Re;
        $Flag ++;  
    }

    $CheckByte %= 10;
    if (0 !== $CheckByte) {
        $CheckByte = 10 - $CheckByte;
        if (1 === ($Flag % 2) ) {
            if (1 === ($CheckByte % 2)) {
                $CheckByte += 9;
            }
            $CheckByte >>= 1;
        }
    }

    return '7'.$CheckByte.$HashStr;
}



  $time_start = microtime(true);
  
  $urls = array(
                'http://www.upsdn.net/',
              'http://www.google.com/',
              'http://www.baidu.com/',
              'http://www.live.com/',
              'http://www.yahoo.com/',
              'http://www.sohu.com/',
              'http://www.kernel.org/',
              'http://www.gnu.org/',
              'http://www.debian.org/',
              'http://www.gliet.edu.cn/',
              'http://www.intel.com/',
              'http://www.ibm.com/',
              'http://www.apple.com/',
              'http://www.ti.com/',
              'http://www.upose.com/',
                'http://www.gov.cn/',
                'http://www.aol.com/',
                'http://www.ebay.com/',
                'http://www.python.org/',
                'http://www.php.net/'
                );
  
  foreach ($urls as $url) {
      echo CheckHash(HashURL($url))."\n";
    }
  
  
  $time_end = microtime(true);
  printf('Execute time: %.5f', $time_end - $time_start);
  
?><!--code2-->
Zend SafeGuard 3.1.0 e for Linux
编码参数
zendenc prs.php pre.php

运行pre.php得到的结果
Execute time: 0.02517
Execute time: 0.00347
Execute time: 0.00371
Execute time: 0.00375
Execute time: 0.00352
Execute time: 0.00413
Execute time: 0.00420
Execute time: 0.00398
Execute time: 0.00329
Execute time: 0.00353

最小 0.00 329 最大 0.02 517(第一次启动,很多东西需要载入)

比较未编码前的结果
Execute time: 0.00538
Execute time: 0.00329
Execute time: 0.00288
Execute time: 0.00293
Execute time: 0.00288
Execute time: 0.00287
Execute time: 0.00560
Execute time: 0.00359
Execute time: 0.00287
Execute time: 0.00303

最小 0.00 287 最大 0.00 560

什么 ZendOptimizer 能提高 40% 性能纯粹是胡扯


用Zend Guard 4.0测试的结果

Execute time: 0.01818
Execute time: 0.00663
Execute time: 0.00597
Execute time: 0.00841
Execute time: 0.00738
Execute time: 0.00605
Execute time: 0.00818
Execute time: 0.00571
Execute time: 0.00772
Execute time: 0.00710


zend guard 4.0没有license,没有被优化,结果更慢了
不知道优化后会怎么样
一個偽裝成白癡的天纔!

TOP

--no-header Do not emit the PHP-compatible header (encoded files
will not display a meaningful error message in case
the Zend Optimizer is not installed)


zend 3.1.0是这个样子的
复制内容到剪贴板
代码:
3222;
.......
exit(1);
?><!--code2-->
zend 3.6.0 php4是
复制内容到剪贴板
代码:
3272;
/*<!--code2-->
4.0 则是
复制内容到剪贴板
代码:
4132;
exit();
__halt_compiler();
一個偽裝成白癡的天纔!

TOP

得自己编译一个PHP

用最简单的参数
./configure --prefix=/home/gavin/php5 # --prefix=/usr
--with-config-file-path=/home/gavin/php5/etc
make
make install
cp -v php.ini-recommended /etc/php.ini
cp php.ini-dist /home/gavin/php5/etc/php.ini

默认是放到
cp php.ini-dist /usr/local/lib/php.ini

--with-config-file-path=/some/path

按照
airsupply/segfault提供的方法

1.将
srm_oparray.c
放到
zend 目录
cp vld-0.8.0/srm_oparray.* php-5.1.6/Zend/


2.修改zend.c

加上
#include "srm_oparray.h"
vld_dump_oparray(EG(active_op_array));

3.
然后修改 configure(不是configure.in,那个供autoconf用的吧)
加上
srm_oparray.c

zend.c一块编译

4.
修改srm_oparray.c
加宏定义
ZEND_JMP_NO_CTOR
如果是PHP 5.1以上的话

5.修改srm_oparray.h
//#include "php.h"
#include "zend_compile.h"
因为php.h 定义了一个zenderror宏,phperror却无实现

6.配置
./configure --prefix=/home/gavin/php5 --with-config-file-path=/home/gavin/php5/etc


6.make

成功了

make install
Installing PHP SAPI module: cgi
Installing PHP CGI into: /home/gavin/php5/bin/
Installing build environment: /home/gavin/php5/lib/php/build/
Installing header files: /home/gavin/php5/include/php/
Installing helper programs: /home/gavin/php5/bin/
program: phpize
program: php-config
Installing man pages: /home/gavin/php5/man/man1/
page: phpize.1
page: php-config.1
Installing PEAR environment: /home/gavin/php5/lib/php/


[PEAR] Archive_Tar - installed: 1.3.1
[PEAR] Console_Getopt - installed: 1.2
pear/PEAR can optionally use package "pear/XML_RPC" (version >= 1.4.0)
[PEAR] PEAR - installed: 1.4.9
Wrote PEAR system config file at: /home/gavin/php5/etc/pear.conf
You may want to add: /home/gavin/php5/lib/php to your php.ini include_path
Installing PDO headers: /home/gavin/php5/include/php/ext/pdo/

7. 配置文件

cp php.ini-recommended /home/gavin/php5/etc/php.ini

将这个加到最后
zend_extension=/opt/zend/ZendOptimizer.so

效果不是太好

换PHP4.3.11 Optimizer 2.5.7 再看看

build 和编译有关的目录。
ext 扩展库代码,例如 Mysql、zlib、iconv 等我们熟悉的扩展库。
main 主目录。
sapi 和各种服务器的接口调用,例如apache、IIS等,也包含一般的fastcgi、cgi等。
win32 和 Windows 下编译 PHP 有关的脚本。用了 WSH。
Zend 文件夹核心的引擎

PHP使用Lex和Yacc对语法进行解析。
在 Zend 目录下有两个文件 zend_language_parser.y 与 zend_language_scanner.l 他们是Yacc和Lex的脚本文件,通过这两个脚本文件生成对应的.c和.h文件,实际上这在 linux 下非常普遍,gcc 也使用它们产生语树。
一個偽裝成白癡的天纔!

TOP

GNU Auto Tools是上个世纪90年代开始发展起来的一系列辅助开发,打安装包的自动化工具.
各种工具分别开发,但是协同工作的很好.比如autoconf, automake, libtool等等.
但是,连开发者自己也承认,这套工具虽然好用,但是学习曲线很陡峭.
困难在于:一套新的思路和习惯,一套自定义的宏和规则,使用者的交流


1. hello.c
#include
int main()
{
printf("Hello World!\n");
return 0;
}

2. Makefile.am
bin_PROGRAMS = hello
hello_SOURCES = hello.c

3. configure.in
AC_INIT(hello.c)
AM_INIT_AUTOMAKE(hello, 0.1)
AC_PROG_CC
AC_OUTPUT(Makefile)

4. aclocal

5. autoconf

6. automake --add-missing --foreign

7. ./configure

8. make

makefile格式

三元组(Target, Dependency, Command)
Target : Dep1 Dep2
Cmd1
Cmd2
[Blank Line]
.s1 .s2 :
Cmd1
$(var), $<, $@

autoscan
configure.scan
configure.in
Makefile.am
Makefile.in
Makefile
automake
edit
aclocal.m4
aclocal
configure
autoconf

configure.in
是configure脚本的输入文件,为了解决在不同unix变种之间移植程序的问题:库名可能不同,应用程序名可能不同,结构和常量的定义可能不同……
configure脚本完成autoconf与automake的初始化工作,为不同的平台定义相应的宏,检测并指定适当的程序名,库名,结构和常量名等等,指定要为哪些目录输出Makefile文件.总之,为编译程序做好一切准备工作.

configure.in的八股文
基本初始化部分:包括AC_INIT (必须第一个出现), AM_INIT_AUTOMAKE(程序包名,版本号), AC_CONFIG_HEADER
可选宏:如AC_ARG_ENABLE
检测某些程序的存在性
检查程序用到的库

检查某些头文件是否存在.
检查Typedefs and structures.
检查Functions.
指定在哪些目录输出Makefile.

Makefile.am
一种比Makefile更高层次的规则.只指定要生成什么目标,它由什么源文件生成,要安装到什么目录.

可执行文件:
bin_PROGRAMS = foo
foo_SOURCES = foo1.c foo1.h foo2.c
foo_LDADD = foo3.o -lm foo4.a
foo_LDFLAGS = -L
foo_DEPENDENCIES =

对静态库:
lib_LIBRARIES = libfoo.a
foo_a_SOURCES =
foo_a_LDADD =
foo_a_LIBADD =
foo_a_LDFLAGS =
只在make时做静态连接用,不安装的库:
noinst_LIBRARIES = libfoo.a
……
对头文件:include_HEADERS = foo.h
对数据文件:data_DATA = data1 data2
全局变量(对所有目标都适用)
INCLUDES = -I/dir1 -I/dir2
LDFLAGS = -L/dir1 -L/dir2
LDADD = foo.o foo.a -lfoo
EXTRA_DIST = file1 file2 源程序和一些默认的文件自动打入.tar.gz包,其它文件若要进入.tar.gz包可以用这种办法,比如配置文件, 数据文件等等.
SUBDIRS = dir1 dir2 在处理本目录之前要递归处理哪些子目录
标准安装路径
$(prefix) = /usr/local 是所有安装目录的默认前缀,可以通过./configure --prefix=的方法覆盖.
其它的预定义目录如:bindir = $(prefix)/bin, libdir = $(prefix)/lib, datadir = $(prefix)/share, sysconfdir = $(prefix)/etc, …
想定义一个新的安装路径 比如config, 可定义
confdir = $(prefix)/config, 然后conf_DATA = file1 file2,则file1, file2会作为数据文件安装到$(prefix)/config目录下.
尽量用相对路径引用源程序的位置,以下两个变量是预定义好的:
$(top_srcdir)无论在哪个目录层次,该变量定义了包含src目录的目录位置,用于引用源程序;
$(top_builddir)定义了生成目标文件上最上层目录,用于引用.o等编译出来的目标文件.

configure脚本生成的Makefile中已经带了很多常用的目标如:check, all, install, uninstall, clean, dist, distcheck, distclean, tags, maintainerclean.
If configure or make did it, make distclean undoes it.
If make did it, make clean undoes it.
If make install did it, make uninstall undoes it.
If you did it, make maintainer-clean undoes it.

libtool简介
生成各种程序库的方便工具.
提供一个统一的接口,程序员不用关心各种烦人的底层细节:不同的平台的库可能要求不同的后缀,不同平台对库的安装方法不同,有些平台不支持动态库等等.
生成高层次的库,称为libtool library,后缀是.la.用它连接时,默认产生动态连接库,也可以用-static参数指定生成静态连接库.
既可单独使用又可与automake和autoconf一起使用更加强大,方便.

使用libtool
在configure.in文件中加上AC_PROG_LIBTOOL宏,如果原来有AC_PROG_RANLIB宏,删去它.
在Makefile.am文件中:
lib_LTLIBRARIES = libshell.la
libshell_la_SOURCES = object.c subr.c symbol.c
与原来的写法非常相似!
.la库只能连入.lo(使用libtool生成的目标文件)
libshell_la_LDADD = xmalloc.lo @LTLIBOBJS@
传入库的版本号:
libshell_la_LDFLAGS = -version-info 1:0:1
与其它目标文件连接时用LDFLAGS指定连接的方式(默认是动态方式):-static, --all-static指定静态连接.

Autoconf:根据用户提供的configure.in文件,生成一个名为configure的脚本.该脚本可以搜集有关移植性的平台相关信息,这些信息被用来生成Makefiles,配置头文件和其它平台相关的文件.

Automake:根据用户提供的一个高层次的生成规则Makefile.am,生成Makefile文件的模板

Makefile.in.Automake生成的Makefiles符合GNU的Makefile标准,用户无需再手工编写Makefile文件.

Libtool:使得生成内存位置无关的代码且让共享库在不同平台间移成为可能.它可以不依赖autoconf和automake单独运行,另一方面,automake和libtool可以无缝地集成使用.
一個偽裝成白癡的天纔!

TOP

依赖关系应该还有问题
ext/standard/basic_functions.lo: Zend/zend_language_parser.h
Zend/zend_language_parser.h:
ext/tokenizer/tokenizer.lo: Zend/zend_language_parser.h

Zend/zend_language_scanner.lo: Zend/zend_language_parser.h

Zend/zend_language_parser.h: Zend/zend_language_parser.c
Zend/zend_language_parser.c: /home/gavin/web/build/php-5.1.6/Zend/zend_language_parser.y
@$(YACC) -p zend -v -d /home/gavin/web/build/php-5.1.6/Zend/zend_language_parser.y -o $@

zend_language_parser.c是由zend_language_parser.y生成的

Zend/zend_indent.lo Zend/zend_highlight.lo Zend/zend_compile.lo: Zend/zend_language_parser.h

Zend/zend_language_parser.lo: Zend/zend_language_parser.c
$(LIBTOOL) --mode=compile $(CC) -IZend/ -I/home/gavin/web/build/php-5.1.6/Zend/ $(COMMON_FLAGS) $(CFLAGS_CLEAN) $(EXTRA_CFLAGS) -c Zend/zend_language_parser.c -o Zend/zend_language_parser.lo

标准扩展库里 依赖zend_language_parser.h
zend_language_parser又依赖zend
zend又依赖srm_oparray
srm_oparray 却又依赖 标准扩展库的url

所以就挂了
上面分析的原因不对

应该加上make install-cli
PHP 4.3.11 + Optimizer 2.5.7 的效果不错

PHP 4.3.8 + Optimizer 2.5.3 正在试验

问题
1)变量名、函数名、类名也无法dump
2)函数内部的操作无法dump,类的内部也无法dump
一個偽裝成白癡的天纔!

TOP

vld遍历操作码的方法不对啊

CHECK_SYMBOL_TABLES() \
EX(opline)++;

==============
zend_API.c中的
zend_hash_find
zend_hash_init
zend_hash_index_find

会被Zend Optimizer调用
用来建立符号表(函数,变量,类的名称)

zend_hash_internal_pointer_reset_ex

zend_hash_exits

zend_hash_num_elements

zend_hash_quick_find


zend_execute 这个函数也居然有

zend_hash_func

zend_hash_add_or_update

zend_excute_internal

zend_hash_get_current_key_ex

zend_hasn_index_update_or_next_insert

zend_hash_find


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

destroy_op_array 也会在Optimizer用到
这个是在zend_execute.c中执行完后会调用到

get_unary_op

convert_to_array


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

zend_compile_file 被劫持
===================
启动
if (php_module_startup(&cli_sapi_module, NULL, 0)==FAILURE) {

会用hash查找

这里开始执行
php_execute_script(&file_handle TSRMLS_CC);

retval = (zend_execute_scripts(ZEND_REQUIRE TSRMLS_CC, NULL, 3, prepend_file_p, primary_file, append_file_p) == SUCCESS);

用Optimizer接管zend

int zend_register_extension(zend_extension *new_extension, DL_HANDLE handle)
{
#if ZEND_EXTENSIONS_SUPPORT
zend_extension extension;

extension = *new_extension;
extension.handle = handle;

zend_extension_dispatch_message(ZEND_EXTMSG_NEW_EXTENSION, &extension);

zend_llist_add_element(&zend_extensions, &extension);

/*fprintf(stderr, "Loaded %s, version %s\n", extension.name, extension.version);*/
#endif

return SUCCESS;
}


extension_version_info = (zend_extension_version_info *) DL_FETCH_SYMBOL(handle, "extension_version_info");

下载 Optimzer的符号表中的 extension_version_info

初始化
startup
shutdown的函数地址


startup反汇编

zend_hash_find 的确是调用zend_hash.c中的函数
一個偽裝成白癡的天纔!

TOP

编译器全局变量 compiler_globals 非线程安全

struct _zend_compiler_globals {
zend_stack bp_stack;
zend_stack switch_cond_stack;
zend_stack foreach_copy_stack;
zend_stack object_stack;
zend_stack declare_stack;

zend_class_entry class_entry, *active_class_entry;
zval active_ce_parent_class_name;

/* variables for list() compilation */
zend_llist list_llist;
zend_llist dimension_llist;
zend_stack list_stack;

zend_stack function_call_stack;

char *compiled_filename;

int zend_lineno;
int comment_start_line;
char *heredoc;
int heredoc_len;

zend_op_array *active_op_array; /*当前活动的操作码*/

HashTable *function_table; /* function symbol table */
HashTable *class_table; /* class table */

HashTable filenames_table;

HashTable *auto_globals;

zend_bool in_compilation;
zend_bool short_tags;
zend_bool asp_tags;
zend_bool allow_call_time_pass_reference;

zend_declarables declarables;

/* For extensions support */
zend_bool extended_info; /* generate extension information for debugger/profiler */
zend_bool handle_op_arrays; /* run op_arrays through op_array handlers */

zend_bool unclean_shutdown;

zend_bool ini_parser_unbuffered_errors;

zend_llist open_files;

struct _zend_ini_parser_param *ini_parser_param;

int interactive;

zend_bool increment_lineno;

#ifdef ZEND_MULTIBYTE
zend_encoding **script_encoding_list;
int script_encoding_list_size;

zend_encoding *internal_encoding;

zend_encoding_detector encoding_detector;
zend_encoding_converter encoding_converter;
zend_multibyte_oddlen multibyte_oddlen;
#endif /* ZEND_MULTIBYTE */
};
一個偽裝成白癡的天纔!

TOP

开始编译之前,先初始化一个操作码数组
init_op_array(op_array, ZEND_USER_FUNCTION, INITIAL_OP_ARRAY_SIZE TSRMLS_CC);
设置全局标志,正在编译中
CG(in_compilation) = 1;
将刚才新建的opcode数组设置为当前活动数组
CG(active_op_array) = op_array;
开始编译,分析函数,类等关系,写入到 执行引擎全局变量
compiler_result = zendparse(TSRMLS_C);
变量信息放到znode里面
zend_do_return(&retval_znode, 0 TSRMLS_CC);
恢复全局编译标志
CG(in_compilation) = original_in_compilation;
如果编译出现错误
if (compiler_result==1) { /* parser error */
zend_bailout();
}
设置编译成功标志
compilation_successful=1;


if (retval) {
CG(active_op_array) = original_active_op_array;
编译成功,进行第2遍分析,生成操作码
if (compilation_successful) {
pass_two(op_array TSRMLS_CC);
} else {
efree(op_array);
retval = NULL;
}
}
一個偽裝成白癡的天纔!

TOP

 417 12
发新话题