Skip to content

Instantly share code, notes, and snippets.

@zb3
Last active November 28, 2022 17:30
Show Gist options
  • Save zb3/3bc626cb3c265708946cb5e516c64256 to your computer and use it in GitHub Desktop.
Save zb3/3bc626cb3c265708946cb5e516c64256 to your computer and use it in GitHub Desktop.
PHP deobfuscation utils
<?php
/*
note this is not "correct"
it was made to deobfuscate particular obfuscated files
"proper" tool should probably operate on AST to properly parse the file
but even that's not enough - you can't assume things we do here
*/
function unwrap_hexstr_literals($src) //currently only \xHH and \nnn supported
{
$pat = '/["]([^$"]*)["]/';
$src = preg_replace_callback($pat, function($m)
{
return preg_replace_callback('/\\\\x([0-9a-f][0-9a-f]|[0-9a-f])|\\\\([0-3][0-7][0-7]?|[0-7][0-7]?)/', function($m2)
{
if (strlen($m2[1]))
$v = hexdec($m2[1]);
else
$v = octdec($m2[2]);
if ($v != 36 && $v != 34 && $v != 92 && $v != 10 && $v != 13)
return chr($v);
else
return $m2[0];
}, $m[0]);
}, $src);
return $src;
}
function unwrap_str_literal_index($src) //"abc"[0]
{
//we can't make the second part mandatory here
//coz closing " could be mistaken for opening "
$pat = '/"((?:\\\\"|[^"]+)*)"(?:\\[(\\d+)\\])?/';
$src = preg_replace_callback($pat, function($m)
{
if (isset($m[2]))
{
$real = eval('return ord('.$m[0].');');
return 'chr('.$real.')';
}
else
return $m[0];
}, $src);
return $src;
}
function unwrap_intval($src)
{
$callback = function($m)
{
$real = eval('return '.$m[0].';');
return ''.$real;
};
$src = preg_replace_callback('/intval[(]"([^"]*)"[)]/', $callback, $src);
$src = preg_replace_callback('/intval[(]\'([^\']*)\'[)]/', $callback, $src);
return $src;
}
function unwrap_ord($src)
{
$callback = function($m)
{
$real = eval('return '.$m[0].';');
return ''.$real;
};
$src = preg_replace_callback('/ord[(]"([^$"]*)"[)]/', $callback, $src);
$src = preg_replace_callback('/ord[(]\'([^\']*)\'[)]/', $callback, $src);
return $src;
}
//I mean... really simple :)
function simple_arithmetic($src)
{
$callback = function($m)
{
$real = eval('return '.$m[0].';');
return ''.$real;
};
$src = preg_replace_callback('/(\\d+)\\s*[*\\/]\\s*\\d+/', $callback, $src);
$src = preg_replace_callback('/(\\d+)\\s*[+-]\\s*\\d+/', $callback, $src);
return $src;
}
function unwrap_chr($src)
{
$pat = '/chr[(]([0-9^]+)[)]/';
$src = preg_replace_callback($pat, function($m)
{
$iv = eval('return intval('.$m[1].');');
if ($iv>=32 && $iv<127 && $iv != 36 && $iv != 34 && $iv != 92)
{
$real = eval('return '.$m[0].';');
return '"'.$real.'"';
}
else return $m[0];
}, $src);
return $src;
}
function unwrap_function_vars($src)
{
$pat = '/[$]([a-zA-Z0-9]+)\\s*=\\s*"([^"]+)";/';
$pat2 = '/[$]([a-zA-Z0-9]+)\\s*=\\s*\'([^\']+)\';/';
preg_match_all($pat, $src, $out, PREG_SET_ORDER);
preg_match_all($pat2, $src, $out2, PREG_SET_ORDER);
$out = array_merge($out, $out2);
foreach($out as $m)
{
$src = str_replace('$'.$m[1].'(', $m[2].'(', $src);
}
return $src;
}
function unwrap_variable_names($src) //${"lol"}
{
$callback = function($m)
{
$real = eval('return '.$m[1].';');
return '$'.$real;
};
$pat = '/[$][{]("[^$"{}]*")[}]/';
$src = preg_replace_callback($pat, $callback, $src);
$pat = '/[$][{](\'[^$\'{}]*\')[}]/';
$src = preg_replace_callback($pat, $callback, $src);
return $src;
}
function unwrap_global_assignments($src)
{
$pat = '/[$]GLOBALS[[](["\'][a-zA-Z0-9]+["\'])[\\]]\\s*=\\s*("[^"]+"|\'[^\']+\');/';
preg_match_all($pat, $src, $out, PREG_SET_ORDER);
foreach($out as $m)
{
$first_pos = strpos($src, '$GLOBALS['.$m[1].']');
$src = substr($src, 0, $first_pos+1).str_replace('$GLOBALS['.$m[1].']', $m[2], substr($src, $first_pos+1));
}
return $src;
}
//not very reliable - it assumes those dynamic ones are static
function unwrap_dynamic_names($src) //
{
$pat = '/[$]([a-zA-Z0-9]+)\\s*=\\s*"([^"]+)";/';
$pat2 = '/[$]([a-zA-Z0-9]+)\\s*=\\s*\'([^\']+)\';/';
preg_match_all($pat, $src, $out, PREG_SET_ORDER);
preg_match_all($pat2, $src, $out2, PREG_SET_ORDER);
$out = array_merge($out, $out2);
foreach($out as $m)
{
$src = str_replace('${$'.$m[1].'}', '$'.$m[2], $src);
}
return $src;
}
<?php
include('deobf.php');
//404.dig.php :>
//key and decrypt_func_name needs to be provided
$f = $argv[1];
$src = file_get_contents($f);
$decrypt_func_name = 'vj8qeB246jyI';
function decrypt($str)
{
$key = 40;
$SCByfs8LbV7L = "";
for($zCBMMZK=intval('pzntqFtYNJK9'); $zCBMMZK<strlen($str); $zCBMMZK++)
{
$A836ZNXhd=$zCBMMZK % 12;$jOfgylk = ord($str[$zCBMMZK]) - $A836ZNXhd - $key;
if ($jOfgylk < 32)
$jOfgylk = $jOfgylk + 94;
$SCByfs8LbV7L .= chr($jOfgylk);
}
return $SCByfs8LbV7L;
}
$src = str_replace("\t", "", $src);
$src = unwrap_chr($src);
$src = str_replace('"."', '', $src);
$src = unwrap_function_vars($src);
//now we can look for shadowing functions
$pat = '/function ([a-zA-Z0-9]+)[(][$]([a-zA-Z0-9]+)[)][{]return ([a-zA-Z0-9]+)[(][$]\\2[)];[}];/';
$shadowmap = [];
$shadowkey = [];
$src = preg_replace_callback($pat, function ($m) use(&$shadowkey, &$shadowmap)
{
$shadowkey[] = $m[1];
$shadowmap[$m[1]] = $m[3];
return '';
}, $src);
$cnt = -1;
while($cnt)
{
foreach($shadowkey as $sk)
{
$src = preg_replace('/([^a-zA-Z0-9])'.$sk.'[(]/', '\\1'.$shadowmap[$sk].'(', $src, -1, $cnt);
}
}
//now the most interesting part
$pat = '/'.$decrypt_func_name.'[(]"((?:\\\\"|[^"]+)+)"[)]/';
$src = preg_replace_callback($pat, function ($m)
{
$real = eval('return decrypt("'.$m[1].'");');
return "'".addslashes($real)."'";
}, $src);
$src = unwrap_variable_names($src);
$src = unwrap_function_vars($src);
file_put_contents($f.'-deobf.php', $src);
<?php
include('deobf.php');
/*
for simple tricks on PLAIN code only
*/
$f = $argv[1];
$src = file_get_contents($f);
//these aren't always safe
$src = str_replace(';', ";\n", $src);
$src = str_replace('){', ")\n{", $src);
$src = str_replace('; ', ";\n", $src);
$src = str_replace('} ', "}\n", $src);
$src = str_replace('{ ', "{\n", $src);
$osrc = null;
while($osrc != $src)
{
$osrc = $src;
for($p=0;$p<30;$p++)
{
$src = unwrap_intval($src);
$src = unwrap_chr($src);
$src = str_replace('"."', '', $src);
}
$src = unwrap_ord($src);
$src = unwrap_hexstr_literals($src);
$src = unwrap_function_vars($src);
$src = unwrap_variable_names($src);
$src = unwrap_global_assignments($src);
$src = unwrap_variable_names($src);
$src = unwrap_dynamic_names($src);
$src = unwrap_str_literal_index($src);
//$src = simple_arithmetic($src);
}
file_put_contents($f.'-deobf.php', $src);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment