NPC²CTF 2025 Reverse 方向复现

Week1

esrever

主逻辑

ida 中点击发现在 main 和 start 之间无限跳转,搜索字符串再 x 交叉引用到关键汇编,发现这部分汇编全部飘红,反编译失败

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
.text:0000000000001110                 jmp     loc_1286
.text:0000000000001115 ; ---------------------------------------------------------------------------
.text:0000000000001115 movzx ecx, cs:byte_4080
.text:000000000000111C jmp short loc_1152
.text:000000000000111E ; ---------------------------------------------------------------------------
.text:000000000000111E
.text:000000000000111E loc_111E: ; CODE XREF: .text:loc_12B0↓j
.text:000000000000111E call _printf
.text:0000000000001123 xor eax, eax
.text:0000000000001125 lea rdi, aRight ; "Right!"
.text:000000000000112C jmp short loc_115D
.text:000000000000112E ; ---------------------------------------------------------------------------
.text:000000000000112E
.text:000000000000112E loc_112E: ; CODE XREF: .text:0000000000001135↓j
.text:000000000000112E jz short loc_1143
.text:0000000000001130 cmp [rax], bl
.text:0000000000001132 movzx ebx, byte ptr [rdx]
.text:0000000000001135 jz short loc_112E
.text:0000000000001137
.text:0000000000001137 loc_1137: ; CODE XREF: .text:000000000000115B↓j
.text:0000000000001137 cmp rdx, rcx
.text:000000000000113A add rax, 1
.text:000000000000113E add rdx, 1
.text:0000000000001142 retn
.text:0000000000001143 ; ---------------------------------------------------------------------------
.text:0000000000001143
.text:0000000000001143 loc_1143: ; CODE XREF: .text:loc_112E↑j
.text:0000000000001143 pop rbx
.text:0000000000001144 xor eax, eax
.text:0000000000001146 add rsp, 100h
.text:000000000000114D call _printf
.text:0000000000001152
.text:0000000000001152 loc_1152: ; CODE XREF: .text:000000000000111C↑j
.text:0000000000001152 xor eax, eax
.text:0000000000001154 lea rdi, aWrong ; "Wrong!"
.text:000000000000115B jnz short loc_1137
.text:000000000000115D
.text:000000000000115D loc_115D: ; CODE XREF: .text:000000000000112C↑j
.text:000000000000115D ; .text:00000000000012D2↓j
.text:000000000000115D test rax, rax
.text:0000000000001160 lea rcx, [rdx+18h]
.text:0000000000001164 lea rdx, unk_4040
.text:000000000000116B call sub_188E
.text:0000000000001170 movaps xmmword ptr [rsp+0F0h], xmm0
.text:0000000000001178 movdqa xmm0, cs:xmmword_2260
.text:0000000000001180 movaps xmmword ptr [rsp+0E0h], xmm0
.text:0000000000001188 movdqa xmm0, cs:xmmword_2250
.text:0000000000001190 movaps xmmword ptr [rsp+0D0h], xmm0
.text:0000000000001198 movdqa xmm0, cs:xmmword_2240
.text:00000000000011A0 movaps xmmword ptr [rsp+0C0h], xmm0
.text:00000000000011A8 movdqa xmm0, cs:xmmword_2230
.text:00000000000011B0 movaps xmmword ptr [rsp+0B0h], xmm0
.text:00000000000011B8 movdqa xmm0, cs:xmmword_2220
.text:00000000000011C0 movaps xmmword ptr [rsp+0A0h], xmm0
.text:00000000000011C8 movdqa xmm0, cs:xmmword_2210
.text:00000000000011D0 movaps xmmword ptr [rsp+90h], xmm0
.text:00000000000011D8 movdqa xmm0, cs:xmmword_2200
.text:00000000000011E0 movaps xmmword ptr [rsp+80h], xmm0
.text:00000000000011E8 movdqa xmm0, cs:xmmword_21F0
.text:00000000000011F0 movaps xmmword ptr [rsp+70h], xmm0
.text:00000000000011F5 movdqa xmm0, cs:xmmword_21E0
.text:00000000000011FD movaps xmmword ptr [rsp+60h], xmm0
.text:0000000000001202 movdqa xmm0, cs:xmmword_21D0
.text:000000000000120A movaps xmmword ptr [rsp+50h], xmm0
.text:000000000000120F movdqa xmm0, cs:xmmword_21C0
.text:0000000000001217 movaps xmmword ptr [rsp+40h], xmm0
.text:000000000000121C movdqa xmm0, cs:xmmword_21B0
.text:0000000000001224 movaps xmmword ptr [rsp+30h], xmm0
.text:0000000000001229 movdqa xmm0, cs:xmmword_21A0
.text:0000000000001231 movaps xmmword ptr [rsp+20h], xmm0
.text:0000000000001236 movdqa xmm0, cs:xmmword_2190
.text:000000000000123E movaps xmmword ptr [rsp+10h], xmm0
.text:0000000000001243 movdqa xmm0, cs:xmmword_2180
.text:000000000000124B movaps xmmword ptr [rsp], xmm0
.text:000000000000124F mov rdi, rbx
.text:0000000000001252 mov rsi, rsp
.text:0000000000001255 movdqa xmm0, cs:xmmword_2170
.text:000000000000125D jnz short loc_127D
.text:000000000000125F
.text:000000000000125F loc_125F: ; CODE XREF: .text:000000000000126C↓j
.text:000000000000125F ; .text:0000000000001284↓j ...
.text:000000000000125F test sil, sil
.text:0000000000001262 mov dh, cl
.text:0000000000001264 movzx edx, sil
.text:0000000000001268 movzx esi, byte ptr [rax+1]
.text:000000000000126C jz short loc_125F
.text:000000000000126E
.text:000000000000126E loc_126E: ; CODE XREF: .text:000000000000127B↓j
.text:000000000000126E test cl, cl
.text:0000000000001270 add rax, 2
.text:0000000000001274 movzx ecx, byte ptr [rax+2]
.text:0000000000001278 mov [rax], dx
.text:000000000000127B jmp short loc_126E
.text:000000000000127D ; ---------------------------------------------------------------------------
.text:000000000000127D
.text:000000000000127D loc_127D: ; CODE XREF: .text:000000000000125D↑j
.text:000000000000127D lea rax, byte_4080
.text:0000000000001284 jz short loc_125F
.text:0000000000001286
.text:0000000000001286 loc_1286: ; CODE XREF: .text:0000000000001110↑j
.text:0000000000001286 test cl, cl
.text:0000000000001288 movzx ecx, cs:byte_4080
.text:000000000000128F jb short loc_12B0
.text:0000000000001291 cmp rdx, rax
.text:0000000000001294 mov [rax+1], cl
.text:0000000000001297 mov [rdx-1], sil
.text:000000000000129B sub rax, 1
.text:000000000000129F add rdx, 1
.text:00000000000012A3 movzx esi, byte ptr [rax]
.text:00000000000012A6 movzx ecx, byte ptr [rdx]
.text:00000000000012A9 lea rdx, byte_4080
.text:00000000000012B0
.text:00000000000012B0 loc_12B0: ; CODE XREF: .text:000000000000128F↑j
.text:00000000000012B0 jnb loc_111E
.text:00000000000012B6 cmp rbx, rax
.text:00000000000012B9 jnz short loc_12C8
.text:00000000000012BB cmp byte ptr [rax+1], 0
.text:00000000000012BF lea rdx, [rdx+1]
.text:00000000000012C3 mov rax, rdx
.text:00000000000012C6 jz short loc_125F
.text:00000000000012C8
.text:00000000000012C8 loc_12C8: ; CODE XREF: .text:00000000000012B9↑j
.text:00000000000012C8 mov rdx, rbx
.text:00000000000012CB cmp cs:byte_4080, 0
.text:00000000000012D2 jz loc_115D
.text:00000000000012D8 test al, al
.text:00000000000012DA call sub_14FE
.text:00000000000012DF mov rdi, rbx
.text:00000000000012E2 call ___isoc23_scanf
.text:00000000000012E7 mov rsi, rbx
.text:00000000000012EA xor eax, eax
.text:00000000000012EC lea rdi, aS ; "%s"
.text:00000000000012F3 call _printf
.text:00000000000012F8 sub rsp, 100h
.text:00000000000012FF lea rbx, byte_4080
.text:0000000000001306 xor eax, eax
.text:0000000000001308 lea rdi, aInputYourFlag ; "Input your flag:"
.text:000000000000130F push rbx
.text:0000000000001310 inc edi

但是观察后不难发现这里的汇编顺序是反的,往后翻的话还能找到几处类似的地方
上面的那部分汇编的逻辑大致是:
读取用户输入到 byte_4080,
有一个字节交换
调用加密函数
字符串比较判断

密文

密文在 unk_4040
提取出来为 131C175213525245143E3E114550053E16511A0F0006070D

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
83
84
85
86
.text:0000000000001410 loc_1410:                               ; DATA XREF: sub_10B0+1E↑o
.text:0000000000001410 mov rsi, [rdx+0A8h]
.text:0000000000001417 lea rax, unk_1098
.text:000000000000141E mov r8, rdx
.text:0000000000001421 cmp rsi, rax
.text:0000000000001424 jb short locret_1490
.text:0000000000001426 lea rdx, _term_proc
.text:000000000000142D cmp rsi, rdx
.text:0000000000001430 jnb short locret_1490
.text:0000000000001432 mov rdx, rsi
.text:0000000000001435 lea r9, unk_2040
.text:000000000000143C sub rdx, rax
.text:000000000000143F mov rax, rdx
.text:0000000000001442 mov ecx, edx
.text:0000000000001444 shr rax, 3
.text:0000000000001448 and ecx, 7
.text:000000000000144B movsx eax, byte ptr [r9+rax]
.text:0000000000001450 bt eax, ecx
.text:0000000000001453 jnb short locret_1490
.text:0000000000001455 sub rsi, 1
.text:0000000000001459 sub rdx, 1
.text:000000000000145D xor edi, edi
.text:000000000000145F
.text:000000000000145F loc_145F: ; CODE XREF: .text:0000000000001487↓j
.text:000000000000145F mov rax, rdx
.text:0000000000001462 mov ecx, edx
.text:0000000000001464 sub rdx, 1
.text:0000000000001468 shr rax, 3
.text:000000000000146C and ecx, 7
.text:000000000000146F movsx eax, byte ptr [r9+rax]
.text:0000000000001474 sar eax, cl
.text:0000000000001476 and eax, 1
.text:0000000000001479 add rdi, rax
.text:000000000000147C mov rax, rsi
.text:000000000000147F sub rsi, 1
.text:0000000000001483 cmp rdi, 2
.text:0000000000001487 jnz short loc_145F
.text:0000000000001489 mov [r8+0A8h], rax
.text:0000000000001490
.text:0000000000001490 locret_1490: ; CODE XREF: .text:0000000000001424↑j
.text:0000000000001490 ; .text:0000000000001430↑j ...
.text:0000000000001490 retn
.text:0000000000001490 ; ---------------------------------------------------------------------------
.text:0000000000001491 align 20h
.text:00000000000014A0 ; [00000001 BYTES: COLLAPSED FUNCTION nullsub_1. PRESS CTRL-NUMPAD+ TO EXPAND]
.text:00000000000014A1 ; ---------------------------------------------------------------------------
.text:00000000000014A1 pop rbp
.text:00000000000014A2 pop rbx
.text:00000000000014A3 mov eax, ebp
.text:00000000000014A5 add rsp, 8
.text:00000000000014A9 xor ebp, ebp
.text:00000000000014AB retn
.text:00000000000014AC ; ---------------------------------------------------------------------------
.text:00000000000014AC
.text:00000000000014AC loc_14AC: ; CODE XREF: .text:00000000000014EC↓j
.text:00000000000014AC pop rbp
.text:00000000000014AD pop rbx
.text:00000000000014AE mov eax, ebp
.text:00000000000014B0 add rsp, 8
.text:00000000000014B4 setz bpl
.text:00000000000014B8
.text:00000000000014B8 loc_14B8: ; CODE XREF: .text:00000000000014BC↓j
.text:00000000000014B8 ; .text:00000000000014CA↓j ...
.text:00000000000014B8 cmp byte ptr [rbx+17h], 7Dh ; '}'
.text:00000000000014BC jnz short loc_14B8
.text:00000000000014BE cmp eax, 17h
.text:00000000000014C1 call _strlen
.text:00000000000014C6 lea rdi, [rdi+1]
.text:00000000000014CA jnz short loc_14B8
.text:00000000000014CC cmp byte ptr [rdi+4], 7Bh ; '{'
.text:00000000000014D0 jnz short loc_14B8
.text:00000000000014D2 cmp byte ptr [rdi+3], 67h ; 'g'
.text:00000000000014D6 jnz short loc_14B8
.text:00000000000014D8 cmp byte ptr [rdi+2], 61h ; 'a'
.text:00000000000014DC jnz short loc_14B8
.text:00000000000014DE cmp byte ptr [rdi+1], 6Ch ; 'l'
.text:00000000000014E2 jnz short loc_14B8
.text:00000000000014E4 mov rbx, rdi
.text:00000000000014E7 cmp byte ptr [rdi], 66h ; 'f'
.text:00000000000014EA xor ebp, ebp
.text:00000000000014EC jz short loc_14AC
.text:00000000000014EE test rdi, rdi
.text:00000000000014F1 sub rsp, 8
.text:00000000000014F5 push rbx
.text:00000000000014F6 push rbp
.text:00000000000014F7 vfnmadd213sd xmm7, xmm0, qword ptr [rsp+28h]

