RCTF 2024 wp [复现]

2048

image-20250518111321106

2048游戏,分数达到1000000即可获得flag。

每轮将获得分数为输入的sorce,上限为当前得分,初次上限为1w,那么每次成功分数翻倍的情况下用不了几次即可到达100w分:

RCTF{you_are_2048_master}

bloker_vm

字符串追踪到sub_411A10

image-20250518144332054

密文给出了,sub_41100F是个RC4加密,那我们其实可以猜出来这个流程了

RC4->移位->异或

RC4->异或->移位….组合测一下其实也可以出

先看看能不能直接调试出吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
rc4_key = "thisisyoursecretke"
enc = [
0x80, 0x05, 0xE3, 0x2F, 0x18, 0x2F, 0xC5, 0x8C,
0x25, 0x70, 0xBC, 0x05, 0x1C, 0x4F, 0xF2, 0x02,
0xE5, 0x3E, 0x02, 0x2F, 0xE5, 0x11, 0xA3, 0xC0
]

from Crypto.Cipher import ARC4
key = [ord(rc4_key[i]) for i in range(len(rc4_key))]

rc4 = ARC4.new(bytes(key))
data = rc4.decrypt(bytes(enc))

xor_data = bytearray(data)

for i in range(len(xor_data)):
xor_data[i] = ((xor_data[i] >> 6) | (xor_data[i] << 2)) & 0xFF
print(chr(xor_data[i] ^ 0x7D),end='')

值得注意的是key长度是18而不是19

1
RCTF{a_baby_debug_bloker}

Dr.Akira

image-20250520171640617

Ergrelet/unlicense 在 0.4.0

这个工具脱壳,定位到函数

image-20250521170838368

这段 CTF 逆向题中的反汇编代码是在 访问 Windows 注册表的某个键值并尝试获取一个叫“MyKeys”的值

尝试访问注册表路径:HKEY_CURRENT_USER\Software\DrAkira,并读取其中名为 "MyKeys" 的键值内容。如果该注册表键不存在,则报错 "Registry key not found.";如果键存在但没有该键值,则报错 "MyKeys value not found.";否则,将该键值存入传入的参数 a2 中。

添加后即可

交叉引用一下这里的函数sub_1001A2BB0

sub_1001A2F20

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
char __fastcall sub_1001A2F20(__int64 a1)
{
__int64 v2; // rax
char v3; // si
__int64 v4; // rax
__int64 v6; // [rsp+38h] [rbp-28h] BYREF
__int64 v7; // [rsp+40h] [rbp-20h] BYREF
__int64 v8; // [rsp+48h] [rbp-18h] BYREF
__int64 v9; // [rsp+50h] [rbp-10h] BYREF
__int64 v10; // [rsp+58h] [rbp-8h] BYREF
__int64 savedregs; // [rsp+60h] [rbp+0h] BYREF

v10 = 0LL;
v9 = 0LL;
v8 = 0LL;
v6 = 0LL;
sub_1001A2BB0(a1, &v10);
v2 = v10;
if ( v10 )
v2 = *(_QWORD *)(v10 - 8);
if ( v2 )
{
v4 = v10;
if ( v10 )
v4 = *(_QWORD *)(v10 - 8);
v7 = v4 / 2;
sub_10000D7C0(&v9, &qword_10024A080, 1LL, &v7);
sub_1001A27B0(a1, &v9, v10);
sub_1001A2CB0(a1, &v6, v9);
sub_10000D770(&v8, v6, (__int64)&qword_10024A080);
sub_1001A2710(a1, &v8);
v3 = sub_1001A5A10(v8);
if ( v3 )
sub_10000D770((__int64 *)(a1 + 24), v9, (__int64)&qword_10024A0B8);
}
else
{
v3 = 0;
}
sub_1001A2ED0(&savedregs);
return v3;
}

sub_10000D7C0可能是构造结构数组 / 字符串数组

sub_1001A27B0加密 / 映射 / 字符转换

