XYCTF2025 逆向WP

虽然这次失去Web手,但是配合新来的师傅,小队的队员们也一起努力打了不错的成绩。

墨水师傅的MDriver题也是拼尽全力无法战胜(,总体逆向题的质量挺不错的,没什么烂活,值得一试。

WARMUP

网上抄的VBS解密代码

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
Function Defuscator(vbs)
Dim t
t = InStr(1, vbs, "Execute", 1)
t = Mid(vbs, t + Len("Execute"))
t = Eval(t)
Defuscator = t
End Function

Dim fso, i, outFile
Const ForReading = 1, ForWriting = 2
Set fso = CreateObject("Scripting.FileSystemObject")

' 创建或打开一个文件用于写入输出
Set outFile = fso.OpenTextFile("output.txt", ForWriting, True)

For i = 0 To WScript.Arguments.Count - 1
Dim FileName
FileName = WScript.Arguments(i)
Dim MyFile
Set MyFile = fso.OpenTextFile(FileName, ForReading)
Dim vbs
vbs = MyFile.ReadAll
outFile.WriteLine Defuscator(vbs)
MyFile.Close
Next

outFile.Close
Set fso = Nothing

output.txt:

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
MsgBox "Dear CTFER. Have fun in XYCTF 2025!"
flag = InputBox("Enter the FLAG:", "XYCTF")
wefbuwiue = "90df4407ee093d309098d85a42be57a2979f1e51463a31e8d15e2fac4e84ea0df622a55c4ddfb535ef3e51e8b2528b826d5347e165912e99118333151273cc3fa8b2b3b413cf2bdb1e8c9c52865efc095a8dd89b3b3cfbb200bbadbf4a6cd4" ' 棰勮鐨凴C4鍔犲瘑缁撴灉锛堝崄鍏繘鍒舵牸寮忥級
qwfe = "rc4key"

' 淇鍚庣殑RC4鍔犲瘑鍑芥暟
Function RunRC(sMessage, strKey)
Dim kLen, i, j, temp, pos, outHex
Dim s(255), k(255)

' 鍒濆鍖栧瘑閽?
kLen = Len(strKey)
For i = 0 To 255
s(i) = i
k(i) = Asc(Mid(strKey, (i Mod kLen) + 1, 1)) ' 瀵嗛挜浣跨敤ASCII缂栫爜
Next

' KSA瀵嗛挜璋冨害
j = 0
For i = 0 To 255
j = (j + s(i) + k(i)) Mod 256
temp = s(i)
s(i) = s(j)
s(j) = temp
Next

' PRGA鍔犲瘑娴佺▼
i = 0 : j = 0 : outHex = ""
For pos = 1 To Len(sMessage)
i = (i + 1) Mod 256
j = (j + s(i)) Mod 256
temp = s(i)
s(i) = s(j)
s(j) = temp

' 鍔犲瘑骞惰浆涓哄崄鍏繘鍒?
Dim plainChar, cipherByte
plainChar = Asc(Mid(sMessage, pos, 1)) ' 鏄庢枃鎸堿SCII澶勭悊
cipherByte = s((s(i) + s(j)) Mod 256) Xor plainChar
outHex = outHex & Right("0" & Hex(cipherByte), 2)
Next

RunRC = outHex
End Function

' 涓婚獙璇侀€昏緫
If LCase(RunRC(flag, qwfe)) = LCase(wefbuwiue) Then
MsgBox "Congratulations! Correct FLAG!"
Else
MsgBox "Wrong flag."
End If



RC4解密,密钥为rc4key

flag{We1c0me_t0_XYCTF_2025_reverse_ch@lleng3_by_th3_w@y_p3cd0wn‘s_chall_is_r3@lly_gr3@t_&_fuN!}

ezVM

通过字符串界面里的unicorn和加密函数的一些特征发现是使用了unicorn框架调用了一串代码。

找一个使用unicorn框架的程序进行bindiff恢复一些unicorn函数的符号。

发现是调用了一串ARM64的代码字节进行模拟执行,将输入字符串传入加密返回,并附上了一些data和栈空间初始化。

将以上调用write写入的数据提取,随便找一个ARM64框架的.so复制到对应地址,以便反编译看代码。

最后得到一个函数,很清晰的看出里面是一个VM虚拟机执行的流程。

使用c++编写代码调用unicorn库进行模拟。

使用Hook,在关键计算地址处进行Hook,输出各个计算流程以及数据。

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
#include <iostream>
#include <Windows.h>
#include <string>
#include <vector>
#include <iostream>
#include "data.hpp"
#include "unicorn/unicorn.h"
#pragma comment(lib,"unicorn-import.lib")

static void hook_code(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) {
uint64_t w0,w1,w2,w3,w4;

if (address == 0x1fac)
{
uc_reg_read(uc, UC_ARM64_REG_W1, &w1);
uc_reg_read(uc, UC_ARM64_REG_W0, &w0);

printf("W3 = W1 ^ W0 --- %llx ^ %llx = %llx\n", w1,w0,w1^w0);
}
if (address == 0x2BE4)
{
uc_reg_read(uc, UC_ARM64_REG_W1, &w1);
uc_reg_read(uc, UC_ARM64_REG_W0, &w0);

printf("W3 = W1 >> W0 --- %llx >> %llx = %llx\n", w1, w0, w1 >> w0);
}
if (address == 0x2DB4)
{
uc_reg_read(uc, UC_ARM64_REG_W1, &w1);
uc_reg_read(uc, UC_ARM64_REG_W0, &w0);

printf("W3 = W1 << W0 --- %llx << %llx = %llx\n", w1, w0, w1 << w0);
}
if (address == 0x232C)
{
uc_reg_read(uc, UC_ARM64_REG_W3, &w3);
uc_reg_read(uc, UC_ARM64_REG_W0, &w0);

printf("W2 = W0 & W3 --- %llx & %llx = %llx\n", w0, w3, w0 & w3);
}
if (address == 0x2054)
{
uc_reg_read(uc, UC_ARM64_REG_W3, &w3);
uc_reg_read(uc, UC_ARM64_REG_W0, &w0);

printf("W2 = W0 ^ W3 --- %llx ^ %llx = %llx\n", w0, w3, w3 ^ w0);
}
if (address == 0x2238)
{
uc_reg_read(uc, UC_ARM64_REG_W3, &w3);
uc_reg_read(uc, UC_ARM64_REG_W0, &w0);

printf("W2 = W0 ^ W3 --- %llx ^ %llx = %llx\n", w0, w3, w3 ^ w0);
}
if (address == 0x2180)
{
uc_reg_read(uc, UC_ARM64_REG_W1, &w1);
uc_reg_read(uc, UC_ARM64_REG_W4, &w4);

printf("W1 = W1 ^ W4 --- %llx ^ %llx = %llx\n", w1, w4, w1 ^ w4);
}
if (address == 0x1CC4)
{
uc_reg_read(uc, UC_ARM64_REG_W0, &w0);
uc_reg_read(uc, UC_ARM64_REG_W1, &w1);
printf("W2 = W1 + W0 --- %llx + %llx = %llx\n", w1, w0, w1 + w0);
}
if (address == 0x1A0C)
{
uc_reg_read(uc, UC_ARM64_REG_W0, &w0);
uc_reg_read(uc, UC_ARM64_REG_W1, &w1);
printf("W2 = W1 + W0 --- %llx + %llx = %llx\n", w1, w0, w1 + w0);
}
}

int main()
{
uint8_t Input[] = "11112222111111111111111111111111";
uc_engine* uc{};
uc_hook hook;
uint64_t InputAddr = 0x14C28;
uint64_t Stack = 0x7F0000;
uint8_t MyEncFlag[100]{};
uint64_t Base = 0x0000000001000000;
uc_open(uc_arch::UC_ARCH_ARM64, uc_mode::UC_MODE_ARM, &uc);
uc_mem_map(uc, 0, Base, UC_PROT_ALL);
uc_mem_map(uc, Stack - 4096, 0x4000, UC_PROT_ALL);
uc_hook_add(uc, &hook, UC_HOOK_CODE, hook_code, NULL, 1, 0);
uc_mem_write(uc, 0x14C28, (LPVOID)&Input, 0x20u);
uc_mem_write(uc, 0x0C70, (LPVOID)&Code, 8840u);
uc_mem_write(uc, 0x14010, (LPVOID)Data1, 0x9B8u);
uc_mem_write(uc, 0x30F0, (LPVOID)&Data2, 0x54u);
uc_mem_write(uc, 0x149E8, (LPVOID)&Data3, 8u);
uc_reg_write(uc, 0xC7, (LPVOID)&InputAddr);// X0
uc_reg_write(uc, 4, (LPVOID)&Stack); // SP
if (!uc_emu_start(uc, 0x0C70, 0x2EF4, 0, 0))
{
uc_mem_read(uc, InputAddr, &MyEncFlag, 48);
}
for (int i = 0; i < 32; i++)
{
printf("%X ", MyEncFlag[i]);
}
uc_close(uc);
return 0;
}

最后运行输出得到一个vm加密流程

以下是部分输出内容。

output:

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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
W3 = W1 << W0 --- 4 << 0 = 4
W3 = W1 << W0 --- 4 << 1 = 8
W3 = W1 >> W0 --- 32323232 >> 5 = 1919191
W3 = W1 << W0 --- 32323232 << 6 = c8c8c8c80
W3 = W1 ^ W0 --- 1919191 ^ 8c8c8c80 = 8d1d1d11
W2 = W0 ^ W3 --- 32323232 ^ 11223344 = 23100176
W2 = W1 + W0 --- 8d1d1d11 + 23100176 = b02d1e87
W2 = W0 & W3 --- 0 & 3 = 0
W2 = W1 + W0 --- 776f6853 + 0 = 776f6853
W2 = W0 ^ W3 --- 776f6853 ^ abab1212 = dcc47a41
W3 = W1 ^ W0 --- b02d1e87 ^ dcc47a41 = 6ce964c6
W2 = W1 + W0 --- 6ce964c6 + 31313131 = 9e1a95f7
W3 = W1 >> W0 --- 9e1a95f7 >> 4 = 9e1a95f
W3 = W1 << W0 --- 9e1a95f7 << 7 = 4f0d4afb80
W3 = W1 ^ W0 --- 9e1a95f ^ d4afb80 = 4ab52df
W2 = W0 ^ W3 --- 9e1a95f7 ^ 55667788 = cb7ce27f
W2 = W1 + W0 --- 4ab52df + cb7ce27f = d028355e
W3 = W1 >> W0 --- 5f5fe6e7 >> b = bebfc
W2 = W0 & W3 --- bebfc & 3 = 0
W2 = W1 + W0 --- 776f6853 + 5f5fe6e7 = d6cf4f3a
W2 = W0 ^ W3 --- d6cf4f3a ^ 23235566 = f5ec1a5c
W3 = W1 ^ W0 --- f5ec1a5c ^ d028355e = 25c42f02
W2 = W1 + W0 --- 25c42f02 + 32323232 = 57f66134
W3 = W1 >> W0 --- 57f66134 >> 5 = 2bfb309
W3 = W1 << W0 --- 57f66134 << 6 = 15fd984d00
W3 = W1 ^ W0 --- 2bfb309 ^ fd984d00 = ff27fe09
W2 = W0 ^ W3 --- 57f66134 ^ 11223344 = 46d45270
W2 = W1 + W0 --- ff27fe09 + 46d45270 = 145fc5079
W2 = W0 & W3 --- 5f5fe6e7 & 3 = 3
W2 = W1 + W0 --- 74696564 + 5f5fe6e7 = d3c94c4b
W2 = W0 ^ W3 --- d3c94c4b ^ abab1212 = 78625e59
W3 = W1 ^ W0 --- 45fc5079 ^ 78625e59 = 3d9e0e20
W2 = W1 + W0 --- 3d9e0e20 + 9e1a95f7 = dbb8a417
W3 = W1 >> W0 --- dbb8a417 >> 4 = dbb8a41
W3 = W1 << W0 --- dbb8a417 << 7 = 6ddc520b80
W3 = W1 ^ W0 --- dbb8a41 ^ dc520b80 = d1e981c1
W2 = W0 ^ W3 --- dbb8a417 ^ 55667788 = 8eded39f
W2 = W1 + W0 --- d1e981c1 + 8eded39f = 160c85560
W3 = W1 >> W0 --- bebfcdce >> b = 17d7f9
W2 = W0 & W3 --- 17d7f9 & 3 = 1
W2 = W1 + W0 --- 656b616d + bebfcdce = 1242b2f3b
W2 = W0 ^ W3 --- 242b2f3b ^ 23235566 = 7087a5d
W3 = W1 ^ W0 --- 7087a5d ^ 60c85560 = 67c02f3d
W2 = W1 + W0 --- 67c02f3d + 57f66134 = bfb69071
W3 = W1 >> W0 --- bfb69071 >> 5 = 5fdb483
W3 = W1 << W0 --- bfb69071 << 6 = 2feda41c40
W3 = W1 ^ W0 --- 5fdb483 ^ eda41c40 = e859a8c3
W2 = W0 ^ W3 --- bfb69071 ^ 11223344 = ae94a335
W2 = W1 + W0 --- e859a8c3 + ae94a335 = 196ee4bf8
W2 = W0 & W3 --- bebfcdce & 3 = 2
W2 = W1 + W0 --- 616d5f72 + bebfcdce = 1202d2d40
W2 = W0 ^ W3 --- 202d2d40 ^ abab1212 = 8b863f52
W3 = W1 ^ W0 --- 96ee4bf8 ^ 8b863f52 = 1d6874aa
W2 = W1 + W0 --- 1d6874aa + dbb8a417 = f92118c1
W3 = W1 >> W0 --- f92118c1 >> 4 = f92118c
W3 = W1 << W0 --- f92118c1 << 7 = 7c908c6080
W3 = W1 ^ W0 --- f92118c ^ 908c6080 = 9f1e710c
W2 = W0 ^ W3 --- f92118c1 ^ 55667788 = ac476f49
W2 = W1 + W0 --- 9f1e710c + ac476f49 = 14b65e055
W3 = W1 >> W0 --- 1e1fb4b5 >> b = 3c3f6
W2 = W0 & W3 --- 3c3f6 & 3 = 2
W2 = W1 + W0 --- 616d5f72 + 1e1fb4b5 = 7f8d1427
W2 = W0 ^ W3 --- 7f8d1427 ^ 23235566 = 5cae4141
W3 = W1 ^ W0 --- 5cae4141 ^ 4b65e055 = 17cba114
W2 = W1 + W0 --- 17cba114 + bfb69071 = d7823185
W3 = W1 >> W0 --- d7823185 >> 5 = 6bc118c
W3 = W1 << W0 --- d7823185 << 6 = 35e08c6140
W3 = W1 ^ W0 --- 6bc118c ^ e08c6140 = e63070cc
W2 = W0 ^ W3 --- d7823185 ^ 11223344 = c6a002c1
W2 = W1 + W0 --- e63070cc + c6a002c1 = 1acd0738d
W2 = W0 & W3 --- 1e1fb4b5 & 3 = 1
W2 = W1 + W0 --- 656b616d + 1e1fb4b5 = 838b1622
W2 = W0 ^ W3 --- 838b1622 ^ abab1212 = 28200430
W3 = W1 ^ W0 --- acd0738d ^ 28200430 = 84f077bd
W2 = W1 + W0 --- 84f077bd + f92118c1 = 17e11907e
W3 = W1 >> W0 --- 7e11907e >> 4 = 7e11907
W3 = W1 << W0 --- 7e11907e << 7 = 3f08c83f00
W3 = W1 ^ W0 --- 7e11907 ^ 8c83f00 = f292607
W2 = W0 ^ W3 --- 7e11907e ^ 55667788 = 2b77e7f6
W2 = W1 + W0 --- f292607 + 2b77e7f6 = 3aa10dfd
W3 = W1 >> W0 --- 7d7f9b9c >> b = faff3
W2 = W0 & W3 --- faff3 & 3 = 3
W2 = W1 + W0 --- 74696564 + 7d7f9b9c = f1e90100
W2 = W0 ^ W3 --- f1e90100 ^ 23235566 = d2ca5466
W3 = W1 ^ W0 --- d2ca5466 ^ 3aa10dfd = e86b599b
W2 = W1 + W0 --- e86b599b + d7823185 = 1bfed8b20
W3 = W1 >> W0 --- bfed8b20 >> 5 = 5ff6c59
W3 = W1 << W0 --- bfed8b20 << 6 = 2ffb62c800
W3 = W1 ^ W0 --- 5ff6c59 ^ fb62c800 = fe9da459
W2 = W0 ^ W3 --- bfed8b20 ^ 11223344 = aecfb864
W2 = W1 + W0 --- fe9da459 + aecfb864 = 1ad6d5cbd
W2 = W0 & W3 --- 7d7f9b9c & 3 = 0
W2 = W1 + W0 --- 776f6853 + 7d7f9b9c = f4ef03ef
W2 = W0 ^ W3 --- f4ef03ef ^ abab1212 = 5f4411fd
W3 = W1 ^ W0 --- ad6d5cbd ^ 5f4411fd = f2294d40
W2 = W1 + W0 --- f2294d40 + 7e11907e = 1703addbe
W3 = W1 >> W0 --- 703addbe >> 4 = 703addb
W3 = W1 << W0 --- 703addbe << 7 = 381d6edf00
W3 = W1 ^ W0 --- 703addb ^ 1d6edf00 = 1a6d72db
W2 = W0 ^ W3 --- 703addbe ^ 55667788 = 255caa36
W2 = W1 + W0 --- 1a6d72db + 255caa36 = 3fca1d11
W3 = W1 >> W0 --- dcdf8283 >> b = 1b9bf0
W2 = W0 & W3 --- 1b9bf0 & 3 = 0
W2 = W1 + W0 --- 776f6853 + dcdf8283 = 1544eead6
W2 = W0 ^ W3 --- 544eead6 ^ 23235566 = 776dbfb0
W3 = W1 ^ W0 --- 776dbfb0 ^ 3fca1d11 = 48a7a2a1
W2 = W1 + W0 --- 48a7a2a1 + bfed8b20 = 108952dc1
W3 = W1 >> W0 --- 8952dc1 >> 5 = 44a96e
W3 = W1 << W0 --- 8952dc1 << 6 = 2254b7040
W3 = W1 ^ W0 --- 44a96e ^ 254b7040 = 250fd92e
W2 = W0 ^ W3 --- 8952dc1 ^ 11223344 = 19b71e85
W2 = W1 + W0 --- 250fd92e + 19b71e85 = 3ec6f7b3
W2 = W0 & W3 --- dcdf8283 & 3 = 3
W2 = W1 + W0 --- 74696564 + dcdf8283 = 15148e7e7
W2 = W0 ^ W3 --- 5148e7e7 ^ abab1212 = fae3f5f5
W3 = W1 ^ W0 --- 3ec6f7b3 ^ fae3f5f5 = c4250246
W2 = W1 + W0 --- c4250246 + 703addbe = 1345fe004
W3 = W1 >> W0 --- 345fe004 >> 4 = 345fe00
W3 = W1 << W0 --- 345fe004 << 7 = 1a2ff00200
W3 = W1 ^ W0 --- 345fe00 ^ 2ff00200 = 2cb5fc00
W2 = W0 ^ W3 --- 345fe004 ^ 55667788 = 6139978c
W2 = W1 + W0 --- 2cb5fc00 + 6139978c = 8def938c
W3 = W1 >> W0 --- 3c3f696a >> b = 787ed
W2 = W0 & W3 --- 787ed & 3 = 1
W2 = W1 + W0 --- 656b616d + 3c3f696a = a1aacad7
W2 = W0 ^ W3 --- a1aacad7 ^ 23235566 = 82899fb1
W3 = W1 ^ W0 --- 82899fb1 ^ 8def938c = f660c3d
W2 = W1 + W0 --- f660c3d + 8952dc1 = 17fb39fe
W3 = W1 >> W0 --- 17fb39fe >> 5 = bfd9cf
W3 = W1 << W0 --- 17fb39fe << 6 = 5fece7f80
W3 = W1 ^ W0 --- bfd9cf ^ fece7f80 = fe71a64f
W2 = W0 ^ W3 --- 17fb39fe ^ 11223344 = 6d90aba
W2 = W1 + W0 --- fe71a64f + 6d90aba = 1054ab109
W2 = W0 & W3 --- 3c3f696a & 3 = 2
W2 = W1 + W0 --- 616d5f72 + 3c3f696a = 9dacc8dc
W2 = W0 ^ W3 --- 9dacc8dc ^ abab1212 = 3607dace
W3 = W1 ^ W0 --- 54ab109 ^ 3607dace = 334d6bc7
W2 = W1 + W0 --- 334d6bc7 + 345fe004 = 67ad4bcb
W3 = W1 >> W0 --- 67ad4bcb >> 4 = 67ad4bc
W3 = W1 << W0 --- 67ad4bcb << 7 = 33d6a5e580
W3 = W1 ^ W0 --- 67ad4bc ^ d6a5e580 = d0df313c
W2 = W0 ^ W3 --- 67ad4bcb ^ 55667788 = 32cb3c43
W2 = W1 + W0 --- d0df313c + 32cb3c43 = 103aa6d7f
W3 = W1 >> W0 --- 9b9f5051 >> b = 1373ea
W2 = W0 & W3 --- 1373ea & 3 = 2
W2 = W1 + W0 --- 616d5f72 + 9b9f5051 = fd0cafc3
W2 = W0 ^ W3 --- fd0cafc3 ^ 23235566 = de2ffaa5
W3 = W1 ^ W0 --- de2ffaa5 ^ 3aa6d7f = dd8597da
W2 = W1 + W0 --- dd8597da + 17fb39fe = f580d1d8
W3 = W1 >> W0 --- f580d1d8 >> 5 = 7ac068e
W3 = W1 << W0 --- f580d1d8 << 6 = 3d60347600
W3 = W1 ^ W0 --- 7ac068e ^ 60347600 = 6798708e
W2 = W0 ^ W3 --- f580d1d8 ^ 11223344 = e4a2e29c
W2 = W1 + W0 --- 6798708e + e4a2e29c = 14c3b532a
W2 = W0 & W3 --- 9b9f5051 & 3 = 1
W2 = W1 + W0 --- 656b616d + 9b9f5051 = 1010ab1be
W2 = W0 ^ W3 --- 10ab1be ^ abab1212 = aaa1a3ac
W3 = W1 ^ W0 --- 4c3b532a ^ aaa1a3ac = e69af086
W2 = W1 + W0 --- e69af086 + 67ad4bcb = 14e483c51
W3 = W1 >> W0 --- 4e483c51 >> 4 = 4e483c5
W3 = W1 << W0 --- 4e483c51 << 7 = 27241e2880
W3 = W1 ^ W0 --- 4e483c5 ^ 241e2880 = 20faab45
W2 = W0 ^ W3 --- 4e483c51 ^ 55667788 = 1b2e4bd9
W2 = W1 + W0 --- 20faab45 + 1b2e4bd9 = 3c28f71e
W3 = W1 >> W0 --- faff3738 >> b = 1f5fe6
W2 = W0 & W3 --- 1f5fe6 & 3 = 2
W2 = W1 + W0 --- 616d5f72 + faff3738 = 15c6c96aa
W2 = W0 ^ W3 --- 5c6c96aa ^ 23235566 = 7f4fc3cc
W3 = W1 ^ W0 --- 7f4fc3cc ^ 3c28f71e = 436734d2
W2 = W1 + W0 --- 436734d2 + f580d1d8 = 138e806aa

通过观察可以发现是一个魔改的XTea加密。

通过对比标准XTea加密流程,可以得到里面参与计算的4个key值 {0x776f6853,0x656b616d,0x616d5f72,0x74696564} 以及delta值 0x5f5fe6e7

写出对应加密的c++代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void encipher(uint32_t v[2], const uint32_t key[4])
{
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], sum = 0, delta = 0x5f5fe6e7;
for (i = 0; i < 72; i++)
{
auto tmp = (((v1 << 6) ^ (v1 >> 5)) + (v1 ^ 0x11223344));
auto tmp2 = (((key[sum & 3] + sum) ^ 0xabab1212) ^ tmp);
v0 += tmp2;
sum += delta;
auto tmp3 = (((v0 << 7) ^ (v0 >> 4)) + (v0 ^ 0x55667788));
auto tmp4 = ((key[(sum >> 11) & 3] + sum) ^ 0x23235566 ^ tmp3);
v1 += tmp4;
}
v[0] = v0;
v[1] = v1;
}

