SBCTF2024

Last updated on February 5, 2024 am

SBCTF2024 Reverse wp

Week1

babymath

z3一把梭

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
from z3 import *

flag = [BitVec('flag_%i' % i, 7) for i in range(21)]

s = Solver()
s.add(flag[0]*831+flag[1]*310+flag[2]*873+flag[3]*420+flag[4]*547+flag[5]*15+flag[6]*563+flag[7]*361+flag[8]*221+flag[9]*573+flag[10]*575+flag[11]*572+flag[12]*889+flag[13]*881+flag[14]*624+flag[15]*669+flag[16]*397+flag[17]*903+flag[18]*859+flag[19]*594+flag[20]*2==1073066)

s.add(flag[0]*89+flag[1]*862+flag[2]*606+flag[3]*600+flag[4]*823+flag[5]*800+flag[6]*826+flag[7]*596+flag[8]*79+flag[9]*473+flag[10]*413+flag[11]*420+flag[12]*78+flag[13]*284+flag[14]*545+flag[15]*656+flag[16]*503+flag[17]*399+flag[18]*79+flag[19]*243+flag[20]*88==829594)

s.add(flag[0]*619+flag[1]*701+flag[2]*128+flag[3]*598+flag[4]*572+flag[5]*297+flag[6]*852+flag[7]*939+flag[8]*295+flag[9]*536+flag[10]*362+flag[11]*26+flag[12]*597+flag[13]*900+flag[14]*605+flag[15]*169+flag[16]*78+flag[17]*469+flag[18]*1+flag[19]*561+flag[20]*387==896243)

s.add(flag[0]*568+flag[1]*639+flag[2]*808+flag[3]*747+flag[4]*978+flag[5]*23+flag[6]*600+flag[7]*300+flag[8]*354+flag[9]*881+flag[10]*23+flag[11]*153+flag[12]*251+flag[13]*619+flag[14]*624+flag[15]*85+flag[16]*301+flag[17]*741+flag[18]*904+flag[19]*46+flag[20]*747==936424)

s.add(flag[0]*714+flag[1]*276+flag[2]*589+flag[3]*743+flag[4]*133+flag[5]*205+flag[6]*779+flag[7]*675+flag[8]*166+flag[9]*271+flag[10]*971+flag[11]*679+flag[12]*787+flag[13]*894+flag[14]*736+flag[15]*537+flag[16]*751+flag[17]*84+flag[18]*143+flag[19]*242+flag[20]*682==1023780)

s.add(flag[0]*113+flag[1]*695+flag[2]*514+flag[3]*550+flag[4]*895+flag[5]*763+flag[6]*175+flag[7]*314+flag[8]*98+flag[9]*835+flag[10]*161+flag[11]*330+flag[12]*119+flag[13]*581+flag[14]*109+flag[15]*917+flag[16]*764+flag[17]*185+flag[18]*556+flag[19]*854+flag[20]*937==1018657)

s.add(flag[0]*821+flag[1]*513+flag[2]*572+flag[3]*799+flag[4]*642+flag[5]*164+flag[6]*418+flag[7]*639+flag[8]*400+flag[9]*467+flag[10]*943+flag[11]*715+flag[12]*419+flag[13]*978+flag[14]*127+flag[15]*618+flag[16]*392+flag[17]*867+flag[18]*386+flag[19]*760+flag[20]*483==1152010)

s.add(flag[0]*50+flag[1]*170+flag[2]*348+flag[3]*966+flag[4]*744+flag[5]*444+flag[6]*341+flag[7]*389+flag[8]*12+flag[9]*946+flag[10]*723+flag[11]*534+flag[12]*498+flag[13]*668+flag[14]*201+flag[15]*815+flag[16]*921+flag[17]*433+flag[18]*128+flag[19]*803+flag[20]*879==1067008)

s.add(flag[0]*717+flag[1]*133+flag[2]*243+flag[3]*425+flag[4]*394+flag[5]*4+flag[6]*994+flag[7]*34+flag[8]*724+flag[9]*949+flag[10]*426+flag[11]*908+flag[12]*293+flag[13]*962+flag[14]*444+flag[15]*808+flag[16]*860+flag[17]*988+flag[18]*878+flag[19]*424+flag[20]*121==1105913)

s.add(flag[0]*536+flag[1]*765+flag[2]*835+flag[3]*849+flag[4]*838+flag[5]*446+flag[6]*520+flag[7]*233+flag[8]*392+flag[9]*243+flag[10]*598+flag[11]*735+flag[12]*926+flag[13]*381+flag[14]*366+flag[15]*50+flag[16]*252+flag[17]*719+flag[18]*796+flag[19]*395+flag[20]*928==1112118)

s.add(flag[0]*140+flag[1]*859+flag[2]*665+flag[3]*768+flag[4]*240+flag[5]*845+flag[6]*97+flag[7]*822+flag[8]*682+flag[9]*958+flag[10]*691+flag[11]*778+flag[12]*949+flag[13]*125+flag[14]*809+flag[15]*466+flag[16]*340+flag[17]*353+flag[18]*627+flag[19]*728+flag[20]*117==1150956)

s.add(flag[0]*858+flag[1]*288+flag[2]*297+flag[3]*619+flag[4]*462+flag[5]*536+flag[6]*646+flag[7]*202+flag[8]*298+flag[9]*130+flag[10]*491+flag[11]*5+flag[12]*855+flag[13]*442+flag[14]*535+flag[15]*201+flag[16]*67+flag[17]*417+flag[18]*789+flag[19]*930+flag[20]*939==971309)

s.add(flag[0]*573+flag[1]*891+flag[2]*576+flag[3]*230+flag[4]*457+flag[5]*430+flag[6]*745+flag[7]*987+flag[8]*383+flag[9]*253+flag[10]*169+flag[11]*280+flag[12]*709+flag[13]*710+flag[14]*453+flag[15]*344+flag[16]*126+flag[17]*829+flag[18]*531+flag[19]*782+flag[20]*502==1044165)