将输入字符串(注册表中的MyKeys)进行逐字节处理、截取,转化为另一个字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
__int64 __fastcall sub_1001A27B0(__int64 rcx0, __int64 a2, __int64 a3)
{
__int64 v5; // rax
int v6; // ebx
int n13_1; // r12d
int i; // r13d
char v9; // al
__int64 v11; // [rsp+50h] [rbp-20h] BYREF
__int64 v12; // [rsp+58h] [rbp-18h] BYREF
__int64 n13; // [rsp+60h] [rbp-10h] BYREF
__int64 a1; // [rsp+68h] [rbp-8h] BYREF
__int64 savedregs; // [rsp+70h] [rbp+0h] BYREF

a1 = 0LL;
v11 = 0LL;
v12 = 0LL;
LODWORD(v5) = a3;
if ( a3 )
v5 = *(_QWORD *)(a3 - 8);
v6 = v5;
n13_1 = (int)v5 / 2;
if ( (int)v5 % 2LL )
++n13_1;
n13 = n13_1;
sub_10000D7C0(&a1, (__int64)&qword_10024A048, 1LL, &n13);
if ( n13_1 >= 1 )
{
for ( i = 0; i < n13_1; *(_BYTE *)(a1 + i - 1) = v9 )
{
if ( 2LL * ++i > v6 )
{
sub_100008F20(&v11);
sub_10000A0B0(&v12, a3, 2LL * i - 1, 1LL);
sub_1000090D0(&v11, &unk_100249ED8, v12, 0LL);
v9 = sub_100053BF0(v11);
}
else
{
sub_100008F20(&v12);
sub_10000A0B0(&v11, a3, 2LL * i - 1, 2LL);
sub_1000090D0(&v12, &unk_100249ED8, v11, 0LL);
v9 = sub_100053BF0(v12);
}
}
}
sub_10000D770(a2, a1, &qword_10024A0B8);
return sub_1001A2770(&savedregs);
}

应该是两两处理,输入可能是16进制的字符串

sub_1001A2CB0可能是加密函数

其中发现了check函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
char __fastcall sub_1001A5A10(__int64 n30_2)
{
__int64 v1; // rax
__int64 v2; // rax
__int64 v3; // rax
__int64 v4; // rax
__int64 v5; // rax
unsigned int v6; // edi
int n12; // ebx
int n5; // esi
__int64 v9; // rax
__int64 v10; // rax
char v11; // si
__int64 n30; // rax
__int64 v13; // rax
int v14; // eax
int v15; // ebx
int n4; // ebx
_QWORD v18[5]; // [rsp+40h] [rbp-50h]
_BYTE v19[19]; // [rsp+68h] [rbp-28h]
_BYTE j_[13]; // [rsp+7Bh] [rbp-15h] BYREF
__int64 n30_1; // [rsp+88h] [rbp-8h]
__int64 savedregs; // [rsp+90h] [rbp+0h] BYREF

n30_1 = n30_2;
sub_10000D720(n30_2);
v1 = sub_1001A5C60(&unk_10024A610, 1LL);
if ( v1 )
v1 += 32LL;
v18[0] = v1;
v2 = sub_1001A5D70(&unk_10024A738, 1LL);
if ( v2 )
v2 += 40LL;
v18[1] = v2;
v3 = sub_1001A5EC0(&unk_10024A860, 1LL);
if ( v3 )
v3 += 40LL;
v18[2] = v3;
v4 = sub_1001A6010(&unk_10024A988, 1LL);
if ( v4 )
v4 += 32LL;
v18[3] = v4;
v5 = sub_1001A6150(&unk_10024AAB0, 1LL);
if ( v5 )
v5 += 32LL;
v18[4] = v5;
v6 = 0;
n12 = -1;
do
{
n5 = byte_1001C6EA0[++n12] - 1;
if ( n5 >= 0 && n5 < 5LL )
{
sub_1001A31B0();
v9 = sub_10000D650(n30_1);
(*(void (__fastcall **)(_QWORD, __int64, __int64, _QWORD))(*(_QWORD *)v18[n5] + 8LL))(v18[n5], n30_1, v9, v6);
v10 = sub_10000D650(n30_1);
v6 = (**(__int64 (__fastcall ***)(_QWORD, __int64, __int64, _QWORD))v18[n5])(v18[n5], n30_1, v10, v6);
}
}
while ( n12 < 12 );
v11 = 1;
v19[0] = 69;
v19[1] = -85;
v19[2] = -67;
v19[3] = -66;
v19[4] = -81;
v19[5] = -85;
v19[6] = 70;
v19[7] = 70;
v19[8] = 39;
v19[9] = 93;
v19[10] = -120;
v19[11] = -103;
v19[12] = -98;
v19[13] = 113;
v19[14] = 126;
v19[15] = -104;
v19[16] = 0;
v19[17] = -110;
v19[18] = -105;
qmemcpy(j_, "j#", 2);
j_[2] = 127;
j_[3] = 123;
j_[4] = 87;
j_[5] = -52;
j_[6] = 109;
j_[7] = 100;
j_[8] = 120;
j_[9] = -32;
j_[10] = 97;
n30 = n30_1;
if ( n30_1 )
n30 = *(_QWORD *)(n30_1 - 8) + 1LL;
if ( n30 <= 30 )
{
LODWORD(v13) = n30_1;
if ( n30_1 )
v13 = *(_QWORD *)(n30_1 - 8) + 1LL;
v14 = v13 - 1;
if ( v14 >= 0 )
{
v15 = -1;
do
{
++v15;
if ( *(_BYTE *)(n30_1 + v15) != v19[v15] )
v11 = 0;
}
while ( v14 > v15 );
}
}
else
{
v11 = 0;
}
n4 = -1;
do
v18[++n4] = 0LL;
while ( n4 < 4 );
sub_1001A59E0(&savedregs);
return v11;
}