解密代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void decipher(uint32_t v[2], const uint32_t key[4])
{
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], sum = 0x5f5fe6e7 * 72, delta = 0x5f5fe6e7;
for (i = 0; i < 72; i++)
{
auto tmp3 = (((v0 << 7) ^ (v0 >> 4)) + (v0 ^ 0x55667788));
auto tmp4 = ((key[(sum >> 11) & 3] + sum) ^ 0x23235566 ^ tmp3);
v1 -= tmp4;
sum -= delta;
auto tmp = (((v1 << 6) ^ (v1 >> 5)) + (v1 ^ 0x11223344));
auto tmp2 = (((key[sum & 3] + sum) ^ 0xabab1212) ^ tmp);
v0 -= tmp2;
}
v[0] = v0;
v[1] = v1;
}

提取chal程序中的密文,进行解密即可。

完整解密代码:

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
#include <iostream>

void decipher(uint32_t v[2], const uint32_t key[4])
{
unsigned int i;
uint32_t v0 = v[0], v1 = v[1], sum = 0x5f5fe6e7 * 72, delta = 0x5f5fe6e7;
for (i = 0; i < 72; i++)
{
auto tmp3 = (((v0 << 7) ^ (v0 >> 4)) + (v0 ^ 0x55667788));
auto tmp4 = ((key[(sum >> 11) & 3] + sum) ^ 0x23235566 ^ tmp3);
v1 -= tmp4;
sum -= delta;
auto tmp = (((v1 << 6) ^ (v1 >> 5)) + (v1 ^ 0x11223344));
auto tmp2 = (((key[sum & 3] + sum) ^ 0xabab1212) ^ tmp);
v0 -= tmp2;
}
v[0] = v0;
v[1] = v1;
}

int main()
{
uint32_t key[]{
0x776f6853,
0x656b616d,
0x616d5f72,
0x74696564};
unsigned int Encflag[8] = {
0x696C2E9A, 0x76ADE8E1, 0xE67D5CA4, 0x5C76BD38,
0xB7AC0787, 0xBFEA0C65, 0x01C2FF10, 0x6D16FD38};
decipher(Encflag, key);
decipher((uint32_t *)((uint64_t)Encflag + 8), key);
decipher((uint32_t *)((uint64_t)Encflag + 16), key);
decipher((uint32_t *)((uint64_t)Encflag + 24), key);
printf("%.32s\n", Encflag);
return 0;
}

XYCTF{fun_un1c0rn_with_4rm64_VM}

Moon

跟到moon.xor_crypt实际加密处。

发现是进行了单次xor,并加入到一个list中,前后过程不清楚。

断在xor这个命令,运行附加调试,随便输入一串1

发现是输入的’1’和一个0x24进行xor,多运行几次发现就是将输入的字符串都异或上一些值。

直接断在return处,v20是最后将list转成Bytes的结果。

发现是28长度的一串字节,从0x15开始的,就是我们输入字符串长度以及异或完的结果。

继续运行会返回到check_flag代码处,底下有一个RichCompare比较两个数据。