s.add(flag[0]*539+flag[1]*750+flag[2]*726+flag[3]*930+flag[4]*798+flag[5]*940+flag[6]*52+flag[7]*980+flag[8]*620+flag[9]*103+flag[10]*873+flag[11]*509+flag[12]*107+flag[13]*21+flag[14]*913+flag[15]*579+flag[16]*877+flag[17]*372+flag[18]*960+flag[19]*975+flag[20]*255==1208007)

s.add(flag[0]*265+flag[1]*171+flag[2]*419+flag[3]*27+flag[4]*298+flag[5]*784+flag[6]*273+flag[7]*570+flag[8]*189+flag[9]*711+flag[10]*905+flag[11]*88+flag[12]*463+flag[13]*848+flag[14]*598+flag[15]*675+flag[16]*796+flag[17]*153+flag[18]*983+flag[19]*460+flag[20]*638==1014193)

s.add(flag[0]*972+flag[1]*595+flag[2]*872+flag[3]*767+flag[4]*357+flag[5]*42+flag[6]*978+flag[7]*104+flag[8]*640+flag[9]*373+flag[10]*142+flag[11]*509+flag[12]*838+flag[13]*113+flag[14]*359+flag[15]*687+flag[16]*613+flag[17]*957+flag[18]*387+flag[19]*524+flag[20]*787==1069708)

s.add(flag[0]*35+flag[1]*354+flag[2]*49+flag[3]*280+flag[4]*22+flag[5]*824+flag[6]*686+flag[7]*555+flag[8]*635+flag[9]*130+flag[10]*278+flag[11]*928+flag[12]*728+flag[13]*39+flag[14]*608+flag[15]*941+flag[16]*37+flag[17]*39+flag[18]*136+flag[19]*470+flag[20]*86==757483)

s.add(flag[0]*413+flag[1]*464+flag[2]*795+flag[3]*567+flag[4]*587+flag[5]*303+flag[6]*218+flag[7]*594+flag[8]*770+flag[9]*822+flag[10]*140+flag[11]*780+flag[12]*730+flag[13]*521+flag[14]*286+flag[15]*368+flag[16]*737+flag[17]*344+flag[18]*73+flag[19]*72+flag[20]*53==904238)

s.add(flag[0]*703+flag[1]*574+flag[2]*758+flag[3]*976+flag[4]*65+flag[5]*231+flag[6]*292+flag[7]*601+flag[8]*783+flag[9]*83+flag[10]*726+flag[11]*413+flag[12]*837+flag[13]*779+flag[14]*803+flag[15]*503+flag[16]*675+flag[17]*230+flag[18]*365+flag[19]*604+flag[20]*102==1032025)

s.add(flag[0]*20+flag[1]*381+flag[2]*303+flag[3]*396+flag[4]*155+flag[5]*721+flag[6]*934+flag[7]*467+flag[8]*798+flag[9]*250+flag[10]*979+flag[11]*959+flag[12]*945+flag[13]*894+flag[14]*143+flag[15]*601+flag[16]*298+flag[17]*273+flag[18]*373+flag[19]*386+flag[20]*364==1049473)

s.add(flag[0]*224+flag[1]*850+flag[2]*379+flag[3]*209+flag[4]*112+flag[5]*127+flag[6]*740+flag[7]*714+flag[8]*295+flag[9]*506+flag[10]*632+flag[11]*444+flag[12]*121+flag[13]*174+flag[14]*792+flag[15]*216+flag[16]*47+flag[17]*453+flag[18]*589+flag[19]*898+flag[20]*482==830787)

s.check()
m = s.model()
for i in flag:
print(chr(m[i].as_long()),end='')
# SBCTF{EquaTion1Seasy}

simple

