REVERSE-Conway’s trap
ISCC2026 WriteUp 提交模板
REVERSE-Conway’s trap
解题思路
1.先简单看下交互:

程序会先读取一个seed,然后判断是否正确,
打开IDA看一下,正确后走什么逻辑:

就在main函数中,seed输入正确的话,就会要求输入flag了。
我们去分析一下seed的逻辑:

在seed附近,有这么一段逻辑:
cur = v96[0]; // 指向“原始 seed 字节数组”的当前位置
n32_1 = 0;
delta = (char *)v94[0] - (char *)v96[0];
do
{
v11 = (void **)seed_str[0];
v12 = (void **)seed_str[0];
if ( n15 < 16 ) // 循环 16 次,把 32 个 hex 字符变成 16 个字节
v12 = seed_str;
v13 = *((_BYTE *)v12 + n32_1);
if ( n15 < 0x10 )
v11 = seed_str;
v14 = *((_BYTE *)v11 + n32_1 + 1);
if ( (unsigned __int8)(v13 - 48) > 9u )
{
if ( (unsigned __int8)(v13 - 97) > 5u )
{
if ( (unsigned __int8)(v13 - 65) > 5u )
v15 = 0;
else
v15 = v13 - 55;
}
else
{
v15 = v13 - 87;
}
}
else
{
v15 = v13 - 48;
}
if ( (unsigned __int8)(v14 - 48) > 9u )
{
if ( (unsigned __int8)(v14 - 97) > 5u )
{
if ( (unsigned __int8)(v14 - 65) > 5u )
v16 = 0;
else
v16 = v14 - 55;
}
else
{
v16 = v14 - 87;
}
}
else
{
v16 = v14 - 48;
}
v17 = v16 | (16 * v15); // 组合成一个字节

seed 输入长度要求为 0x20,随后每两个十六进制字符转成 1 字节,共得到 16 字节。之后程序对这 16 字节调用 0x401990 五次,并把结果按 %02x 格式转回 32 字节 hex 字符串,与运行时构造的目标串比较。
在比较点 0x4022cc 读取全局 std::string,目标串为:
df7b6a5d4da0f5facf32c4ee4b28b792
因此可以反向实现 0x401990,从目标状态倒推原始 seed。
这个字符串是从交叉引用后在这里找到,是通过CRT去初始化的:

2.逆向seed

0x401990 每轮先按偶奇字节混合:
new_even = old_even + old_odd
new_odd = rol8(new_even ^ old_odd, 1)
整体左移 1 字节,把原第 0 字节放到末尾。因为两个操作都是可逆的,所以从目标状态逆 5 轮即可得到 seed:
0b5e321c68e0e4fb2d972226e8c70f8d
3.flag逻辑
继续走,就到了flag的逻辑了


这个函数会提取flag的中间部分

然后sub_401EB0 里比较生成出来的十六进制串和目标串。
在 sub_401EB0 里调用了 sub_401E00。sub_401E00 ,上来就在算两个字符串的 hash,然后分别去解析:
- 0x401E02:"ntdll.dll"
- 0x401E39:"RtlAddVectoredExceptionHandler"
这已经很像“运行时解析 API”,而且解析的偏偏是 AddVectoredExceptionHandler。
接着看 0x401E70 之后的调用:
401e70 push offset sub_401DC0
401e75 push 1 401e77 call eax
eax 就是刚解析出来的 RtlAddVectoredExceptionHandler。参数形状也完全对得上:
RtlAddVectoredExceptionHandler(1, sub_401DC0);
到这一步,VEH 已经基本坐实了。
然后继续往下看,发现它不是只注册 handler 就完事,而是马上改代码:
401e84 mov eax, offset sub_401CB0
401e89 mov dl, [eax]
401e8b mov byte_41E46A, dl
401e91 mov byte ptr [eax], 0CCh
401e94 mov byte_41E46B, 1
- 保存 sub_401CB0 的首字节
- 把 sub_401CB0 首字节改成 0xCC
0xCC 就是 int3。看到“注册 VEH + 写 0xCC 到代码段”这一组组合拳,基本就已经可以判定这是用 breakpoint 异常做控制流跳转了
最后再看 handler sub_401DC0
401dc8 cmp dword ptr [eax], 80000003h
401dd0 cmp dword ptr [eax+0Ch], offset sub_401CB0
401ddc mov dword ptr [eax+0B8h], offset sub_401D00
401de6 or eax, 0FFFFFFFFh
所以真正的 flag 校验在 0x401d00。该函数先把栈上的 23 字节常量逐字节异或 0xcc,得到期望数组:

80 de 09 09 a6 a8 f2 30 32 d1 c3 88 4f c4 ef b8 5c 17 07 fb 05 6e 3e
然后对 inner[i] 做如下比较:
rol8(((seed[i & 15] ^ inner[i]) + i * 23) & 0xff, 3) ^ 0xaa == expected[i]
已知 seed 和 expected,我们就可以逐字节逆出 inner[i]。

Exp
#!/usr/bin/env python3
from struct import pack
TARGET = bytes.fromhex("df7b6a5d4da0f5facf32c4ee4b28b792")
def rol(x, n):
return ((x << n) & 0xff) | (x >> (8 - n))
def ror(x, n):
return (x >> n) | ((x << (8 - n)) & 0xff)
def undo_round(state):
# 先还原最后的整体左移一字节
mixed = [state[-1], *state[:-1]]
# 再逐组还原偶奇字节混合
plain = [0] * 16
for i in range(0, 16, 2):
new_a, new_b = mixed[i], mixed[i + 1]
old_b = ror(new_b, 1) ^ new_a
old_a = (new_a - old_b) & 0xff
plain[i] = old_a
plain[i + 1] = old_b
return bytes(plain)
def get_seed():
state = TARGET
for _ in range(5):
state = undo_round(state)
return state
def get_flag(seed):
enc = (
pack("<I", 0xc5c5124c)
+ pack("<I", 0xfc3e646a)
+ pack("<I", 0x440f1dfe)
+ pack("<I", 0x74230883)
+ pack("<I", 0x37cbdb90)
+ pack("<H", 0xa2c9)
+ bytes([0xf2])
)
target = [x ^ 0xcc for x in enc]
inner = []
for i, want in enumerate(target):
t = ror(want ^ 0xaa, 3)
ch = ((t - i * 23) & 0xff) ^ seed[i % 16]
inner.append(ch)
return b"ISCC{" + bytes(inner) + b"}"
def get_success_text():
key = b"DoNotPatchMe"
data = [
0x08200007, 0x01153106, 0x0c39090f, 0x4e3d012b,
0x010e0954, 0x132c0043, 0x00284f21, 0x54053e01,
0x45280017, 0x0a3b1d30, 0x150d3654,
]
cipher = b"".join(pack("<I", x) for x in data) + pack("<H", 0x4604)
return bytes(c ^ key[i % len(key)] for i, c in enumerate(cipher))
seed = get_seed()
flag = get_flag(seed)
print(f"seed: {seed.hex()}")
print(f"flag: {flag.decode()}")
print(f"message: {get_success_text().decode()}")

浙公网安备 33010602011771号