v45可以看到就是将刚刚v20的bytes直接unhex转成了一串字符串。

那么v9就应该是flag的密文,可以数出一共是要35字节。

重新调试运行输入35个1,在check_flag开头断点,把输入的字符串全都patch成0。

最后在RichCompare处就可以得到xor密文的列表。

将v9的密文与这个数据进行xor即可得到flag。

flag{but_y0u_l00k3d_up_@t_th3_mOOn}

Dragon(5m10v3师傅解题)

.bc 后缀

反编译为LLVM IR

1
llvm-dis-17 Dragon.bc -o Dragon.ll

分析得知为crc64,以两个为一组进行校验,直接爆破就行

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
#include <stdio.h>
#include <stdint.h>
#include <string.h>

uint64_t calculate_crc64_direct(const unsigned char* data, uint64_t length) {
uint64_t crc = 0xFFFFFFFFFFFFFFFFULL;

for (uint64_t i = 0; i < length; i++) {
crc ^= ((uint64_t)data[i] << 56);

for (uint64_t j = 0; j < 8; j++) {
if (crc & 0x8000000000000000ULL) {
crc = (crc << 1) ^ 0x42F0E1EBA9EA3693ULL;
}
else {
crc = crc << 1;
}
}
}

return ~crc;
}

int main() {
uint64_t enc[12] = {
-2565957437423125689,
224890624719110086,
1357324823849588894,
- 8941695979231947288,
- 253413330424273460,
- 7817463785137710741,
- 5620500441869335673,
984060876288820705,
- 6993555743080142153,
- 7892488171899690683,
7190415315123037707,
- 7218240302740981077
};
char flag[25] = { 0 };
int flag_index = 0;


for (int k = 0; k < 12; k++) {
uint64_t target_crc = enc[k];
int found = 0;
for (unsigned char c1 = 32; c1 < 127 && !found; c1++) {
for (unsigned char c2 = 32; c2 < 127 && !found; c2++) {
unsigned char test[2] = { c1, c2 };
uint64_t crc = calculate_crc64_direct(test, 2);
if (crc == target_crc) {
flag[flag_index++] = c1;
flag[flag_index++] = c2;
break;
}
}
}
}

flag[flag_index] = '\0';
printf("Decrypted flag: %s\n", flag);

return 0;
}
//flag{LLVM_1s_Fun_Ri9h7?}

Summer(5m10v3师傅解题)

haskell程序

函数式编程语言,这意味着一切都是惰性计算,什么是惰性计算? 简单来说就是在调用之前不会对该值进行计算

浏览 main 函数,可以看到 hs_main 将 ZCMain_main_closure 作为它的参数,它指向 haskell 程序的真正入口点

ZCMain_main_closure里面我们发现它调用了stg_ap_p_fast,这个是底层函数,主要调用Main_main_closure这个函数

GHCziInternalziBase也是底层函数,主要关注两个参数

第一个参数的地址处的函数为打印字符串

GHCziInternalziList_length 为处理我们的传入的字符串的长度,直接调用的是zdwlenAcc**,**zdwlenAcc 将通过检查下一个是否是列表的末尾来计算 “flagTable” 的长度(这里的”flagTable” 是我自己命名,其实就是存储惰性列表,我们可以根据惰性列表的指针数判断字符串的长度)

我们这里就用flag进行测试,一方面是为了查看他的返回值

此时他是将rbx此处(即为惰性列表的末尾),可以人工数(即为50)

另外一种为看返回值,第一次断下是返回我们输入字符串的长度,第二次断下是返回密钥的长度,第三次断下是返回密文的长度

另外一处为GHCziInternalziNum_zdfNumIntzuzdczp,这个也是在网上一篇文章看到的,在add rbx, [rax] 在经过几次迭代后,我可以看到一些字符开始出现,此时我们可以得到密钥为Klingsor’s_Last_Summer

我们在.data段得到了密钥,因此我们可以猜测下面可能为密文,并且下面都是指针+元素的存储形式

然后通过CE调试得到明文和密文,然后得出为rc4+xor

flag{Us3_H@sk3ll_t0_f1nd_th3_truth_1n_th1s_Summ3R}

Lake

单步跟到主函数。

这边输入字符串后先赋值到了另一个数组,然后进行了一次简易VM计算进行了第一次加密,

然后接着第二次加密,最后循环比较。

发现VM只用到了加减和XOR计算,在这三个地方的关键点打断点,输出寄存器和计算流程,这边为了方便直接复制到代码里面解密,将加减断点里面的输出运算符反过来,输出出来的代码直接复制到代码就是进行解密的流程。

调试输出:

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
Input[2] += 12;
Input[26] -= 85;
Input[35] -= 12;
Input[14] += 9;
Input[27] -= 6;
Input[6] ^= 5;
Input[1] ^= 5;
Input[27] += 14;
Input[25] += 3;
Input[26] += 4;
Input[4] ^= 8;
Input[3] -= 12;
Input[12] += 10;
Input[37] -= 2;
Input[32] -= 2;
Input[9] -= 12;
Input[26] ^= 5;
Input[4] += 13;
Input[8] ^= 15;
Input[10] += 14;
Input[16] -= 7;
Input[12] -= 7;
Input[34] ^= 8;
Input[21] ^= 10;
Input[39] -= 126;
Input[7] += 2;
Input[15] ^= 3;
Input[10] ^= 10;
Input[34] -= 11;
Input[18] += 8;
Input[25] += 9;
Input[14] ^= 6;
Input[0] ^= 5;
Input[10] -= 8;
Input[27] ^= 7;
Input[13] ^= 6;
Input[13] ^= 4;
Input[23] ^= 12;
Input[34] ^= 14;
Input[18] += 52;
Input[38] -= 119;

这也就是第一层加密的解密代码。

第二层加密直接对着写即可,我写的有点问题(懒得改),其中几个字节解密不对,不过根据解密出的flag也能猜出是啥,替换完那几个字节就得到完整的flag。

完整解密代码:

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
#include <iostream>
#include <windows.h>

void decrypt_func(unsigned char *data, int len)
{
unsigned char temp[40];
memcpy(temp, data, 40);

for (int i = 0; i < 10; i++)
{
int base = 4 * i;
unsigned char block[4];

if (base < len)
{
block[0] = ((temp[base + 2] & 0x1F) << 5) | (temp[base + 3] >> 3);
block[1] = (temp[base] >> 3) | ((temp[base + 3] & 0x07) << 5);
block[2] = ((temp[base] & 0x07) << 5) | (temp[base + 1] >> 3);
block[3] = ((temp[base + 1] & 0x07) << 5) | (temp[base + 2] >> 3);

for (int j = 0; j < 4 && base + j < len; j++)
{
data[base + j] = block[j];
}
}
}
}