简单的字节码阅读,是个经典的TEA算法加密,不过需要注意的是这里的运算都是有符号的

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
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
public class simple extends java.lang.Object
{

public void <init>()
{
simple r0;

r0 := @this: simple;

specialinvoke r0.<java.lang.Object: void <init>()>();

return;
}

private static int[] method1(byte[])
{
byte[] r0;
int $i0, $i1, $i2, $i3, $i5, $i6, $i8, $i9, $i10, $i12, $i13, $i14, $i16, $i17, $i18, i19, i20;
int[] r1;
byte $b4, $b7, $b11, $b15;

r0 := @parameter0: byte[]; //参数 字节数组

$i0 = lengthof r0;

$i1 = $i0 >> 2; //字节数组长度 / 4

r1 = newarray (int)[$i1];

i19 = 0;

i20 = 0;

label1:
$i2 = lengthof r0;

if i20 >= $i2 goto label2; //i20索引

$i3 = i20 + 3;

$b4 = r0[$i3]; // b4 = r0[i+3]

$i5 = staticinvoke <simple: int method4(byte)>($b4);

$i6 = i20 + 2;

$b7 = r0[$i6]; // b7 = r0[i+2]

$i8 = staticinvoke <simple: int method4(byte)>($b7);

$i9 = $i8 << 8; //i9 = r0[i+2] << 8

$i12 = $i5 | $i9; // i12 = (r0[i+2] << 8) | r0[i+3]

$i10 = i20 + 1;

$b11 = r0[$i10];

$i13 = staticinvoke <simple: int method4(byte)>($b11);

$i14 = $i13 << 16;

$i17 = $i12 | $i14; // i17 = (r0[i+1] << 16 )| (r0[i+2] << 8) | r0[i+3]

$b15 = r0[i20];

$i16 = $b15 << 24;

$i18 = $i17 | $i16; // i18 = (r0[i+0] << 24 )| (r0[i+1] << 16 )| (r0[i+2] << 8) | r0[i+3]

r1[i19] = $i18; // r1[j] = i18

i19 = i19 + 1; // j += 1

i20 = i20 + 4; // i += 4

goto label1;

label2:
return r1;
}

private static byte[] method2(int[])
{
int[] r0;
int $i0, $i1, $i2, $i3, $i4, $i6, $i7, $i8, $i9, $i11, $i12, $i13, $i14, $i16, $i17, $i18, $i19, i21, i22;
byte[] r1;
byte $b5, $b10, $b15, $b20;

r0 := @parameter0: int[]; // 参数 r0 整形数组

$i0 = lengthof r0;

$i1 = $i0 << 2; // 整形数组长度 * 4

r1 = newarray (byte)[$i1];

i21 = 0;

i22 = 0;

label1:
$i2 = lengthof r1; // i2 整形数组长度

if i22 >= $i2 goto label2;

$i6 = i22 + 3;

$i3 = r0[i21];

$i4 = $i3 & 255;

$b5 = (byte) $i4;

r1[$i6] = $b5; // r1[i+3] = r0[i21] & 0xff

$i11 = i22 + 2;

$i7 = r0[i21];

$i8 = $i7 >> 8;

$i9 = $i8 & 255;

$b10 = (byte) $i9;

r1[$i11] = $b10; // r1[i+2] = (r0[i21] >> 8) & 0xff

$i16 = i22 + 1;

$i12 = r0[i21];

$i13 = $i12 >> 16;

$i14 = $i13 & 255;

$b15 = (byte) $i14;

r1[$i16] = $b15; // r1[i+1] = (r0[i21] >> 16) & 0xff

$i17 = r0[i21];

$i18 = $i17 >> 24;

$i19 = $i18 & 255;

$b20 = (byte) $i19;

r1[i22] = $b20; // r1[i] = (r0[i21] >> 24) & 0xff

i21 = i21 + 1; // i21 += 1

i22 = i22 + 4; // i += 4

goto label1;

label2:
return r1;
}

public static byte[] method3(byte[], int[])
{
byte[] r0, $r2;
int[] r1, r3;
int $i0, $i1, $i4, $i5, $i6, $i7, $i8, $i9, $i10, $i11, $i12, $i13, $i14, $i15, $i16, $i17, $i18, $i19, $i20, $i21, $i22, i23, i24, i25, i26, i27;

r0 := @parameter0: byte[]; // 参数 字节数组 r0

r3 := @parameter1: int[]; // 参数 整形数组 r3 Tea的key数组

r1 = staticinvoke <simple: int[] method1(byte[])>(r0); // 调用method1 得到整形数组r1

i23 = 0;

label1:
$i0 = lengthof r1; // i0 整形数组r1长度

if i23 >= $i0 goto label4; // i23索引

i24 = r1[i23]; // i24 = r1[i] v1

$i1 = i23 + 1;

i25 = r1[$i1]; // i25 = r1[i+1] v2

i26 = 0;

i27 = 0;

label2:
if i27 >= 32 goto label3; // i27 第二层循环次数 k

i26 = i26 + -1640531527; // i26 delta += -1640531527

$i6 = i25 << 4; // i6 = v2 << 4

$i5 = r3[0]; // i5 = key[0]

$i8 = $i6 + $i5; // i8 = (v2 << 4) + key[0]

$i7 = i25 + i26; // i7 = v2 + delta

$i12 = $i8 ^ $i7; // i12 = ((v2 << 4) + key[0]) ^ (v2 + delta)

$i10 = i25 >> 5; // i10 = v2 >> 5

$i9 = r3[1]; // i9 = key[1]

$i11 = $i10 + $i9; // i11 = (v2 >> 5 )+key[1]

$i13 = $i12 ^ $i11; // i13 = (((v2 << 4) + key[0]) ^ (v2 + delta)) ^ ((v2 >> 5 )+key[1])

i24 = i24 + $i13; // *** v1 += (((v2 << 4) + key[0]) ^ (v2 + delta)) ^ ((v2 >> 5 )+key[1]) ***

$i15 = i24 << 4; // i15 = v1 << 4

$i14 = r3[2]; // i14 = key[2]

$i17 = $i15 + $i14; // i17 = (v1 << 4) + key[2]

$i16 = i24 + i26; // i16 = v1 + delta

$i21 = $i17 ^ $i16; // i21 = ((v1 << 4) + key[2]) ^ (v1 + delta)

$i19 = i24 >> 5; // i19 = v1 >> 5

$i18 = r3[3]; // i18 = key[3]

$i20 = $i19 + $i18; // i20 = (v1 >> 5) + key[3]

$i22 = $i21 ^ $i20; // i22 = (((v1 << 4) + key[2]) ^ (v1 + delta)) ^ ((v1 >> 5) + key[3])

i25 = i25 + $i22; // *** v2 += (((v1 << 4) + key[2]) ^ (v1 + delta)) ^ ((v1 >> 5) + key[3]) ***

i27 = i27 + 1; // k += 1

goto label2;

label3:
r1[i23] = i24; // result[i] = v1

$i4 = i23 + 1;

r1[$i4] = i25; // result[i+1] = v2

i23 = i23 + 2;

goto label1;

label4:
$r2 = staticinvoke <simple: byte[] method2(int[])>(r1); // 调用method2方法,将整形数组拆分为字节数组

return $r2;
}

private static int method4(byte)
{
byte b0;
int i1;

b0 := @parameter0: byte;

i1 = b0;

if b0 >= 0 goto label1;

i1 = b0 + 256;

label1:
return i1;
}

public static void main(java.lang.String[])
{
int[] $r0;
java.io.PrintStream $r2, $r11, $r12;
java.io.BufferedReader $r3;
java.io.InputStreamReader $r4;
java.io.InputStream $r5;
java.lang.String $r7;
byte[] $r8, r10, r16;
boolean $z0;
java.io.IOException $r13;
java.lang.RuntimeException $r14;
java.lang.String[] r15;

r15 := @parameter0: java.lang.String[];

$r0 = newarray (int)[4];

$r0[0] = 1732584193;

$r0[1] = -271733879;

$r0[2] = -1732584194;

$r0[3] = 271733878;

$r2 = <java.lang.System: java.io.PrintStream out>;

virtualinvoke $r2.<java.io.PrintStream: void println(java.lang.String)>("please input your flag:");

$r3 = new java.io.BufferedReader;

$r4 = new java.io.InputStreamReader;

$r5 = <java.lang.System: java.io.InputStream in>;

specialinvoke $r4.<java.io.InputStreamReader: void <init>(java.io.InputStream)>($r5);

specialinvoke $r3.<java.io.BufferedReader: void <init>(java.io.Reader)>($r4);

label1:
$r7 = virtualinvoke $r3.<java.io.BufferedReader: java.lang.String readLine()>();

r16 = virtualinvoke $r7.<java.lang.String: byte[] getBytes()>(); //得到输入为字节型

$r8 = newarray (byte)[24];

$r8[0] = 73;

$r8[1] = -65;

$r8[2] = 27;

$r8[3] = -19;

$r8[4] = -77;

$r8[5] = 28;

$r8[6] = 108;

$r8[7] = 82;

$r8[8] = 43;

$r8[9] = 60;

$r8[10] = -14;

$r8[11] = 58;

$r8[12] = 28;

$r8[13] = 44;

$r8[14] = -21;

$r8[15] = 77;

$r8[16] = 31;

$r8[17] = 114;

$r8[18] = 43;

$r8[19] = 98;

$r8[20] = 88;

$r8[21] = 17;

$r8[22] = 23;

$r8[23] = -9;

r10 = staticinvoke <simple: byte[] method3(byte[],int[])>(r16, $r0); // 对输入加密

$z0 = staticinvoke <java.util.Arrays: boolean equals(byte[],byte[])>(r10, $r8); // 验证输入

if $z0 == 0 goto label2;

$r12 = <java.lang.System: java.io.PrintStream out>;

virtualinvoke $r12.<java.io.PrintStream: void println(java.lang.String)>("right flag!");

goto label3;

label2:
$r11 = <java.lang.System: java.io.PrintStream out>;

virtualinvoke $r11.<java.io.PrintStream: void println(java.lang.String)>("wrong flag!");

label3:
goto label5;

label4:
$r13 := @caughtexception;

$r14 = new java.lang.RuntimeException;

specialinvoke $r14.<java.lang.RuntimeException: void <init>(java.lang.Throwable)>($r13);

throw $r14;

label5:
return;

catch java.io.IOException from label1 to label3 with label4;
}
}

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
from ctypes import *