需要先逆向check函数

中间通过LLM了解到他这里开头几个函数涉及虚表,可能需要动调,但是动调有很多反调试,Sharod可过,如下拉满即可(注意的是勾选完需要重启调试器)

涉及的细节可以参考SharpOD 反反调试插件 v0.6e (增加功能和修复BUG) - 吾爱破解 - 52pojie.cn

image-20250623171923813

经过漫长的动调寻找,终于找到了隐藏的check逻辑

image-20250623205437271

从下面找过去的,00000001001A628F

动调发现的,会跳转到以上地址

image-20250624100739424

image-20250623205513314

从byte数组发现这个顺序:1, 4, 5, 4, 3, 2, 5, 2, 3, 1, 4, 2, 1

在x64dbg中已经可以看出来了,其中几个是没用的,有用的就5个,按顺序其实已经可以知道逻辑了

image-20250624103448935

注意要逆向,我们从右边可以发现是0x1-0x5

image-20250624111145187

也可以通过参数推导其行为(下图是check5)

image-20250624111504131

参考RCTF2024赛后复现(上) - 0xD009(可以看看学习下具体的反调试技术,不依赖工具)

2024 XCTF 联赛 RCTF 部分题解 - S1uM4i

因此得到以下题解,加密部分还包括维纳攻击

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from Crypto.Util.number import *

def check1(arr):
for i in range(len(arr)):
arr[i] ^= 8

def check23(arr, choice, idx):
if choice == 0:
arr[idx+1] ^= 0x80
elif choice == 1:
arr[idx+1] ^= 0x44
elif choice == 2:
arr[idx] ^= 0x22

def check4(arr, start, end):
for i in range(start, end+1):
arr[i] += 0xbe
arr[i] &= 0xff

def check5(arr, start, end):
for i in range(start, end+1):
arr[i] -= 0xef
arr[i] &= 0xff

enc = bytes.fromhex('45 AB BD BE AF AB 46 46 27 5D 88 99 9E 71 7E 98 00 92 97 6A 23 7F 7B 57 CC 6D 64 78 E0 61')
enc = list(enc)
check1(enc)
check23(enc, 0, 0x1b)
check4(enc, 0xf, 0x13)
check1(enc)
check23(enc, 1, 0x13)
check23(enc, 0, 0x17)
check5(enc, 0xd, 0x12)
check23(enc, 0, 0xf)
check23(enc, 1, 0x12)
check4(enc, 0xa, 0xe)
check5(enc, 0x5, 0xa)
check4(enc, 0x1, 0x5)
check1(enc)
print(bytes(enc))

