吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 25274|回复: 22
收起左侧

[Web逆向] 【原创】PHP 魔方一代加密 逆向调试过程笔记外加讨论

[复制链接]
jane35622 发表于 2018-2-8 20:25
本帖最后由 jane35622 于 2018-2-8 23:25 编辑

新人第一次发帖,有啥违规的地方麻烦各位指出一下,我看到了一定立马修改。
首先感谢@Ganlv 大大的教程给了我思路,输出伪汇编代码再分析,让逆向工作从不停的下断点调试中解脱出来。
咳、其他的感谢词就不说了,小子在这里给各位坛友拜个小年,祝各位坛友在新的一年里学习日益精进,生活美满团圆。

0x0 开整。
首先拿到了个样本文件: 样本.zip (112.72 KB, 下载次数: 183)
解压打开后发现全是乱码,遂上php-parser,用的format脚本是这一个:
[PHP] 纯文本查看 复制代码
<?php
use PhpParser\Error;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter;
require 'vendor/autoload.php';
$code = file_get_contents($argv[1]);
$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP5);
try {
    $ast = $parser->parse($code);
} catch (Error $error) {
    echo "Parse error: {$error->getMessage()}\n";
    return;
}
$prettyPrinter = new PrettyPrinter\Standard;
$prettyCode = $prettyPrinter->prettyPrintFile($ast);
file_put_contents($argv[1], $prettyCode);


命令行下输入
[Shell] 纯文本查看 复制代码
php format.php 要格式化的PHP文件路径

回车即可将原文件格式化。
之后得到的样本内部变量名依然是不可阅读的,我没学过AST,也不知道php-parser的其他用法(毕竟文档以及示例代码太少,开着谷歌翻译都看不懂其他情况下到底该怎么使),于是上自己老本行PY来一次:

[Python] 纯文本查看 复制代码
import json
import base64

php_file_path = input('请输入要修复变量名的php文件路径:')
file = open(php_file_path, 'rb').read()
var_list = []
var_open = False
in_str = False
sq = 39
dq = 34
sl = 92
inq = 0
for p, x in enumerate(file):
    if in_str:
        if x != inq:
            continue
        elif file[p - 1] == sl:
            count = 1
            for j in range(2, 100):
                if file[p - j] == sl:
                    count += 1
                    continue
                break
            if count % 2 == 0:
                in_str = False
                inq = 0
            continue
        else:
            in_str = False
            inq = 0
            continue

    if x in (sq, dq):
        in_str = True
        inq = x
        continue

    if x > 124:
        if file[p - 1] == 36:
            var_open = True
            var_list.append(bytearray([36, x]))
        elif var_open:
            var_list[-1].append(x)
    elif var_open:
        var_open = False
var_list = [bytes(x) for x in var_list]
# 保有原有索引顺序分配变量名
var_dict = {}
i = 0
for x in var_list:
    if x not in var_dict:
        var_dict[x] = "$_var_" + str(i)
        i += 1
var_dict[b'eval('] = 's_eval('

temp_file = file
for k, v in var_dict.items():
    temp_file = temp_file.replace(k, v.encode('utf-8'))
var_dict=dict((v, base64.b64encode(k).decode()) for k, v in var_dict.items())
json_data = json.dumps(var_dict)
header_info = """<?php
function object_to_array($obj) {
    $obj = (array)$obj;
    foreach ($obj as $k => $v) {
        if (gettype($v) == 'resource') {
            return;
        }
        if (gettype($v) == 'object' || gettype($v) == 'array') {
            $obj[$k] = (array)object_to_array($v);
        }
    }
    return $obj;
}
$var_dict=object_to_array(json_decode('{json_data}'));
foreach ($var_dict as $var_key=>$var_value){
        $var_dict[$var_key]=base64_decode($var_value);
}
function  s_eval ($code){
        global $var_dict;
        global {var_list};
        foreach ($var_dict as $var_key=>$var_value){
                if (strpos($code,$var_value)===false){
                        continue ;
                }else {
                        $code=str_replace($var_value,$var_key,$code);
                }
        }
        return eval($code);
}
?>"""
header_info = header_info.replace('{json_data}', json_data).replace('{var_list}',', '.join(list(var_dict)[0:-1])).encode()