def decrypt(v1: c_int32, v2: c_int32, key: list):
delta = c_int32(-1640531527 * 32)
for k in range(32):
v2.value -= ((v1.value << 4) + key[2].value) ^ (v1.value + delta.value) ^ ((v1.value >> 5) + key[3].value)
v1.value -= ((v2.value << 4) + key[0].value) ^ (v2.value + delta.value) ^ ((v2.value >> 5) + key[1].value)
delta.value += 1640531527
for i in range(4):
print(chr(((v1.value << (8 * i)) & 0xff000000) >> 24), end='')
for i in range(4):
print(chr(((v2.value << (8 * i)) & 0xff000000) >> 24), end='')


cipher = [73, -65, 27, -19, -77, 28, 108, 82, 43, 60, -14, 58, 28, 44, -21, 77, 31, 114, 43, 98, 88, 17, 23, -9]
for i in range(len(cipher)):
cipher[i] = c_uint8(cipher[i])


key = [c_int32(1732584193), c_int32(-271733879), c_int32(-1732584194), c_int32(271733878)]
for i in range(0, 24, 8):
v1 = (cipher[i + 0].value << 24) | (cipher[i + 1].value << 16) | (cipher[i + 2].value << 8) | cipher[i + 3].value
v2 = (cipher[i + 0 + 4].value << 24) | (cipher[i + 1 + 4].value << 16) | (cipher[i + 2 + 4].value << 8) | cipher[
i + 3 + 4].value
decrypt(c_int32(v1), c_int32(v2), key)

# SBCTF{have_fun_with_tea}

babysmc

ESP定律脱壳,然后简单SMC,下断点在函数处,然后使用p让IDA重新分析为函数

1
2
3
4
5
6
7
8
a = [3776109546, 4260205215, 116586236, 4076271869, 4093020152, 4059887356, 251038144, 4140300481, 130517195,150898624]

for i in range(10):
a[i] ^= 0x87654321
a[i] -= 305419896
for k in range(4):
print(chr((a[i] >> (8*k))&0xff),end='')
# SBCTF{The_code_can_be_edit_when_running}

babyhash

题目先要求输入6个key,并要求为小写字符

然后与字符串12345进行拼接操作

然后进行一系列操作,每个函数都有很多个调用,直接从最后面的比较看起

截屏2024-01-19 02.35.43

跟入sub_7FF618207E04,然后一直跟入到sub_7FF6182175F0

截屏2024-01-19 02.38.50

其中函数sub_7FF6182061FD最终只是调用memcmp函数进行比较,因此这里就是输入的key和正确的key进行比较的地方,查看比较的两个参数

a1 是输入的密文 6d6eaa76a827ebae246617c27107c8dd

a3 是真正的密文 f27d71f53ae17679fb352baa5ea326db

这两个看起来很像是hash后的值,结合题目信息,用一些md5进行尝试计算

截屏2024-01-19 02.44.02

发现真正用来md5的是12345aaaaaa67890,也就是输入的六位key会和12345以及67890进行拼接,那么直接进行爆破得到真正的key

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
# f27d71f53ae17679fb352baa5ea326db
import itertools
import hashlib


def string_to_md5(string):
md5_val = hashlib.md5(string.encode('utf8')).hexdigest()
return md5_val


# print(string_to_md5('12345aaaaaa67890'))

pre = '12345'
suf = '67890'
table = 'abcdefghijklmnopqrstuvwxyz'
for s in itertools.product(table, repeat=6):
mid = ''.join(s)
key = pre + mid + suf
md5_val = string_to_md5(key)


if md5_val == 'f27d71f53ae17679fb352baa5ea326db':
print(mid)
break
# maikei

在得到key后继续往后审计

截屏2024-01-19 02.47.07

关键只有两处,sub_7FF618208C4B这个函数对输入进行加密,72行处的if语句对输入进行判断

查看加密函数,发现就是普通的RC4加密,密钥则是12345maikei67890

截屏2024-01-19 02.48.45

直接解密即可

截屏2024-01-19 02.50.19

babydecode

题目用到两个反调试手段

