
又写到第三篇了,也是最后的要解决的时候了。
0x01:前文回顾
在上篇文章中,我们拿到了一个 flag,但是竟然是假的。后来通过分析,又拿到了一组算法,和一组数据。算法如下:
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]);拿到的数据如下:
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 库来进行模拟吧。
完整代码如下:
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))
运行查看输出:
enc_flag: Y0u_hav3_r3v3rs3_1t!0x03:验证 flag 是否正确
可以看到,这次输出的值和上次不一样了,我们把该 flag 放入我们之前的 C 语言代码中,并运行 C 语言的代码。
修改的代码如下:
strcpy(pMem, "Y0u_hav3_r3v3rs3_1t!");
dwLength = strlen(buf) + 1;这里计算长度为什么要加 1 呢?因为后面它比较的时候会减 1 的。
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) 的函数。说明我们的比较是成功的。
好了,最终的结果是:
Y0u_hav3_r3v3rs3_1t!