<?
class phpmorph {
var $options = array();
var $reserved = array('include', 'include_once', 'require', 'return', 'eval', 'isset', 'count', 'for', 'foreach', 'if', 'while', 'echo', 'print', 'list', 'or', 'and', 'xor', 'declare');
var $reserved_vars = array('_ENV', '_SERVER', '_GET', '_POST', '_REQUEST', 'GLOBALS');
var $var_prefix = 'V';
var $morph_level = 0;
var $current_morph_level = 0;
var $allvars = array();
var $vars = array();
var $vars_fixed = array();
var $trace_info = array();
var $debug = 0;
//=========================================================
function phpmorph() {
$o = &$this->options;
$p = 'obfuscate';
$o[$p]['enable'] = 1;
$o[$p]['variables'] = 1;
$p = 'string';
$o[$p]['enable'] = 1;
$o[$p]['chance'] = 100;
$o[$p]['chance_split'] = 50;
$o[$p]['min_len'] = 7;
$p = 'string2';
$o[$p]['enable'] = 1;
$o[$p]['chance'] = 100;
$p = 'func';
$o[$p]['enable'] = 1;
$o[$p]['chance'] = 100;
$p = 'eval_calls';
$o[$p]['enable'] = 1;
$o[$p]['chance'] = 100;
$p = 'eval_op';
$o[$p]['enable'] = 1;
$o[$p]['chance'] = 30;
$o[$p]['max_len'] = 70;
$p = 'func_defs';
$o[$p]['enable'] = 1;
$o[$p]['chance'] = 100;
$o[$p]['morph_body'] = 1;
$o[$p]['body_base64'] = 1;
$o[$p]['evaled_creation'] = 1;
$p = 'process';
$o[$p]['clear_vars'] = 1;
}
//=========================================================
function to_log($msg) {
echo "$msg\n";
}
//=========================================================
function chance($percent) {
return (rand(1, 100) <= $percent);
}
//=========================================================
function space_nonphp(&$phpsource) {
$s = $phpsource;
preg_match_all("/\?>(.+?)<\?/s", $s, $regs, PREG_OFFSET_CAPTURE + PREG_SET_ORDER);
for ($i=0; $i<count($regs); $i++) {
$s = substr($s, 0, $regs[$i][1][1]).str_repeat(' ', strlen($regs[$i][1][0])).substr($s, $regs[$i][1][1] + strlen($regs[$i][1][0]));
}
return $s;
}
//=========================================================
function in_string($pos, &$str) {
if (!$this->in_php($pos, $str)) return false;
$s = substr($str, 0, $pos);
$s = $this->space_nonphp($s);
$dblqnum = preg_match_all("/(?<!\\\)\"/", $s, $matches);
$qnum = preg_match_all("/(?<!\\\)'/", $s, $matches);
return (($qnum%2) or ($dblqnum%2));
}
//=========================================================
function in_php($pos, &$str) {
$s = substr($str, 0, $pos);
$po = substr_count($s, '<?');
$pc = substr_count($s, '?>');
return ($po > $pc);
}
//=========================================================
function gen_str($len, $a = '123456789') {
$code = '';
for ($i=1;$i<=$len;$i++) $code .= $a[rand(0, strlen($a)-1)];
return $code;
}
//=========================================================
function gen_varname($len, $store = false) {
do {
$name = $this->var_prefix.$this->gen_str($len);
} while ((isset($this->vars[$name])) or (isset($this->allvars[$name])));
if ($store) $this->vars[$name] = '';
return $name;
}
//=========================================================
function get_func_alias($func) {
$a = array_flip($this->vars);
if (array_key_exists("'$func'", $a))
return '$'.$a["'$func'"];
return false;
}
//=========================================================
function split_string($s) {
if (strlen($s) < 4) return array($s);
if ((strlen($s) - substr_count($s, '\\')) < 3) return array($s);
$i = 0;
do {
$p = rand(2, strlen($s)-2);
} while ((($s[$p-1] == '\\') or ($s[$p] == '\\') or ($s[$p-1] == "'")) and ($i++ < 10));
if ($i == 10) return array($s);
$a[] = substr($s, 0, $p);
$a[] = substr($s, $p);
return $a;
}
//=========================================================
function morph_string($str, $q = "'") {
if (strlen($str) < $this->options['string']['min_len']) return "$q$str$q";
$chance = $this->options['string']['chance_split'];
if ($this->chance($chance)) {
$as = $this->split_string($str);
} else
$as[] = $str;
foreach ($as as $s) {
if (strlen($s) < 30) $m = rand(0,3);
else $m = rand(0,2);
switch ($m) {
case 0:
$as2[] = "$q$s$q";
break;
case 1:
$alias = $this->get_func_alias('strrev');
if (!$alias) $alias = 'strrev';
$s = str_replace("\\$q", $q, $s);
$s = strrev($s);
$s = str_replace($q, "\\$q", $s);
$as2[] = "$alias($q".$s."$q)";
break;
case 2:
$alias = $this->get_func_alias('str_rot13');
if (!$alias) $alias = 'str_rot13';
$s = str_rot13($s);
$as2[] = "$alias($q".$s."$q)";
break;
case 3:
$alias = $this->get_func_alias('base64_decode');
if (!$alias) $alias = 'base64_decode';
$s = str_replace("\\$q", $q, $s);
$s = base64_encode($s);
$as2[] = "$alias($q".$s."$q)";
break;
}
}
$s = implode('.', $as2);
return $s;
}
//=============================================================
function nonbracketed_explode($spl, &$l, $br = '{}') {
$n = 0;
$a = explode($spl, $l);
foreach($a as $k => $s) {
for ($i=0;$i<strlen($s);$i++) {
if ($s[$i] == $br[0]) $n++; else
if ($s[$i] == $br[1]) $n--;
}
$ss .= $spl.$s;
if ($n == 0) {
$b[] = trim($ss, $spl);
$ss = '';
}
}
if ($ss) $b[] = trim($ss, $spl);
return $b;
}
//=============================================================
function get_bracketed($s, $pos, $br = '{}') {
$i = $pos;
$opened = false;
$n = 0;
while ((!$opened or $n) and ($i<strlen($s))) {
if ($s[$i] == $br[0]) {
if (!$opened) $opened = true;
}
if ($s[$i] == $br[0]) $n++; else
if ($s[$i] == $br[1]) $n--;
$i++;
}
return $i;
}
//=========================================================
function morph_func_def($phpsource, $funcdef, &$rep) {
preg_match("/function\s*([a-zA-Z0-9_]+)\s*\((.*?)\)\s*\{/s", $funcdef, $matches);
$body = substr($funcdef, strlen($matches[0]));
$body = substr($body, 0, strlen($body)-strlen(strrchr($body, '}')));
$body = trim($body);
if ($this->options['func_defs']['morph_body']) {
$morpher = new phpmorph;
$morpher->options['process']['clear_vars'] = 0;
$morpher->options['eval_op']['enable'] = 0;
$morpher->vars = $this->vars;
$body = $morpher->process('<'.'? {MORPH_VARS}'.$body.'?'.'>', 1);
$body = trim(substr($body, 2, strlen($body) - 4));
$body = preg_replace("/{[A-Z_]+}/", '', $body);
unset($morpher);
}
$name = $matches[1];
$newname = $this->gen_varname(3, true);
$this->vars_fixed[] = $newname;
$args = $matches[2];
if ($this->options['func_defs']['body_base64']) {
$line = $this->trace_info['func'];
$lines_prefix = $line?str_repeat("\n", $line - 1):'';
$body = base64_encode($lines_prefix.$body);
if ($this->options['func_defs']['evaled_creation']) {
$s = "\$$newname=create_function('$args',base64_decode('$body'));";
$line = $this->trace_info['eval'];
$lines_prefix = $line?str_repeat("\n", $line - 1):'';
$s = $lines_prefix.$s;
$s = "eval(base64_decode('".base64_encode($s)."'));";
} else
$s = "\$$newname=create_function('$args',base64_decode('$body'));";
} else {
$body = str_replace('\\', '\\\\', $body);
$body = str_replace("'", "\'", $body);
$body = preg_replace("/[\r\n\s]+/", ' ', $body);
$s = "\$$newname=create_function('$args','$body');";
}
$rep[] = array('pos' => strpos($phpsource, $funcdef), 'len' => strlen($funcdef), 'new' => $s);
preg_match_all("/$name\(/", $phpsource, $matches, PREG_OFFSET_CAPTURE + PREG_SET_ORDER);
for ($i=1; $i<count($matches); $i++) {
$rep[] = array('pos' => $matches[$i][0][1], 'len' => strlen($matches[$i][0][0]), 'new' => "\$$newname(");
}
}
//=========================================================
function morph_func($func) {
$newname = $this->gen_varname(3);
if ($func[0] == '$') {
$this->vars[$newname] = "'".trim($func, '$')."'";
return "\$\$$newname";
} else {
$a = array_flip($this->vars);
if (array_key_exists("'$func'", $a)) {
$newname = $a["'$func'"];
} else {
$this->vars[$newname] = "'$func'";
}
}
$s = "\$$newname";
return $s;
}
//=========================================================
function morph_eval($funcname, $funcargs) {
$s = base64_encode("return $funcname($funcargs);");
$s = "eval(base64_decode('$s'))";
return $s;
}
//=========================================================
function morph_obfuscate(&$phpsource) {
preg_match_all("/([;{}])[\r\n\s]+/s", $phpsource, $out, PREG_OFFSET_CAPTURE + PREG_SET_ORDER);
$rep = array();
for ($i=0; $i<count($out); $i++) {
if (!$this->in_php($out[$i][0][1], $phpsource)) continue;
if ($this->in_string($out[$i][0][1], $phpsource)) continue;
$rep[] = array('pos' => $out[$i][0][1], 'len' => strlen($out[$i][0][0]), 'new' => $out[$i][1][0]);
}
$phpsource = $this->do_replacements($phpsource, $rep);
preg_match_all("/\s*([=\+\-\*\/\.\[\]\(\),%])\s*/s", $phpsource, $out, PREG_OFFSET_CAPTURE + PREG_SET_ORDER);
$rep = array();
for ($i=0; $i<count($out); $i++) {
if (!$this->in_php($out[$i][0][1], $phpsource)) continue;
if ($this->in_string($out[$i][0][1], $phpsource)) continue;
$rep[] = array('pos' => $out[$i][0][1], 'len' => strlen($out[$i][0][0]), 'new' => $out[$i][1][0]);
}
$phpsource = $this->do_replacements($phpsource, $rep);
if ($this->options['obfuscate']['variables']) {
preg_match_all("/\\\$([A-Za-z0-6_]+)/", $phpsource, $out, PREG_OFFSET_CAPTURE + PREG_SET_ORDER);
$rep = array();
$vars = array();
for ($i=0; $i<count($out); $i++) {
if ($out[$i][1][0][0] == $this->var_prefix) continue;
if (in_array($out[$i][1][0], $this->reserved_vars)) continue;
if (!$this->in_php($out[$i][0][1], $phpsource)) continue;
$name = $out[$i][1][0];
if (!isset($vars[$name])) {
$vars[$name] = $this->gen_varname(3, true);
}
$newname = $vars[$name];
$rep[] = array('pos' => $out[$i][1][1], 'len' => strlen($name), 'new' => $newname);
}
unset($vars);
$phpsource = $this->do_replacements($phpsource, $rep);
}
$phpsource = preg_replace("/(<\?(php)?)\s*/", '\\1 ', $phpsource);
}
//=========================================================
function do_replacements($str, $rep) {
for ($i=count($rep)-1; $i>=0; $i--) {
$str = substr($str, 0, $rep[$i]['pos']).$rep[$i]['new'].substr($str, $rep[$i]['pos'] + $rep[$i]['len'], strlen($str));
}
return $str;
}
//=========================================================
function process($phpsource, $level = 1, $options = false) {
if ($options !== false) $o = $options; else $o = &$this->options;
$this->morph_level = $level;
$mods = 0;
if ($o['process']['clear_vars']) {
$this->allvars = array();
$this->vars_fixed = array();
}
for ($l=1; $l<=$level; $l++) {
$this->current_morph_level = $l;
if ($o['process']['clear_vars']) $this->vars = array();
if ($l == 1)
if ($o['obfuscate']['enable']) {
$this->morph_obfuscate($phpsource);
}
if ($o['func_defs']['enable']) {
while (preg_match_all("/function\s*([a-zA-Z0-9-_]+)\s*\((.*?)\)\s*\{/s", $phpsource, $out, PREG_OFFSET_CAPTURE + PREG_SET_ORDER)) {
if (isset($prevcount)) if (count($out) == $prevcount) break;
$prevcount = count($out);
$i=0;
if (!$this->in_php($out[$i][0][1], $phpsource)) continue;
$len = $this->get_bracketed($phpsource, $out[$i][0][1]);
$rep = array();
$s = $this->morph_func_def($phpsource, substr($phpsource, $out[$i][0][1], $len-$out[$i][0][1]), $rep);
$phpsource = $this->do_replacements($phpsource, $rep);
}
}
if ($o['string']['enable']) {
preg_match_all("/'(.*?)(?<!\\\)'/s", $this->space_nonphp($phpsource), $out, PREG_OFFSET_CAPTURE + PREG_SET_ORDER);
$rep = array();
for ($i=0; $i<count($out); $i++) {
if (!$this->chance($o['string']['chance'])) continue;
if (strlen($out[$i][1][0]) == 0) continue;
$s = $this->morph_string($out[$i][1][0]);
$rep[] = array('pos' => $out[$i][0][1], 'len' => strlen($out[$i][0][0]), 'new' => $s);
}
$phpsource = $this->do_replacements($phpsource, $rep);
}
if ($o['string2']['enable']) {
preg_match_all("/\"([^\"\$;\r\n\\\]{3,})\"/s", $this->space_nonphp($phpsource), $out, PREG_OFFSET_CAPTURE + PREG_SET_ORDER);
$rep = array();
for ($i=0; $i<count($out); $i++) {
if (!$this->chance($o['string2']['chance'])) continue;
$s = $this->morph_string($out[$i][1][0], '"');
$rep[] = array('pos' => $out[$i][0][1], 'len' => strlen($out[$i][0][0]), 'new' => $s);
}
$phpsource = $this->do_replacements($phpsource, $rep);
}
if ($o['func']['enable']) {
preg_match_all('/([A-Za-z$_][A-Za-z0-6_]+?)\s?\(/s', $phpsource, $out, PREG_OFFSET_CAPTURE + PREG_SET_ORDER);
$rep = array();
for ($i=0; $i<count($out); $i++) {
if (!$this->chance($o['func']['chance'])) continue;
if (!$this->in_php($out[$i][1][1], $phpsource)) continue;
if ($this->in_string($out[$i][1][1], $phpsource)) continue;
if (in_array(trim($out[$i][1][0], '$'), $this->vars_fixed)) continue;
if (in_array($out[$i][1][0], $this->reserved)) continue;
$s = $this->morph_func($out[$i][1][0]);
$rep[] = array('pos' => $out[$i][1][1], 'len' => strlen($out[$i][1][0]), 'new' => $s);
}
$phpsource = $this->do_replacements($phpsource, $rep);
}
if ($o['eval_calls']['enable']) {
preg_match_all("/[\.=;]\s*(([A-Za-z0-6_$]+?)\s*\(([^;'\\\"\.\+\s]*?)\))[;\.\+\s]/s", $phpsource, $out, PREG_OFFSET_CAPTURE + PREG_SET_ORDER);
$rep = array();
for ($i=0; $i<count($out); $i++) {
if (!$this->chance($o['eval_calls']['chance'])) continue;
if (!$this->in_php($out[$i][1][1], $phpsource)) continue;
if (in_array($out[$i][2][0], $this->reserved)) continue;
if (preg_match("/[\(\)]+/", $out[$i][3][0])) continue;
if ($this->in_string($out[$i][1][1], $phpsource)) continue;
$s = $this->morph_eval($out[$i][2][0], $out[$i][3][0]);
$rep[] = array('pos' => $out[$i][1][1], 'len' => strlen($out[$i][1][0]), 'new' => $s);
}
$phpsource = $this->do_replacements($phpsource, $rep);
}
if (strpos($phpsource, 'for') === false)
if ($o['eval_op']['enable']) {
preg_match_all("/;([^\?}]+?)(?=;)/s", $phpsource, $out, PREG_OFFSET_CAPTURE + PREG_SET_ORDER);
$rep = array();
for ($i=0; $i<count($out); $i++) {
if (!$this->chance($o['eval_op']['chance'])) continue;
if (strlen($out[$i][1][0]) > $o['eval_op']['max_len']) continue;
if (!$this->in_php($out[$i][1][1], $phpsource)) continue;
if (strpos($out[$i][1][0], '{') !== false) continue;
if ($this->in_string($out[$i][1][1], $phpsource)) continue;
if (strtolower($out[$i][1][0]) == 'return') continue;
$s = "eval(base64_decode('".base64_encode($out[$i][1][0].';')."'))";
if (strpos($out[$i][1][0], 'return') !== false) $s = 'return '.$s;
$rep[] = array('pos' => $out[$i][1][1], 'len' => strlen($out[$i][1][0]), 'new' => $s);
}
$phpsource = $this->do_replacements($phpsource, $rep);
}
$vars = '';
foreach ($this->vars as $name => $value) {
if ($value <> '') $vars .= "\$$name=$value;";
}
$phpsource = str_replace('{MORPH_VARS}', '{MORPH_VARS}'.$vars, $phpsource);
$this->allvars = $this->allvars + $this->vars;
$this->vars = array();
}
return $phpsource;
}
}
?>