q = [0x3F, 0x68, 0x38, 0x6D, 0x7C, 0x52, 0xFB, 0xB3, 0xDF, 0xE3,
0xCC, 0xAC, 0xC3, 0xDC, 0xFD, 0x1B, 0xA6, 0x6C, 0xAA, 0xC7,
0xA5, 0xFC, 0x04, 0x0F, 0x7C, 0xC0, 0xE5, 0x5B, 0xF1, 0xAB,
0xEB, 0x92, 0x33, 0x0B, 0xA4, 0x92, 0xAC, 0x81, 0x08, 0x47,
0x91, 0xCB, 0xD7, 0x0B, 0x97, 0xA5, 0x45, 0xA1, 0xCF, 0x9D,
0x0C, 0xC8, 0x7E, 0x15, 0x9C, 0x28, 0x69, 0x64, 0xB9, 0x06,
0x95, 0x5C, 0x79, 0x88, 0x30, 0xD3, 0x6F, 0x18, 0xDC, 0xB0,
0x03, 0xB8, 0xB4, 0x77, 0xCE, 0x3E, 0x5A, 0xB1, 0x57, 0x6C,
0x54, 0x84, 0xA6, 0xF6, 0xD1, 0xD7, 0x97, 0x18, 0xBD, 0xA5,
0x0B, 0xDC, 0x3C, 0x1A, 0x59, 0x10, 0xDF, 0xE6, 0xF0, 0xDC,
0x90, 0x08, 0x0B, 0x3F, 0x29, 0x13, 0xC2, 0x41, 0x9F, 0xDA,
0x46, 0x91, 0xED, 0x6D, 0x7C, 0x2A, 0x3E, 0x7C, 0xF9, 0x2C,
0x3F, 0xCD, 0x60, 0xD5, 0x56, 0x03, 0x68, 0xE9, 0x01]

p = [0xC5, 0xB3, 0x8C, 0xCE, 0x2D, 0x27, 0x13, 0x9C, 0x5B, 0x55,
0x2C, 0x93, 0xF8, 0xFE, 0x3D, 0x6C, 0xFB, 0x2B, 0x03, 0xF7,
0x0E, 0x27, 0xBC, 0x80, 0xBB, 0xD1, 0x78, 0xC9, 0x91, 0xE9,
0x10, 0xDB, 0x55, 0x3A, 0x63, 0x29, 0x63, 0x6B, 0x74, 0xB4,
0x10, 0x6F, 0xC2, 0x09, 0xCB, 0xFB, 0x6B, 0x00, 0x43, 0x4F,
0x03, 0xB2, 0xD9, 0xB7, 0xEE, 0x0C, 0xE2, 0xF8, 0x16, 0xD1,
0x4F, 0x68, 0xBE, 0xAE, 0x89, 0x1C, 0xA3, 0xAE, 0x6C, 0x1F,
0x13, 0x77, 0x2F, 0x1C, 0x9D, 0x2F, 0x48, 0x54, 0xBB, 0x9A,
0xD8, 0x12, 0x50, 0xEE, 0x1E, 0x1D, 0x3A, 0x40, 0x39, 0x14,
0x47, 0x10, 0x81, 0x49, 0x11, 0xA2, 0x4B, 0x1A, 0x9D, 0x59,
0xED, 0x9F, 0x04, 0xA2, 0x56, 0x63, 0x2D, 0x7E, 0xCE, 0xD4,
0xAA, 0x3F, 0xBB, 0xB9, 0x62, 0xCD, 0xB9, 0x04, 0x7F, 0xDF,
0x83, 0xA9, 0x04, 0xF6, 0xD6, 0x89, 0x2D, 0x24]

e = bytes_to_long(bytes(p[::-1]))
N = bytes_to_long(bytes(q[::-1]))

# print(e)
# print(N)
# wiener attack
d = 24148649361037793876488182229470214987409054478429360732252558238236696170653

m = pow(bytes_to_long(bytes(enc[::-1])), d, N)
# print(hex(m))
print(hex(bytes_to_long(long_to_bytes(m)[::-1])))

最后解得,改下后缀即可,改成jpg

diary

PPTT

根据约束写出了这样的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
from z3 import *
from Crypto.Util.number import *

# 定义主变量
v23 = BitVec('v23', 64)
v24 = BitVec('v24', 64)
v25 = BitVec('v25', 64)

# 中间变量必须由主变量计算得出
v20 = v24 & v23
v19 = (v20 | (v25 & v23)) + 65670
v18 = ((v25 & v23) ^ (v25 & v24)) - 1131796
v17 = v20 ^ (v25 & v23)

s = Solver()

# 其他约束
s.add(v23 ^ ( (v20 & ~v18) | (v20 & ~v19) | (v17 & v19) | (v25 & v23 & ~v18) ) == 0x400010000622000)
s.add(v18 ^ (v19 - v20) == 0x2100A0203EFBB8B)
s.add(v17 ^ v19 ^ v20 == 0x4083102108E)
s.add( (v19 ^ v17) - v18 == 0x1551566F3C6485ED )
s.add(v18 ^ v19 ^ (v25 & v24) == 0x40836ECAB9A)
s.add( (v17 ^ v20) - v18 == 0x3E51566F3C718563 )
s.add(v23 - v24 == 0x1AEFF6FDFC121BF1)
s.add((v23 + v24 + v25) % 10 == 8)