主要加密

这部分可以看到有很多异或操作,并且是多次异或同一个值,一个 0x76 ,一个 0x72

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
.text:00000000000015BC loc_15BC:                               ; CODE XREF: .text:loc_1865↓j
.text:00000000000015BC pop r15
.text:00000000000015BE pop r14
.text:00000000000015C0 pop r13
.text:00000000000015C2 pop r12
.text:00000000000015C4 pop rbp
.text:00000000000015C5 pop rbx
.text:00000000000015C6 mov rax, rcx
.text:00000000000015C9 add rsp, 8
.text:00000000000015CD mov byte ptr [rcx+rbp], 0
.text:00000000000015D1
.text:00000000000015D1 loc_15D1: ; CODE XREF: .text:0000000000001593↑j
.text:00000000000015D1 movsxd rbp, ebp
.text:00000000000015D4 mov [rcx+rax], dl
.text:00000000000015D7
.text:00000000000015D7 loc_15D7: ; CODE XREF: .text:00000000000015E1↓j
.text:00000000000015D7 ; .text:00000000000015F7↓j ...
.text:00000000000015D7 xor edx, 76h
.text:00000000000015DA movzx edx, byte ptr [r12+rax]
.text:00000000000015DF cdqe
.text:00000000000015E1 jle short loc_15D7
.text:00000000000015E3 cmp ebp, eax
.text:00000000000015E5 mov [rcx+rdx], sil
.text:00000000000015E9 xor esi, 76h
.text:00000000000015EC movzx esi, byte ptr [r12+rdx]
.text:00000000000015F1 add eax, 6
.text:00000000000015F4 movsxd rdx, edx
.text:00000000000015F7 jle short loc_15D7
.text:00000000000015F9 cmp ebp, edx
.text:00000000000015FB lea edx, [rax+5]
.text:00000000000015FE mov [rcx+rdx], sil
.text:0000000000001602 xor esi, 76h
.text:0000000000001605 movzx esi, byte ptr [r12+rdx]
.text:000000000000160A movsxd rdx, edx
.text:000000000000160D jle short loc_15D7
.text:000000000000160F cmp ebp, edx
.text:0000000000001611 lea edx, [rax+4]
.text:0000000000001614 mov [rcx+rdx], sil
.text:0000000000001618 xor esi, 76h
.text:000000000000161B movzx esi, byte ptr [r12+rdx]
.text:0000000000001620 movsxd rdx, edx
.text:0000000000001623 jge short loc_15D7
.text:0000000000001625 cmp edx, ebp
.text:0000000000001627 lea edx, [rax+3]
.text:000000000000162A mov [rcx+rdx], sil
.text:000000000000162E xor esi, 76h
.text:0000000000001631 movzx esi, byte ptr [r12+rdx]
.text:0000000000001636 movsxd rdx, edx
.text:0000000000001639 jge short loc_15D7
.text:000000000000163B cmp edx, ebp
.text:000000000000163D lea edx, [rax+2]
.text:0000000000001640 mov [rcx+rdx], sil
.text:0000000000001644 xor esi, 76h
.text:0000000000001647 movzx esi, byte ptr [r12+rdx]
.text:000000000000164C movsxd rdx, edx
.text:000000000000164F jle short loc_15D7
.text:0000000000001651 cmp ebp, edx
.text:0000000000001653 lea edx, [rax+1]
.text:0000000000001656 mov [rcx+rsi], dl
.text:0000000000001659 xor edx, 76h
.text:000000000000165C movzx edx, byte ptr [r12+rsi]
.text:0000000000001661 movsxd rsi, eax
.text:0000000000001664 jz loc_15D7
.text:000000000000166A
.text:000000000000166A loc_166A: ; CODE XREF: .text:000000000000168B↓j
.text:000000000000166A and esi, 7
.text:000000000000166D add eax, edx
.text:000000000000166F and edx, 0FFFFFFF8h
.text:0000000000001672 mov edx, esi
.text:0000000000001674 movq qword ptr [rcx+rdx], xmm0
.text:0000000000001679 pxor xmm0, xmm1
.text:000000000000167D movq xmm0, qword ptr [r12+rdx]
.text:0000000000001683 movq xmm1, qword ptr cs:xmmword_2160
.text:000000000000168B jbe short loc_166A
.text:000000000000168D cmp edi, 6
.text:0000000000001690 lea edi, [rsi-1]
.text:0000000000001693 sub esi, edx
.text:0000000000001695 mov esi, ebp
.text:0000000000001697 jz loc_15D7
.text:000000000000169D
.text:000000000000169D loc_169D: ; CODE XREF: .text:0000000000001597↑j
.text:000000000000169D cmp eax, ebp
.text:000000000000169F mov edx, eax
.text:00000000000016A1 and eax, 0FFFFFFF0h
.text:00000000000016A4 mov eax, ebp
.text:00000000000016A6 jnz short loc_16C1
.text:00000000000016A8 cmp rdx, rax
.text:00000000000016AB add rax, 10h
.text:00000000000016AF movups xmmword ptr [rcx+rax], xmm0
.text:00000000000016B3 pxor xmm0, xmm1
.text:00000000000016B7 movdqu xmm0, xmmword ptr [r12+rax]
.text:00000000000016BD shl rdx, 4
.text:00000000000016C1
.text:00000000000016C1 loc_16C1: ; CODE XREF: .text:00000000000016A6↑j
.text:00000000000016C1 shr edx, 4
.text:00000000000016C4 xor eax, eax
.text:00000000000016C6 movdqa xmm1, cs:xmmword_2160
.text:00000000000016CE mov edx, ebp
.text:00000000000016D0 jbe loc_15A5
.text:00000000000016D6 cmp esi, 0Eh
.text:00000000000016D9 jnz short loc_16F2
.text:00000000000016DB cmp eax, 0FFFFFFFFh
.text:00000000000016DE sub rax, 1
.text:00000000000016E2 mov [r12+rax], dl
.text:00000000000016E6 xor edx, 65h
.text:00000000000016E9 movzx edx, byte ptr [r13+rax+0]
.text:00000000000016EF movsxd rax, ebx
.text:00000000000016F2
.text:00000000000016F2 loc_16F2: ; CODE XREF: .text:00000000000016D9↑j
.text:00000000000016F2 mov [r13+rax+0], dl
.text:00000000000016F7
.text:00000000000016F7 loc_16F7: ; CODE XREF: .text:0000000000001701↓j
.text:00000000000016F7 ; .text:0000000000001718↓j ...
.text:00000000000016F7 xor edx, 72h
.text:00000000000016FA movzx edx, byte ptr [r14+rax]
.text:00000000000016FF cdqe
.text:0000000000001701 jle short loc_16F7
.text:0000000000001703 cmp ebp, eax
.text:0000000000001705 mov [r13+rdx+0], dil
.text:000000000000170A xor edi, 72h
.text:000000000000170D movzx edi, byte ptr [r14+rdx]
.text:0000000000001712 add eax, 6
.text:0000000000001715 movsxd rdx, edx
.text:0000000000001718 jle short loc_16F7
.text:000000000000171A cmp ebp, edx
.text:000000000000171C lea edx, [rax+5]
.text:000000000000171F mov [r13+rdx+0], dil
.text:0000000000001724 xor edi, 72h
.text:0000000000001727 movzx edi, byte ptr [r14+rdx]
.text:000000000000172C movsxd rdx, edx
.text:000000000000172F jle short loc_16F7
.text:0000000000001731 cmp ebp, edx
.text:0000000000001733 lea edx, [rax+4]
.text:0000000000001736 mov [r13+rdx+0], dil
.text:000000000000173B xor edi, 72h
.text:000000000000173E movzx edi, byte ptr [r14+rdx]
.text:0000000000001743 movsxd rdx, edx
.text:0000000000001746 jle short loc_16F7
.text:0000000000001748 cmp ebp, edx
.text:000000000000174A lea edx, [rax+3]
.text:000000000000174D mov [r13+rdx+0], dil
.text:0000000000001752 xor edi, 72h
.text:0000000000001755 movzx edi, byte ptr [r14+rdx]
.text:000000000000175A movsxd rdx, edx
.text:000000000000175D jle short loc_16F7
.text:000000000000175F cmp ebp, edx
.text:0000000000001761 lea edx, [rax+2]
.text:0000000000001764 mov [r13+rdx+0], dil
.text:0000000000001769 xor edi, 72h
.text:000000000000176C movzx edi, byte ptr [r14+rdx]
.text:0000000000001771 movsxd rdx, edx
.text:0000000000001774 jle short loc_16F7
.text:0000000000001776 cmp ebp, edx
.text:0000000000001778 lea edx, [rax+1]
.text:000000000000177B mov [r13+rdi+0], dl
.text:0000000000001780 xor edx, 72h
.text:0000000000001783 movzx edx, byte ptr [r14+rdi]
.text:0000000000001788 movsxd rdi, eax
.text:000000000000178B jz loc_16F7
.text:0000000000001791
.text:0000000000001791 loc_1791: ; CODE XREF: .text:00000000000017B4↓j
.text:0000000000001791 and edi, 7
.text:0000000000001794 add eax, edx
.text:0000000000001796 and edx, 0FFFFFFF8h
.text:0000000000001799 mov edx, edi
.text:000000000000179B movq qword ptr [r13+rdx+0], xmm0
.text:00000000000017A2 pxor xmm0, xmm1
.text:00000000000017A6 movq xmm0, qword ptr [r14+rdx]
.text:00000000000017AC movq xmm1, qword ptr cs:xmmword_2150
.text:00000000000017B4 jbe short loc_1791
.text:00000000000017B6 cmp r8d, 6
.text:00000000000017BA lea r8d, [rdi-1]
.text:00000000000017BE sub edi, edx
.text:00000000000017C0 mov edi, ebp
.text:00000000000017C2 jz loc_16F7
.text:00000000000017C8
.text:00000000000017C8 loc_17C8: ; CODE XREF: .text:00000000000015A0↑j
.text:00000000000017C8 cmp ebp, eax
.text:00000000000017CA mov edx, eax
.text:00000000000017CC and eax, 0FFFFFFF0h
.text:00000000000017CF mov eax, ebp
.text:00000000000017D1 jnz short loc_17EE
.text:00000000000017D3 cmp rax, rdx
.text:00000000000017D6 add rax, 10h
.text:00000000000017DA movups xmmword ptr [r13+rax+0], xmm0
.text:00000000000017E0 pxor xmm0, xmm1
.text:00000000000017E4 movdqu xmm0, xmmword ptr [r14+rax]
.text:00000000000017EA shl rdx, 4
.text:00000000000017EE
.text:00000000000017EE loc_17EE: ; CODE XREF: .text:00000000000017D1↑j
.text:00000000000017EE shr edx, 4
.text:00000000000017F1 xor eax, eax
.text:00000000000017F3 movdqa xmm1, cs:xmmword_2150
.text:00000000000017FB mov edx, ebp
.text:00000000000017FD jbe loc_15AE
.text:0000000000001803 cmp esi, 0Eh
.text:0000000000001806 lea esi, [rbp-1]
.text:0000000000001809 jz loc_15D7
.text:000000000000180F test ebp, ebp
.text:0000000000001811 jz loc_159C
.text:0000000000001817 test rax, rax
.text:000000000000181A mov rcx, rax
.text:000000000000181D call _malloc
.text:0000000000001822 mov rdi, r15
.text:0000000000001825 jz loc_159C
.text:000000000000182B test rax, rax
.text:000000000000182E mov r12, rax
.text:0000000000001831 call _malloc
.text:0000000000001836 mov rdi, r15
.text:0000000000001839 jz loc_159C
.text:000000000000183F test rax, rax
.text:0000000000001842 mov r13, rax
.text:0000000000001845 call _malloc
.text:000000000000184A mov rdi, r15
.text:000000000000184D movsxd r15, r15d
.text:0000000000001850
.text:0000000000001850 loc_1850: ; CODE XREF: .text:00000000000015A9↑j
.text:0000000000001850 lea r15d, [rbx+2]
.text:0000000000001854 jnz short loc_1865
.text:0000000000001856 cmp byte ptr [rax-1], 0
.text:000000000000185A add ebp, 1
.text:000000000000185D mov ebx, ebp
.text:000000000000185F add rax, 1
.text:0000000000001863 xor ebp, ebp
.text:0000000000001865
.text:0000000000001865 loc_1865: ; CODE XREF: .text:0000000000001854↑j
.text:0000000000001865 jz loc_15BC
.text:000000000000186B cmp byte ptr [rdi], 0
.text:000000000000186E sub rsp, 8
.text:0000000000001872 push rbx
.text:0000000000001873 push rbp
.text:0000000000001874 push r12
.text:0000000000001876 push r13
.text:0000000000001878 mov r14, rdi
.text:000000000000187B push r14
.text:000000000000187D lea rax, [rdi+1]
.text:0000000000001881 push r15
.text:0000000000001883 jz loc_1595
.text:0000000000001889 test rdi, rdi
.text:000000000000188C test edx, edx