第一个是在函数sub_401F7B中调用Ptrace函数进行反调试,许多调试器的功能都是基于ptrace这一个强大的系统调用实现,但是一个进程在同一时间只能被另外一个进程调试,那么这个程序就可以调用ptrace函数来调试自己,如果失败,说明有进程在调试自己。这一个教程是十分清晰的解释了这个函数使用。

  1. ptrace PTRACE_ATTACH可以附加到目标进程上,对其进行调试。
  2. 反调试的方式
    跟踪自己:因为,一个进程在同一时间只能被一个进程跟踪。如果进程在启动时,就调用ptrace PTRACE_TRACEME跟踪了自己。那么这个进程将无法被其他进程附加

将两个if条件patch一下就可以绕过

截屏2024-01-19 16.03.52

还有一处反调试是使用sys_alram这一函数,通过字符串查找很容易到相关地方。

截屏2024-01-19 16.12.46

这里绕过方式也很多,可以修改时间,或者直接nop掉函数。

完成patch后就可以正常调试。

整体加密流程也很简单,先是正常TEA算法加密,然后进行一个BASE64的变表处理,然后用所得Base64加密后在一个新表里面找索引,最终的索引为密文

截屏2024-01-19 16.15.06

现在中间变量都可以通过调试正常正确拿到

得到Base加密

1
2
3
4
5
6
7
8
9
10
11
enc = [75, 254, 150, 14, 254, 98, 236, 207, 133, 254, 95, 172, 115, 223, 95, 27, 226, 197, 180, 210, 177, 117, 120, 53, 190, 89, 172, 28, 53, 199, 183, 226, 183, 55, 137, 18, 207, 49, 230, 249, 75, 133, 137, 168]

table = [156, 131, 136, 132, 13, 148, 144, 58, 207, 254, 152, 212, 1, 40, 84, 137, 53, 125, 54, 130, 5, 166, 184, 15, 82, 43, 93, 80, 99, 91, 141, 63, 72, 2, 108, 217, 201, 192, 8, 51, 203, 90, 26, 14, 142, 39, 206, 234, 251, 56, 220, 60, 231, 105, 250, 101, 248, 237, 127, 29, 20, 216, 77, 138, 246, 124, 211, 229, 228, 145, 165, 95, 173, 196, 41, 76, 214, 28, 208, 123, 172, 46, 255, 18, 223, 3, 78, 164, 149, 52, 65, 198, 181, 179, 167, 48, 47, 16, 85, 4, 188, 178, 204, 122, 186, 6, 253, 128, 175, 195, 96, 87, 174, 92, 191, 112, 109, 98, 199, 10, 67, 73, 37, 222, 239, 0, 12, 45, 50, 243, 236, 19, 160, 104, 187, 232, 59, 88, 129, 194, 155, 162, 230, 140, 159, 126, 176, 35, 221, 213, 111, 119, 185, 17, 71, 235, 33, 161, 244, 36, 31, 205, 197, 245, 182, 249, 219, 163, 61, 44, 83, 157, 110, 106, 27, 134, 24, 55, 200, 146, 114, 42, 177, 86, 147, 169, 11, 21, 154, 133, 81, 247, 69, 135, 218, 209, 227, 89, 75, 57, 23, 34, 139, 224, 180, 66, 116, 117, 143, 193, 74, 153, 183, 252, 9, 241, 158, 202, 168, 70, 121, 62, 226, 97, 30, 7, 103, 238, 150, 38, 113, 107, 100, 225, 120, 22, 49, 170, 215, 32, 115, 94, 118, 242, 64, 25, 189, 151, 190, 102, 210, 240, 79, 171, 68, 233]

enc_base = []
for i in enc:
enc_base.append(chr(table[i]))

print(''.join(enc_base))

# LDoTDU1uhD0npa0PgYrJ7bCiQ4nci9VgVeX6u8qfLhX=

得到TEA加密

截屏2024-01-19 16.17.54

最终解密

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
from ctypes import *

def decrypt(v1, v2, key):
delta = c_uint32(-1640531527 * 32)
for k in range(32):
v2.value -= (v1.value + delta.value) ^ (16 * v1.value + key[2]) ^ ((v1.value >> 5) + key[3])
v1.value -= (v2.value + delta.value) ^ (16 * v2.value + key[0]) ^ ((v2.value >> 5) + key[1])
delta.value += 1640531527

for i in range(4):
print(chr(((v1.value >> (8 * i)) & 0xff)), end='')
for i in range(4):
print(chr(((v2.value >> (8 * i)) & 0xff)), end='')


key = [0x55, 0x16f, 0x1e6, 0x2a0]

enc_tea = [0x38, 0x6a, 0xd6, 0x19, 0x7d, 0x71, 0x90, 0x6d, 0x2a, 0xb1, 0xdd, 0x12, 0x8c, 0x1b, 0x8c, 0xed, 0xe1, 0x65,
0x4f, 0x8a, 0x9f, 0x97, 0xd6, 0x23, 0x62, 0x10, 0x39, 0xc7, 0xcb, 0x62, 0x3a, 0x40]


for i in range(0,len(enc_tea),8):
v1 = (enc_tea[i + 3] << 24) | (enc_tea[i + 2] << 16) | (enc_tea[i + 1] << 8) | enc_tea[i + 0]
v2 = (enc_tea[i + 3 + 4] << 24) | (enc_tea[i + 2 + 4] << 16) | (enc_tea[i + 1+ 4] << 8) | enc_tea[i + 0 + 4]

decrypt(c_uint32(v1), c_uint32(v2), key)
# SBCTF{2e_easy_Anti-debug_in_ELF}

Week2

start_main

程序逻辑很简单,输入36位的flag然后经过一个变表的base64解码,然后再经过一个RC4解密,通过调试可以拿到变表RSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJKLMNOPQ和RC4密钥lFaUmVp=,其中用变表可以解RC4密钥为SBCTF

不过奇怪的是并没有看到密文比较的地方,先运行看看

是有错误的输出提示的,但是直接搜这个字符串却搜不到,看来是做了一点加密。不过这也告诉我们一个东西,即在main函数运行完后还有函数在运行,这实际上就是fini_array了。