with open(php_file_path[:-3]+'decode.php', 'wb') as f:
    f.write(header_info)
    f.write(temp_file)

如果电脑上没装python3环境的话得装一个,之后运行
[Shell] 纯文本查看 复制代码
python parser.py

然后输入上面格式化好的PHP文件路径回车即可生成以.decode.php结尾的修复好变量名并劫持了eval到自定义函数s_eval的文件。
用编辑器打开这个文件,里面被编码的那段虚拟机内运行的代码依然是乱码,这里可以切换文件编码到ISO 8859-1来编辑,npp修改的方式如下图:

QQ图片20180208193031.png
这个文件内有两个VM,我们现在只研究第一个VM,因此第二个函数内的VM相关变量代码都可以删掉。之后到第一个VM那段乱码的下面一行加入file_put_contents('pec.vmc',$_var_6);,把虚拟机内部执行的代码另存为出来,之后修改文件,删掉判断VM内执行代码是否加载的部分并美化,即可得到下面的文件:
初步处理后的文件.zip (108.17 KB, 下载次数: 22)
QQ截图20180208195734.png
之后便可以开始研究了。
在程序代码中咱们可以找到这样一句明文:KIVIUQ VIRTUAL MACHINE ERROR : Access violation at address ,于是咱便可以搜索这一句话里面的前几个词找到一个别人破解的资料,但是他的那个代码感觉写的有点不好,而且弄的很乱,因此咱们可以参照他的研究结果按照自己的喜好修复变量名变可读,以及将相关部分自行实现,完成自己的需求。

通过阅读switch可得知,case 1,2,3的时候实际上是设置临时变量数值,a是pop,b是花指令,c是push null,d是啥没搞懂,e是将字符串变为函数指针,f是push 字符串到$memory,emmm这段我可能说的不大明白,因此我建议您对照底下我修改完成的的调试代码来理解。
因为在虚拟机运行过程中程序没有输出,因此我们要在各个地方插桩输出日志,因此理解了各个case是干啥的再在对应的地方设置一下就好了。

[PHP] 纯文本查看 复制代码
<?php
define("DEBUG", "vv");

/**
 * # DEBUG 调试说明
 *
 * ## 基础设置:
 *     DEBUG 设置为v输出eval的指令,vv输出全部虚拟机指令,vvv输出更多扩展信息
 *
 * ## DEBUG 日志代码缩写对应表
 *      s2p   String to Pointer, 字符串转指针
 *      pop   将$memory中$pointer指向的变量删除。//unset($memory[$pointer--]);
 *      push  将值添加到$memory的末尾,并++$pointer
 *      run   通过eval执行代码
 *
 * # 日志格式对应意义
 *      $src_index           $method         $code
 *      当前代码执行位置     方法缩写        相关信息
 *
 */

// Discuz 内部模拟
define("IN_DISCUZ", true);
define("IN_ADMINCP", true);
define("DISCUZ_ROOT", ' C:\Users\computer\PhpstormProjects\untitled');

/**
 * @param $obj object
 * @Return array
 */
function object_to_array($obj)
{
    $obj = (array)$obj;
    foreach ($obj as $k => $v) {
        if (gettype($v) == 'object' || gettype($v) == 'array') {
            $obj[$k] = (array)object_to_array($v);
        }
    }
    return $obj;
}