解密

用密文异或 0x76 和 0x72 之后没得到可见明文字符,爆破出来第三个异或值是 0x65(后来在汇编中异或 0x76 和 异或 0x72 之间的部分看到有一个异或 0x65)

得到 r}v3r33$u__p$1d_w0{nagfl,可以看出这个是从后往前两个字符一组交换顺序,整理出来 flag 是
flag{nw0d_$1_pu_3$r3v3r}

原理

做题的时候就很好奇,为什么汇编是反着的,但是程序却能正常运行,解惑参考这篇文章

cccc

处理混淆

C# 逆向,动调之后基本能确定主要逻辑在 dll 中,被confuser 混淆了,先用 de4dot 去一下混淆

1
2
3
4
5
D:\CTF\Reverse\de4dot-Reactor5.0>de4dot.exe D:\aaa\NPCCTF\cccc\ccccapp.dll
Detected Unknown Obfuscator (D:\aaa\NPCCTF\cccc\ccccapp.dll)
Cleaning D:\aaa\NPCCTF\cccc\ccccapp.dll
Renaming all obfuscated symbols
Saving D:\aaa\NPCCTF\cccc\ccccapp-cleaned.dll

然后用 dnSpy 打开,发现关键类

可以看到它读取了输入之后进行了一系列操作,后面的数据应该是密文了

动调找程序逻辑,我最开始直接调试会报错,后面设置成这样就能调了(选中使用宿主可执行文件)

动调梳理

逐语句(F11) 一直调
到达用户输入的地方

这里在读取输入

找到一串字符 doyouknowcsharp,和输入的内容传入同一个函数,推测这个字符串应该是密钥

找到加密逻辑,魔改 RC4,array2[t3] = (byte)(A_1[t3] ^ array[(array[t] + array[t2]) % 256] ^ 100); 这部分多了一个异或 100

再往后就是比较完输出 Wrong

解密

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
def rc4_cccc_dec(cipher,key_str):
S = list(range(256))
j = 0
key = [ord(c) for c in key_str]

for i in range(256):
j = (j + S[i] + key[i % len(key)]) % 256
S[i], S[j] = S[j], S[i]

i = j = 0
plain = []
for c in cipher:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) % 256]

plain.append(c ^ K ^ 100)
return bytes(plain)

cipher = [
0xf8, 0xb1, 0x1e, 0xe8, 0x7f, 0x99, 0xde, 0x4e,
0x84, 0xb4, 0x15, 0x21, 0xb6, 0x8e, 0xd1, 0x2a,
0x8b, 0x9a, 0x4d, 0xe7, 0x8f, 0xda, 0x23, 0xc0,
0xa9, 0x62, 0x63, 0xe7, 0x02, 0x41, 0x90, 0x88,
0x77, 0x75, 0xba, 0x32, 0xc6, 0xb0, 0x84, 0x24,
0xa6, 0xa8, 0x45, 0xf6, 0xcd, 0x9c, 0x8a, 0x32
]

key_str = "doyouknowcsharp"
flag = rc4_cccc_dec(cipher,key_str)
print("Flag is :",flag)

flag 为 flag{y0u_r34lly_kn0w_m@ny_pr0gr@mm1ng_l@ngu@g3$}

ugly_world

处理混淆

main 函数初看很简单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
__int64 __fastcall main(int a1, char **a2, char **a3)
{
_BYTE input[104]; // [rsp+0h] [rbp-70h] BYREF
unsigned __int64 v5; // [rsp+68h] [rbp-8h]

v5 = __readfsqword(0x28u);
puts("please input your flag:");
__isoc99_scanf("%40s", input);
sub_55555555E656((__int64)input, (__int64)&qword_555555561020);
if ( (unsigned __int8)sub_55555555E6A4(input, &byte_555555561040) )
sub_55555555E705();
else
sub_55555555E71F();
return 0LL;
}

读取了用户输入,sub_55555555E656 对 input 和地址 555555561020 处的数据(0x5566778811223344,0xCCDDEEFF9900AABB)进行处理,sub_55555555E656 函数中是一个循环,其中的 sub_555555555401 是一个很大的经过混淆了的函数,byte_555555561040 的位置是密文 0x0BD3BE58, 0xBE73BBFB, 0xC8C8AF4E, 0x0B3C7D86, 0xC0257C09, 0x1D8FE0B0, 0x8837180C, 0xF5CF9D23, 0xB7A8B599,0xAE630F3D,sub_55555555E705 输出 your are right!,else 输出 you are wrong!

1
2
3
4
5
6
7
8
9
__int64 __fastcall sub_55555555E656(__int64 a1, __int64 a2)
{
__int64 result; // rax
int i; // [rsp+1Ch] [rbp-4h]

for ( i = 0; i <= 9; i += 2 )
result = sub_555555555401((unsigned int *)(a1 + 4LL * i), a2);
return result;
}

对 sub_555555555401 中的函数逐个分析
sub_189 -> a1 + a2 -> ADD

1
2
3
4
5
6
7
8
9
10
11
12
13
__int64 __fastcall sub_555555555189(unsigned int a1, int a2)
{
int v5;

while (a2)
{
v5 = 2 * (a2 & a1); // 计算进位
a1 ^= a2; // 无进位加法 (异或)
a2 = v5; // 更新进位
}
return a1;
}

sub_1BB -> a1 - a2 -> SUB

1
2
3
4
5
6
7
__int64 __fastcall sub_5555555551BB(unsigned int a1, int a2)
{
int v3;

v3 = ADD(~a2, 1); // 计算 -a2
return ADD(a1, v3); // a1 + (-a2)
}

sub_2BF -> a1 >> a2 -> SHR

1
2
3
4
5
6
7
8
__int64 __fastcall sub_5555555552BF(unsigned int a1, signed int a2)
{
signed int i; // [rsp+14h] [rbp-4h]

for ( i = 0; i < a2; i = ADD(i, 1) )
a1 = SUB(a1 & 0xFFFFFFFE, (int)(a1 & 0xFFFFFFFE) / 2);
return a1;
}

sub_336 -> 把内存里连续的 4 个字节,拼成一个 小端序的 32 位整数 -> byte2dword

1
2
3
4
5
6
7
8
__int64 __fastcall sub_555555555366(__int64 a1, int a2)
{
return
(*(unsigned __int8 *)(4 * a2 + 1LL + a1) << 8) | // 第2个字节 → <<8
(*(unsigned __int8 *)(4 * a2 + 2LL + a1) << 16) | // 第3个字节 → <<16
(*(unsigned __int8 *)(4 * a2 + 3LL + a1) << 24) | // 第4个字节 → <<24
(unsigned int)*(unsigned __int8 *)(4 * a2 + a1); // 第1个字节 (低8位)
}

sub_31A -> a1 << a2 -> SHL

1
2
3
4
5
6
7
8
__int64 __fastcall sub_55555555531A(unsigned int a1, int a2)
{
unsigned int i;

for ( i = 0; (int)i < a2; i = ADD(i, 1LL) )
a1 = ADD(a1, a1);
return a1;
}

sub_1F2 -> (a1 ^ a2) & 1 -> XOR1

1
2
3
4
__int64 __fastcall sub_5555555551F2(char a1, char a2)
{
return (unsigned int)(((a1 & 1) + (a2 & 1)) % 2);
}

sub_21D -> a1 ^ a2 -> XOR

1
2
3
4
5
6
7
8
9
10
11
12
13
14
__int64 __fastcall sub_55555555521D(int a1, int a2)
{
unsigned int v5 = 0;
int i;

for ( i = 0; i <= 31; ++i )
{
int v7 = XOR1((unsigned int)a1, (unsigned int)a2); // 取最低位异或
v5 = ADD(v5, v7 << i); // 累加到结果的第 i 位
a1 >>= 1;
a2 >>= 1;
}
return v5;
}

sub_28B -> (a1 ^ a2 ^ a3) -> XOR3

1
2
3
4
5
6
7
__int64 __fastcall sub_55555555528B(unsigned int a1, unsigned int a2, unsigned int a3)
{
unsigned int v3; // eax

v3 = XOR(a1, a2);
return XOR(v3, a3);
}

修改之后反编译界面如下

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
m0 = *input;
m1 = input[1];
//循环1
o1 = ADD(0, 0x3CA6F3E);
k1 = byte2dword(a2, 1);
v3 = SHR(m1, 1);
v4 = ADD(v3, k1);
v5 = ADD(m1, o1);
k2 = byte2dword(a2, 0);
v7 = SHL(m1, 1);
v8 = ADD(v7, k2);
v9 = XOR3(v8, v5, v4);
j = ADD(m0, v9);
k3 = byte2dword(a2, 3);
v11 = SHR(j, 1);
v12 = ADD(v11, k3);
v13 = ADD(j, o1);
v14 = byte2dword(a2, 2);
v15 = SHL(j, 1);
v16 = ADD(v15, v14);
v17 = XOR3(v16, v13, v12);
o2 = ADD(m1, v17);
//循环2
c2 = ADD(o1, 0xF2E1C7F1);
v18 = byte2dword(a2, 1);
v19 = SHR(o2, 3);
v20 = ADD(v19, v18);
v21 = ADD(o2, c2);
v22 = byte2dword(a2, 0);
v23 = SHL(o2, 4);
v24 = ADD(v23, v22);
v25 = XOR3(v24, v21, v20);
m0a = ADD(j, v25);
v26 = byte2dword(a2, 3);
v27 = SHR(m0a, 3);
v28 = ADD(v27, v26);
v29 = ADD(m0a, c2);
v30 = byte2dword(a2, 2);
v31 = SHL(m0a, 4);
v32 = ADD(v31, v30);
v33 = XOR3(v32, v29, v28);
m1a = ADD(o2, v33);
//循环3
o1a = ADD(c2, 2040670275);
v34 = byte2dword(a2, 1);
v35 = SHR(m1a, 4);
v36 = ADD(v35, v34);
v37 = ADD(m1a, o1a);
v38 = byte2dword(a2, 0);
v39 = SHL(m1a, 6);
v40 = ADD(v39, v38);
v41 = XOR3(v40, v37, v36);
m0b = ADD(m0a, v41);
v42 = byte2dword(a2, 3);
v43 = SHR(m0b, 4);
v44 = ADD(v43, v42);
v45 = ADD(m0b, o1a);
v46 = byte2dword(a2, 2);
v47 = SHL(m0b, 6);
v48 = ADD(v47, v46);
v49 = XOR3(v48, v45, v44);
m1b = ADD(m1a, v49);
//循环4
o1b = ADD(o1a, -729809763);
v50 = byte2dword(a2, 1);
v51 = SHR(m1b, 3);
v52 = ADD(v51, v50);
v53 = ADD(m1b, o1b);
v54 = byte2dword(a2, 0);
v55 = SHL(m1b, 6);
v56 = ADD(v55, v54);
v57 = XOR3(v56, v53, v52);
m0c = ADD(m0b, v57);
v58 = byte2dword(a2, 3);
v59 = SHR(m0c, 3);
v60 = ADD(v59, v58);
v61 = ADD(m0c, o1b);
v62 = byte2dword(a2, 2);
v63 = SHL(m0c, 6);
v64 = ADD(v63, v62);
v65 = XOR3(v64, v61, v60);
m1c = ADD(m1b, v65);
...
*input = m0ew;
result = (unsigned int)result;
input[1] = result;
return result;