int main()
{
unsigned char Input[48] = {
0x4A, 0xAB, 0x9B, 0x1B, 0x61, 0xB1, 0xF3, 0x32, 0xD1, 0x8B, 0x73, 0xEB, 0xE9, 0x73, 0x6B, 0x22,
0x81, 0x83, 0x23, 0x31, 0xCB, 0x1B, 0x22, 0xFB, 0x25, 0xC2, 0x81, 0x81, 0x73, 0x22, 0xFA, 0x03,
0x9C, 0x4B, 0x5B, 0x49, 0x97, 0x87, 0xDB, 0x51};

decrypt_func(Input, 40);
Input[2] += 12;
Input[26] -= 85;
Input[35] -= 12;
Input[14] += 9;
Input[27] -= 6;
Input[6] ^= 5;
Input[1] ^= 5;
Input[27] += 14;
Input[25] += 3;
Input[26] += 4;
Input[4] ^= 8;
Input[3] -= 12;
Input[12] += 10;
Input[37] -= 2;
Input[32] -= 2;
Input[9] -= 12;
Input[26] ^= 5;
Input[4] += 13;
Input[8] ^= 15;
Input[10] += 14;
Input[16] -= 7;
Input[12] -= 7;
Input[34] ^= 8;
Input[21] ^= 10;
Input[39] -= 126;
Input[7] += 2;
Input[15] ^= 3;
Input[10] ^= 10;
Input[34] -= 11;
Input[18] += 8;
Input[25] += 9;
Input[14] ^= 6;
Input[0] ^= 5;
Input[10] -= 8;
Input[27] ^= 7;
Input[13] ^= 6;
Input[13] ^= 4;
Input[23] ^= 12;
Input[34] ^= 14;
Input[18] += 52;
Input[38] -= 119;
printf("%.40s\n", Input);

// flag{L3@rn-ng_1n_0ld_sch00b_@nd_g3x_j0y} -> flag{L3@rn1ng_1n_0ld_sch00l_@nd_g3t_j0y}
return 0;
}

flag{L3@rn1ng_1n_0ld_sch00l_@nd_g3t_j0y}

EzObf

main_0函数跟入发现有混淆,红框处为原真实汇编指令,其他都是混淆指令。

混淆流程:

  1. 执行真实指令
  2. call $+5执行pop rax,rax就是call时push到栈的返回地址,也就是pop rax指令的地址。
  3. 给ebx赋值,进行rol计算,最后用rax加上或减去(共两种)rbx,得到跳转地址,进行jmp rax。

之后每jmp过去一次,那边就都是一样的结构,popfq和pushfq之间就是真实汇编。

deobf的思路即为nop那一堆pop和push,保留真实汇编指令,然后计算跳转地址,手动计算相对地址写jmp,保持代码执行流程。

deobf idc脚本:

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
static NopCode(Addr, Length)
{
auto i;
for (i = 0; i < Length; i++)
{
PatchByte(Addr + i, 0x90);
}
}

static rol(value, count, bits = 32)
{
count = count % bits;
return ((value << count) | (value >> (bits - count))) & ((1 << bits) - 1);
}

// 搜索真实汇编代码的下一个地址
static FindEnd(Addr)
{
auto i;
for (i = 0; i < 0x90; i++)
{
auto v = Dword(Addr + i);
if (v == 0x5153509C)
{
return Addr + i;
}
}
return 0;
}

// 搜索最后的jmp rax指令
static FindJmpRax(Addr)
{
auto i;
for (i = 0; i < 0x90; i++)
{
auto v = Word(Addr + i);
if (v == 0xE0FF)
{
return Addr + i;
}
}
return 0;
}

// 搜索call $+5
static FindCall(Addr)
{
auto i;
for (i = 0; i < 0x90; i++)
{
auto v = Dword(Addr + i);
if (v == 0xE8)
{
return Addr + i;
}
}
return 0;
}

static main()
{
auto StartAddr = 0x1401F400D;
while (1)
{
// 搜索真实汇编代码的下一个指令地址
auto EndAddr = FindEnd(StartAddr);
if (EndAddr == 0)
{
break;
}
// 真实汇编代码的字节长度
auto CodeLength = EndAddr - addr - 13;
// 搜索Call $+5
auto CallAddr = FindCall(addr + 13 + CodeLength);
if (CallAddr == 0)
{
break;
}
// call $+5的下一条指令地址,即call时push到栈的返回地址
auto CalcAddr = CallAddr + 5;
auto ebx = Dword(CalcAddr + 2);
auto rol_Value = Byte(CalcAddr + 8);
auto Mode = Dword(CalcAddr + 9);
ebx = rol(ebx, rol_Value);

// 搜索最尾部的jmp rax指令地址
auto JmpRaxAddr = FindJmpRax(addr);
if (JmpRaxAddr == 0)
{
break;
}
// 第一部分垃圾指令长度
auto TrushCodeLength_1 = CallAddr - (addr + 13 + CodeLength);
// 第二部分垃圾指令长度
auto TrushCodeLength_2 = JmpRaxAddr - CallAddr + 2;
// Nop掉无用的所有代码
NopCode(CallAddr, TrushCodeLength_2);

NopCode(addr, 13);

NopCode(addr + 13 + CodeLength, TrushCodeLength_1);
// 一共两种地址计算,加和减
if (Mode == 0xffC32B48)
{
CalcAddr = CalcAddr - ebx;
}
if (Mode == 0xffC30348)
{
CalcAddr = CalcAddr + ebx;
}
auto JmpCodeAddr = EndAddr;
// 计算相对跳转地址
auto JmpOffset = CalcAddr - JmpCodeAddr + 5;
// 写入jmp指令
PatchByte(JmpCodeAddr, 0xE9);
PatchDword(JmpCodeAddr + 1, JmpOffset);
// jmp的地址为下一次deobf起始地址
addr = CalcAddr;
}
}

执行完,把main_0剩余代码都手动nop即可。

然后Apply patches to input file,应用一下patch,重新打开ida载入程序分析。

从main_0的jmp进入两层到这边,然后用IDA Delete Function删除sub_1401F7B77函数,然后对jmp那边按E即可重新重构完main函数(如图2),F5即可分析。

Main函数原代码:

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
int __fastcall main_0(int argc, const char **argv, const char **envp)
{
__int64 v3; // rbp
__int64 v4; // rdi
__int64 i; // rcx
_DWORD *v7; // rdi
HANDLE CurrentProcess; // rax
__int64 v9; // [rsp-20h] [rbp-458h] BYREF
_DWORD v10[2]; // [rsp+0h] [rbp-438h] BYREF
_BYTE v11[64]; // [rsp+8h] [rbp-430h] BYREF
_BYTE *v12; // [rsp+48h] [rbp-3F0h]
unsigned int v13; // [rsp+64h] [rbp-3D4h]
int v14; // [rsp+84h] [rbp-3B4h]
unsigned int v15; // [rsp+A4h] [rbp-394h]
int v16; // [rsp+C4h] [rbp-374h]
int v17; // [rsp+E4h] [rbp-354h]
unsigned int k; // [rsp+104h] [rbp-334h]
int v19; // [rsp+124h] [rbp-314h]
int v20; // [rsp+144h] [rbp-2F4h]
int v21; // [rsp+164h] [rbp-2D4h]
_DWORD v22[11]; // [rsp+188h] [rbp-2B0h] BYREF
unsigned __int16 v23; // [rsp+1B4h] [rbp-284h]
BOOL v24; // [rsp+1D4h] [rbp-264h] BYREF
unsigned __int64 j; // [rsp+1F8h] [rbp-240h]
unsigned __int64 v26; // [rsp+218h] [rbp-220h]
_DWORD v27[12]; // [rsp+238h] [rbp-200h]
unsigned __int64 m; // [rsp+268h] [rbp-1D0h]
int v29; // [rsp+3F4h] [rbp-44h]
unsigned int v30; // [rsp+3F8h] [rbp-40h]
int v31; // [rsp+3FCh] [rbp-3Ch]
__int64 v32; // [rsp+400h] [rbp-38h]
int v33; // [rsp+408h] [rbp-30h]
unsigned __int64 v34; // [rsp+410h] [rbp-28h]
__int64 v35; // [rsp+428h] [rbp-10h]
__int64 v36; // [rsp+430h] [rbp-8h]

v36 = v3;
v35 = v4;
v7 = v10;
for ( i = 170; i; --i )
*v7++ = -858993460;
v34 = (unsigned __int64)v10 ^ 0x1401D9000LL;
j___CheckForDebuggerJustMyCode(0x1401ED104LL);
memset(v11, 0, 0x20u);
sub_140087C02(0x1401A1190LL);
if ( !IsDebuggerPresent() )
{
sub_1400868E3();
v12 = v11;
memset(v22, 0, 0x10u);
v23 = 8;
v29 = 8;
v20 = 12;
v15 = 0;
v16 = 0x61C88646;
v21 = 0x95664B48;
v19 = 7;
v24 = 0;
CurrentProcess = GetCurrentProcess();
CheckRemoteDebuggerPresent(CurrentProcess, &v24);
if ( !v24 )
{
j_srand(0xAABBu);
for ( j = 0; j < 4; ++j )
v22[j] = j_rand();
while ( 1 )
{
v29 = v19--;
v30 = v29 != 0;
if ( !v30 )
break;
v15 += v16;
v17 = (v15 >> 2) & 3;
for ( k = 0; k < v23; ++k )
{
v26 = __rdtsc();
v13 = *(_DWORD *)&v12[4 * ((k + 1) % v23)];
v29 = (4 * v13) ^ (*(_DWORD *)&v12[4 * ((k + v23 - 1) % v23)] >> 5);
v30 = k + v23 - 1;
v31 = ((16 * *(_DWORD *)&v12[4 * (v30 % v23)]) ^ (v13 >> 3)) + v29;
v32 = ((unsigned __int8)v17 ^ (unsigned __int8)k) & 3;
v33 = (((*(_DWORD *)&v12[4 * (v30 % v23)] ^ v22[v32]) + (v13 ^ v15)) ^ v31) + *(_DWORD *)&v12[4 * k];
*(_DWORD *)&v12[4 * k] = v33;
v14 = v33;
if ( __rdtsc() - v26 > 0x83C0 )
goto LABEL_20;
}
}
v27[0] = 0xA9934E2F;
v27[1] = 0x30B90FA;
v27[2] = 0xDCBF1D3;
v27[3] = 0x328B5BDE;
for ( m = 0; m < 4; ++m )
{
if ( v27[m] != v10[m + 2] )
{
sub_140087C02(0x1401A11A8LL);
j_system(byte_1401A11A0);
goto LABEL_20;
}
}
sub_140087C02(0x1401A11C0LL);
j_system(byte_1401A11A0);
}
}
LABEL_20:
j__RTC_CheckStackVars(&v9, (_RTC_framedesc *)&unk_1401A1140);
j___security_check_cookie((unsigned __int64)v10 ^ v34);
return sub_1401F8E9B();
}