$var_dict = object_to_array(json_decode('{"$_var_0": "JLWMyIuD", "$memory": "JLWMyJ/D", "$_var_2": "JLWMyJPw", "$pointer": "JLWMyNag", "$_var_4": "JLWMyLfF", "$src_index": "JLWMyLTk", "$encoded_src": "JLWMyOmV", "$tmp": "JLWMyOSx", "$plain_src": "JLWMyPz6", "s_eval(": "ZXZhbCg="}'));
foreach ($var_dict as $var_key => $var_value) {
    $var_dict[$var_key] = base64_decode($var_value);
}
function s_eval($code)
{
    global $var_dict;
    /** @noinspection PhpUnusedLocalVariableInspection */
    global $_var_0, $memory, $_var_2, $pointer, $_var_4, $src_index, $encoded_src, $tmp, $plain_src;
    foreach ($var_dict as $var_key => $var_value) {
        if (strpos($code, $var_value) === false) {
            continue;
        } else {
            $code = str_replace($var_value, $var_key, $code);
        }
    }
    if (preg_match_all('/"(?:\\\\\d{1,3})+?"/', $code, $matches)) {
        $num_string = $matches[0][0];
        $code = str_replace($num_string, '"' . eval("return $num_string;") . '"', $code);
    }
    write_log_line('run', $code);
    $echo_vminfo = false;
    if (!(strpos($code, '$pointer') === false)) $echo_vminfo = true;
    //if($echo_vminfo)dump_vminfo();
    //dump_memory();
    $eval_result = eval($code);
    if ($echo_vminfo) dump_vminfo();
    dump_memory();
    return $eval_result;
}

function write_log_line($method, $code)
{
    if (!defined('DEBUG') || substr_count(DEBUG, 'v') < 1) return;
    if ($method != 'eval' && substr_count(DEBUG, 'v') < 2) return;
    global $src_index;
    print(sprintf("0x%08X\t%-4s %s \r\n", $src_index,$method,$code));
    return;
}

function dump_memory()
{
    if (!defined('DEBUG') || substr_count(DEBUG, 'v') < 3) return;
    global $memory, $pointer;
    for ($i = 0; $i <= $pointer; $i++)
        echo "  \$memory[$i]: $memory[$i]\n";
    return;
}

function dump_vminfo()
{
    if (!defined('DEBUG') || substr_count(DEBUG, 'v') < 3) return;
    global $pointer, $src_index;
    echo "  \$pointer:$pointer\t\$src_index:" . sprintf('0x%08X', $src_index) . "\r\n";
    return;
}