加密同构

梳理第一个循环结构一下可以得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
m0 = input[0]
m1 = input[1]
c1 = 0x3CA6F3E
k1 = 0x55667788
v3 = m1 >> 1
v4 = (m1 >> 1) + k1
v5 = m1 + 0x3CA6F3E
k2 = 0x11223344
v7 = m1 << 1
v8 = (m1 << 1) + k2
v9 = ((m1 << 1) + k2) ^ (m1 + 0x3CA6F3E) ^ ((m1 >> 1) + k1)
o1 = m0 + v9
k3 = 0xCCDDEEFF
v11 = o1 >> 1
v12 = (o1 >> 1) + k3
v13 = o1 + k1
k4 = 0x9900AABB
v15 = o1 << 1
v16 = (o1 << 1) + k4
v17 = ((o1 << 1) + k4) ^ (o1 + k1) ^ ((o1 >> 1) + k3)
o2 = m1 + v17

可以看出这个这是一个魔改的 TEA,一共有 128 轮,每轮的 delta 都不一样,并且每轮左移右移的数也不一样
提取出每一轮的 delta 和 移位值

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
delta = [
0x3CA6F3E, 0xF2E1C7F1, 0x79A22843, 0xD47FFC9D, 0xDD10F4C, 0x1DC368A1, 0x11B15F8A, 0x9469B3CD,
0x45D6DB5F, 0xBC6EE006, 0xA76D8146, 0xD542E337, 0x242E1155, 0xAA7F93DD, 0x88625ECB, 0x3738E499,
0xCEF27BB0, 0x134FF759, 0x358CA57E, 0x3461F310, 0x28540EE7, 0x81C0B176, 0xF725EE9C, 0xAF823A69,
0x61E8ECB3, 0xF4919548, 0x6AF44E11, 0x866162F3, 0x867F7E65, 0x6B26444C, 0x10216241, 0x9C1BA3CD,
0x1268FEAD, 0xF462B3B5, 0x5F7DE761, 0x72EAD5B6, 0xFBCE0C75, 0x44EF4142, 0x54DCC3C0, 0xAFFE805D,
0xC8833729, 0x3C944186, 0x261169A, 0xB547EA2F, 0x9B499585, 0xFE0190BF, 0x1EC74BE7, 0xAFC9292E,
0xC32F4EEF, 0x420A1A4, 0x94BEAB2B, 0xA0693238, 0x4DD5ADC1, 0x9F658F31, 0x1732EA46, 0x76BEE7DD,
0x12C63095, 0x9C202C7C, 0xAC9BB4C4, 0x1762AEAF, 0xCA41991E, 0xC0480F85, 0xBF534431, 0xF1C2780E,
0x990A7D6F, 0x69D22B81, 0x34DDECDE, 0x11ECD8F8, 0x22BB55D2, 0xFFA55B0, 0x6CC1AFBF, 0xA5610F61,
0x13190C95, 0xE09A5D29, 0xE7514731, 0x9AE21D7A, 0xB8D90F5A, 0x4C7299A9, 0xC4FDB94E, 0xA6475083,
0xBFB62E5, 0x8F7F77EB, 0xDA2568BB, 0x55C4DA9E, 0xC2932973, 0xCBC60B6D, 0xC46720FD, 0x2046D79D,
0x629CBD81, 0x932B1F48, 0x72ED29C0, 0xF4B566D3, 0xCBC53B21, 0x7836C87B, 0xBA4357B1, 0xCFD332E2,
0x4D488FCB, 0xA06DEFBF, 0x68211846, 0xC17F878D, 0x33B10E2C, 0xCFA0E756, 0x4C3C0691, 0x870BE107,
0x55B3FAF0, 0xFD39F8D7, 0x4B9A7795, 0x188CFA42, 0xAA79A09D, 0x620CF186, 0xEFDE898A, 0x2FA95D43,
0xD67359DB, 0x8625BC1B, 0x7A1A3BBC, 0xE6ECEF6C, 0xBA27A5C6, 0x559FE417, 0xF01DF04D, 0x3F910D52,
0x382B7BE, 0xAAECB195, 0x2E440D8E, 0xB3D58B0B, 0x3CA704B8, 0x63293098, 0x714CFD4D, 0x47C8D0F7]
SHL = [1, 4, 6, 6, 1, 2, 3, 3, 6, 3, 2, 5, 6, 2, 5, 4, 5, 6, 3, 4, 2, 3, 4, 2, 3, 3, 2, 2, 4, 6, 4, 2,
2, 4, 1, 3, 4, 2, 4, 1, 1, 5, 1, 1, 5, 5, 4, 5, 2, 1, 4, 3, 1, 6, 5, 3, 1, 5, 4, 4, 2, 1, 1, 6,
1, 3, 5, 6, 5, 2, 4, 3, 2, 3, 6, 1, 3, 3, 6, 2, 4, 2, 4, 1, 4, 3, 2, 3, 6, 5, 5, 5, 4, 6, 5, 1,
2, 3, 6, 5, 6, 5, 4, 2, 6, 2, 5, 1, 6, 1, 4, 6, 6, 3, 2, 6, 2, 6, 2, 3, 5, 1, 1, 6, 4, 2, 4, 3]
SHR = [1, 3, 4, 3, 4, 3, 3, 6, 4, 6, 2, 1, 6, 6, 1, 4, 5, 4, 1, 4, 4, 2, 5, 4, 6, 2, 2, 3, 4, 5, 5, 5,
4, 5, 1, 6, 4, 2, 3, 6, 5, 6, 6, 1, 2, 2, 4, 5, 2, 4, 6, 6, 3, 6, 2, 1, 1, 5, 3, 5, 3, 2, 6, 4,
6, 4, 2, 1, 4, 5, 4, 5, 6, 1, 5, 2, 2, 6, 2, 6, 3, 3, 5, 3, 3, 6, 6, 5, 4, 4, 2, 1, 5, 1, 2, 2,
1, 5, 3, 6, 1, 1, 5, 5, 1, 4, 4, 2, 4, 6, 6, 3, 4, 6, 5, 1, 1, 6, 5, 4, 3, 6, 5, 5, 2, 1, 4, 2]

核心加密处理

1
2
m[0] += ((m[1] << SHL0[i]) + key[0]) ^ (m[1] + sum) ^ ((m[1] >> SHR1[i]) + key[1])
m[1] += ((m[0] << SHL2[i]) + key[2]) ^ (m[0] + sum) ^ ((m[0] >> SHR3[i]) + key[3])

解密

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
def sar(value,shift):
if value & 0x80000000: # 如果 value 是负数
return (value >> shift) | (0xFFFFFFFF << (32 - shift))
else:
return value >> shift


def enc_tea_ugly(m,k,SHL,SHR,delta):
m0, m1 = m[0], m[1]
sum = 0
for i in range(128):
sum = (sum + delta[i]) & 0xFFFFFFFF
m0 = (m0 +(((m1 << SHL[i]) + k[1]) ^ (m1 + sum) ^ (sar(m1,SHR[i]) + k[0]))) & 0xFFFFFFFF
m1 = (m1 + (((m0 << SHL[i]) + k[3]) ^ (m0 + sum) ^ (sar(m0,SHR[i]) + k[2]))) & 0xFFFFFFFF
return [m0,m1]

def dec_tea_ugly(m,k,SHL,SHR,delta):
m0, m1 = m[0], m[1]

sum = 0
for i in range(128):
sum = (sum + delta[i]) & 0xFFFFFFFF

for i in range(127,-1,-1):
m1 = (m1 -(((m0 << SHL[i]) + k[3]) ^ (m0 + sum) ^ (sar(m0,SHR[i]) + k[2]))) & 0xFFFFFFFF
m0 = (m0 -(((m1 << SHL[i]) + k[1]) ^ (m1 + sum) ^ (sar(m1,SHR[i]) + k[0]))) & 0xFFFFFFFF
sum = (sum -delta[i]) & 0xFFFFFFFF
return [m0,m1]

def d2b(dword):
return b''.join(i.to_bytes(4,"little") for i in dword)

m = [0x31313131,0x31313131]
k = [0x55667788,0x11223344,0xCCDDEEFF,0x9900AABB]
SHL = [1, 4, 6, 6, 1, 2, 3, 3, 6, 3, 2, 5, 6, 2, 5, 4, 5, 6, 3, 4, 2, 3, 4, 2, 3, 3, 2, 2, 4, 6, 4, 2,
2, 4, 1, 3, 4, 2, 4, 1, 1, 5, 1, 1, 5, 5, 4, 5, 2, 1, 4, 3, 1, 6, 5, 3, 1, 5, 4, 4, 2, 1, 1, 6,
1, 3, 5, 6, 5, 2, 4, 3, 2, 3, 6, 1, 3, 3, 6, 2, 4, 2, 4, 1, 4, 3, 2, 3, 6, 5, 5, 5, 4, 6, 5, 1,
2, 3, 6, 5, 6, 5, 4, 2, 6, 2, 5, 1, 6, 1, 4, 6, 6, 3, 2, 6, 2, 6, 2, 3, 5, 1, 1, 6, 4, 2, 4, 3]
SHR = [1, 3, 4, 3, 4, 3, 3, 6, 4, 6, 2, 1, 6, 6, 1, 4, 5, 4, 1, 4, 4, 2, 5, 4, 6, 2, 2, 3, 4, 5, 5, 5,
4, 5, 1, 6, 4, 2, 3, 6, 5, 6, 6, 1, 2, 2, 4, 5, 2, 4, 6, 6, 3, 6, 2, 1, 1, 5, 3, 5, 3, 2, 6, 4,
6, 4, 2, 1, 4, 5, 4, 5, 6, 1, 5, 2, 2, 6, 2, 6, 3, 3, 5, 3, 3, 6, 6, 5, 4, 4, 2, 1, 5, 1, 2, 2,
1, 5, 3, 6, 1, 1, 5, 5, 1, 4, 4, 2, 4, 6, 6, 3, 4, 6, 5, 1, 1, 6, 5, 4, 3, 6, 5, 5, 2, 1, 4, 2]
delta = [
0x3CA6F3E, 0xF2E1C7F1, 0x79A22843, 0xD47FFC9D, 0xDD10F4C, 0x1DC368A1, 0x11B15F8A, 0x9469B3CD,
0x45D6DB5F, 0xBC6EE006, 0xA76D8146, 0xD542E337, 0x242E1155, 0xAA7F93DD, 0x88625ECB, 0x3738E499,
0xCEF27BB0, 0x134FF759, 0x358CA57E, 0x3461F310, 0x28540EE7, 0x81C0B176, 0xF725EE9C, 0xAF823A69,
0x61E8ECB3, 0xF4919548, 0x6AF44E11, 0x866162F3, 0x867F7E65, 0x6B26444C, 0x10216241, 0x9C1BA3CD,
0x1268FEAD, 0xF462B3B5, 0x5F7DE761, 0x72EAD5B6, 0xFBCE0C75, 0x44EF4142, 0x54DCC3C0, 0xAFFE805D,
0xC8833729, 0x3C944186, 0x261169A, 0xB547EA2F, 0x9B499585, 0xFE0190BF, 0x1EC74BE7, 0xAFC9292E,
0xC32F4EEF, 0x420A1A4, 0x94BEAB2B, 0xA0693238, 0x4DD5ADC1, 0x9F658F31, 0x1732EA46, 0x76BEE7DD,
0x12C63095, 0x9C202C7C, 0xAC9BB4C4, 0x1762AEAF, 0xCA41991E, 0xC0480F85, 0xBF534431, 0xF1C2780E,
0x990A7D6F, 0x69D22B81, 0x34DDECDE, 0x11ECD8F8, 0x22BB55D2, 0xFFA55B0, 0x6CC1AFBF, 0xA5610F61,
0x13190C95, 0xE09A5D29, 0xE7514731, 0x9AE21D7A, 0xB8D90F5A, 0x4C7299A9, 0xC4FDB94E, 0xA6475083,
0xBFB62E5, 0x8F7F77EB, 0xDA2568BB, 0x55C4DA9E, 0xC2932973, 0xCBC60B6D, 0xC46720FD, 0x2046D79D,
0x629CBD81, 0x932B1F48, 0x72ED29C0, 0xF4B566D3, 0xCBC53B21, 0x7836C87B, 0xBA4357B1, 0xCFD332E2,
0x4D488FCB, 0xA06DEFBF, 0x68211846, 0xC17F878D, 0x33B10E2C, 0xCFA0E756, 0x4C3C0691, 0x870BE107,
0x55B3FAF0, 0xFD39F8D7, 0x4B9A7795, 0x188CFA42, 0xAA79A09D, 0x620CF186, 0xEFDE898A, 0x2FA95D43,
0xD67359DB, 0x8625BC1B, 0x7A1A3BBC, 0xE6ECEF6C, 0xBA27A5C6, 0x559FE417, 0xF01DF04D, 0x3F910D52,
0x382B7BE, 0xAAECB195, 0x2E440D8E, 0xB3D58B0B, 0x3CA704B8, 0x63293098, 0x714CFD4D, 0x47C8D0F7]

