首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >babyvm 逆向分析(三)

babyvm 逆向分析(三)

作者头像
码农UP2U
发布2026-03-16 17:22:22
发布2026-03-16 17:22:22
650
举报
文章被收录于专栏:码农UP2U码农UP2U

又写到第三篇了,也是最后的要解决的时候了。

0x01:前文回顾

在上篇文章中,我们拿到了一个 flag,但是竟然是假的。后来通过分析,又拿到了一组算法,和一组数据。算法如下:

代码语言:javascript
复制
pMem[0] = pMem[0] ^ pMem[1];
pMem[1] = pMem[1] ^ pMem[2];
pMem[2] = pMem[2] ^ pMem[3];
pMem[3] = pMem[3] ^ pMem[4];
pMem[4] = pMem[4] ^ pMem[5];
pMem[5] = pMem[5] ^ pMem[6];

pMem[6] = (pMem[8] + 2 * pMem[7] + 3 * pMem[6]) * pMem[12];
pMem[7] = (pMem[9] + 2 * pMem[8] + 3 * pMem[7]) * pMem[12];
pMem[8] = (pMem[10] + 2 * pMem[9] + 3 * pMem[8]) * pMem[12];

swap(pMem[13], pMem[19]);
swap(pMem[14], pMem[18]);
swap(pMem[15], pMem[17]);

拿到的数据如下:

代码语言:javascript
复制
char EncCodeFlagNew[] = {
    0x69, 0x45, 0x2A, 0x37, 0x9, 0x17, 0x0C5, 0x0B, 0x5C, 0x72, 0x33,
    0x76, 0x33, 0x21, 0x74, 0x31, 0x5F, 0x33, 0x73, 0x72, 0x0, 0x0, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
};

0x02:解密思路

如果 EncCodeFlagNew 数组中的值是结果值,那么就需要通过这组值来还原 flag 了。

还原的顺序应该和执行 opcode 的流程是相反的,首先应该是 swap 交换,然后是 处理算术运算,最后是 处理异或运算。不过 swap 没有和其他的步骤没有关系,所以靠前靠后无所谓。

比较复杂的部分是第二步,也就是处理算术运算,我们用 z3 库来进行模拟吧。

完整代码如下:

代码语言:javascript
复制
from z3 import *

enc_flag = [
    0x69, 0x45, 0x2A, 0x37, 0x9, 0x17, 0x0C5, 0x0B, 0x5C, 0x72, 0x33,
    0x76, 0x33, 0x21, 0x74, 0x31, 0x5F, 0x33, 0x73, 0x72, 0x0, 0x0, 0x0,
    0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
]

enc_flag[15], enc_flag[17] = enc_flag[17], enc_flag[15]
enc_flag[14], enc_flag[18] = enc_flag[18], enc_flag[14]
enc_flag[13], enc_flag[19] = enc_flag[19], enc_flag[13]

s = Solver()

r = [BitVec(f"r{i}", 8) for i in range(13)]

s.add(enc_flag[0] == r[0] ^ r[1])
s.add(enc_flag[1] == r[1] ^ r[2])
s.add(enc_flag[2] == r[2] ^ r[3])
s.add(enc_flag[3] == r[3] ^ r[4])
s.add(enc_flag[4] == r[4] ^ r[5])
s.add(enc_flag[5] == r[5] ^ r[6])

s.add(enc_flag[6] == (3 * r[6] + 2 * r[7] + r[8]) * r[12])
s.add(enc_flag[7] == (3 * r[7] + 2 * r[8] + r[9]) * r[12])
s.add(enc_flag[8] == (3 * r[8] + 2 * r[9] + r[10]) * r[12])

s.add(enc_flag[9] == r[9])
s.add(enc_flag[10] == r[10])
s.add(enc_flag[11] == r[11])
s.add(enc_flag[12] == r[12])

if s.check() == sat:
    t = s.model()
    str_flag = []

    for i in r:
        str_flag.append(chr(t[i].as_long()))

    for i in range(13, 20):
        str_flag.append(chr(enc_flag[i]))

    print("enc_flag:", ''.join(str_flag))

运行查看输出:

代码语言:javascript
复制
enc_flag: Y0u_hav3_r3v3rs3_1t!

0x03:验证 flag 是否正确

可以看到,这次输出的值和上次不一样了,我们把该 flag 放入我们之前的 C 语言代码中,并运行 C 语言的代码。

修改的代码如下:

代码语言:javascript
复制
strcpy(pMem, "Y0u_hav3_r3v3rs3_1t!");
dwLength = strlen(buf) + 1;

这里计算长度为什么要加 1 呢?因为后面它比较的时候会减 1 的。

代码语言:javascript
复制
void CheckFlagNew()
{
    int i;

    for ( i = 0; dwLength - 1 > i; ++i )
    {
        if ( *((_BYTE *)pMem + i) != (unsigned char)EncCodeFlagNew[i] )
        {
            exit(0);
        }
    }
}

注意上面的代码,把 EncCodeFlagNew 强转为 unsigned char 了,否则比较会不成功。

编译运行,如下图:

可以看到,输出了 "And the flag is GWHT{true flag}" 。说明,上面的 CheckFlagNew 函数没有执行 exit(0) 的函数。说明我们的比较是成功的。

好了,最终的结果是:

代码语言:javascript
复制
Y0u_hav3_r3v3rs3_1t!
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-12-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 源代码010 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档