diff --git a/bin/phpdc.phpr b/bin/phpdc.phpr index 969cbb5..0c1bc08 100755 --- a/bin/phpdc.phpr +++ b/bin/phpdc.phpr @@ -1,6 +1,9 @@ #! /usr/bin/php -dopen_basedir= decompileDasm(' . file_get_contents($argv[2]) . ');'); -} -else if (isset($argv[1])) { - if ($dc->decompileFile($argv[1]) === false) { - exit(2); +$inputType = 'php'; +$outputType = 'decompile'; +$files = array(); + +reset($argv); +while (($arg = next($argv)) !== false) { + switch ($arg) { + case '-h': + echo "Usage: phpdc.phpr [-dca] [filename]"; + echo " -c: decompile into PHP code"; + echo " -d: dump into opcode"; + echo " -a: input file is dasm opcode return from xcache_dasm_*"; + echo " -h: this help page"; + break; + + case '-c': + $outputType = 'php'; + break; + + case '-d': + $outputType = 'opcode'; + break; + + case '--': + break 2; + + case '-a': + $inputType = 'opcode'; + break; + + default: + $files[] = $arg; + break; } } -else { + +if (!$files) { $phpcode = ''; if (!defined('stdin')) { define('stdin', fopen('php://stdin', 'rb')); @@ -29,9 +58,30 @@ else { while (!feof(stdin)) { $phpcode .= fgets(stdin); } + $dc = new Decompiler($outputType); if ($dc->decompileString($phpcode) === false) { exit(2); } + $dc->output(); +} +else { + foreach ($files as $file) { + $dc = new Decompiler($outputType); + switch ($inputType) { + case 'opcode': + eval('$opcode = ' . file_get_contents($file) . ';'); + if ($dc->decompileDasm($opcode) === false) { + exit(2); + } + break; + + case 'php'; + if ($dc->decompileFile($file) === false) { + exit(2); + } + break; + } + $dc->output(); + } } -$dc->output(); diff --git a/bin/phpdop.phpr b/bin/phpdop.phpr deleted file mode 100755 index 03d40d4..0000000 --- a/bin/phpdop.phpr +++ /dev/null @@ -1,142 +0,0 @@ -#! /usr/bin/php - 8, 'op1' => 20, 'op2' => 20); - $opcodes = &$op_array['opcodes']; - $decompiler->fixOpcode($opcodes); - $decompiler->buildJmpInfo($op_array); - foreach ($opcodes as $line => $op) { - echo $indent; - echo sprintf("%3d ", $op['lineno']); - echo sprintf("%3d ", $line); - $name = xcache_get_opcode($op['opcode']); - - if (substr($name, 0, 5) == 'ZEND_') { - $name = substr($name, 5); - } - echo str_pad($name, 25); - - foreach ($types as $which => $len) { - echo str_pad(get_op($op, $which), $len); - } - printf("%5s", isset($op['extended_value']) ? $op['extended_value'] : ""); - if (isset($op['jmpouts']) || isset($op['jmpins'])) { - printf("%10s %10s" - , (isset($op['jmpouts']) ? '>' . implode(',', $op['jmpouts']) : '') - , (isset($op['jmpins']) ? '<' . implode(',', $op['jmpins']) : '') - ); - } - if (isset($op['isCatchBegin'])) { - echo 'CB'; - } - - echo "\n"; - } -} - -function dump_function($name, $func, $indent = '') -{ - if (isset($func['op_array'])) { - $op_array = $func['op_array']; - unset($func['op_array']); - } - else { - $op_array = null; - } - var_dump($func); - echo $indent, 'function ', $name, "\n"; - if (isset($op_array)) { - dump_opcodes($op_array, " " . $indent); - } -} - -function dump_class($name, $class, $indent = '') -{ - if (isset($class['function_table'])) { - $funcs = $class['function_table']; - unset($class['function_table']); - } - else { - $funcs = null; - } - echo $indent, 'class ', $name, "\n"; - if (isset($funcs)) { - foreach ($funcs as $name => $func) { - dump_function($name, $func, " " . $indent); - } - } -} - -if (!isset($argv[1])) { - die("Usage: $argv[0] \n"); -} -$decompiler = new Decompiler(); -if (isset($argv[2])) { - eval('$pk = ' . file_get_contents($argv[2]) . ';'); -} -else { - $pk = xcache_dasm_file($argv[1]); -} -$op_array = $funcs = $classes = null; -if (isset($pk['op_array'])) { - $op_array = $pk['op_array']; - unset($pk['op_array']); -} -if (isset($pk['function_table'])) { - $funcs = $pk['function_table']; - unset($pk['function_table']); -} -if (isset($pk['class_table'])) { - $classes = $pk['class_table']; - unset($pk['class_table']); -} -var_dump($pk); -if (isset($classes)) { - foreach ($classes as $name => $class) { - dump_class($name, $class); - } -} -if (isset($funcs)) { - foreach ($funcs as $name => $func) { - dump_function($name, $func); - } -} -if (isset($op_array)) { - dump_opcodes($op_array); -} - diff --git a/devel/run b/devel/run index bd6790d..fc98adc 100755 --- a/devel/run +++ b/devel/run @@ -313,7 +313,8 @@ run() { dop) shift cmd=(./php-cli -c devel.ini) - phpApp=(./bin/phpdop.phpr) + phpApp=(./bin/phpdc.phpr) + set -- -d "$@" ;; fcgi) shift diff --git a/lib/Decompiler.class.php b/lib/Decompiler.class.php index 93e72b0..dc843b8 100644 --- a/lib/Decompiler.class.php +++ b/lib/Decompiler.class.php @@ -591,9 +591,11 @@ class Decompiler var $activeClass; var $activeMethod; var $activeFunction; + var $dumpOnly; - function Decompiler() + function Decompiler($outputType) { + $this->dumpOnly = $outputType == 'opcode'; $GLOBALS['__xcache_decompiler'] = $this; // {{{ testing // XC_UNDEF XC_OP_DATA @@ -760,7 +762,10 @@ class Decompiler case XC_IS_TMP_VAR: $T = &$EX['Ts']; if (!isset($T[$op['var']])) { - printBacktrace(); + if (!$this->dumpOnly) { + printBacktrace(); + } + return null; } $ret = $T[$op['var']]; if ($free && empty($this->keepTs)) { @@ -1536,16 +1541,30 @@ class Decompiler $EX['value2constant'][$this->activeFunction] = '__FUNCTION__'; } - /* dump whole array - $this->keepTs = true; - $this->dasmBasicBlock($EX, $range); - for ($i = $range[0]; $i <= $range[1]; ++$i) { - echo $i, "\t", $this->dumpop($opcodes[$i], $EX); - } - // */ - // decompile in a tree way $range = array(0, count($opcodes) - 1); - $this->recognizeAndDecompileClosedBlocks($EX, $range); + if ($this->dumpOnly) { + $this->keepTs = true; + $this->dasmBasicBlock($range); + $this->dumpRange($range); + } + else { + // decompile in a tree way + $this->recognizeAndDecompileClosedBlocks($range); + } + return $EX; + } + // }}} + function &dump_op_array($op_array, $indent = '') // {{{ + { + $op_array['opcodes'] = $this->fixOpCode($op_array['opcodes']); + $this->buildJmpInfo($op_array); + + $EX = array(); + $EX['op_array'] = &$op_array; + $EX['opcodes'] = &$op_array['opcodes']; + $EX['indent'] = $indent; + $range = array(0, count($EX['opcodes']) - 1); + $this->dumpRange($EX, $range); return $EX; } // }}} @@ -1576,10 +1595,10 @@ class Decompiler if ($opname == 'UNDEF' || !isset($opname)) { echo '// UNDEF OP:'; - $this->dumpop($op, $EX); + $this->dumpOp($op, $EX); continue; } - // echo $i, ' '; $this->dumpop($op, $EX); //var_dump($op); + // echo $i, ' '; $this->dumpOp($op, $EX); //var_dump($op); $resvar = null; unset($curResVar); @@ -1734,7 +1753,7 @@ class Decompiler $src = new Decompiler_ListBox($list); if (!isset($op1['var'])) { - $this->dumpop($op, $EX); + $this->dumpOp($op, $EX); var_dump($op); die('missing var'); } @@ -2443,60 +2462,69 @@ class Decompiler return implode(', ', $args); } // }}} - function dumpop($op, &$EX) // {{{ + function getOp($op, $which, &$EX) // {{{ { - assert('isset($op)'); - $op1 = $op['op1']; - $op2 = $op['op2']; - $d = array(xcache_get_opcode($op['opcode']), $op['opcode']); + switch ($op['op_type']) { + case XC_IS_UNUSED: + return '?' . $op['opline_num']; - foreach (array('op1' => '1:', 'op2' => '2:', 'result' => '>') as $k => $kk) { - switch ($op[$k]['op_type']) { - case XC_IS_UNUSED: - $d[$kk] = 'U:' . $op[$k]['opline_num']; - break; + case XC_IS_VAR: + $s = '$' . $op['var']; + if ($which != 'result' && isset($EX['Ts'])) { + $s .= ':' . str($this->getOpVal($op, $EX)); + } + return $s; - case XC_IS_VAR: - $d[$kk] = '$' . $op[$k]['var']; - if ($k != 'result') { - $d[$kk] .= ':' . str($this->getOpVal($op[$k], $EX)); - } - break; + case XC_IS_TMP_VAR: + $s = '#' . $op['var']; + if ($which != 'result' && isset($EX['Ts'])) { + $s .= ':' . str($this->getOpVal($op, $EX)); + } + return $s; - case XC_IS_TMP_VAR: - $d[$kk] = '#' . $op[$k]['var']; - if ($k != 'result') { - $d[$kk] .= ':' . str($this->getOpVal($op[$k], $EX)); - } - break; + case XC_IS_CONST: + return isset($EX['Ts']) ? str($this->getOpVal($op, $EX)) : $op['var'] . var_export($op['constant'], true); - case XC_IS_CV: - $d[$kk] = $this->getOpVal($op[$k], $EX); - break; + default: + return isset($EX['Ts']) ? str($this->getOpVal($op, $EX)) : $op['op_type'] . '?' . $op['var']; + } + } + // }}} + function dumpOp($op, &$EX) // {{{ + { + assert('isset($op)'); + echo str_pad($op['lineno'], 4); - default: - $d[$kk] = $this->getOpVal($op[$k], $EX); - } + $name = xcache_get_opcode($op['opcode']); + + if (substr($name, 0, 5) == 'ZEND_') { + $name = substr($name, 5); + } + echo str_pad($name, 25); + + $types = array('result' => 9, 'op1' => 20, 'op2' => 20); + foreach ($types as $which => $len) { + echo str_pad(($which == 'result' ? '>' : '') . $this->getOp($op[$which], $which, $EX), $len); + } + echo "\t;", $op['extended_value']; + if (isset($op['isCatchBegin'])) { + echo ' CB'; } - $d[';'] = $op['extended_value']; if (!empty($op['jmptos'])) { - $d['>>'] = implode(',', $op['jmptos']); + echo "\t>>", implode(',', $op['jmptos']); } if (!empty($op['jmpfroms'])) { - $d['<<'] = implode(',', $op['jmpfroms']); + echo "\t<<", implode(',', $op['jmpfroms']); } - foreach ($d as $k => $v) { - echo is_int($k) ? '' : $k, str($v), "\t"; - } echo PHP_EOL; } // }}} function dumpRange(&$EX, $range) // {{{ { for ($i = $range[0]; $i <= $range[1]; ++$i) { - echo $EX['indent'], $i, "\t"; - $this->dumpop($EX['opcodes'][$i], $EX); + echo $EX['indent'], str_pad($i, 4); + $this->dumpOp($EX['opcodes'][$i], $EX); } echo $EX['indent'], "==", PHP_EOL; } @@ -2847,7 +2875,11 @@ class Decompiler // }}} function output() // {{{ { - echo "dumpOnly) { + echo " // dump opcode only"; + } + echo "\n\n"; foreach ($this->dc['class_table'] as $key => $class) { if ($key{0} != "\0") { $this->activeClass = $class['name'];