# res = enc_tea_ugly(m, k, SHL, SHR, delta)
# print([hex(x) for x in res])

data =[0x0BD3BE58, 0xBE73BBFB, 0xC8C8AF4E, 0x0B3C7D86, 0xC0257C09,
0x1D8FE0B0, 0x8837180C, 0xF5CF9D23, 0xB7A8B599, 0xAE630F3D]

flag = []
for i in range(0,len(data),2):
block =[data[i],data[i+1]]
dec = dec_tea_ugly(block, k, SHL, SHR, delta)
flag.extend(dec) # 用 extend 不是 append

flag_str = d2b(flag)
print(flag_str)
print(flag_str.decode("utf-8", errors="ignore"))

flag 是flag{UG1y_T3A_m@k35_[m3]_fe31_NaU5Eou5!}

babyre

之前写过了,移步[NPC²CTF 2025]babyVM WP

week2

randomXor

分析

ida 反编译的 main 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int __fastcall main(int argc, const char **argv, const char **envp)
{
int i; // [xsp+1Ch] [xbp+1Ch]

puts("Welcome to house of jjkk");
__isoc99_scanf("%128s", input);
if ( strlen(input) == len )
{
srand(114514);
for ( i = 0; i <= 127; ++i )
input[i] ^= rand();
if ( !memcmp(input, &cipher, len) )
puts("Right! ");
else
puts("Wrong! ");
return 0;
}
else
{
puts("Lenth wrong! ");
return 0;
}
}

其中 srand 和 rand 组成了一个类似 MT19937 的伪随机数生成器

1
2
3
4
5
6
7
8
9
10
11
12
__int16 *__fastcall srand(int a1)
{
__int16 *result; // x0
unsigned int i; // [xsp+1Ch] [xbp-4h]

mt[0] = a1;
for ( i = 1; i <= 0x71; ++i )
mt[i] = (0xA7A25365 * (mt[i - 1] ^ ((unsigned int)mt[i - 1] >> 30)) + i) ^ 0x56;
result = &index1;
index1 = 114;
return result;
}
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
__int64 rand()
{
int v1; // [xsp+Ch] [xbp-14h]
unsigned int i; // [xsp+10h] [xbp-10h]
unsigned int v3; // [xsp+14h] [xbp-Ch]
unsigned int v4; // [xsp+1Ch] [xbp-4h]
unsigned int v5; // [xsp+1Ch] [xbp-4h]

v1 = (unsigned __int16)index1;
if ( (unsigned __int16)index1 > 0x71u )
{
for ( i = 0; i <= 0x71; ++i )
{
v3 = (mt[i] & 0x80000 | mt[(i + 1) % 0x72] & 0x7FFFFu) >> 1;
if ( (mt[(i + 1) % 0x72] & 1) != 0 )
v3 ^= 0x8908B0DF;
mt[i] = mt[(i + 514) % 0x72] ^ v3;
}
index1 = 0;
v1 = 0;
}
v4 = mt[v1];
index1 = v1 + 1;
v5 = v4 ^ (v4 >> 11) ^ ((v4 ^ (v4 >> 11)) << 7) & 0x9D2C5680;
return v5 ^ (v5 << 15) & 0xEFC60000 ^ ((v5 ^ (v5 << 15) & 0xEFC60000) >> 18);
}

& cipher 处提取出密文(128 字节)

1
2
3
4
5
6
7
8
9
10
11
12
13
0xCD, 0xC7, 0xE2, 0x86, 0x3A, 0x19, 0xB9, 0xB6, 0xF1, 0x81,
0x45, 0xB4, 0xB6, 0xE5, 0x0D, 0xD4, 0xB4, 0xA6, 0xD3, 0xF7,
0x33, 0x5D, 0x5F, 0x09, 0x95, 0xAF, 0x9A, 0xBE, 0x89, 0xEA,
0x54, 0x71, 0x68, 0xC6, 0x8B, 0x84, 0x05, 0x14, 0xBB, 0x41,
0xDD, 0x34, 0x91, 0x1B, 0x21, 0x83, 0xDE, 0x15, 0x52, 0x22,
0x8A, 0xE1, 0xBD, 0x33, 0x4D, 0x7E, 0xD2, 0xA3, 0xA9, 0x12,
0xF1, 0xE9, 0x88, 0x60, 0x24, 0xE6, 0xAB, 0x54, 0xCF, 0x02,
0x1B, 0x6B, 0x6B, 0xBA, 0x26, 0x09, 0xFB, 0x10, 0x97, 0xDF,
0x17, 0xBF, 0xCC, 0xE7, 0xB0, 0x42, 0xE5, 0x32, 0x4C, 0x0F,
0xE0, 0x60, 0x47, 0xD0, 0x5A, 0xE8, 0x48, 0xAE, 0x74, 0x4D,
0x98, 0x7A, 0xB3, 0xD2, 0xDE, 0x1B, 0x96, 0x91, 0x7A, 0x03,
0x67, 0xCF, 0xF0, 0x31, 0x32, 0x49, 0x9A, 0xE0, 0xC2, 0x3B,
0x51, 0x63, 0xFF, 0x84, 0x60, 0x5B, 0xC1, 0x8E

解密

伪随机算法已经反编译得很清楚了,稍作修改就可以了,要注意加密 input[i] ^= rand()是对单字节操作,所以获得 rand() 值时要取低八位

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
cipher = [
0xCD, 0xC7, 0xE2, 0x86, 0x3A, 0x19, 0xB9, 0xB6, 0xF1, 0x81,
0x45, 0xB4, 0xB6, 0xE5, 0x0D, 0xD4, 0xB4, 0xA6, 0xD3, 0xF7,
0x33, 0x5D, 0x5F, 0x09, 0x95, 0xAF, 0x9A, 0xBE, 0x89, 0xEA,
0x54, 0x71, 0x68, 0xC6, 0x8B, 0x84, 0x05, 0x14, 0xBB, 0x41,
0xDD, 0x34, 0x91, 0x1B, 0x21, 0x83, 0xDE, 0x15, 0x52, 0x22,
0x8A, 0xE1, 0xBD, 0x33, 0x4D, 0x7E, 0xD2, 0xA3, 0xA9, 0x12,
0xF1, 0xE9, 0x88, 0x60, 0x24, 0xE6, 0xAB, 0x54, 0xCF, 0x02,
0x1B, 0x6B, 0x6B, 0xBA, 0x26, 0x09, 0xFB, 0x10, 0x97, 0xDF,
0x17, 0xBF, 0xCC, 0xE7, 0xB0, 0x42, 0xE5, 0x32, 0x4C, 0x0F,
0xE0, 0x60, 0x47, 0xD0, 0x5A, 0xE8, 0x48, 0xAE, 0x74, 0x4D,
0x98, 0x7A, 0xB3, 0xD2, 0xDE, 0x1B, 0x96, 0x91, 0x7A, 0x03,
0x67, 0xCF, 0xF0, 0x31, 0x32, 0x49, 0x9A, 0xE0, 0xC2, 0x3B,
0x51, 0x63, 0xFF, 0x84, 0x60, 0x5B, 0xC1, 0x8E
]

mt = [0] * 0x72 # 114
# 状态数组
index1 = 114
# 当前随机数生成的位置索引

def srand(a): # 初始化伪随机数生成器
global mt, index1
mt[0] = a
for i in range(1,0x72):
mt[i] = (0xA7A25365 * (mt[i - 1] ^ (mt[i - 1] >> 30)) + i) ^ 0x56
mt[i] &= 0xFFFFFFFF
index1 = 114

def rand(): # 生成伪随机数
global mt,index1
v1 = index1
if index1 > 0x71:
for i in range(0x72):
v3 = (mt[i] & 0x80000 | mt[(i + 1) % 0x72] & 0x7FFFF) >> 1
if mt[(i + 1) % 0x72] & 1:
v3 ^= 0x8908B0DF
mt[i] = mt[(i + 514) % 0x72] ^ v3
index1 = 0
v1 = 0
v4 = mt[v1]
index1 = v1 + 1
v5 = v4 ^ (v4 >> 11) ^ ((v4 ^ (v4 >> 11)) << 7) & 0x9D2C5680
v5 = v5 ^ (v5 << 15) & 0xEFC60000 ^ ((v5 ^ ((v5 << 15) & 0xEFC60000)) >> 18)
return v5 & 0xFFFFFFFF

def dec(cipher):
srand(114514)
flag_bytes = []
for i in cipher:
r = rand() & 0xFF # 取低八位
flag_bytes.append(i ^ r)
return bytes(flag_bytes)

flag = dec(cipher)
print(flag.decode())

flag 为 flag{R4ndom_rand0m_happy_Funnny_R4ndom_1s_r3ally_funNy!!_hhh233HHH_this_1s_just_a_pi3ce_of_sh1t_fffkkkkkk_f4ck_jjjjKKKKKjjjjasd}

simple

java 层

主要逻辑在 com.example.simpleandroid.CheckActivity

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
public class CheckActivity extends AppCompatActivity {
private ActivityCheckBinding binding;

public native boolean CheckData(String str);

static {
System.loadLibrary("check");
}

public static synchronized void init() {
synchronized (CheckActivity.class) {
ShadowHook.init();
}
}

@Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();
this.binding = ActivityCheckBinding.inflate(getLayoutInflater());
setContentView(this.binding.getRoot());
Button button = this.binding.check2;
button.setOnClickListener(new View.OnClickListener() { // from class: com.example.simpleandroid.CheckActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View v) {
EditText editText = CheckActivity.this.binding.inputFlag2;
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(30)});
String input = editText.getText().toString();
if (CheckActivity.this.isValidInput(input) && CheckActivity.this.CheckData(input)) {
Toast.makeText(CheckActivity.this, "You are right", 0).show();
} else {
Toast.makeText(CheckActivity.this, "You are wrong", 0).show();
}
}
});
}

/* JADX INFO: Access modifiers changed from: private */
public boolean isValidInput(String input) {
return input.startsWith("flag{") && input.endsWith("}") && input.length() == 32;
}
}

声明了一个 native 方法 CheckData,在本地库 libcheck.so 里
读取字符串输入之后,先调用 isValidInput(input) 做格式检查,再调用 native 方法 CheckData(input) 做进一步校验

native 层

先找到 CheckData

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
__int64 __fastcall Java_com_example_simpleandroid_CheckActivity_CheckData(__int64 a1, __int64 a2, __int64 a3)
{
_BYTE *v3; // x9
char v4; // w8
int i; // [xsp+4h] [xbp-2Ch]
__int64 StringUTFChars; // [xsp+8h] [xbp-28h]

StringUTFChars = _JNIEnv::GetStringUTFChars();
for ( i = 0; i < 32; ++i )
{
v3 = (_BYTE *)(StringUTFChars + i);
v4 = *v3 ^ (ee[i] != 0x7F);
*v3 = v4;
if ( v4 )
{
((void (__fastcall *)(__int64, __int64, __int64))_JNIEnv::ReleaseStringUTFChars)(a1, a3, StringUTFChars);
return 0;
}
}
((void (__fastcall *)(__int64, __int64, __int64))_JNIEnv::ReleaseStringUTFChars)(a1, a3, StringUTFChars);
return 1;
}