if (isset($_var_0)) {
    array_push($_var_0, $memory, $_var_2, $pointer, $_var_4, $src_index);
} else {
    $_var_0 = array();
}
static $encoded_src = null;
if (empty($encoded_src)) {
    $encoded_src = file_get_contents('pec.vmc');
}
$memory = array(__FILE__);
$_var_2 = array(0);
$pointer = $_var_4 = $src_index = 0;
$tmp = $plain_src = null;
try {
    while (1) {
        while ($src_index >= 0) {
            $plain_src = $encoded_src[$src_index++];
            switch ($plain_src ^ $encoded_src[$src_index++]) {
                case '1':
                    $tmp = (int)(($plain_src ^ $encoded_src[$src_index]) . ($plain_src ^ $encoded_src[$src_index + 1]));
                    $src_index += 2;
                    break;
                case '2':
                    $tmp = (int)(($plain_src ^ $encoded_src[$src_index]) . ($plain_src ^ $encoded_src[$src_index + 1]) . ($plain_src ^ $encoded_src[$src_index + 2]) . ($plain_src ^ $encoded_src[$src_index + 3]));
                    $src_index += 4;
                    break;
                case '3':
                    $tmp = (int)(($plain_src ^ $encoded_src[$src_index]) . ($plain_src ^ $encoded_src[$src_index + 1]) . ($plain_src ^ $encoded_src[$src_index + 2]) . ($plain_src ^ $encoded_src[$src_index + 3]) . ($plain_src ^ $encoded_src[$src_index + 4]) . ($plain_src ^ $encoded_src[$src_index + 5]) . ($plain_src ^ $encoded_src[$src_index + 6]) . ($plain_src ^ $encoded_src[$src_index + 7]) . ($plain_src ^ $encoded_src[$src_index + 8]) . ($plain_src ^ $encoded_src[$src_index + 9]));
                    $src_index += 10;
                    break;
                case 'a':
                    write_log_line('pop', "$pointer");
                    unset($memory[$pointer--]);
                    dump_memory();
                    continue 2;
                case 'b':
                    //花指令
                    $plain_src = null;
                    continue 2;
                case 'c':
                    $memory[++$pointer] = null;
                    write_log_line('push', "\$memory[$pointer], null");
                    dump_memory();
                    continue 2;
                case 'd':
                    if (is_scalar($memory[$pointer - 1])) {
                        $plain_src = $memory[$pointer - 1];
                        unset($memory[$pointer - 1]);
                        $memory[$pointer - 1] = $plain_src[$memory[$pointer]];
                    } else {
                        if (!is_array($memory[$pointer - 1])) {
                            $memory[$pointer - 1] = array();
                        }
                        $plain_src =& $memory[$pointer - 1][$memory[$pointer]];
                        unset($memory[$pointer - 1]);
                        $memory[$pointer - 1] =& $plain_src;
                        unset($plain_src);
                    }
                    continue 2;
                case 'e':
                    //s2p String to Pointer, 字符串转指针
                    write_log_line('s2p', "$memory[$pointer]");
                    switch ($memory[$pointer]) {
                        case 'this':
                            $memory[$pointer] =& $this;
                            break;
                        case 'GLOBALS':
                            $memory[$pointer] =& $GLOBALS;
                            break;
                        case '_SERVER':
                            $memory[$pointer] =& $_SERVER;
                            break;
                        case '_GET':
                            $memory[$pointer] =& $_GET;
                            break;
                        case '_POST':
                            $memory[$pointer] =& $_POST;
                            break;
                        case '_FILES':
                            $memory[$pointer] =& $_FILES;
                            break;
                        case '_COOKIE':
                            $memory[$pointer] =& $_COOKIE;
                            break;
                        case '_SESSION':
                            $memory[$pointer] =& $_SESSION;
                            break;
                        case '_REQUEST':
                            $memory[$pointer] =& $_REQUEST;
                            break;
                        case '_ENV':
                            $memory[$pointer] =& $_ENV;
                            break;
                        default:
                            $memory[$pointer] =& ${$memory[$pointer]};
                    }
                    dump_memory();
                    continue 2;
                case 'f':
                    $tmp = $plain_src ^ $encoded_src[$src_index++];
                    if ($tmp == 'd') {
                        $tmp = (int)(($plain_src ^ $encoded_src[$src_index]) . ($plain_src ^ $encoded_src[$src_index + 1]));
                        $src_index += 2;
                    } elseif ($tmp == 'q') {
                        $tmp = (int)(($plain_src ^ $encoded_src[$src_index]) . ($plain_src ^ $encoded_src[$src_index + 1]) . ($plain_src ^ $encoded_src[$src_index + 2]) . ($plain_src ^ $encoded_src[$src_index + 3]));
                        $src_index += 4;
                    } elseif ($tmp == 'x') {
                        $tmp = (int)(($plain_src ^ $encoded_src[$src_index]) . ($plain_src ^ $encoded_src[$src_index + 1]) . ($plain_src ^ $encoded_src[$src_index + 2]) . ($plain_src ^ $encoded_src[$src_index + 3]) . ($plain_src ^ $encoded_src[$src_index + 4]) . ($plain_src ^ $encoded_src[$src_index + 5]) . ($plain_src ^ $encoded_src[$src_index + 6]) . ($plain_src ^ $encoded_src[$src_index + 7]) . ($plain_src ^ $encoded_src[$src_index + 8]) . ($plain_src ^ $encoded_src[$src_index + 9]));
                        $src_index += 10;
                    } else {
                        break 2;
                    }
                    $memory[++$pointer] = '';
                    while ($tmp-- > 0) {
                        $memory[$pointer] .= $plain_src ^ $encoded_src[$src_index++];
                    }
                    write_log_line('push', "\$memory[$pointer], '$memory[$pointer]'");
                    dump_memory();
                    continue 2;
                default:
                    break 2;
            }
            while ($tmp-- > 0) {
                $plain_src .= $plain_src[0] ^ $encoded_src[$src_index++];
            }
            s_eval(substr($plain_src, 1));
        }
        if ($src_index == -1) {
            break;
        } elseif ($src_index == -2) {
            s_eval($_var_2[$_var_4 - 1]);
            $src_index = $_var_2[$_var_4];
            $_var_4 -= 2;
        } else {
            exit('KIVIUQ VIRTUAL MACHINE ERROR : Access violation at address ' . ($src_index < 0 ? $src_index : sprintf('%08X', $src_index)));
        }
    }
} catch (Exception $plain_src) {
    if (!empty($_var_0)) {
        $src_index = array_pop($_var_0);
        $_var_4 = array_pop($_var_0);
        $pointer = array_pop($_var_0);
        $_var_2 = array_pop($_var_0);
        $memory = array_pop($_var_0);
    }
    /** @noinspection PhpUnhandledExceptionInspection */
    throw $plain_src;
}
if (!empty($_var_0)) {
    $src_index = array_pop($_var_0);
    $_var_4 = array_pop($_var_0);
    $pointer = array_pop($_var_0);
    $_var_2 = array_pop($_var_0);
    $memory = array_pop($_var_0);
}