很清晰看出来是XXTEA加密,密钥是固定种子随机数随机得到的,Delta被魔改,然后密文也能看到。

注:写WP时用的是旧版附件分析,缺失了后面16字节密文

完整密文数据:

1
2
0xa9934e2f, 0x30b90fa, 0xdcbf1d3, 0x328b5bde,
0x44fab4e, 0x1dcf0051, 0x85ebbe55, 0x93aa773a

解密代码:

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
#include <iostream>
#define DELTA 0x61C88646
#define MX (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z)))

void xxtea(uint32_t *v, int n, uint32_t const key[4])
{
uint32_t y, z, sum;
unsigned p, rounds, e;
if (n > 1) /* Coding Part */
{
rounds = 7;
sum = 0;
z = v[n - 1];
do
{
sum += DELTA;
e = (sum >> 2) & 3;
for (p = 0; p < n - 1; p++)
{
y = v[p + 1];
z = v[p] += MX;
if (z == 0xA4F41487)
printf("11\n");
if (y == 0xA4F41487)
printf("11\n");
}
y = v[0];
z = v[n - 1] += MX;

} while (--rounds);
}
else if (n < -1) /* Decoding Part */
{
n = -n;
rounds = 7;
sum = rounds * DELTA;
y = v[0];
do
{
e = (sum >> 2) & 3;
for (p = n - 1; p > 0; p--)
{
z = v[p - 1];
y = v[p] -= MX;
}
z = v[n - 1];
y = v[0] -= MX;
sum -= DELTA;
} while (--rounds);
}
}

int main()
{
srand(0xAABB);
uint32_t key[4]{};
uint32_t Enc[]{
0xa9934e2f, 0x30b90fa, 0xdcbf1d3, 0x328b5bde,
0x44fab4e, 0x1dcf0051, 0x85ebbe55, 0x93aa773a};

for (int i = 0; i < 4; i++)
{
key[i] = rand();
}
xxtea(Enc, -8, key);
printf("%.32s\n", Enc);
return 0;
}

flag{th15_15_51mpLe_obf_R19Ht?}

CrackMe