'''
ee = [
0x19, 0x13, 0x1E, 0x18, 0x04, 0x2B, 0x17, 0x16, 0x0C, 0x20,
0x12, 0x1E, 0x06, 0x20, 0x1D, 0x1A, 0x20, 0x1E, 0x20, 0x0C,
0x16, 0x12, 0x0F, 0x13, 0x1A, 0x20, 0x19, 0x13, 0x1E, 0x18,
0x5E, 0x02]
'''

可以看出 ee 数组中的每一个元素都不等于 0x7F,说明 ee[i] != 0x7F 的结果恒等于 1,所以只有当传入的字符串前 32 个字符是 32 个连续的 \x01 时,这个函数才会返回 1,否则返回 0

在 JNI_Onload 里面看到函数 sub_1C430

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
__int64 __fastcall sub_1C430(__int64 a1, __int64 a2, __int64 a3)
{
unsigned __int64 i; // [xsp+18h] [xbp-58h]
__int64 StringUTFChars; // [xsp+20h] [xbp-50h]
unsigned __int8 v8; // [xsp+44h] [xbp-2Ch]
_QWORD v9[5]; // [xsp+48h] [xbp-28h] BYREF

v9[4] = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
sub_1C010();
StringUTFChars = _JNIEnv::GetStringUTFChars();
sub_1C078(v9, StringUTFChars, kk);
eee[0] = 0xCD8413F20B6FCE4CLL;
qword_49828 = 0x5C9B1B43933043CFLL;
qword_49830 = 0x1C6EB6E1A546128ELL;
qword_49838 = 0x77C6E3D7AE009A3ELL;
for ( i = 0LL; i <= 3; ++i )
{
if ( v9[i] != eee[i] )
{
_JNIEnv::ReleaseStringUTFChars(a1, a3, StringUTFChars);
v8 = 0;
goto LABEL_7;
}
}
_JNIEnv::ReleaseStringUTFChars(a1, a3, StringUTFChars);
v8 = 1;
LABEL_7:
_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
return v8;
}

提取出密文: 0xCD8413F20B6FCE4C, 0x5C9B1B43933043CF, 0x1C6EB6E1A546128E, 0x77C6E3D7AE009A3E

sub_1C010

1
2
3
4
5
6
7
8
__int64 sub_1C010()
{
__int64 result; // x0

result = shadowhook_hook_sym_addr(sub_11073, sub_21345, 0LL);
stub = result;
return result;
}

sub_11073

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
void __fastcall sub_11073(const char *a1)
{
int v1; // w9
int k; // [xsp+24h] [xbp-13Ch]
int v3; // [xsp+28h] [xbp-138h]
int j; // [xsp+2Ch] [xbp-134h]
unsigned int v5; // [xsp+30h] [xbp-130h]
unsigned int i; // [xsp+34h] [xbp-12Ch]
_OWORD v8[16]; // [xsp+50h] [xbp-110h] BYREF
__int64 v9; // [xsp+158h] [xbp-8h]

v9 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
for ( i = 0; i <= 0xFF; ++i )
ss[i] = i;
v5 = __strlen_chk(a1, 0xFFFFFFFFFFFFFFFFLL);
srand(0x217u);
memset(v8, 0, sizeof(v8));
for ( j = 0; j <= 255; ++j )
*((_BYTE *)v8 + j) = a1[j % v5];
v3 = 0;
for ( k = 0; k <= 255; ++k )
{
v1 = (unsigned __int8)(v3 + ss[k] + *((_BYTE *)v8 + k));
if ( v3 + ss[k] + *((unsigned __int8 *)v8 + k) <= 0 )
v1 = -(unsigned __int8)-(char)(v3 + ss[k] + *((_BYTE *)v8 + k));
v3 = v1;
sub_1BC30((char *)&ss[k], (char *)&ss[v1]);
}
_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
}

char *__fastcall sub_1BC30(char *result, char *a2)
{
char v2; // [xsp+Fh] [xbp-11h]

v2 = *result;
*result = *a2;
*a2 = v2;
return result;
}

sub_11073 主要实现了 RC4 里面的 KSA,这里找到的 key 是 this_is_a_key
等效 python :

1
2
3
4
5
6
7
8
def KSA(key:bytes) -> list:
S = list(range(256))
key_len = len(key)
j = 0
for i in range(256):
j = (j + S[i] + key[i % key_len]) % 256
S[i], S[j] = S[j], S[i]
return S

这里面还有个 srand(0x217) 先放一边

sub_21345

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void __fastcall sub_21345(unsigned __int8 *a1)
{
void *v1; // x30
void (__fastcall *prev_func)(_BYTE *); // [xsp+8h] [xbp-58h]
int i; // [xsp+24h] [xbp-3Ch]
_BYTE v4[8]; // [xsp+30h] [xbp-30h] BYREF
_BYTE v5[13]; // [xsp+38h] [xbp-28h]
_BYTE v6[16]; // [xsp+48h] [xbp-18h] BYREF
__int64 v7; // [xsp+58h] [xbp-8h]

v7 = *(_QWORD *)(_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2)) + 40);
ShadowhookStackScope::ShadowhookStackScope((ShadowhookStackScope *)v4, v1);
*(_QWORD *)&v5[5] = 0x3C37311A241A312CLL; //这是把 8 字节写入 v5[5]~v5[12]
*(_QWORD *)v5 = 0x1A312C1A20332C02LL; // 这是把 8 字节写入 v5[0]~v5[7]
for ( i = 0; i <= 12; ++i )
v6[i] = v5[i] ^ 0x45;
prev_func = (void (__fastcall *)(_BYTE *))shadowhook_get_prev_func(sub_11073);
prev_func(v6);
srand(0x159357u);
ShadowhookStackScope::~ShadowhookStackScope((ShadowhookStackScope *)v4);
_ReadStatusReg(ARM64_SYSREG(3, 3, 13, 0, 2));
}

对 v5 数组中的前 13 个数据进行异或 0x45 得到 v6 数组 ,然后 hook 了函数 sub_11073,把 v6 作为参数传入,srand(0x159357)设置了一个随机数种子,把之前的 0x217 给改了

计算出 v6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
v5 = [0] * 13

# 写入 v5[5]~v5[12] (小端序)
val1 = 0x3C37311A241A312C
v5_bytes_5_12 = [(val1 >> (8*i)) & 0xFF for i in range(8)]
v5[5:13] = v5_bytes_5_12

# 写入 v5[0]~v5[7] (小端序) 注意有覆盖
val2 = 0x1A312C1A20332C02
v5_bytes_0_7 = [(val2 >> (8*i)) & 0xFF for i in range(8)]
v5[0:8] = v5_bytes_0_7


v6 = ''.join(chr(v5[i] ^ 0x45) for i in range(13))
print(v6)

# Give_it_a_try

所以 RC4 的密钥即为 Give_it_a_try

加密


先是一个RC4(密钥被修改过的),再根据随机数进行了移位异或

base

和题目描述的一样,坑点确实多

修花

main 函数有花指令,三种结构
一种是 jz jnz 的

一种 xor test + jz jnz 的

一种是 call retn 的

修好之后的 main 函数

其中 sub_41132F 里面是反调 IsDebuggerPresent(), sub_4110D2 里是 RC4 的 KSA

sub_41111D 里面是 PRGA ,但是在最后又多了一步异或 0x47

除此之外,还发现了三个 PRGA

sub_4111FE 是一个查表替换

反调试

程序有反调试,一开始调试就退出
打断点测试发现程序在 _initterm(First, Last);初始化时会触发异常退出,在此处打断点进行单步调试,发现了函数 sub_412AA0

通过动态解析的方式调用 api,动调解密发现是 Ntdll 和 ZwSetInformationThread,反调试

1
2
3
4
5
6
7
8
9
10
11
12
00412B7E                 mov     esi, esp
00412B80 push 0
00412B82 push 0
00412B84 push 11h
00412B86 mov edi, esp
00412B88 call ds:GetCurrentThread
00412B8E cmp edi, esp
00412B90 call j___RTC_CheckEsp
00412B95 push eax
00412B96 call [ebp+var_2C] ; 这里是反调试函数的调用
00412B99 cmp esi, esp
00412B9B call j___RTC_CheckEsp

在调用 call [ebp+var_2C] 之前,代码使用 mov esi, esp 保存了当前的栈指针 esp,调用之后,有 cmp esi, esp 和 call j___RTC_CheckEsp 来检查栈指针是否一致,如果只 NOP 掉 call 指令,栈上的参数不会被清理,导致 esp 比 esi 小 16 字节(因为参数仍在栈上),从而触发栈检查失败,程序会异常终止

函数调用前有四个参数被压栈

1
2
3
4
push 0
push 0
push 11h
push eax (来自GetCurrentThread)

每个push操作压入 4 字节,所以总共压入了 16 字节,在正常的函数调用中,这些参数会被被调函数清理(如果使用stdcall调用约定),或者由调用者清理(如果使用cdecl调用约定)。但在这里,由于我们跳过了调用,就需要自己清理这些参数,以保持栈平衡,所以要把这些 push 和 call 一并 nop 掉

或者换种方式,把 00412B96 地址处原本的 call [ebp+var_2C](调用反调试函数) 替换成 add esp, 10h,即把原始的机器码 FF 55 D4 改成 83 C4 10;add esp, 10h 指令将栈指针 esp 增加 16 字节,清理了栈上的 4 个参数;替换后,栈指针 esp 恢复到参数压栈前的状态,与 esi 保存的值一致,因此后续的 cmp esi, esp 会通过,栈检查不会触发异常,同时也绕过了反调试

进入 main 函数逻辑,nop 掉上文提到的 IsDebuggerPresent 绕过反调试,只 nop 调试时还是会报错,将 004139C2 的 call sub_41132F 也 nop 掉,然后把 004139C7 的 test eax, eax 替换为 xor eax, eax(33 C0),这样 eax 总为 0 ,jz 跳转一定会执行

除此之外,main 里面还有 try except 结构手动触发除零异常反调试

有一行 idiv [ebp+var_80],其中 [ebp+var_80] 被设置为 0,这会导致除以零异常,从而触发 __except 块
修改代码,不执行 __try 块,直接跳转到 __except 块(地址 00413AD0);开始地址是 00413A96,jmp 指令长度:2 字节(EB + 偏移量),下一条指令地址:00413A98,目标地址:00413AD0,偏移量 = 00413AD0 - 00413A98 = 38,因此用 EB 38 覆盖 00413A96 处的指令,并用 90 填充剩余字节

加解密

改完之后 main 函数的逻辑多了一块,现在加密逻辑完整了

用 key1 经过 KSA 之后生成了一个 S_box1 -> 经过 PRGA0 + 异或 0x47 处理得到 byte_41C1B8 -> 输入 input -> 用 key2 由经过了一次 KSA 生成 S_box2 -> sub_4111FE 函数使用 byte_41C1B8 对 input 进行查表替换 -> 根据模 3 的结果用不同的 PRGA 进行处理(魔改的最后一步异或值在动调的时候取) -> 对 input 进行循环异或

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
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
def KSA(key):
key_length = len(key)
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key[i % key_length]) % 256
S[i], S[j] = S[j], S[i]
return S

def single_byte_PRGA(S, i, j, xor_value):
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
K = S[(S[i] + S[j]) % 256]
return K ^ xor_value, i, j

def one_byte_prga(sbox, data_byte, key_byte):
index = sbox[1]
sbox[1], sbox[index] = sbox[index], sbox[1]
k = sbox[(sbox[1] + sbox[index]) % 256]
result = data_byte ^ k ^ key_byte
return sbox, result


def enc_lastxor(data):
data = bytearray(data)
for i in range(7):
for j in range(1, 6):
data[6 * i + j] ^= data[6 * i + j - 1]
data[6 * i] ^= data[6 * i + 5]
return data

def dec_lastxor(data):
data = bytearray(data)
for i in range(7):
data[6 * i] ^= data[6 * i + 5]
for j in range(5, 0, -1):
data[6 * i + j] ^= data[6 * i + j - 1]
return data

def substitution(data,S_box):
for i in range(len(data)):
data[i] = data[S_box[i]]

def re_substitution(data,S_box):
inv_S2 = [0] * 256
for i in range(256):
inv_S2[S_box[i]] = i
for i in range(len(data)):
data[i] = inv_S2[data[i]]