运行这个文件,得到输出:
QQ截图20180208202241.png
[Asm] 纯文本查看 复制代码
C:\myphp_www\PHPTutorial\php\php-5.6.27-nts\php.exe C:\Users\computer\PhpstormProjects\untitled\kvm.php
0x00000002        push $memory[1], null 
0x0000000E        push $memory[2], 'defined'
0x0000001C        push $memory[3], 'IN_DISCUZ'
0x00000052        run  $memory[$pointer-2]=$memory[$pointer-1]($memory[$pointer]);			//$memory[1]=defined('IN_DISCUZ');
0x00000054        pop  3 
0x00000056        pop  2 
0x0000007B        run  $memory[$pointer]=!$memory[$pointer]; 								//$memory[1]=!$memory[1];
0x000000A3        run  $memory[$pointer]=(bool)$memory[$pointer]; 
0x000000CB        run  if($memory[$pointer])$src_index=0x000001E6; 							//if(!defined('IN_DISCUZ'))$src_index=0x000001E6;
0x000000CD        pop  1 
0x000000CF        push $memory[1], null 
0x00000103        run  $memory[++$pointer]="defined"; 
0x00000143        run  $memory[++$pointer]="IN_ADMINCP"; 
0x00000179        run  $memory[$pointer-2]=$memory[$pointer-1]($memory[$pointer]);			//$memory[1]=defined('IN_ADMINCP');
0x0000017B        pop  3 
0x00000191        run  $src_index=0x00000197; 
0x00000199        pop  2 
0x000001BE        run  $memory[$pointer]=!$memory[$pointer]; 								//$memory[1]=!$memory[1];
0x000001E6        run  $memory[$pointer]=(bool)$memory[$pointer]; 
0x0000020E        run  if($memory[$pointer])$src_index=0x00000242; 							//if(!defined('IN_ADMINCP'))$src_index=0x00000242;
0x00000210        pop  1 
0x00000226        run  $src_index=0x000002A3; 
0x000002B9        push $memory[1], 'comiis_app_portal'
0x000002C7        push $memory[2], 'plugin_id'
0x000002C9        s2p  plugin_id															//$memory[2]=$plugin_id
0x000002ED        run  $memory[$pointer]=$memory[$pointer-1];								//$plugin_id='comiis_app_portal';
0x000002F1        pop  2 
0x000002F3        pop  1 
0x000002F5        push $memory[1], null 
0x00000309        push $memory[2], 'function_exists'
0x0000032D        push $memory[3], 'comiis_app_load_app_portal_data'
0x00000363        run  $memory[$pointer-2]=$memory[$pointer-1]($memory[$pointer]);			//$memory[1]=function_exists('comiis_app_load_app_portal_data');
0x00000365        pop  3 
0x00000367        pop  2 
0x0000038C        run  $memory[$pointer]=!$memory[$pointer]; 
0x000003A2        run  $src_index=0x000003A8; 
0x000003D0        run  if($memory[$pointer])$src_index=0x000003E8; 
0x000003EA        pop  1 
0x000003EC        push $memory[1], null 
0x000003FC        push $memory[2], 'file_exists'
0x000003FE        push $memory[3], null 
0x0000041D        run  $memory[$pointer]=DISCUZ_ROOT; 
0x00000471        run  $memory[++$pointer]="./source/plugin/"; 
0x000004A6        run  $memory[$pointer-1]=$memory[$pointer-1].$memory[$pointer]; 
0x000004A8        pop  4 
0x000004B6        push $memory[4], 'plugin_id'
0x000004B8        s2p  plugin_id 
0x000004D0        run  $src_index=0x000004D6; 
0x0000050B        run  $memory[$pointer-1]=$memory[$pointer-1].$memory[$pointer]; 
0x0000050D        pop  4 
0x00000529        push $memory[4], '/comiis_info/comiis.php'
0x0000055E        run  $memory[$pointer-1]=$memory[$pointer-1].$memory[$pointer]; 
0x00000560        pop  4 
0x00000596        run  $memory[$pointer-2]=$memory[$pointer-1]($memory[$pointer]); 
0x00000598        pop  3 
0x0000059A        pop  2 
0x000005C4        run  if($memory[$pointer])$src_index=0x000005DC; 
0x000005C6        pop  1 
0x000005DC        run  $src_index=0x00000746; 
0x00000761        run  $memory[++$pointer]=false; 
0x0000076F        run  $src_index=-1; 
 Process finished with exit code 0