有反调试,在WinMain开头断点,使用ScyllaHide一把梭去除((。

从WinMain可以跟踪到窗口消息函数,图四就是验证函数按钮消息。

从TLS那边可以看到启动了一个线程,线程函数如下

这边死循环判断了一个值,然后调用CallBack,随便输入flag,点击验证,发现会先调用CallBack中的mark2函数进行第一次验证。

将这边v4都异或上0xBB会得到”flag{“五个字符,就明白这边是检测输入flag开头是否为”flag{“,进行了第一次验证,然后继续下一次验证进入了case 5的mark3函数。

mark3这边是用固定值生成了一个v5数值列表,a1是输入的字符串,但是可以看到+5跳过了前面的五个字符,然后对括号内的前7个字符做一些加密计算然后和v5列表前7个数值进行检验。

这边就可以直接提取v5生成的数值列表,然后利用爆破得到括号内的前七个字符。

爆破代码:

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
unsigned int box1[] = {
0x00000000, 0xC0BA6CAC, 0x5A05DF1B, 0x9ABFB3B7, 0xB40BBE36, 0x74B1D29A,
0xEE0E612D, 0x2EB40D81, 0xB3667A2F, 0x73DC1683, 0xE963A534, 0x29D9C998, 0x076DC419, 0xC7D7A8B5,
0x5D681B02, 0x9DD277AE, 0xBDBDF21D, 0x7D079EB1, 0xE7B82D06, 0x270241AA, 0x09B64C2B, 0xC90C2087,
0x53B39330, 0x9309FF9C, 0x0EDB8832, 0xCE61E49E, 0x54DE5729, 0x94643B85, 0xBAD03604, 0x7A6A5AA8,
0xE0D5E91F, 0x206F85B3, 0xA00AE279, 0x60B08ED5, 0xFA0F3D62, 0x3AB551CE, 0x14015C4F, 0xD4BB30E3,
0x4E048354, 0x8EBEEFF8, 0x136C9856, 0xD3D6F4FA, 0x4969474D, 0x89D32BE1, 0xA7672660, 0x67DD4ACC,
0xFD62F97B, 0x3DD895D7, 0x1DB71064, 0xDD0D7CC8, 0x47B2CF7F, 0x8708A3D3, 0xA9BCAE52, 0x6906C2FE,
0xF3B97149, 0x33031DE5, 0xAED16A4B, 0x6E6B06E7, 0xF4D4B550, 0x346ED9FC, 0x1ADAD47D, 0xDA60B8D1,
0x40DF0B66, 0x806567CA, 0x9B64C2B1, 0x5BDEAE1D, 0xC1611DAA, 0x01DB7106, 0x2F6F7C87, 0xEFD5102B,
0x756AA39C, 0xB5D0CF30, 0x2802B89E, 0xE8B8D432, 0x72076785, 0xB2BD0B29, 0x9C0906A8, 0x5CB36A04,
0xC60CD9B3, 0x06B6B51F, 0x26D930AC, 0xE6635C00, 0x7CDCEFB7, 0xBC66831B, 0x92D28E9A, 0x5268E236,
0xC8D75181, 0x086D3D2D, 0x95BF4A83, 0x5505262F, 0xCFBA9598, 0x0F00F934, 0x21B4F4B5, 0xE10E9819,
0x7BB12BAE, 0xBB0B4702, 0x3B6E20C8, 0xFBD44C64, 0x616BFFD3, 0xA1D1937F, 0x8F659EFE, 0x4FDFF252,
0xD56041E5, 0x15DA2D49, 0x88085AE7, 0x48B2364B, 0xD20D85FC, 0x12B7E950, 0x3C03E4D1, 0xFCB9887D,
0x66063BCA, 0xA6BC5766, 0x86D3D2D5, 0x4669BE79, 0xDCD60DCE, 0x1C6C6162, 0x32D86CE3, 0xF262004F,
0x68DDB3F8, 0xA867DF54, 0x35B5A8FA, 0xF50FC456, 0x6FB077E1, 0xAF0A1B4D, 0x81BE16CC, 0x41047A60,
0xDBBBC9D7, 0x1B01A57B, 0xEDB88321, 0x2D02EF8D, 0xB7BD5C3A, 0x77073096, 0x59B33D17, 0x990951BB,
0x03B6E20C, 0xC30C8EA0, 0x5EDEF90E, 0x9E6495A2, 0x04DB2615, 0xC4614AB9, 0xEAD54738, 0x2A6F2B94,
0xB0D09823, 0x706AF48F, 0x5005713C, 0x90BF1D90, 0x0A00AE27, 0xCABAC28B, 0xE40ECF0A, 0x24B4A3A6,
0xBE0B1011, 0x7EB17CBD, 0xE3630B13, 0x23D967BF, 0xB966D408, 0x79DCB8A4, 0x5768B525, 0x97D2D989,
0x0D6D6A3E, 0xCDD70692, 0x4DB26158, 0x8D080DF4, 0x17B7BE43, 0xD70DD2EF, 0xF9B9DF6E, 0x3903B3C2,
0xA3BC0075, 0x63066CD9, 0xFED41B77, 0x3E6E77DB, 0xA4D1C46C, 0x646BA8C0, 0x4ADFA541, 0x8A65C9ED,
0x10DA7A5A, 0xD06016F6, 0xF00F9345, 0x30B5FFE9, 0xAA0A4C5E, 0x6AB020F2, 0x44042D73, 0x84BE41DF,
0x1E01F268, 0xDEBB9EC4, 0x4369E96A, 0x83D385C6, 0x196C3671, 0xD9D65ADD, 0xF762575C, 0x37D83BF0,
0xAD678847, 0x6DDDE4EB, 0x76DC4190, 0xB6662D3C, 0x2CD99E8B, 0xEC63F227, 0xC2D7FFA6, 0x026D930A,
0x98D220BD, 0x58684C11, 0xC5BA3BBF, 0x05005713, 0x9FBFE4A4, 0x5F058808, 0x71B18589, 0xB10BE925,
0x2BB45A92, 0xEB0E363E, 0xCB61B38D, 0x0BDBDF21, 0x91646C96, 0x51DE003A, 0x7F6A0DBB, 0xBFD06117,
0x256FD2A0, 0xE5D5BE0C, 0x7807C9A2, 0xB8BDA50E, 0x220216B9, 0xE2B87A15, 0xCC0C7794, 0x0CB61B38,
0x9609A88F, 0x56B3C423, 0xD6D6A3E9, 0x166CCF45, 0x8CD37CF2, 0x4C69105E, 0x62DD1DDF, 0xA2677173,
0x38D8C2C4, 0xF862AE68, 0x65B0D9C6, 0xA50AB56A, 0x3FB506DD, 0xFF0F6A71, 0xD1BB67F0, 0x11010B5C,
0x8BBEB8EB, 0x4B04D447, 0x6B6B51F4, 0xABD13D58, 0x316E8EEF, 0xF1D4E243, 0xDF60EFC2, 0x1FDA836E,
0x856530D9, 0x45DF5C75, 0xD80D2BDB, 0x18B74777, 0x8208F4C0, 0x42B2986C, 0x6C0695ED, 0xACBCF941,
0x36034AF6, 0xF6B9265A, 0xCCCCCCCC, 0xCCCCCC00, 0x00000100, 0x00000000, 0xF6B9265A, 0xCCCCCCCC,
0x00000008, 0x00000000};

uint32_t enc1[]{
0x46A95BAD,
0x1CAC84B6,
0xA67CB2B2,
0x32188937,
0x4872D39F,
0xF2A2E59B,
0x011B94D2,
};

// 爆破前7字节
for (int i = 0; i < 7; i++)
{
for (int c = 28; c < 132; c++)
{
if ((~box1[(uint8_t)c ^ 0x79] ^ 0xB0E0E879) == enc1[i])
{
printf("%c", c);
break;
}
}
}

得到前七个字符为:moshui_

第三次Check是在case 0处,程序起始的时候启了一个线程,死循环然后这边判断前两次Check是否成功,然后进入最后一次Check代码。

开始的时候利用前五个字节以及括号内前七个字节生成了两个四字节密钥,然后又赋值了另外两个固定的密钥值。

由于前五字节和括号内前七个字节是已知固定的,所以生成的密钥也是固定,可以直接提取计算完的密钥。

密钥:0x42B2986C, 0x12345678, 0x0D6D6A3E, 0x89ABCDEF

然后下面赋值了密文到v7,判断输入的字符串第29个字符是否为’}’,这边可知flag长度为29,然后利用密钥和输入字符串,进行加密,最后和v7判断。

加密是8字节8字节加密,观察sub_7FF7ADAB1640可知是IDEA加密算法,循环加密0x10000次没什么用,因为Input和Output在两个不同数组,所以和加密一次是一样结果。

利用IDEA解密算法配合密钥解密v7的值即可得到后16字节,最后拼接得到完整flag。

解密代码:

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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
#include <iostream>
#include <bitset>
#include <cmath>
#include <windows.h>
#include <algorithm>
using namespace std;

typedef bitset<16> code;
typedef bitset<128> key;

bitset<16> sub_key[52];
bitset<16> inv_sub_key[52];

code XOR(code code_1, code code_2)
{
return code_1 ^ code_2;
}

code Plus(code code_1, code code_2)
{
int tmp = 0;
for (int i = 0; i < 16; i++)
{
tmp += code_1[i] * pow(2, i) + code_2[i] * pow(2, i);
}
tmp %= 65536;
return bitset<16>(tmp);
}

code invPlus(code code_in)
{
int tmp = 0;
for (int i = 0; i < 16; i++)
tmp += code_in[i] * pow(2, i);
tmp = 65536 - tmp;
return bitset<16>(tmp);
}

code Times(code code_1, code code_2)
{
long long tmp_1 = 0, tmp_2 = 0;
for (int i = 0; i < 16; i++)
{
tmp_1 += code_1[i] * pow(2, i);
tmp_2 += code_2[i] * pow(2, i);
}
if (tmp_1 == 0)
tmp_1 = 65536;
if (tmp_2 == 0)
tmp_2 = 65536;
long long tmp = (tmp_1 * tmp_2) % 65537;
return bitset<16>(tmp == 65536 ? 0 : tmp);
}

void Exgcd(int a, int b, int &x, int &y)
{
if (!b)
x = 1, y = 0;
else
Exgcd(b, a % b, y, x), y -= a / b * x;
}

code invTimes(code code_in)
{
int tmp = 0;
for (int i = 0; i < 16; i++)
tmp += code_in[i] * pow(2, i);
int x, y;
int p = 65537;
Exgcd(tmp, p, x, y);
x = (x % p + p) % p;
return bitset<16>(x);
}

void subkeys_get(code keys_input[8])
{
key keys;
for (int i = 0; i < 8; i++)
for (int j = 0; j < 16; j++)
keys[j + 16 * i] = keys_input[7 - i][j];

for (int i = 0; i < 8; i++)
for (int j = 0; j < 16; j++)
sub_key[i][15 - j] = keys[127 - (j + 16 * i)];

for (int i = 0; i < 5; i++)
{
key tmp_keys = keys >> 103;
keys = (keys << 25) | tmp_keys;
for (int j = (8 + 8 * i); j < (8 * (i + 2)); j++)
for (int k = 0; k < 16; k++)
sub_key[j][15 - k] = keys[127 - (k + 16 * (j - 8 - 8 * i))];
}

key tmp_keys = keys >> 103;
keys = (keys << 25) | tmp_keys;
for (int i = 48; i < 52; i++)
for (int j = 0; j < 16; j++)
sub_key[i][15 - j] = keys[127 - (j + 16 * (i - 48))];
}

void inv_subkeys_get(code sub_key[52])
{
for (int i = 6; i < 48; i += 6)
{
inv_sub_key[i] = invTimes(sub_key[48 - i]);
inv_sub_key[i + 1] = invPlus(sub_key[50 - i]);
inv_sub_key[i + 2] = invPlus(sub_key[49 - i]);
inv_sub_key[i + 3] = invTimes(sub_key[51 - i]);
}

for (int i = 0; i < 48; i += 6)
{
inv_sub_key[i + 4] = sub_key[46 - i];
inv_sub_key[i + 5] = sub_key[47 - i];
}

inv_sub_key[0] = invTimes(sub_key[48]);
inv_sub_key[1] = invPlus(sub_key[49]);
inv_sub_key[2] = invPlus(sub_key[50]);
inv_sub_key[3] = invTimes(sub_key[51]);

inv_sub_key[48] = invTimes(sub_key[0]);
inv_sub_key[49] = invPlus(sub_key[1]);
inv_sub_key[50] = invPlus(sub_key[2]);
inv_sub_key[51] = invTimes(sub_key[3]);
}

bitset<64> dencrypt(bitset<64> cipher)
{
bitset<16> I_1, I_2, I_3, I_4;
for (int i = 0; i < 16; i++)
{
I_1[15 - i] = cipher[63 - i];
I_2[15 - i] = cipher[47 - i];
I_3[15 - i] = cipher[31 - i];
I_4[i] = cipher[i];
}
for (int i = 0; i < 48; i += 6)
{
bitset<16> tmp_1 = Times(inv_sub_key[i], I_1);
bitset<16> tmp_2 = Plus(inv_sub_key[i + 1], I_2);
bitset<16> tmp_3 = Plus(inv_sub_key[i + 2], I_3);
bitset<16> tmp_4 = Times(inv_sub_key[i + 3], I_4);
bitset<16> tmp_5 = XOR(tmp_1, tmp_3);
bitset<16> tmp_6 = XOR(tmp_2, tmp_4);
bitset<16> tmp_7 = Times(inv_sub_key[i + 4], tmp_5);
bitset<16> tmp_8 = Plus(tmp_6, tmp_7);
bitset<16> tmp_9 = Times(tmp_8, inv_sub_key[i + 5]);
bitset<16> tmp_10 = Plus(tmp_7, tmp_9);
I_1 = XOR(tmp_1, tmp_9);
I_2 = XOR(tmp_3, tmp_9);
I_3 = XOR(tmp_2, tmp_10);
I_4 = XOR(tmp_4, tmp_10);
}
bitset<16> Y_1 = Times(I_1, inv_sub_key[48]);
bitset<16> Y_2 = Plus(I_3, inv_sub_key[49]);
bitset<16> Y_3 = Plus(I_2, inv_sub_key[50]);
bitset<16> Y_4 = Times(I_4, inv_sub_key[51]);

bitset<64> plaint;
for (int i = 0; i < 16; i++)
{
plaint[i] = Y_4[i];
plaint[i + 16] = Y_3[i];
plaint[i + 32] = Y_2[i];
plaint[i + 48] = Y_1[i];
}
return plaint;
}

int main()
{
unsigned int box1[] = {
0x00000000, 0xC0BA6CAC, 0x5A05DF1B, 0x9ABFB3B7, 0xB40BBE36, 0x74B1D29A,
0xEE0E612D, 0x2EB40D81, 0xB3667A2F, 0x73DC1683, 0xE963A534, 0x29D9C998, 0x076DC419, 0xC7D7A8B5,
0x5D681B02, 0x9DD277AE, 0xBDBDF21D, 0x7D079EB1, 0xE7B82D06, 0x270241AA, 0x09B64C2B, 0xC90C2087,
0x53B39330, 0x9309FF9C, 0x0EDB8832, 0xCE61E49E, 0x54DE5729, 0x94643B85, 0xBAD03604, 0x7A6A5AA8,
0xE0D5E91F, 0x206F85B3, 0xA00AE279, 0x60B08ED5, 0xFA0F3D62, 0x3AB551CE, 0x14015C4F, 0xD4BB30E3,
0x4E048354, 0x8EBEEFF8, 0x136C9856, 0xD3D6F4FA, 0x4969474D, 0x89D32BE1, 0xA7672660, 0x67DD4ACC,
0xFD62F97B, 0x3DD895D7, 0x1DB71064, 0xDD0D7CC8, 0x47B2CF7F, 0x8708A3D3, 0xA9BCAE52, 0x6906C2FE,
0xF3B97149, 0x33031DE5, 0xAED16A4B, 0x6E6B06E7, 0xF4D4B550, 0x346ED9FC, 0x1ADAD47D, 0xDA60B8D1,
0x40DF0B66, 0x806567CA, 0x9B64C2B1, 0x5BDEAE1D, 0xC1611DAA, 0x01DB7106, 0x2F6F7C87, 0xEFD5102B,
0x756AA39C, 0xB5D0CF30, 0x2802B89E, 0xE8B8D432, 0x72076785, 0xB2BD0B29, 0x9C0906A8, 0x5CB36A04,
0xC60CD9B3, 0x06B6B51F, 0x26D930AC, 0xE6635C00, 0x7CDCEFB7, 0xBC66831B, 0x92D28E9A, 0x5268E236,
0xC8D75181, 0x086D3D2D, 0x95BF4A83, 0x5505262F, 0xCFBA9598, 0x0F00F934, 0x21B4F4B5, 0xE10E9819,
0x7BB12BAE, 0xBB0B4702, 0x3B6E20C8, 0xFBD44C64, 0x616BFFD3, 0xA1D1937F, 0x8F659EFE, 0x4FDFF252,
0xD56041E5, 0x15DA2D49, 0x88085AE7, 0x48B2364B, 0xD20D85FC, 0x12B7E950, 0x3C03E4D1, 0xFCB9887D,
0x66063BCA, 0xA6BC5766, 0x86D3D2D5, 0x4669BE79, 0xDCD60DCE, 0x1C6C6162, 0x32D86CE3, 0xF262004F,
0x68DDB3F8, 0xA867DF54, 0x35B5A8FA, 0xF50FC456, 0x6FB077E1, 0xAF0A1B4D, 0x81BE16CC, 0x41047A60,
0xDBBBC9D7, 0x1B01A57B, 0xEDB88321, 0x2D02EF8D, 0xB7BD5C3A, 0x77073096, 0x59B33D17, 0x990951BB,
0x03B6E20C, 0xC30C8EA0, 0x5EDEF90E, 0x9E6495A2, 0x04DB2615, 0xC4614AB9, 0xEAD54738, 0x2A6F2B94,
0xB0D09823, 0x706AF48F, 0x5005713C, 0x90BF1D90, 0x0A00AE27, 0xCABAC28B, 0xE40ECF0A, 0x24B4A3A6,
0xBE0B1011, 0x7EB17CBD, 0xE3630B13, 0x23D967BF, 0xB966D408, 0x79DCB8A4, 0x5768B525, 0x97D2D989,
0x0D6D6A3E, 0xCDD70692, 0x4DB26158, 0x8D080DF4, 0x17B7BE43, 0xD70DD2EF, 0xF9B9DF6E, 0x3903B3C2,
0xA3BC0075, 0x63066CD9, 0xFED41B77, 0x3E6E77DB, 0xA4D1C46C, 0x646BA8C0, 0x4ADFA541, 0x8A65C9ED,
0x10DA7A5A, 0xD06016F6, 0xF00F9345, 0x30B5FFE9, 0xAA0A4C5E, 0x6AB020F2, 0x44042D73, 0x84BE41DF,
0x1E01F268, 0xDEBB9EC4, 0x4369E96A, 0x83D385C6, 0x196C3671, 0xD9D65ADD, 0xF762575C, 0x37D83BF0,
0xAD678847, 0x6DDDE4EB, 0x76DC4190, 0xB6662D3C, 0x2CD99E8B, 0xEC63F227, 0xC2D7FFA6, 0x026D930A,
0x98D220BD, 0x58684C11, 0xC5BA3BBF, 0x05005713, 0x9FBFE4A4, 0x5F058808, 0x71B18589, 0xB10BE925,
0x2BB45A92, 0xEB0E363E, 0xCB61B38D, 0x0BDBDF21, 0x91646C96, 0x51DE003A, 0x7F6A0DBB, 0xBFD06117,
0x256FD2A0, 0xE5D5BE0C, 0x7807C9A2, 0xB8BDA50E, 0x220216B9, 0xE2B87A15, 0xCC0C7794, 0x0CB61B38,
0x9609A88F, 0x56B3C423, 0xD6D6A3E9, 0x166CCF45, 0x8CD37CF2, 0x4C69105E, 0x62DD1DDF, 0xA2677173,
0x38D8C2C4, 0xF862AE68, 0x65B0D9C6, 0xA50AB56A, 0x3FB506DD, 0xFF0F6A71, 0xD1BB67F0, 0x11010B5C,
0x8BBEB8EB, 0x4B04D447, 0x6B6B51F4, 0xABD13D58, 0x316E8EEF, 0xF1D4E243, 0xDF60EFC2, 0x1FDA836E,
0x856530D9, 0x45DF5C75, 0xD80D2BDB, 0x18B74777, 0x8208F4C0, 0x42B2986C, 0x6C0695ED, 0xACBCF941,
0x36034AF6, 0xF6B9265A, 0xCCCCCCCC, 0xCCCCCC00, 0x00000100, 0x00000000, 0xF6B9265A, 0xCCCCCCCC,
0x00000008, 0x00000000};

uint32_t enc1[]{
0x46A95BAD,
0x1CAC84B6,
0xA67CB2B2,
0x32188937,
0x4872D39F,
0xF2A2E59B,
0x011B94D2,
};

// 爆破前7字节
for (int i = 0; i < 7; i++)
{
for (int c = 28; c < 132; c++)
{
if ((~box1[(uint8_t)c ^ 0x79] ^ 0xB0E0E879) == enc1[i])
{
printf("%c", c);
break;
}
}
}

// 后16字节进行IDEA解密
unsigned char enc2[16] = {
0x5C, 0x2F, 0xD0, 0xEC, 0x82, 0x0E, 0x67, 0x57,
0x6A, 0x9F, 0x91, 0xF6, 0x95, 0xA4, 0xAC, 0x90};
// unsigned int key[4] = {
// 0x42B2986C, 0x12345678, 0x0D6D6A3E, 0x89ABCDEF};
unsigned int key[4] = {
0x6C98B242, 0x78563412, 0x3E6A6D0D, 0xEFCDAB89};

code keys_input[8];
for (int i = 0; i < 4; i++)
{
keys_input[2 * i + 1] = key[i] & 0xFFFF;
keys_input[2 * i] = (key[i] >> 16) & 0xFFFF;
}

unsigned char result[16];
bitset<64> cipher1, cipher2;
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
cipher1[63 - (i * 8 + j)] = (enc2[i] >> (7 - j)) & 1;
cipher2[63 - (i * 8 + j)] = (enc2[i + 8] >> (7 - j)) & 1;
}
}
subkeys_get(keys_input);
inv_subkeys_get(sub_key);

bitset<64> plain1 = dencrypt(cipher1);
bitset<64> plain2 = dencrypt(cipher2);

uint64_t plain1_val = plain1.to_ullong();
uint64_t plain2_val = plain2.to_ullong();

uint8_t dec2[16]{};
memcpy(dec2, &plain2_val, 8);
memcpy(dec2 + 8, &plain1_val, 8);
reverse(dec2, dec2 + 16);

printf("%.16s\n", dec2);
return 0;
}

flag{moshui_build_this_block}