简单说main函数并不是第一个运行的函数,第一个运行的函数应该是start,然后调用_libc_start_main_libc_start_main再调用main函数,并且在main函数开始前运行init_array里面的函数,main函数结束后运行fini_array里面的函数。

截屏2024-01-23 02.28.15

因此只需转过去看看fini_array有函数即可,在IDA中按shift+F7即可打开段窗口,在里面就有fini_array地址

截屏2024-01-23 02.31.15

截屏2024-01-23 02.31.34

发现一个函数sub_55934E697E47,跟过去看看

这就是很明显的比较流程了,这里俩函数就是解密失败成功字符串的。至此整题的流程就清楚了,当然init_array也有一个函数,那个函数就是起到一个变表的作用。除了这种方式找之外,也可以直接搜索输入的引用,同样能够找到这个地方

最后的base解码就是猜测了,因为输入的字符会进行一个解码,那么猜测输入的字符串也应该是经过base加密的。

ez_ptrace

程序逻辑也很简单

截屏2024-01-23 19.05.20

函数sub_124D写了一个文件son,然后子进程使用调用excel执行这个文件,父进程收到消息然后发出信号,父子进程函数也很简单。

父进程:

截屏2024-01-23 19.07.23

子进程:

截屏2024-01-23 19.07.40

具体流程如下

  1. sub_124d 写文件
  2. fork一个子进程
  3. 父进程执行wait函数等待子进程信号
  4. 子进程执行ptrace()函数,参数为PTRACE_TRACEME,表示希望得到调试,然后调用execl函数,将刚刚的写好的son文件进行运行,并替换当前的进程,这个操作回向父进程发送信号
  5. 父进程收到信号,使用ptrace函数修改子进程内存中的一个值,然后向子进程发送继续运行信号
  6. 父进程等待子进程退出

所以这里面的关键就是找到这个son文件,有挺多方法可以得到这个son文件。

第一个思路是既然父进程中写了文件,但是程序运行起来却看不到这个文件,说明调用了文件删除函数,这个函数需要一个文件路径的参数,那么把文件名字改一下,让文件删除函数无法删除即可

截屏2024-01-23 19.13.32

比如修改写文件的名字,或者找一下子进程里面的字符串,也比较好找。

截屏2024-01-23 19.14.21

第二个思路就是对程序下断点,先不让子进程运行excel函数,或者在父进程发送继续运行处下断点。

第三个思路就是手动提取,这里简单用个idapython脚本即可

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
path = 'my_own.elf'

f = open(path,'wb')

data = idc.get_bytes(0x4020,1208)
for d in data:
f.write((d^0x67).to_bytes(length=1,byteorder='little'))

for d in range(2888):
f.write((0).to_bytes(length=1,byteorder='little'))

data = idc.get_bytes(0x44e0,1488)
for d in data:
f.write((d^0x30).to_bytes(length=1,byteorder='little'))

for d in range(2608):
f.write((0).to_bytes(length=1,byteorder='little'))

data = idc.get_bytes(0x4ac0,420)
for d in data:
f.write((d).to_bytes(length=1,byteorder='little'))

for d in range(3352):
f.write((0).to_bytes(length=1,byteorder='little'))

data = idc.get_bytes(0x4c80,1764)
for d in data:
f.write((d).to_bytes(length=1,byteorder='little'))

f.close()
print('ok')

总之现在得到了son文件,打开看看

截屏2024-01-23 19.16.25

函数流程也很简单,输入32位flag,然后sub_6000131对输入进行4位一组的合并,变成一个32位的整型,然后丢进sub_600011ED进行加密

截屏2024-01-23 19.17.42

这个加密算法的逆运算也很好搓,但这个时候还有一个地方需要注意,父进程调用了ptrace函数修改了一个值,我们需要找到这个地方,修改地址是dword_4c64

dword_4c646000404c,这个是RVA,还需要转换为FOA,即文件偏移,这里应该就是404c

截屏2024-01-23 19.20.28

在子文件中找到这个地方,发现是把密钥最后一个给改了

截屏2024-01-23 19.22.02

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
from ctypes import *

key = [0x74, 0x71, 0x6c, 0x31]

rounds = 12

def encrypt(input_):
delta = c_uint32(0)
v8 = c_uint32(input_[len(input_)-1].value)
v4 = c_uint32(0)
for i in range(rounds):
delta.value -= 0x61C88647
for k in range(8):
v4.value = input_[(k + 1) % 8].value
input_[k].value += ((v4.value ^ delta.value) + (v8.value ^ key[((delta.value >> 2) & 3) ^ (k & 3)])) ^ (
((4 * v4.value) ^ (v8.value >> 5)) + ((v4.value >> 3) ^ (16 * v8.value)))
v8.value = input_[k].value


def decrypt(input_):
delta = c_uint32(-0x61C88647 * rounds)
v8 = c_uint32(0)
v4 = c_uint32(0)
for i in range(rounds):
for k in range(7, -1, -1):
v8.value = input_[(k - 1) % 8].value
if i == rounds - 1 and k == 0:
v8.value = input_[len(input_)-1].value
v4.value = input_[(k + 1) % 8].value
input_[k].value -= ((v4.value ^ delta.value) + (v8.value ^ key[(delta.value >> 2) & 3 ^ k & 3])) ^ (
((4 * v4.value) ^ (v8.value >> 5)) + ((v4.value >> 3) ^ (16 * v8.value)))
delta.value += 0x61C88647
return input_


src = [c_uint32(0x61616161), c_uint32(0x61616161), c_uint32(0x61616161), c_uint32(0x61616161),
c_uint32(0x61616161), c_uint32(0x61616161), c_uint32(0x61616161), c_uint32(0x61616161)]

enc = [c_uint32(0x0913bc10),c_uint32(0x7ad2de6f),c_uint32(0xec01321e),c_uint32(0x33c2a85d),
c_uint32(0x4476c2de),c_uint32(0x59714e3b),c_uint32(0xf5b45769),c_uint32(0x04b4e6ec)]