已经可以被人理解了,对着写逻辑即可完成反编译。
但是,怎么自动化实现反编译这一过程呢?麻烦各位热心的坛友支支招吧。

免费评分

参与人数 6威望 +2 吾爱币 +14 热心值 +4 收起 理由
粉藍弟 + 1 + 1 我很赞同!
Hmily + 2 感谢发布原创作品,吾爱破解论坛因你更精彩!
L4Nce + 10 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
Ganlv + 1 + 1 我很赞同!
罗婷 + 1 [b]case 1 2 3,只能说是调用现有堆栈执行指令,建议多多观察[\b]
phper + 1 + 1 热心回复!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

yxh51930 发表于 2018-2-9 08:12
谢谢  大佬 分享!~~~~~~~~~
 楼主| jane35622 发表于 2018-2-9 14:27
liang939558108 发表于 2018-2-9 13:44
代码地址:https://pan.baidu.com/s/1jJVxWnO
您好,按照您的这个教程我试了一下,木有成功,能不能帮忙看 ...

这个函数名也被加密了,暂时不清楚是什么加密,事必以专,滴水穿石,慢慢来相信你自己也可以的
sgege 发表于 2018-2-8 20:42
谢谢你 很需要啊 讲得很详细  祝你生活愉快
linuxprobe 发表于 2018-2-8 20:59
看这些代码,感觉很高大上。
萌萌哒的小白 发表于 2018-2-8 21:08
慢慢学习!谢谢分享!
dj592 发表于 2018-2-8 21:17
多谢讲解,学习了
藤原拓海. 发表于 2018-2-8 22:43
支持一下
lucky. 发表于 2018-2-8 23:09
感谢分享。
蚂蚁不敢花 发表于 2018-2-9 04:08
谢谢分享
壹天 发表于 2018-2-9 09:13
感谢楼主分享!
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-19 05:22

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表