PHP头条
热点:

phpjm php加密的解密过程,phpjmphp加密解密


前言

最近接手一套系统,安装后发现不能运行,被告知必须修改hosts绑定域名才能执行,怒,这怎么可以?暴力破解它。

准备篇

看了一下用的是phpjm.net的加密。

其实还是挺简单的,主要是利用了php变量名和数组索引名支持特殊字符,准确的来说是:

\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*

phpjm就是把里面的变量名弄成乱码,然后再使用base64和gzcompress经过数次加密。

手动分析

先把乱码都替换成稍微正常一点的东西,至少能让人明白是啥玩意。

<?
/**
 *Author: 塞北的雪
 *Website:http://blog.csdn.net/sbdx
*/
$filename='文件名';
$str = file_get_contents("$filename.php");
function tolog($str)
{
    file_put_contents("replace_log.txt", $str . "\n", FILE_APPEND);
}
preg_match_all('|\$[a-zA-Z_\x7f-\xff][\w\x7f-\xff]*|', $str, $params) or die('err 1.');
$params = array_unique($params[0]); // 去重复
$replace = array();
$i = 1;
foreach ($params as $k=>$v)
{
    if(preg_match('/\$[a-zA-Z_]+/',$v,$re))//过滤掉正常的英文变量名
    {
        unset($params[$k]); continue;
    }
    $replace[] = '$p'.$i;
    tolog($v . ' => $p' . $i); // 记录到日志
    $i++;
}
$str = str_replace($params, $replace, $str);

// 第二步 替换所有函数名
// 正则 function ([a-zA-Z_\x7f-\xff][\w\x7f-\xff]*)
preg_match_all('|function ([a-zA-Z_\x7f-\xff][\w\x7f-\xff]*)|', $str, $params) or die('err 2.');
$params = array_unique($params[1]); // 去重复
$replace = array();
$i = 1;
foreach ($params as $v)
{
    $replace[] = 'fun' . $i;
    tolog($v . ' => fun' . $i); // 记录到日志
    $i++;
}
$str = str_replace($params, $replace, $str);

// 第三步 替换所有不可显示字符,可选,如果转义后则不能用decode函数解码了
function tohex($m)
{
    $p = urlencode($m[0]); // 把所有不可见字符都转换为16进制、
    $p = str_replace('%', '\x', $p);
    $p = str_replace('+', ' ', $p); // urlencode 会吧 空格转换为 + 
    return $p;
}
$str = preg_replace_callback('|[\x00-\x08\x0e-\x1f\x7f-\xff]|s', "tohex", $str);
*/
// 写到文件
$str=str_replace(';',";\r\n",$str);
$filename++;
file_put_contents($filename."_2.php", $str);

echo 'Done.';

解密变量名的函数:

<?php
/**
 *Author: 塞北的雪
 *Website:http://blog.csdn.net/sbdx
*/
function fun1($p2, $p3 = "")
{
    $p2 = base64_decode($p2);
    if (empty($p2)) return "";
    if ($p3 == "")
    {
        return ~$p2;
    }
    else
    {
        $p4 = base64_decode($p2);
        $p3 = str_pad($p3, 1000, $p3);
        return $p2 ^ $p3;
    }
}

代码的最后会有一串eval执行的一串很长的代码,这个就是下一次要分析的字符串了,如此重复数次就能得到源码了。

自动分析篇

auto_decode v1.1

<?php
/**
 *Author: 塞北的雪
 *Website:http://blog.csdn.net/sbdx
*/
    $filename='sssssssssssssssssss.php';
    $counter=0; 
    function fun1($p2, $p3 = "")
    {
        $p2 = base64_decode($p2);
        if (empty($p2)) return "";
        if ($p3 == "")
        {
            return ~$p2;
        }
        else
        {
            $p3 = str_pad($p3, 1000, $p3);
            return $p2 ^ $p3;
        }
    }
    function decode($str)
    {
        global $counter;
        $debase=base64_decode($str);
        if($debase{0}=='$' or $debase{0}==';')
        {
            echo '使用 base64_decode()<br>';
            return $debase;
        }
        $source=@gzuncompress($debase);
        if($source!==false)
        {
            echo '使用 gzuncompress(base64_decode())<br>';
            return $source;
        }
        $source=@fun1($debase);
        if($source!='')
        {
            echo '使用 fun1(base64_decode())<br>';
            return $source;
        }
        die('第 '.$counter.' 次解密失败!');
    }
    $str=file_get_contents($filename);

    for($i=1;$i<=8;$i++)
    {
        if(stripos($str,'Access Denied')!==FALSE){die("解密完成,请查看 auto_$counter.php");}
        $counter++;
        echo '第 '.$counter.' 次解密开始!<br>'; 
        //echo '原始字串:'.$str;
        preg_match_all("#(?<='\]\(').+?(?=')#",$str,$re);
        $code=$re[0][count($re[0])-1];
        //echo '提取字串:'.$code.'<br>';
        $str=decode($code);
        //echo '解密字串:'.$str.'<hr>';
        $str=str_replace(';',";\r\n",$str);
        file_put_contents("auto_$counter.php",$str);
    }

auto_decode v2

发现上个版本有的文件解不了,又重写了一个版本,这次

<?php
$source=file_get_contents('weixin.templatemessage.php');
$level=1;
function decode($p1, $p2 = "")
{
    $p1 = base64_decode($p1);
    if (empty($p1))  return "";
    if ($p2 == "")
    {
        return ~$p1;
    }
    else
    {
        $p4 = strlen($p1);
        $p2 = str_pad($p2, $p4, $p2);
        return $p1 ^ $p2;
    }
}

function crack($source)
{
    global $level;
    preg_match_all("#eval.*\('(.+?)'#i",$source,$re);
    $str=$re[1][0];
    $s=@base64_decode($str);

    if(false===$s)
    {
        $s=decode($str);
    }
    if(@gzuncompress($s)!==false)
    {
        $s=gzuncompress($s);
    }
    if(stripos($s,'$')===false)
    {
        if(@gzuncompress($s)!==false)
        {
            $s=gzuncompress($s);
        }
        elseif(preg_match('#[^a-zA-Z+-=]#i',$s))
        {
            $s=decode($s);
        }
        else
        {
            $s=base64_decode(s);
        }

    }
    //file_put_contents($level.'.txt',$s);
    $level++;
    return $s;
}

//5轮循环,如果不够再增加次数,最后一次关键字unset
for($i=1;$i<=5;$i++)
{
    $source=crack($source);
}
echo $source.'<br>';

解密完后里面还是会有点垃圾代码,大家清理掉就可以了。我懒得再用正则写了。

总结

正则是个好东西。

版权

随意转发,但是请保留源出处,谢谢。

参考文献

[1]http://www.jb51.net/article/50788.htm 作者、来源已不可考
[2]http://blog.csdn.net/sbdx/article/details/45170255 以前写的第一版,针对性太强

www.phpzy.comtrue/php/421.htmlTechArticlephpjm php加密的解密过程,phpjmphp加密解密 前言 最近接手一套系统,安装后发现不能运行,被告知必须修改hosts绑定域名才能执行,怒,这怎么可以?暴力破解它。 准备篇 看了一下用的...

相关文章

相关频道:

PHP之友评论

今天推荐