decrypt(enc)
for k in enc:
for i in range(4):
print(chr(((k.value >> (8 * i)) & 0xff)),end='')
# SBCTF{enjoy_the_time_with_XXTea}

happydebug

用了很多IsDebugpresent来进行反调试,逐一patch掉,其中还有一个TLS函数,TLS会先于main函数运行

然后在主函数下断点调试,发现断不下来,而且会有错误输出,那么就肯定还有反调试手段。继续找,发现第二个TLS函数

发现这里的逻辑与主函数基本相同,不过对一些字符串进行了加密,防止直接搜索,看来这里才是真正程序逻辑的地方,通过搜索字符串找的函数是虚假的,即使通过手撕也无法解出flag。

加密很简单,前面的for循环都是对密钥做处理,与输入无关直接跳到sub_7FF79c22112c,这个函数就是个简单的TEA加密

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
from ctypes import *
key = [0x12D30F06,0x0FDF514EC,0x12E9B197,0x61C7BBEF]

def encrypt(v1,v2):
total = c_uint32(0)
for i in range(0x2e):
v1.value += (key[total.value & 3] + total.value) ^ (v2.value + ((v2.value >> 5) ^ (16 * v2.value)))
total.value += 0x2222BCDF
v2.value += (key[(total.value >> 11) & 3] + total.value) ^ (v1.value + ((v1.value >> 5) ^ (16 * v1.value)))
print(hex(v1.value),hex(v2.value))

def decrypt(v1,v2):
total = c_uint32(0x2222BCDF * 0x2e)
for i in range(0x2e):
v2.value -= (key[(total.value >> 11) & 3] + total.value) ^ (v1.value + ((v1.value >> 5) ^ (16 * v1.value)))
total.value -= 0x2222BCDF
v1.value -= (key[total.value & 3] + total.value) ^ (v2.value + ((v2.value >> 5) ^ (16 * v2.value)))

for i in range(4):
print(chr((v1.value >> (8 * i)) & 0xff),end='')
for i in range(4):
print(chr((v2.value >> (8 * i)) & 0xff),end='')

enc = [1311132138, 997226442, 2621451714, 335682888, 3794794971, 2686665224, 1619254560, 1051520087, 3959481899, 3509109772, 3096538602, 3026677228]

for k in range(0,12,2):
v1 = c_uint32(enc[k])
v2 = c_uint32(enc[k+1])
decrypt(v1,v2)

# SBCTF{there_22e_many_evil_methods_to_anti_debug}

Week3

babymath

pyinstaller打包,解包然后pycdc反编译

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
# Source Generated with Decompyle++
# File: babymaze.pyc (Python 3.11)

import sys

(x, y) = (0, 0)
i = 0
maze = [[3, 0, 0, 1, 0, 1, 1, 1, 1, 0, ],
[1, 1, 1, 0, 1, 1, 1, 0, 0, 1, ],
[1, 0, 1, 0, 1, 1, 1, 0, 1, 0, ],
[1, 0, 0, 1, 1, 1, 0, 0, 0, 0, ],
[1, 1, 1, 1, 0, 1, 0, 0, 0, 0, ],
[0, 0, 1, 0, 1, 1, 0, 0, 0, 0, ],
[0, 0, 1, 1, 0, 1, 1, 1, 0, 0, ],
[1, 1, 1, 0, 1, 1, 0, 0, 1, 0, ],
[1, 0, 0, 0, 1, 0, 1, 1, 1, 1, ],
[1, 0, 1, 1, 2, 1, 0, 1, 1, 1, ]]
if 10 <= x < 0 and 10 <= y < 0 or maze[x][y] == 0:
print('Wrong path!')
sys.exit(0)
elif maze[x][y] == 3:
print('Start from here')
elif maze[x][y] == 2:
print('you are clever, flag is SBCTF{all_your_input}')
sys.exit(0)
if i > 17:
print('Not short enough!')
sys.exit(0)
key = input('next step:')
i = i + 1
if key == 'a':
x = x - 1
elif key == 'b':
x = x + 1
elif key == 'c':
y = y - 1
elif key == 'd':
y = y + 1
else:
print('Wrong input!')
sys.exit(0)
# WARNING: Decompyle incomplete

简单迷宫题

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
import queue

MIN = 9999
maze = [[3, 0, 0, 1, 0, 1, 1, 1, 1, 0, ],
[1, 1, 1, 0, 1, 1, 1, 0, 0, 1, ],
[1, 0, 1, 0, 1, 1, 1, 0, 1, 0, ],
[1, 0, 0, 1, 1, 1, 0, 0, 0, 0, ],
[1, 1, 1, 1, 0, 1, 0, 0, 0, 0, ],
[0, 0, 1, 0, 1, 1, 0, 0, 0, 0, ],
[0, 0, 1, 1, 0, 1, 1, 1, 0, 0, ],
[1, 1, 1, 0, 1, 1, 0, 0, 1, 0, ],
[1, 0, 0, 0, 1, 0, 1, 1, 1, 1, ],
[1, 0, 1, 1, 2, 1, 0, 1, 1, 1, ]]


def maze_walk_dfs(Maze, Mark, x, y, end_x, end_y, step, path):
Step_arr = [0, -1, 0, 1, 0]
# Step_dir = ['a', 'w', 'd', 's']
Step_dir = ['c', 'a', 'd', 'b']
if x == end_x and y == end_y:
global MIN
if step <= MIN:
MIN = step
print("dfs:", path)
return

for i in range(4):
next_x = x + Step_arr[i]
next_y = y + Step_arr[i + 1]
if next_x >= len(Maze) or next_y >= len(Maze[0]) or next_x < 0 or next_y < 0 or Maze[next_x][next_y] == 0 or Mark[next_x][next_y] == 1:
continue
Mark[next_x][next_y] = 1
path += Step_dir[i]
# print(path)
maze_walk_dfs(Maze, Mark, next_x, next_y, end_x, end_y, step + 1, path)

path = path[:-1]
Mark[next_x][next_y] = 0
return


def Dir(nx, ny, lx, ly):
if nx == lx and ny - ly == -1:
return 'a'
if nx == lx and ny - ly == 1:
return 'b'
if nx - lx == -1 and ny == ly:
return 'c'
if nx - lx == 1 and ny == ly:
return 'd'


