Last active
November 28, 2022 17:30
-
-
Save zb3/3bc626cb3c265708946cb5e516c64256 to your computer and use it in GitHub Desktop.
PHP deobfuscation utils
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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