while s.check() == sat:
m = s.model()
tmp = long_to_bytes(m[v25].as_long())
s.add(Or(v23 != m[v23], v24 != m[v24], v25 != m[v25]))
if len(str(tmp)) != 11 or tmp.find(b'C') == -1 or tmp.find(b'F') == -1 or tmp.find(b'}') == -1:
continue
sol_v23 = m[v23].as_long()
sol_v24 = m[v24].as_long()
sol_v25 = m[v25].as_long()
print("=========================")
print(f"v23 = {sol_v23}")
print(f"v24 = {sol_v24}")
print(f"v25 = {sol_v25}")
mid1 = long_to_bytes(m[v23].as_long())
mid2 = long_to_bytes(m[v24].as_long())
mid3 = long_to_bytes(m[v25].as_long())
print(mid1[::-1])
print(mid2[::-1])
print(mid3[::-1])
print(len(mid3))
# 后续处理代码(同原版)
processed_v29 = [0] * 24
# 分解v23(块i=0)
for i in range(8):
byte = (sol_v23 >> (56 - 8*i)) & 0xFF
processed_v29[7 - i] = byte
# 分解v24(块i=1)
for i in range(8):
byte = (sol_v24 >> (56 - 8*i)) & 0xFF
processed_v29[15 - i] = byte
# 分解v25(块i=2)
for i in range(8):
byte = (sol_v25 >> (56 - 8*i)) & 0xFF
processed_v29[23 - i] = byte

# 逆交换操作(注意顺序要反向)
swaps = [
(2,0), (14,6), (5,13), (11,12), (1,23),
(10,4), (21,22), (20,9), (3,19), (18,8),
(7,17), (15,16)
]
for a, b in reversed(swaps):
processed_v29[a], processed_v29[b] = processed_v29[b], processed_v29[a]

else:
print("No solution found.")

问题是这个是个多解问题,需要排除掉一些,这意味着我们需要根据一些调试,但是调试会出现异常,应该是有反调

找一下

image-20250518211558779

1
2
.text:00DEC086 xor     eax, eax    ; 将eax寄存器清零
.text:00DEC088 mov eax, [eax] ; 尝试读取地址0x0的内容

在开头发现了

image-20250519114235304

这个函数

image-20250519114338990

if ( *a1 == -1073741819 ) // 0xC0000005 访问违规异常(Access Violation)

1
2
3
4
5
VirtualProtect(*(LPVOID *)(a3 + 184), 2u, 0x40u, flOldProtect);
*(_BYTE *)(*(_DWORD *)(a3 + 184) + 1) = -112; // -112 = 0x90, 是 NOP
VirtualProtect(*(LPVOID *)(a3 + 184), 2u, flOldProtect[0], flOldProtect);
++*(_DWORD *)(a3 + 184);
*(_DWORD *)(a3 + 192) |= 0x100u;

这几行的目的是:

  • 获取并修改指令内存权限为可写执行
  • 把某个地址处的第二个字节修改为 0x90(NOP),通常是用于覆盖断点或异常指令
  • 恢复原来的内存权限
  • 移动该地址指针,并设置一个状态标志位

if (*a1 == 0x80000004) // EXCEPTION_SINGLE_STEP

这是 调试器使用的单步异常(Single Step),通常由 Trap Flag 引发

这里记录了 dword_42944C 的值,并增加计数 dword_429450++

这也是一种检测调试器是否设置了Trap Flag进行调试

交叉引用一下dword_42944C

image-20250519114720562

image-20250519114737035

sub_418200 中出现的 dword_42944C 很有可能就是和这个 dword_429448 相关的全局变量——比如记录触发异常修复的地址。

  1. 检测是否有调试器(利用异常 + 单步)
  2. 如果异常发生了并是预期的访问违规,说明不是调试器触发的,就修复某些代码(比如还原跳转逻辑、去掉非法指令)
  3. 如果异常是调试器造成的(比如触发 Trap Flag),记录下来
  4. 如果调试器没有正确模拟这些行为,程序会因异常而崩溃

我们发现0X41C0D2就是第二个红框的位置,其实就是在跳过第一次swap

image-20250519115413236

