Browse Source

Decompiler: merge phpdop into Decompiler

master
Xuefer 6 years ago
parent
commit
2a6657a008
  1. 70
      bin/phpdc.phpr
  2. 142
      bin/phpdop.phpr
  3. 3
      devel/run
  4. 134
      lib/Decompiler.class.php

70
bin/phpdc.phpr

@ -1,6 +1,9 @@
#! /usr/bin/php -dopen_basedir=
<?php
// you should write your own phpdc.phpr file if you want to do batch decompile
// using glob and/or Recursive Directory Iterator
$srcdir = dirname(__FILE__);
require_once("$srcdir/../lib/Decompiler.class.php");
if (file_exists("$srcdir/phpdc.debug.php")) {
@ -9,19 +12,45 @@ if (file_exists("$srcdir/phpdc.debug.php")) {
if (!isset($argv)) {
$argv = $_SERVER['argv'];
$argc = $_SERVER['argc'];
}
$dc = new Decompiler();
if (isset($argv[2])) {
eval('$dc->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();

142
bin/phpdop.phpr

@ -1,142 +0,0 @@
#! /usr/bin/php
<?php
$srcdir = dirname(__FILE__);
require_once("$srcdir/../lib/Decompiler.class.php");
if (file_exists("$srcdir/phpdc.debug.php")) {
include("$srcdir/phpdc.debug.php");
}
function get_op($op, $which)
{
switch ($op[$which . '_type']) {
case 1: // CONST
return $op[$which . '.var'] . var_export($op[$which . '.constant'], true);
case 2: // IS_TMP_VAR
return 't@' . $op[$which . '.var'];
case 4:
return 'v$' . $op[$which . '.var'];
case 8: // UNUSED
if (isset($op[$which . '.opline_num'])) {
return 'l#' . $op[$which . '.opline_num'];
}
else {
return '-';
}
default:
return $op[$which . '_type'] . '?' . $op[$which . '.var'];
}
}
function dump_opcodes($op_array, $indent = '')
{
global $decompiler;
$types = array('result' => 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] <file>\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);
}

3
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

134
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 "<?". "php\n\n";
echo "<?". "php";
if ($this->dumpOnly) {
echo " // dump opcode only";
}
echo "\n\n";
foreach ($this->dc['class_table'] as $key => $class) {
if ($key{0} != "\0") {
$this->activeClass = $class['name'];

Loading…
Cancel
Save