data1 = [
0x09, 0x76, 0x6A, 0xF8, 0x12, 0x4A, 0x8B, 0xF6, 0x6F, 0xC0,
0x5F, 0x3C, 0xB4, 0x7F, 0x3B, 0xA5, 0x80, 0x1D, 0x62, 0xA1,
0x47, 0x3B, 0xE9, 0x58, 0xB3, 0x17, 0x10, 0xE6, 0xBA, 0x39,
0x97, 0xDB, 0x75, 0x2F, 0x6B, 0x20, 0xD1, 0x7A, 0xD2, 0xF6,
0x4F, 0xBF, 0x9D, 0x93, 0x79, 0x6B, 0x1D, 0x34, 0xE0, 0x33,
0xF2, 0x45, 0xBB, 0x40, 0x31, 0x1A, 0x78, 0xDA, 0x24, 0xBD,
0xEE, 0x03, 0x8D, 0x3B, 0x1F, 0xD9, 0xE8, 0xEB, 0xDC, 0xE6,
0x8C, 0x1E, 0x4B, 0x86, 0xEF, 0x14, 0x11, 0x2D, 0x64, 0x67,
0x36, 0x91, 0x26, 0x09, 0x59, 0x59, 0x86, 0x8E, 0x9E, 0x48,
0x42, 0x89, 0xB7, 0x41, 0x52, 0x1A, 0x1A, 0x63, 0xFE, 0x2A,
0xEF, 0x0E, 0xA1, 0x9F, 0x85, 0xD6, 0xA7, 0x28, 0x19, 0xB8,
0x6E, 0x66, 0x06, 0xD4, 0xFE, 0xAF, 0xD2, 0x20, 0x11, 0x0F,
0xDD, 0x4A, 0xA6, 0x3D, 0xAE, 0x9A, 0x01, 0x09, 0xE6, 0x85,
0x68, 0xB8, 0xE0, 0xA4, 0x8D, 0xBE, 0x00, 0xC4, 0x7D, 0x16,
0x92, 0x8E, 0x87, 0xE5, 0xB1, 0xA5, 0x5D, 0xB3, 0x75, 0x88,
0x0F, 0xA7, 0x2C, 0x21, 0xAD, 0x2E, 0xA8, 0x36, 0xCC, 0xCB,
0x06, 0xEB, 0xB9, 0x73, 0x29, 0xCC, 0xA6, 0x64, 0x4F, 0xB2,
0xB6, 0xD1, 0xFF, 0x05, 0x16, 0xED, 0xD3, 0x17, 0xCA, 0x2B,
0x65, 0x86, 0xCD, 0xB2, 0xEA, 0xE3, 0x8E, 0xB4, 0xF9, 0xBF,
0x6A, 0xE2, 0x07, 0xE6, 0x76, 0x7C, 0x85, 0xDE, 0x50, 0x88,
0xBB, 0x3F, 0xD0, 0x97, 0xAD, 0x3D, 0xAE, 0x4D, 0xC7, 0xFF,
0xF0, 0x67, 0x79, 0xB9, 0x94, 0x95, 0xB0, 0x66, 0xB8, 0x61,
0x95, 0x23, 0x1F, 0xFB, 0x4A, 0xBD, 0x9C, 0x09, 0x9D, 0xAB,
0x31, 0x0E, 0xBA, 0x0A, 0xB9, 0xF3, 0x7F, 0xF6, 0xB4, 0x9F,
0x65, 0x31, 0x69, 0x8E, 0x02, 0xC0, 0x2D, 0x4F, 0x7A, 0xDD,
0x0E, 0x2F, 0x3F, 0xE6, 0xCF, 0x99
]


if __name__ == "__main__":
cipher = bytearray([
0x78, 0xD4, 0xD5, 0x92, 0x98, 0x62, 0x14, 0x14, 0xA5, 0x0F,
0x29, 0xAE, 0x9B, 0x58, 0x14, 0x21, 0x0D, 0x7D, 0xA4, 0x66,
0x48, 0x83, 0x79, 0x30, 0xF3, 0x21, 0xD1, 0xCD, 0xE2, 0x02,
0x8D, 0xAC, 0xBC, 0x4A, 0x81, 0xC1, 0x9C, 0xF5, 0xC2, 0xC8,
0x3F, 0x0A])

key1 = b"f75bf3e3f919"
S1 = KSA(key1)
tmp = bytearray(data1)
i = j = 0
for idx in range(len(tmp)):
out_byte, i, j = single_byte_PRGA(S1, i, j, 0x47)
tmp[idx] ^= out_byte

key2 = b"363296f1-9353"
S2 = KSA(key2)

cipher = dec_lastxor(cipher)

xor_values = [0x25, 0x39, 0x47]
for idx in range(len(cipher)):
xor_val = xor_values[idx % 3]
S2, cipher[idx] = one_byte_prga(S2, cipher[idx], xor_val)

# 逆向中途 input 与 tmp 的查表替换
inv_tmp = [0] * 256
for i in range(256):
inv_tmp[tmp[i]] = i
flag = bytearray()
for i in range(len(cipher)):
flag.append(inv_tmp[cipher[i]])

print(flag.decode())

flag 为 flag{2af4101a-201a-4ab9-a664-903577cb9ff0}

简单的逆向

虚假的逻辑

dnSpy 打开先粗略翻了一下,看到了 RC4 相关加密,没找到密文,动态调试,断在入口点

直接跳转到 Main 的位置

有个很明显的假 flag
猜测这个 array2 里就存的密文
cipher = [
128, 120, 214, 255, 142, 235, 58, 98, 154, 204,
10, 107, 117, 17, 192, 198, 61, 162, 218, 95,
57, 177, 175, 62, 111, 6, 40, 178, 149, 231,
163, 19, 36, 9, 249, 192, 94, 3, 236, 201,
123, 203, 150, 98, 59, 141, 234, 117, 229, 108,
135, 40, 105, 48, 89, 128, 11, 189, 35, 174,
167, 13, 55, 220
]

动调的时候看到逻辑就是一个魔改的 RC4(PRGA 最后一步多了一个异或 0X100),密钥是 17, 69, 20,但是发现解不出来 flag

在 where 这里发现除了 rc4 之外还有两个函数

点进去查看分别是 AES(where.QZoPKLYRExWdGJMn) 和 DES(where.TYuNpWqDZfXoRJvB)

假 flag 在运行时交会打印 Right!,而在运行程序时提交却显示 Wrong!

在 Cor20 头里面看到 ManagedNativeHeader 地址 0968

在该地址处发现 RTR(ReadyToRun)

使用工具 R2RDump 分析附件里的 where.dll
R2RDump.exe --in "D:\aaa\NPCCTF\简单的逆向\where.dll" --disasm --naked --ho --out dump.txt

真实的逻辑

找到 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
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
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
push    r15
UWOP_PUSH_NONVOL R15(15) 0

push r14
UWOP_PUSH_NONVOL R14(14) 0

push r12
UWOP_PUSH_NONVOL R12(12) 0

push rdi
UWOP_PUSH_NONVOL RDI(7) 0

push rsi
UWOP_PUSH_NONVOL RSI(6) 0

push rbp
UWOP_PUSH_NONVOL RBP(5) 0

push rbx
UWOP_PUSH_NONVOL RBX(3) 0

sub rsp, 32
UWOP_ALLOC_SMALL 32 0