这个函数其实是RCTF2024赛后复现(上) - 0xD009

image-20250519114353296

调试:

输入 : abcdefghijklmnopqrstuvwx

先序遍历后: abdhpqirsejtukvwcflxmgno

中序遍历后: phqdrisbtjuevkwaxlfmcngo

swap后 : qopmukwlfcrveisxabtdjgnh

Str1的要求: tyeuaTsm}qjrp

tyeuaTsm}qjrp

根据这个来缩小范围

1
2
3
4
5
6
7
8
9
pre = '?' * 11 + 'tyeuaTsm}qjrp'
dummy = 'abcdefghijklmnopqrstuvwx'
pre_dummy = 'abdhpqirsejtukvwcflxmgno'
ret_dummy = 'qopmukwlfcrveisxabtdjgnh'

res = ''
for i in range(24):
res += pre[pre_dummy.index(ret_dummy[i])]
print(res)

?p?qyeamsT?u???}??t??jr?

或者

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pre = '?' * 11 + 'tyeuaTsm}qjrp'
dummy = 'abcdefghijklmnopqrstuvwx'
pre_dummy = 'abdhpqirsejtukvwcflxmgno'
ret_dummy = 'qopmukwlfcrveisxabtdjgnh'

print()

const = ''
for i in range(24):
const += pre[pre_dummy.index(dummy[i])]
print(const)

# ??T??sj???emqrp????tyua}

ret_const = ''
for i in range(24):
ret_const += const[dummy.index(ret_dummy[i])]
print(ret_const)
# ?p?qyeamsT?u???}??t??jr?

其实上面还是错的,因为题目里藏了一个hook strcmp

1
2
3
4
5
6
7
8
9
int sub_A78370()
{
HMODULE ModuleHandleA; // [esp+DCh] [ebp-8h]

__CheckForDebuggerJustMyCode(&unk_A8C038);
ModuleHandleA = GetModuleHandleA(0);
sub_A71357((int)ModuleHandleA, "strcmp", (int)sub_A71285);
return 0;
}

这个藏在 initterm 的全局初始化函数中。

image-20250520170036532

会对第13个执行一个++的操作

因此 “tyeuaTsm}qjrp”正确的结果应该是 “tyeuaTsm}qjsp”

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from z3 import *
from Crypto.Util.number import *

# 定义主变量
v23 = BitVec('v23', 64)
v24 = BitVec('v24', 64)
v25 = BitVec('v25', 64)

# 中间变量必须由主变量计算得出
v20 = v24 & v23
v19 = (v20 | (v25 & v23)) + 65670
v18 = ((v25 & v23) ^ (v25 & v24)) - 1131796
v17 = v20 ^ (v25 & v23)

s = Solver()

# 其他约束
s.add(v23 ^ ( (v20 & ~v18) | (v20 & ~v19) | (v17 & v19) | (v25 & v23 & ~v18) ) == 0x400010000622000)
s.add(v18 ^ (v19 - v20) == 0x2100A0203EFBB8B)
s.add(v17 ^ v19 ^ v20 == 0x4083102108E)
s.add( (v19 ^ v17) - v18 == 0x1551566F3C6485ED )
s.add(v18 ^ v19 ^ (v25 & v24) == 0x40836ECAB9A)
s.add( (v17 ^ v20) - v18 == 0x3E51566F3C718563 )
s.add(v23 - v24 == 0x1AEFF6FDFC121BF1)
s.add((v23 + v24 + v25) % 10 == 8)

s.add((v23 >> 56) & 0xff == ord('m'))
s.add((v23 >> 48) & 0xff == ord('a'))
s.add((v23 >> 40) & 0xff == ord('e'))
s.add((v23 >> 32) & 0xff == ord('y'))
s.add((v23 >> 24) & 0xff == ord('q'))
s.add((v23 >> 8) & 0xff == ord('p'))

s.add((v24 >> 24) & 0xff == ord('u'))
s.add((v24 >> 8) & 0xff == ord('T'))
s.add((v24 >> 0) & 0xff == ord('s'))

s.add((v25 >> 0) & 0xff == ord('}'))
s.add((v25 >> 48) & 0xff == ord('s'))
s.add((v25 >> 40) & 0xff == ord('j'))
s.add((v25 >> 16) & 0xff == ord('t'))