def maze_walk_bfs(Maze, Mark, x, y, end_x, end_y):
q = queue.Queue()
q.put(x), q.put(y)
Mark[x][y] = 1
father = {(x, y): None}
Step_arr = [0, -1, 0, 1, 0]
while not q.empty():
cx = q.get()
cy = q.get()
Mark[cx][cy] = 1
if cx == end_x and cy == end_y:
ans = ''
while father[cx, cy] is not None:
lx, ly = father[cx, cy]
ans += (Dir(cx, cy, lx, ly))
cx, cy = lx, ly # 一步一步往回找父亲
print('bfs:', ans[::-1])
break

for i in range(4):
nx = cx + Step_arr[i]
ny = cy + Step_arr[i + 1]
if 0 <= nx < len(Maze) and 0 <= ny < len(Maze[0]) and Maze[nx][ny] == '0' and Mark[nx][ny] == 0:
q.put(nx), q.put(ny)
father[(nx, ny)] = (cx, cy) # 关键点:可以确保每个节点的父亲只有一个


if __name__ == '__main__':
Path = ''
mark = [[0 for k in range(len(maze))] for i in range(len(maze[0]))]
mark[0][0] = 1
maze_walk_dfs(maze, mark, 0, 0, 9, 4, 0, Path)
# maze_walk_bfs(maze, mark, 0, 0, 9, 9)
print(MIN)

八色三月七

题目逻辑倒很简单,不过挺烦这个中文编码的(这题中文编码是 GB21312)

输入字符串然后进行Base编码,再输出编码后结果

容易找到码表是这个东西,转化为中文就是一二三四五六七八

编码过程跟base64差不多,同样按照3个字节24bit为一个加密组,然后在24位里面再分6bit一组,由于码表只有8个汉字,于是6bit再分高3个bit和低3个bit来进行索引。这里面D4C2的中文编码,没有起到什么作用,解密直接丢掉就好。所以一个组输入24bit会被加密成8个汉字,每3个bit对应一个汉字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
"""
三五七五二二一四三六一五四二八四三二一七八六四八三六三六八六六五四一三七五六六四四二三六八六二六四一三七二六六一四三三六八六七四四一三七五六七八四二三七八一八八二四三三七六四二四二三八二五五二四八三一~~March7th~~
"""

table = '一二三四五六七八'
a = '三五七五二二一四三六一五四二八四三二一七八六四八三六三六八六六五四一三七五六六四四二三六八六二六四一三七二六六一四三三六八六七四四一三七五六七八四二三七八一八八二四三三七六四二四二三八二五五二四八三一一一一一' # 补一补

ans = []
for i in range(0, len(a), 8):
s = ''
for k in range(8):
s += bin(table.index(a[i+k]))[2:].zfill(3)
ans.append(s)


for i in ans:
for k in range(0, 24, 8):
print(chr(int(i[k:k + 8], base=2)), end='')

# SBCTF{Do_U_laike_Machi_saiwen?--Yes!}

中文也差不多

so many flowers

如题目所言,确实有很多花指令,不再简单局限于jz jnz了,需要稍微分析下

main函数第一处花指令,先pusheax的值然后清零eax,那么这个jz跳转就一定会实现,前面push后面也应该会有相应的pop

patch后如下,那么这两个pushpop也一起patch

这一组花指令最终是这样

截屏2024-01-31 22.34.25

main函数第二组花指令在下面不远处,这里是用了call $+5,即跳到当前指令下一条指令执行。这里需要对callretn熟悉,call指令在会把下一条指令地址压栈,然后jmp去相应地址执行,

简单分析下就知道这三条指令没有任何用处,全部nop掉即可

main函数最后一处花指令在下面的jmp处,也很明显,patch掉

最后main函数反汇编如下

显然sub_411203比较关键,不断跟进去

与前面的花指令差不多,不过稍微绕了一点点,简单分析下。先把eax压栈,然后给eax赋值为4,再eax = eax * eax,然后与0x100比较,然后返回,这时会返回去mul eax处,再eax = eax * eax现在下面那条jz指令就会执行,因此patch如下

反汇编如下

发现就是个把输入4字节一组转为32位整形的操作,加密应该还在后面,接着往下看

不断跟入sub_41126c

一进去就有明显的爆红,因此查看下sub_4113d9

继续跟,关健在这三条汇编,这实际上就是把返回地址给+1了

patch如下,会多出一个字节,按c让IDA重新识别为代码,然后按p分析为函数即可

简单的XXTEA而已

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
from ctypes import *

enc = [1914776603, 2060037819, 1362313334, 1771402966, 933437366, 270220093, 1047910492, 922564584]

from ctypes import *

key = [0x31, 0x74, 0x71, 0x6c]

rounds = 12

def decrypt(input_):
delta = c_uint32(-0x61C88647 * rounds)
v8 = c_uint32(0)
v4 = c_uint32(0)
for i in range(rounds):
for k in range(7, -1, -1):
v8.value = input_[(k - 1) % 8].value
if i == rounds - 1 and k == 0:
v8.value = input_[len(input_)-1].value
v4.value = input_[(k + 1) % 8].value
input_[k].value -= ((v4.value ^ delta.value) + (v8.value ^ key[(delta.value >> 2) & 3 ^ k & 3])) ^ (
((4 * v4.value) ^ (v8.value >> 5)) + ((v4.value >> 3) ^ (16 * v8.value)))
delta.value += 0x61C88647
return input_


enc = [c_uint32(1914776603),c_uint32(2060037819),c_uint32(1362313334),c_uint32(1771402966),
c_uint32(933437366),c_uint32(270220093),c_uint32(1047910492),c_uint32(922564584)]

decrypt(enc)
for k in enc:
for i in range(4):
print(chr(((k.value >> (8 * i)) & 0xff)),end='')
# SBCTF{enjoy_flower_and_XXTea!!!}


SBCTF2024
http://example.com/2024/01/18/SBCTF2024/
Author
yring
Posted on
January 18, 2024
Licensed under