mov rcx, qword ptr ["Input your flag:" (STRING_HANDLE)]
mov rcx, qword ptr [rcx]
RCX is live
call qword ptr [void System.Console.Write(string) (METHOD_ENTRY)]
RCX is dead
call qword ptr [string System.Console.ReadLine() (METHOD_ENTRY)]
RAX is live
mov rsi, rax
RSI is live
call qword ptr [System.Text.Encoding System.Text.Encoding.get_ASCII() (METHOD_ENTRY)]
mov rcx, rax
RCX is live
mov rdx, rsi
RDX is live
lea r11, [byte[] System.Text.Encoding.GetBytes(string) (VIRTUAL_ENTRY)]
cmp dword ptr [rcx], ecx
call qword ptr [byte[] System.Text.Encoding.GetBytes(string) (VIRTUAL_ENTRY)]
RCX is dead
RDX is dead
mov rcx, rax
RCX is live
call qword ptr [byte[] where.TYuNpWqDZfXoRJvB.CZmLWpXqNKOVFRtJ(byte[]) (METHOD_ENTRY)] // DES
RCX is dead
mov rdi, rax // 将处理后的数据放到 rdi
mov ebx, dword ptr [rdi + 8]
movsxd rcx, ebx
call qword ptr [byte[] (NEW_ARRAY)]
mov rbp, rax
RBP is live
mov r14d, 1 // 初始化 r14d1
mov ecx, 3 // 创建长度为 3 的新字节数组,结果在 rax
call qword ptr [byte[] (NEW_ARRAY)]
mov r15, rax
R15 is live
lea rax, [0x3928] // 从地址偏移 0x3928 处加载数据
RAX is dead
mov dx, word ptr [rax]
mov word ptr [r15 + 16], dx // 先加载2字节到dx,存储到r15数组的偏移16
mov dl, byte ptr [rax + 2]
mov byte ptr [r15 + 18], dl // 再加载1字节到dl,存储到偏移18
//说明 r15 是一个 3 字节数组,在对应地址找到是 [0x72,0x32,0x72] -> 'r2r'
xor ecx, ecx
test ebx, ebx
jle 0x0123
mov r12d, dword ptr [rbp + 8]
cmp r12d, ebx
jl 0x00d9
movsxd rax, ecx
movzx edx, byte ptr [rdi + rax + 16]
xor edx, r14d
movzx r8d, dl
mov byte ptr [rbp + rax + 16], r8b // 从 rdi 数组读取字节,与 r14d 异或,结果存储到 rbp 数组
mov edx, 2164392969
mov eax, edx
imul r14d
add edx, r14d
mov r8d, edx
shr r8d, 31
sar edx, 6
add edx, r8d
imul eax, edx, 127
sub r14d, eax
inc r14d // r14d += 1
inc ecx
cmp ebx, ecx
jg 0x009a
RDI is dead
jmp 0x0123
RDI is live
movsxd rax, ecx
movzx eax, byte ptr [rdi + rax + 16]
xor eax, r14d
movzx r8d, al
mov r12d, dword ptr [rbp + 8]
cmp ecx, r12d
jae 0x03d5
movsxd rax, ecx
mov byte ptr [rbp + rax + 16], r8b
mov edx, 2164392969
mov eax, edx
imul r14d
add edx, r14d
mov eax, edx
shr eax, 31
sar edx, 6
add eax, edx
imul eax, eax, 127
sub r14d, eax
inc r14d
inc ecx
cmp ebx, ecx
jg 0x00d9
RDI is dead
mov r12d, dword ptr [rbp + 8]
movsxd rcx, r12d
call qword ptr [byte[] (NEW_ARRAY)]
RAX is live
mov rcx, rax
RCX is live
xor r8d, r8d
test r12d, r12d
jle 0x01dc
cmp dword ptr [rcx + 8], r12d
jl 0x018b
RAX is dead
movsxd r9, r8d
movzx r10d, byte ptr [rbp + r9 + 16]
mov edx, 1431655766
mov eax, edx
imul r8d
mov eax, edx
shr eax, 31
add eax, edx
lea eax, [rax + 2*rax]
mov edx, r8d
sub edx, eax // r8d % 3
cmp edx, 3
jae 0x03d5
movsxd rax, edx
movzx r11d, byte ptr [r15 + rax + 16]
xor r10d, r11d // 用 r15 数组(3字节)的相应字节异或,结果存储到新数组
mov byte ptr [rcx + r9 + 16], r10b
inc r8d
cmp r12d, r8d
jg 0x0145
RBP is dead
R15 is dead
jmp 0x01dc
R15 is live
RBP is live
movsxd rax, r8d
movzx r10d, byte ptr [rbp + rax + 16]
mov edx, 1431655766
mov eax, edx
imul r8d
mov eax, edx
shr eax, 31
add eax, edx
lea eax, [rax + 2*rax]
mov edx, r8d
sub edx, eax
cmp edx, 3
jae 0x03d5
movsxd rax, edx
movzx eax, byte ptr [r15 + rax + 16]
xor r10d, eax
cmp r8d, dword ptr [rcx + 8]
jae 0x03d5
movsxd rax, r8d
mov byte ptr [rcx + rax + 16], r10b
inc r8d
cmp r12d, r8d
jg 0x018b
RBP is dead
R15 is dead
call qword ptr [byte[] where.QZoPKLYRExWdGJMn.OXJTYZWRNQVKLPFm(byte[]) (METHOD_ENTRY)] // AES
RAX is live
RCX is dead
mov rdi, rax
RDI is live
mov ecx, 64
call qword ptr [byte[] (NEW_ARRAY)] // 创建 64 字节数组
mov rbx, rax // 保存到 rbx
RBX is live
lea rcx, [0x3618] 从地址偏移 0x3618 处加载 64 字节硬编码数据 ->正确的密文
movups xmm0, xmmword ptr [rcx]
movups xmmword ptr [rbx + 16], xmm0
movups xmm0, xmmword ptr [rcx + 16]
movups xmmword ptr [rbx + 32], xmm0
movups xmm0, xmmword ptr [rcx + 32]
movups xmmword ptr [rbx + 48], xmm0
movups xmm0, xmmword ptr [rcx + 48]
movups xmmword ptr [rbx + 64], xmm0
mov ebp, dword ptr [rdi + 8]
cmp ebp, 64 // 检查 rdi 数组的长度是否为 64
je 0x0239
RAX is dead
mov rcx, qword ptr ["Wrong!" (STRING_HANDLE)]
mov rcx, qword ptr [rcx]
RCX is live
call qword ptr [void System.Console.WriteLine(string) (METHOD_ENTRY)]
RCX is dead
xor ecx, ecx
call qword ptr [void System.Environment.Exit(int) (METHOD_ENTRY)]
xor r14d, r14d
test ebp, ebp
jle 0x02b0
cmp ebp, 64
jg 0x0275
movsxd rcx, r14d
movzx eax, byte ptr [rdi + rcx + 16]
cmp al, byte ptr [rbx + rcx + 16] // 内容比较
je 0x026b
mov rcx, qword ptr ["Wrong!" (STRING_HANDLE)]
mov rcx, qword ptr [rcx]
RCX is live
call qword ptr [void System.Console.WriteLine(string) (METHOD_ENTRY)]
RCX is dead
xor ecx, ecx
call qword ptr [void System.Environment.Exit(int) (METHOD_ENTRY)]
inc r14d
cmp ebp, r14d
jg 0x0245
RBX is dead
RDI is dead
jmp 0x02b0
RBX is live
RDI is live
movsxd rcx, r14d
movzx ecx, byte ptr [rdi + rcx + 16]
cmp r14d, 64
jae 0x03d5
movsxd rax, r14d
cmp cl, byte ptr [rbx + rax + 16]
je 0x02a8
mov rcx, qword ptr ["Wrong!" (STRING_HANDLE)]
mov rcx, qword ptr [rcx]
RCX is live
call qword ptr [void System.Console.WriteLine(string) (METHOD_ENTRY)]
RCX is dead
xor ecx, ecx
call qword ptr [void System.Environment.Exit(int) (METHOD_ENTRY)]
inc r14d
cmp ebp, r14d
jg 0x0275
RDI is dead
RBX is dead
mov rcx, qword ptr ["Right!" (STRING_HANDLE)]
mov rcx, qword ptr [rcx]
RCX is live
call qword ptr [void System.Console.WriteLine(string) (METHOD_ENTRY)]
RCX is dead
xor ecx, ecx
call qword ptr [void System.Environment.Exit(int) (METHOD_ENTRY)]
mov rdx, qword ptr ["flag{i_am_the_true_flag}" (STRING_HANDLE)] // 假逻辑
mov rdx, qword ptr [rdx]
RDX is live
mov rcx, rsi
RCX is live
call qword ptr [bool System.String.op_Equality(string, string) (METHOD_ENTRY)]
RCX is dead
RDX is dead
test al, al
je 0x02f1
mov rcx, qword ptr ["Right!" (STRING_HANDLE)]
mov rcx, qword ptr [rcx]
RCX is live
call qword ptr [void System.Console.WriteLine(string) (METHOD_ENTRY)]
RCX is dead
jmp 0x0301
mov rcx, qword ptr ["Wrong!" (STRING_HANDLE)]
mov rcx, qword ptr [rcx]
RCX is live
call qword ptr [void System.Console.WriteLine(string) (METHOD_ENTRY)]
RCX is dead
mov ecx, 3
call qword ptr [byte[] (NEW_ARRAY)]
RAX is live
lea rcx, [0x36b0]
mov dx, word ptr [rcx]
mov word ptr [rax + 16], dx
mov dl, byte ptr [rcx + 2]
mov byte ptr [rax + 18], dl
mov rcx, rsi
RCX is live
mov rdx, rax
RDX is live
call qword ptr [byte[] where.rc4.ToRC4(string, byte[]) (METHOD_ENTRY)]
RDX is dead
RSI is dead
RCX is dead
mov rsi, rax
RSI is live
mov ecx, 64
call qword ptr [byte[] (NEW_ARRAY)]
mov rdi, rax
RDI is live
lea rcx, [0x37c0]
movups xmm0, xmmword ptr [rcx]
movups xmmword ptr [rdi + 16], xmm0
movups xmm0, xmmword ptr [rcx + 16]
movups xmmword ptr [rdi + 32], xmm0
movups xmm0, xmmword ptr [rcx + 32]
movups xmmword ptr [rdi + 48], xmm0
movups xmm0, xmmword ptr [rcx + 48]
movups xmmword ptr [rdi + 64], xmm0
mov rcx, rsi
RCX is live
mov rdx, rdi
RDX is live
call qword ptr [bool where.QZoPKLYRExWdGJMn.check1(byte[], byte[]) (METHOD_ENTRY)]
RCX is dead
RDX is dead
RAX is dead
test al, al
je 0x03a5
mov rcx, rsi
RCX is live
mov rdx, rdi
RDX is live
call qword ptr [bool where.TYuNpWqDZfXoRJvB.check2(byte[], byte[]) (METHOD_ENTRY)]
RCX is dead
RDX is dead
test al, al
je 0x03a5
mov rcx, rsi
RCX is live
mov rdx, rdi
RDX is live
call qword ptr [bool where.rc4.check3(byte[], byte[]) (METHOD_ENTRY)]
RDX is dead
RSI is dead
RCX is dead
RDI is dead
test al, al
je 0x03a5
mov rcx, qword ptr ["Right!" (STRING_HANDLE)]
mov rcx, qword ptr [rcx]
RCX is live
call qword ptr [void System.Console.WriteLine(string) (METHOD_ENTRY)]
RCX is dead
jmp 0x03b5
mov rcx, qword ptr ["Wrong!" (STRING_HANDLE)]
mov rcx, qword ptr [rcx]
RCX is live
call qword ptr [void System.Console.WriteLine(string) (METHOD_ENTRY)]
RCX is dead
mov rcx, qword ptr ["Do you believe me?" (STRING_HANDLE)]
mov rcx, qword ptr [rcx]
RCX is live
call qword ptr [void System.Console.WriteLine(string) (METHOD_ENTRY)]
RCX is dead
nop
add rsp, 32
pop rbx
pop rbp
pop rsi
pop rdi
pop r12
pop r14
pop r15
ret
call qword ptr [RNG_CHK_FAIL (HELPER)]
int3

所以真正的加密过程是 DES -> 两次异或 -> AES

解密

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
from Crypto.Cipher import AES,DES

cipher = [
0x96, 0x57, 0x55, 0x7f, 0x25, 0xb0, 0x7d, 0xf9, 0xe7, 0x4f, 0x89, 0x91, 0x00, 0x9b, 0x71, 0xdf,
0x03, 0x42, 0x99, 0xe7, 0x99, 0xee, 0x00, 0xed, 0x82, 0xdf, 0xad, 0x55, 0xb8, 0xf0, 0x75, 0xa2,
0xa7, 0x48, 0x2a, 0xa5, 0xef, 0xc5, 0x58, 0x3d, 0xfe, 0x46, 0xb2, 0x79, 0xbe, 0x00, 0x32, 0x7c,
0x70, 0xce, 0x83, 0x50, 0x9b, 0xed, 0xa6, 0x74, 0x4d, 0x26, 0x03, 0x34, 0x1d, 0x90, 0x87, 0x85
]

# DES
key1 = b'Z1Y2X3W4'
iv1 = b'W4X3Y2Z1'

# AES
key2 = b'A1B2C3D4E5F6G7H8'
iv2 = b'H8G7F6E5D4C3B2A1'

xor_val = 'r2r'

cipher_bytes = bytes(cipher)
aes = AES.new(key2,AES.MODE_CBC,iv2)
aes_dec = aes.decrypt(cipher_bytes)

xor1 = bytearray(aes_dec)
for i in range(len(xor1)):
xor1[i] ^= ord(xor_val[i % 3])

for i in range(len(xor1)):
xor1[i] ^= (i + 1)

des = DES.new(key1,DES.MODE_CBC,iv1)
flag = des.decrypt(bytes(xor1))
print(flag)

所以 flag 是 flag{1_@m_th3_$3cr3t_h1dd3n_1n_th3_d3p7h}

week3

b&w

分析

附件给了两个 exe,一个 txt,txt 里的应该就是密文
分别单独打开两个 exe 的话会显示等待另外一个的连接,用 ida 看了一下,题目是两个进程之间的命名管道(FIFO)通信,先开 black.exe,打开读端,再开 white.exe 打开写端,white 读取输入之后通过 pipe__white_to_black 发给 black 处理,black 处理完再通过 pipe__black_to_white 发回 white ,white 接收后打印输出

动态调试 black.exe, 搜索字符串找主要逻辑,逐步调试找到主要加密,是个 tea

测试输入发现如果输入的数据不是 8 的倍数后面会补 0,如果是 8 的倍数的话会在最后再加上 8 个 \x00 字节;tea 的轮数也不是标准的 32 轮而是 64 轮;同时发现在进 tea 加密之前数据已经改变,观察发现是一个自反的映射,{}不做处理,字母和数字做映射处理;而且调试过程中 delta 的值也是随机生成的

输入 flag{1111111111}

输入 hbmg{3333333333}

1
2
3
abcdefghijklmnopqrstuvwxyz <-> mlkjihgfedcbazyxwvutsrqpon
ABCDEFGHIJKLMNOPQRSTUVWXYZ <-> MLKJIHGFEDCBAZYXWVUTSRQPON
1234567890 <-> 3210987654

解密

测试发现 delta 的值应该在 0x4831FFFF 以内,根据 hbmg 和密文的前 8 个字节 0x5f6f8bf4, 0x3b31cec1 爆破出 delta = 0x48315716

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
cipher = [0x5f6f8bf4, 0x3b31cec1, 0xf66376bd, 0xc5ae96f0, 0xabbcd6a9, 
0x9a7afc5, 0x3eca585a, 0xb5d7c589, 0x8da5b9c8, 0x241108e6]

key = [0x00114514, 0x19198100, 0xDEADBEEF, 0x00070201]

def tea_dec(data,key):
delta = 0x48315716
v0, v1 = data
k0, k1, k2, k3 = key
sum = (delta * 64) & 0xFFFFFFFF
for _ in range(64):
v1 = (v1 - (((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3))) & 0xFFFFFFFF
v0 = (v0 - (((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1))) & 0xFFFFFFFF
sum = (sum - delta) & 0xFFFFFFFF
return v0, v1

def change(s):
lower_map = str.maketrans('abcdefghijklmnopqrstuvwxyz', 'mlkjihgfedcbazyxwvutsrqpon')
upper_map = str.maketrans('ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'MLKJIHGFEDCBAZYXWVUTSRQPON')
digit_map = str.maketrans('1234567890', '3210987654')


result = s.translate(lower_map)
result = result.translate(upper_map)
result = result.translate(digit_map)

return result

def decrypt():
dec_data = []
for i in range(0,len(cipher),2):
block = [cipher[i],cipher[i+1]]
dec_block = tea_dec(block,key)
dec_data.extend(dec_block)

result = b''
for val in dec_data:
result += val.to_bytes(4,'big')

tmp = result.decode('utf-8').rstrip('\x00')
result = change(tmp)

return result

if __name__ == "__main__":
flag = decrypt()
print(flag)

flag 为 flag{Ru57_and_g0_1s_fun_T0_r3v3rs3_XD}


NPC²CTF 2025 Reverse 方向复现
http://example.com/2025/08/26/NPCCTF2025/
作者
Eleven
发布于
2025年8月26日
许可协议