while s.check() == sat:
m = s.model()
s.add(Or(v23 != m[v23], v24 != m[v24], v25 != m[v25]))
# sol_v23 = m[v23].as_long()
# sol_v24 = m[v24].as_long()
# sol_v25 = m[v25].as_long()
flag = b""
# print(f"v23 = {sol_v23}")
# print(f"v24 = {sol_v24}")
# print(f"v25 = {sol_v25}")
flag += long_to_bytes(m[v23].as_long())[::-1]
flag += long_to_bytes(m[v24].as_long())[::-1]
flag += long_to_bytes(m[v25].as_long())[::-1]

dummy = 'abcdefghijklmnopqrstuvwx'
ret_dummy = 'qopmukwlfcrveisxabtdjgnh'

res = ""
for i in range(24):
res += chr(flag[ret_dummy.index(dummy[i])])
if "CTF" in res:
print(res)
print("=========================")
else:
print("No solution found.")

最后出来:

image-20250520165323098

找了下原因是这样:

16,17,9,19,12,8,21,23,13,20,5,7,3,22,1,2,0,10,14,18,4,11,6,15

15,17,9,19,12,8,21,23,13,20,5,7,3,22,1,2,0,10,14,18,4,11,6,16,

突然想起来了是之前我们把那个xor给patch了,所以第一次交换就运行了,但是其实是不做交换的那么码表就是

dpsqyeamsTau{nqR}CtFwjsk

RCTF{sjknwemqspsdaqtyua}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from z3 import *
from Crypto.Util.number import *

# 定义主变量
v23 = BitVec('v23', 64)
v24 = BitVec('v24', 64)
v25 = BitVec('v25', 64)

# 中间变量必须由主变量计算得出
v20 = v24 & v23
v19 = (v20 | (v25 & v23)) + 65670
v18 = ((v25 & v23) ^ (v25 & v24)) - 1131796
v17 = v20 ^ (v25 & v23)

s = Solver()

# 其他约束
s.add(v23 ^ ( (v20 & ~v18) | (v20 & ~v19) | (v17 & v19) | (v25 & v23 & ~v18) ) == 0x400010000622000)
s.add(v18 ^ (v19 - v20) == 0x2100A0203EFBB8B)
s.add(v17 ^ v19 ^ v20 == 0x4083102108E)
s.add( (v19 ^ v17) - v18 == 0x1551566F3C6485ED )
s.add(v18 ^ v19 ^ (v25 & v24) == 0x40836ECAB9A)
s.add( (v17 ^ v20) - v18 == 0x3E51566F3C718563 )
s.add(v23 - v24 == 0x1AEFF6FDFC121BF1)
s.add((v23 + v24 + v25) % 10 == 8)

s.add((v23 >> 56) & 0xff == ord('m'))
s.add((v23 >> 48) & 0xff == ord('a'))
s.add((v23 >> 40) & 0xff == ord('e'))
s.add((v23 >> 32) & 0xff == ord('y'))
s.add((v23 >> 24) & 0xff == ord('q'))
s.add((v23 >> 8) & 0xff == ord('p'))

s.add((v24 >> 24) & 0xff == ord('u'))
s.add((v24 >> 8) & 0xff == ord('T'))
s.add((v24 >> 0) & 0xff == ord('s'))

s.add((v25 >> 0) & 0xff == ord('}'))
s.add((v25 >> 48) & 0xff == ord('s'))
s.add((v25 >> 40) & 0xff == ord('j'))
s.add((v25 >> 16) & 0xff == ord('t'))

while s.check() == sat:
m = s.model()
s.add(Or(v23 != m[v23], v24 != m[v24], v25 != m[v25]))
# sol_v23 = m[v23].as_long()
# sol_v24 = m[v24].as_long()
# sol_v25 = m[v25].as_long()
flag = b""
# print(f"v23 = {sol_v23}")
# print(f"v24 = {sol_v24}")
# print(f"v25 = {sol_v25}")
flag += long_to_bytes(m[v23].as_long())[::-1]
flag += long_to_bytes(m[v24].as_long())[::-1]
flag += long_to_bytes(m[v25].as_long())[::-1]

dummy = 'abcdefghijklmnopqrstuvwx'
ret_dummy = 'qopmukwlfcrveisaxbtdjgnh'

res = ""
for i in range(24):
res += chr(flag[ret_dummy.index(dummy[i])])
if "RCTF" in res:
print(res)
print("=========================")
else:
print("No solution found.")

image-20250520170608597 0