SCTF2023 about wp in the reverse direction.

复现文件保存在链接

SycLang

 64位trans_IR。主要是看那个txt文件。经过化简,可以得到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var15<+56>   -> aa
var16<+64> -> bb
var17<+72> -> cc
var18<+80> -> dd
var19<+88> -> ee
var20<+96> -> ff
var21<+104> -> gg
var12<+32> -> hh
var13<+40> -> kk
var11 -> arr1
var22 -> cp1
var23 -> cp2
var24 -> cp3
var25 -> cp4
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
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
STRUCT exp :
ARRAY .key(int)[24]<+0>
ARRAY .L(int)[8]<+192>
ARRAY .R(int)[8]<+256>
ARRAY .X(int)[8]<+320>


FUNCTION read - 8 :
PARAM var2<+8>
LABEL Flabelread :

FUNCTION writes - 0 :
LABEL Flabelwrites :

FUNCTION writef - 0 :
LABEL Flabelwritef :

FUNCTION exit - 0 :
LABEL Flabelexit :

FUNCTION main - 1640 :
ARRAY arr1(char)[24]<+0>
STRUCT cp1(exp)<+488>
STRUCT cp2(exp)<+872>
STRUCT cp3(exp)<+1256>
STRUCT cp4(exp)<+1640>
ARG arr1<+24> // 输入
CALL read
aa := 0
LABEL label4 :
IF aa < 24
GOTO label3
GOTO label2
LABEL label3 :
hh := 0
bb := aa
hh ::= arr1<1+bb>
dd := 23 - aa
cp1.key[8+8*dd] = hh // 输入的倒序给cp1.key, aa=输入长度
aa := aa + 1
GOTO label4
LABEL label2 :
aa := 23
LABEL label11 :
IF aa > 0
GOTO label10
GOTO label9
LABEL label10 :
dd := aa
ee = cp1.key[8+8*dd] // 输入的第1个值给ee
bb := aa - 1
cc = cp1.key[8+8*bb] // 输入的第2个值给cc
gg := ee - cc
cp1.key[8+8*aa] = gg // cp1.key[aa] = cp1.key[aa]-cp1.key[aa-1],见草稿纸(1)
aa := aa - 1
GOTO label11
LABEL label9 :
cp1(@exp.L[0])<+200><+488> := 0 // cp1.L = [0,15,2,10,6,9,1,4]
cp1(@exp.R[0])<+264><+488> := 8 // cp1.R = [8,23,11,20,13,21,19,17]
cp1(@exp.X[0])<+328><+488> := 11 // cp1.X = [11,-13,17,-19,23,-29,31,-37]
cp1(@exp.L[1])<+208><+488> := 15
cp1(@exp.R[1])<+272><+488> := 23
cp1(@exp.X[1])<+336><+488> := -13
cp1(@exp.L[2])<+216><+488> := 2
cp1(@exp.R[2])<+280><+488> := 11
cp1(@exp.X[2])<+344><+488> := 17
cp1(@exp.L[3])<+224><+488> := 10
cp1(@exp.R[3])<+288><+488> := 20
cp1(@exp.X[3])<+352><+488> := -19
cp1(@exp.L[4])<+232><+488> := 6
cp1(@exp.R[4])<+296><+488> := 13
cp1(@exp.X[4])<+360><+488> := 23
cp1(@exp.L[5])<+240><+488> := 9
cp1(@exp.R[5])<+304><+488> := 21
cp1(@exp.X[5])<+368><+488> := -29
cp1(@exp.L[6])<+248><+488> := 1
cp1(@exp.R[6])<+312><+488> := 19
cp1(@exp.X[6])<+376><+488> := 31
cp1(@exp.L[7])<+256><+488> := 4
cp1(@exp.R[7])<+320><+488> := 17
cp1(@exp.X[7])<+384><+488> := -37
aa := 0
LABEL label43 :
IF aa < 8
GOTO label42
GOTO label41
LABEL label42 :
bb = cp1.L[200+8*aa]
dd = cp1.R[264+8*aa]
ff = cp1.X[328+8*aa]
cc = cp1.key[8+8*bb] // cc = cp1.key[cp1.L[aa]]
ee = cp1.key[8+8*dd] // ee = cp1.key[cp1.R[aa]]
cc := cc + ff
ee := ee - ff
cp1.key[8+8*bb] = cc // cp1.key[cp1.L[aa]] = cp1.key[cp1.L[aa]] + cp1.X[aa]
cp1.key[8+8*dd] = ee // cp1.key[cp1.R[aa]] = cp1.key[cp1.R[aa]] - cp1.X[aa]
aa := aa + 1
GOTO label43 // 见草稿纸(2)
LABEL label41 :
aa := 1
LABEL label54 :
IF aa < 24
GOTO label53
GOTO label52
LABEL label53 :
cc = cp1.key[8+8*aa]
bb := aa - 1
ff = cp1.key[8+8*bb]
cc := cc + ff
cp1.key[8+8*aa] = cc // cp1.key[aa] = cp1.key[aa] + cp1.key[aa-1]
aa := aa + 1 // 见草稿纸(3)
GOTO label54
LABEL label52 :
aa := 0
LABEL label61 :
IF aa < 23
GOTO label60
GOTO label59
LABEL label60 :
bb := aa
hh := cp1.key[bb]
dd := aa + 1
kk := cp1.key[dd]
kk := 0
cp1.key[bb] = hh ^ kk // cp1.key[aa] = cp1.key[aa] ^ cp1.key[aa+1] or cp1.key[aa] = cp1.key[aa]
aa := aa + 1 // 见草稿纸(4)
GOTO label61
LABEL label59 :
cp3(@exp.L[0])<+200><+1256> := 0 // cp3.L = [0,9,9,8,10,9,1,0]
cp3(@exp.R[0])<+264><+1256> := 12 // cp3.R = [12,10,12,19,12,13,22,23]
cp3(@exp.X[0])<+328><+1256> := -19 // cp3.X = [-19,-10,3,-11,-9,3,-19,7]
cp3(@exp.L[1])<+208><+1256> := 9 // cp3.key = [12,31,31,31,31,31,31,31,42,46,45,45,20,23,23,23,23,23,23,12,12,12,-7,0]
cp3(@exp.R[1])<+272><+1256> := 10
cp3(@exp.X[1])<+336><+1256> := -10
cp3(@exp.L[2])<+216><+1256> := 9
cp3(@exp.R[2])<+280><+1256> := 12
cp3(@exp.X[2])<+344><+1256> := 3
cp3(@exp.L[3])<+224><+1256> := 8
cp3(@exp.R[3])<+288><+1256> := 19
cp3(@exp.X[3])<+352><+1256> := -11
cp3(@exp.L[4])<+232><+1256> := 10
cp3(@exp.R[4])<+296><+1256> := 12
cp3(@exp.X[4])<+360><+1256> := -9
cp3(@exp.L[5])<+240><+1256> := 9
cp3(@exp.R[5])<+304><+1256> := 13
cp3(@exp.X[5])<+368><+1256> := 3
cp3(@exp.L[6])<+248><+1256> := 1
cp3(@exp.R[6])<+312><+1256> := 22
cp3(@exp.X[6])<+376><+1256> := -19
cp3(@exp.L[7])<+256><+1256> := 0
cp3(@exp.R[7])<+320><+1256> := 23
cp3(@exp.X[7])<+384><+1256> := 7
cp3(@exp.key[0])<+8><+1256> := 12
cp3(@exp.key[1])<+16><+1256> := 31
cp3(@exp.key[2])<+24><+1256> := 31
cp3(@exp.key[3])<+32><+1256> := 31
cp3(@exp.key[4])<+40><+1256> := 31
cp3(@exp.key[5])<+48><+1256> := 31
cp3(@exp.key[6])<+56><+1256> := 31
cp3(@exp.key[7])<+64><+1256> := 31
cp3(@exp.key[8])<+72><+1256> := 42
cp3(@exp.key[9])<+80><+1256> := 46
cp3(@exp.key[10])<+88><+1256> := 45
cp3(@exp.key[11])<+96><+1256> := 45
cp3(@exp.key[12])<+104><+1256> := 20
cp3(@exp.key[13])<+112><+1256> := 23
cp3(@exp.key[14])<+120><+1256> := 23
cp3(@exp.key[15])<+128><+1256> := 23
cp3(@exp.key[16])<+136><+1256> := 23
cp3(@exp.key[17])<+144><+1256> := 23
cp3(@exp.key[18])<+152><+1256> := 23
cp3(@exp.key[19])<+160><+1256> := 12
cp3(@exp.key[20])<+168><+1256> := 12
cp3(@exp.key[21])<+176><+1256> := 12
cp3(@exp.key[22])<+184><+1256> := -7
cp3(@exp.key[23])<+192><+1256> := 0
aa := 23
LABEL label118 :
IF aa > 0
GOTO label117
GOTO label116
LABEL label117 :
dd := aa
ee = cp3.key[8+8*dd]
bb := aa - 1
cc = cp3.key[8+8*bb]
gg := ee - cc
cp3.key[8+8*aa] = gg // cp3.key[aa] = cp3.key[aa] - cp3.key[aa-1]
aa := aa - 1 // 见草稿纸(5)
GOTO label118
LABEL label116 :
aa := 0
LABEL label126 :
IF aa < 8
GOTO label125
GOTO label124
LABEL label125 :
bb = cp3.L[200+8*aa]
dd = cp3.R[264+8*aa]
ff = cp3.X[328+8*aa]
cc = cp3.key[8+8*bb]
ee = cp3.key[8+8*dd]
cc := cc + ff
ee := ee - ff
cp3.key[8+8*bb] = cc // cp3.key[cp3.L[aa]] = cp3.key[cp3.L[aa]] + cp3.X[aa]
cp3.key[8+8*dd] = ee // cp3.key[cp3.R[aa]] = cp3.key[cp3.R[aa]] - cp3.X[aa]
aa := aa + 1 // 见草稿纸(6)
GOTO label126
LABEL label124 :
aa := 1
LABEL label137 :
IF aa < 24
GOTO label136
GOTO label135
LABEL label136 :
cc = cp3.key[8+8*aa]
bb := aa - 1
ff = cp3.key[8+8*bb]
cc := cc + ff
cp3.key[8+8*aa] = cc // cp3.key[aa] = cp3.key[aa] + cp3.key[aa-1]
aa := aa + 1 // 见(7)
GOTO label137
LABEL label135 :
cp2(@exp.key[0])<+8><+872> := 252 // cp2.key = [252,352,484,470,496,487,539,585,447,474,577,454,466,345,344,486,501,423,490,375,257,203,265,125]
cp2(@exp.key[1])<+16><+872> := 352
cp2(@exp.key[2])<+24><+872> := 484
cp2(@exp.key[3])<+32><+872> := 470
cp2(@exp.key[4])<+40><+872> := 496
cp2(@exp.key[5])<+48><+872> := 487
cp2(@exp.key[6])<+56><+872> := 539
cp2(@exp.key[7])<+64><+872> := 585
cp2(@exp.key[8])<+72><+872> := 447
cp2(@exp.key[9])<+80><+872> := 474
cp2(@exp.key[10])<+88><+872> := 577
cp2(@exp.key[11])<+96><+872> := 454
cp2(@exp.key[12])<+104><+872> := 466
cp2(@exp.key[13])<+112><+872> := 345
cp2(@exp.key[14])<+120><+872> := 344
cp2(@exp.key[15])<+128><+872> := 486
cp2(@exp.key[16])<+136><+872> := 501
cp2(@exp.key[17])<+144><+872> := 423
cp2(@exp.key[18])<+152><+872> := 490
cp2(@exp.key[19])<+160><+872> := 375
cp2(@exp.key[20])<+168><+872> := 257
cp2(@exp.key[21])<+176><+872> := 203
cp2(@exp.key[22])<+184><+872> := 265
cp2(@exp.key[23])<+192><+872> := 125
aa := 0
LABEL label168 :
IF aa < 24
GOTO label167
GOTO label166
LABEL label167 :
bb := aa
cc = cp2.key[8+8*bb]
dd := aa
ee = cp3.key[8+8*dd]
gg := cc ^ ee
cp2.key[8+8*aa] = gg // cp2.key[aa] = cp2.key[aa] ^ cp3.key[aa]
aa := aa + 1 // 见(8)
GOTO label168
LABEL label166 :
aa := 0
LABEL label176 :
IF aa < 8
GOTO label175
GOTO label174
LABEL label175 :
bb := aa * 3
cc = cp1.key[8+8*bb]
cp2.X[328+8*aa] = cc // cp2.X[aa] = cp1.key[aa*3]
aa := aa + 1 // 见(9)
GOTO label176
LABEL label174 :
aa := 23
LABEL label181 :
IF aa > 0
GOTO label180
GOTO label179
LABEL label180 :
dd := aa
ee = cp2.key[8+8*dd]
bb := aa
bb := bb - 1
cc = cp2.key[8+8*bb]
gg := ee - cc
cp2.key[8+8*aa] = gg // cp2.key[aa] = cp2.key[aa] - cp2.key[aa-1]
aa := aa - 1 // 见(10)
GOTO label181
LABEL label179 :
aa := 0
LABEL label190 :
IF aa < 8
GOTO label189
GOTO label188
LABEL label189 :
bb = cp1.L[200+8*aa]
dd = cp1.R[264+8*aa]
ff = cp2.X[328+8*aa]
cc = cp2.key[8+8*bb]
ee = cp2.key[8+8*dd]
cc := cc - ff
ee := ee + ff
cp2.key[8+8*bb] = cc // cp2.key[cp1.L[aa]] = cp2.key[cp1.L[aa]] - cp2.X[aa]
cp2.key[8+8*dd] = ee // cp2.key[cp1.R[aa]] = cp2.key[cp1.R[aa]] + cp2.X[aa]
aa := aa + 1 // 见(11)
GOTO label190
LABEL label188 :
aa := 1
LABEL label201 :
IF aa < 24
GOTO label200
GOTO label199
LABEL label200 :
cc = cp2.key[8+8*aa]
bb := aa - 1
ff = cp2.key[8+8*bb]
cc := cc + ff
cp2.key[8+8*aa] = cc // cp2.key[aa] = cp2.key[aa] + cp2.key[aa-1]
aa := aa + 1 // 见(12)
GOTO label201
LABEL label199 :
aa := 0
LABEL label208 :
IF aa < 7
GOTO label207
GOTO label206
LABEL label207 :
bb := aa
cc = cp1.L[200+8*bb]
dd := aa + 1
ee = cp1.L[200+8*dd]
gg := cc ^ ee // gg = cp1.L[aa] ^ cp1.L[aa+1]
IF gg > 23
GOTO label215
GOTO label214
LABEL label215 :
gg := 23
LABEL label214 :
cp4.L[200+8*aa] = gg // cp4.L[aa] = gg
aa := aa + 1
GOTO label208
LABEL label206 : // cp4.L[7] = 0
cp4.L[256+7*8] = 0 // 见(13)
aa := 0
LABEL label219 :
IF aa < 7
GOTO label218
GOTO label217
LABEL label218 :
bb := aa
cc = cp1.R[264+8*bb]
dd := aa + 1
ee = cp1.R[264+8*dd]
gg := cc ^ ee // gg = cp1.R[aa] ^ cp1.R[aa+1]
IF gg > 23
GOTO label226
GOTO label225
LABEL label226 :
gg := 23
LABEL label225 :
cp4.R[264+8*aa] = gg // cp4.R[aa] = gg
aa := aa + 1
GOTO label219
LABEL label217 :
cp4.R[320+8*7] = 23 // cp4.R[7] = 23
aa := 0 // 见(14)
LABEL label230 :
IF aa < 7
GOTO label229
GOTO label228
LABEL label229 :
bb := aa
cc = cp1.X[328+8*bb]
dd := aa + 1
ee = cp1.X[328+8*dd]
gg := cc ^ ee
cp4.X[328+8*aa] = gg // cp4.X[aa] = cp1.X[aa] ^ cp1.X[aa+1]
aa := aa + 1 // 见(15)
GOTO label230
LABEL label228 :
cp4(@exp.X[7])<+384><+1640> := 12
cp4(@exp.key[0])<+8><+1640> := 127 // cp4.key = [127,111,188,174,195,128,88,121,123,103,57,123,97,74,37,59,21,47,54,28,49,55,??,125]
cp4(@exp.key[1])<+16><+1640> := 111
cp4(@exp.key[2])<+24><+1640> := 188
cp4(@exp.key[3])<+32><+1640> := 174
cp4(@exp.key[4])<+40><+1640> := 195
cp4(@exp.key[5])<+48><+1640> := 128
cp4(@exp.key[6])<+56><+1640> := 88
cp4(@exp.key[7])<+64><+1640> := 121
cp4(@exp.key[8])<+72><+1640> := 123
cp4(@exp.key[9])<+80><+1640> := 103
cp4(@exp.key[10])<+88><+1640> := 57
cp4(@exp.key[11])<+96><+1640> := 123
cp4(@exp.key[12])<+104><+1640> := 97
cp4(@exp.key[13])<+112><+1640> := 74
cp4(@exp.key[14])<+120><+1640> := 37
cp4(@exp.key[15])<+128><+1640> := 59
cp4(@exp.key[16])<+136><+1640> := 21
cp4(@exp.key[17])<+144><+1640> := 47
cp4(@exp.key[18])<+152><+1640> := 54
cp4(@exp.key[19])<+160><+1640> := 28
cp4(@exp.key[20])<+168><+1640> := 49
cp4(@exp.key[21])<+176><+1640> := 55
cp4(@exp.key[22])<+184><+1640> := var1<+8>
cp4(@exp.key[23])<+192><+1640> := 125
aa := 23
LABEL label263 :
IF aa > 0
GOTO label262
GOTO label261
LABEL label262 :
dd := aa
ee = cp4.key[8+8*dd]
bb := aa
bb := bb - 1
cc = cp4.key[8+8*bb]
gg := ee - cc
cp4.key[8+8*aa] = gg // cp4.key[aa] = cp4.key[aa] - cp4.key[aa-1]
aa := aa - 1 // 见(16)
GOTO label263
LABEL label261 :
aa := 0
LABEL label272 :
IF aa < 8
GOTO label271
GOTO label270
LABEL label271 :
bb = cp4.L[200+8*aa]
dd = cp4.R[264+8*aa]
ff = cp4.X[328+8*aa]
cc = cp4.key[8+8*bb]
ee = cp4.key[8+8*dd]
cc := cc - ff
ee := ee + ff
cp4.key[8+8*bb] = cc // cp4.key[cp4.L[aa]] = cp4.key[cp4.L[aa]] - cp4.X[aa]
cp4.key[8+8*dd] = ee // cp4.key[cp4.R[aa]] = cp4.key[cp4.R[aa]] + cp4.X[aa]
aa := aa + 1 // 见(17)
GOTO label272
LABEL label270 :
aa := 1
LABEL label283 :
IF aa < 24
GOTO label282
GOTO label281
LABEL label282 :
cc = cp4.key[8+8*aa]
bb := aa - 1
ff = cp4.key[8+8*bb]
cc := cc + ff
cp4.key[8+8*aa] = cc // cp4.key[aa] = cp4.key[aa] + cp4.key[aa-1]
aa := aa + 1 // 见(18)
GOTO label283
LABEL label281 :
hh := 0
kk := 0
aa := 0
LABEL label292 :
IF aa < 24
GOTO label291
GOTO label290
LABEL label291 : // 比较cp1的key与cp2的key是否相等
bb := aa
hh = cp1.key[8+8*aa]
dd := aa
kk = cp2.key[8+8*aa]
IF kk != hh
GOTO label298
GOTO label297
LABEL label298 :
CALL writef
CALL exit
LABEL label297 :
aa := aa + 1
GOTO label292
LABEL label290 :
CALL writes
CALL exit
LABEL Flabelmain :

 使用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
53
54
55
56
57
58
59
60
61
62
63
64

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
# @Time : 2023/06/17 17:52:54
# @Author: wd-2711
'''

from z3 import *

if __name__ == "__main__":
cp1_key = [Int(('key_%d' % i)) for i in range(24)]

# 1
for i in range(23):
i = 23 - i
cp1_key[i] -= cp1_key[i-1]

cp1_L = [0,15,2,10,6,9,1,4]
cp1_R = [8,23,11,20,13,21,19,17]
cp1_X = [11,-13,17,-19,23,-29,31,-37]

# 2
for i in range(8):
cp1_key[cp1_L[i]] += cp1_X[i]
cp1_key[cp1_R[i]] -= cp1_X[i]

# 3
for i in range(23):
i = i + 1
cp1_key[i] += cp1_key[i-1]

cp2_key = [252,352,484,470,496,487,539,585,447,474,577,454,466,345,344,486,501,423,490,375,257,203,265,125]
cp2_X = [0 for i in range(8)]

# 9
for i in range(8):
cp2_X[i] = cp1_key[i*3]

# 10
for i in range(23):
i = 23 - i
cp2_key[i] -= cp2_key[i-1]
# 11
for i in range(8):
cp2_key[cp1_L[i]] -= cp2_X[i]
cp2_key[cp1_R[i]] += cp2_X[i]
# 12
for i in range(1, 24):
cp2_key[i] += cp2_key[i-1]

# 最后要cp1与cp2的key相等
solver = Solver()
for i in range(24):
solver.add(cp1_key[i] == cp2_key[i])

if sat == solver.check():
m = solver.model()
res = {}
for d in m.decls():
res[d.name()] = int(str(m[d]), 10)
for i in range(24):
print(chr(res["key_" + str(i)]), end = "")
# sctf{r5cbsumyqpjy0stc7u}

 让s0rry师傅先做出来了,呜呜呜,其实主要是没及时更新协作文档,以后注意!!!今天打了一天,感觉和平时做题的感觉还不一样,就很紧张,之后的stm32也没精力看了。

Digital_circuit_learning

参考链接1

参考链接2

 首先,题目没告诉板子型号,所以直接按最常见的(STM32F103ZET6)进行设置:

image-20230621185217510

image-20230621185255446

 之后,对0x08000000-0x080000EC转为四字节,因为这是中断向量表。且第二个中断向量默认为入口函数。如下所示:

image-20230621185524585

 跟进0x8000101,并对0x8000100进行反编译处理,得到:

image-20230621185610030

 根据之前的stm-re文章,跟进unk_80000EC。一直跟进,可以找到主函数sub_8001AA0

image-20230621185841369

image-20230621190016702

image-20230621190108049

 首先,有很多标红的,例如:

image-20230621203458512

 因此,我们要添加SRAM段与Peripherals段(stm-re一文中叙述,用于程序运算时存放变量)。

 但是没有调试符号,所以按照参考链接2,直接打开正点原子的某个项目(尽可能包含各种库函数)。我打开的是内存分配项目,编译并拿到axf文件,用ida打开,就会得到对应的idb文件。之后用bindiff导入符号(技能Get),但是相似度都不是特别高。打算手动瞅瞅(stm-re一文中叙述,用手册恢复符号)。手动恢复后大概长这样:

image-20230622104722404

 接下来就是查看中断函数:

image-20230622104651175

 下图为验证代码,其中重点关注0x200000E0中的数据是如何来的。

image-20230703121636608

 发现0x200000E0的赋值语句:

image-20230703122446627

 其中0x200000BF的来源如下,是来源于输入:

image-20230703122558000

 下面看arr2(0x20000024)的来源,可以查找到:

image-20230703122907697

image-20230703123054736

 可以看出,arr2的值是来源于0x8001F68,跟踪一下:

image-20230703123857374

 OK,程序大体运行流搞清楚了。

(1)sub_800112C函数给arr2赋值,最终,arr2如下:

[0x8001CC1,0x8001D81,0x8001DB5,0x8001DE9,0x8001E1D,0x8001E55,0x8001E95,0x8001ED1,0x8001F0D,0x8001C8D]

(2)dword_200000E0[2*i] = arr2[i]

(3)输入为SCTF{xxx},长度为26,并将中间的xxx赋值给0x2000013A

(4)对0x2000013A进行如下操作,并将结果赋值给0x20000130

1
2
3
4
5
6
7
8
9
10
11
12
13
14
for i in range(0, 20, 2):
if src[i] < '0' or src[i] > '9':
if src[i] < 'a' or src[i] > 'f':
v5 = 3
else:
v5 = src[i] - 'W'
else:
v5 = src[i] - '0'
if src[i+1] < '0' or src[i+1] > '9':
if src[i+1] < 'a' or src[i+1] > 'f':
v6 = 0
else:
v6 = src[i+1] - '0'
det[i//2] = v6 + 16 * 5

(5)0x20000130赋给0x200000BFvar_4 = 'w'var_2 = 0var_3=0

(6)dword_200000E0[2*i+1] = 0x200000BF[i]

(7)进行如下操作:

1
2
3
4
5
6
var_3 += 1
for i in range(10):
assert var_4 == dword_200000E0[2*i+1]
dword_200000E0[2*i](0x200000BF, 10)
var_4 = (((var_4 >> 6) & (var_4 >> 2) & 1) == 0) | ((2 * var_4) & 0x7f)
var_2 += 1

 如果var_4 == dword_200000E0[2*i+1]一直成立,则输入正确。

arr2每个函数的功能如下:

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
sub_8001CC0(a1):
byte_200000B4[var_2 % 11] = 'a'
byte_200000B4[++var_2 % 11] = 0
if var_2 == 10:
if sub_8000156(byte_200000B4, "bdgfciejha"):
fail
else:
sub_80019E8(0x200000C9, a1, 10)
0x200000C9 is flag
sub_8000156(a1, a2):
i = 0
while True:
if a1[i] != a2[i] or !a1[i]:
break
i += 1
return a1[i-1] - a2[i-1]

sub_8001D80(a1, a2):
byte_200000B4[var_2 % 11] = 'b';
for i in range(a2):
a1[i] -= 1

sub_8001DB4(a1, a2):
byte_200000B4[var_2 % 11] = 'c';
for i in range(a2):
a1[i] += 1

sub_8001DE8(a1, a2):
byte_200000B4[var_2 % 11] = 'd';
for i in range(a2):
a1[i] ^= '5'

sub_8001E1C(a1, a2):
byte_200000B4[var_2 % 11] = 'e';
for i in range(a2):
a1[i] ^= a1[9-i]

sub_8001E54(a1, a2):
byte_200000B4[var_2 % 11] = 'f';
for i in range(a2):
a1[i] ^= a1[i+1-a2*((i+1)//a2)]

sub_8001E94(a1, a2):
byte_200000B4[var_2 % 11] = 'g';
for i in range(a2):
a1[i] = (16 * a1[i]) | (a1[i] >> 4)

sub_8001ED0(a1, a2):
byte_200000B4[var_2 % 11] = 'h';
for i in range(a2):
a1[i] = (a1[i] << 6) | (a1[i] >> 2)

sub_8001F0C(a1, a2):
byte_200000B4[var_2 % 11] = 'i';
for i in range(a2):
a1[i] = (a1[i] * 32) | (a1[i] >> 3)

sub_8001C8DA(a1, a2):
byte_200000B4[var_2 % 11] = 'j';
for i in range(a2):
a1[i] ^= 0xF7

 最后写脚本如下:

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
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
# @Time : 2023/07/03 15:46:03
# @Author: wd-2711
'''


def sub_8001CC0(a2):
res = [0 for i in range(25)]
for i in range(0, 20, 2):
if (a2[i//2] & 0xF) > 9:
res[i+1] = (a2[i//2] & 0xF) + 87
else:
res[i+1] = (a2[i//2] & 0xF) + 48
if (a2[i//2] >> 4) > 9:
res[i] = (a2[i//2] >> 4) + 87
else:
res[i] = (a2[i//2] >> 4) + 48
return res

def sub_8001D80(a1, a2):
for i in range(a2):
a1[i] -= 1
return a1

def sub_8001DB4(a1, a2):
for i in range(a2):
a1[i] += 1
return a1

def sub_8001DE8(a1, a2):
for i in range(a2):
a1[i] ^= ord('5')
return a1

def sub_8001E1C(a1, a2):
for i in range(a2):
a1[i] ^= a1[9-i]
return a1

def sub_8001E54(a1, a2):
for i in range(a2):
a1[i] ^= a1[i+1-a2*((i+1)//a2)]
return a1

def sub_8001E94(a1, a2):
for i in range(a2):
a1[i] = (16 * a1[i]) | (a1[i] >> 4)
a1[i] = a1[i] & 0xff
return a1

def sub_8001ED0(a1, a2):
for i in range(a2):
a1[i] = (a1[i] << 6) | (a1[i] >> 2)
a1[i] = a1[i] & 0xff
return a1

def sub_8001F0C(a1, a2):
for i in range(a2):
a1[i] = (a1[i] * 32) | (a1[i] >> 3)
a1[i] &= 0xff
return a1

def sub_8001C8A(a1, a2):
for i in range(a2):
a1[i] ^= 0xF7
return a1

var_4 = [ord('w')]
for i in range(1, 10):
tmp = (((var_4[i-1] >> 6) & (var_4[i-1] >> 2) & 1) == 0) | ((2 * var_4[i-1]) & 0xff)
var_4.append(tmp)

origin_200000BF = [0] * len(var_4)
i = 0
for c in "bdgfciejha":
origin_200000BF[ord(c) - ord('a')] = var_4[i]
i += 1

arr_200000BF = sub_8001D80(origin_200000BF, 10)
arr_200000BF = sub_8001DE8(arr_200000BF, 10)
arr_200000BF = sub_8001E94(arr_200000BF, 10)
arr_200000BF = sub_8001E54(arr_200000BF, 10)
arr_200000BF = sub_8001DB4(arr_200000BF, 10)
arr_200000BF = sub_8001F0C(arr_200000BF, 10)
arr_200000BF = sub_8001E1C(arr_200000BF, 10)
arr_200000BF = sub_8001C8A(arr_200000BF, 10)
arr_200000BF = sub_8001ED0(arr_200000BF, 10)



res = sub_8001CC0(arr_200000BF)
print("SCTF{", end = "")
for i in res[:20]:
print(chr(i), end = "")
print("}")

# SCTF{5149ac8b033d602bf6d3}

hidden_in_the_network

1
2
你能帮助Sophia找到Christopher吗?
Tips:压缩包中的三个附件几乎是一样的,只是略有不同,只需解决一个就可以了

 发现是Go语言的64位程序。使用Go_parser处理一下(使用的话是alt+F7执行go_parser.py脚本)。报异常Exception: Failed to find firstmoduledata address!,找不到第一个模块的地址。对main_1->main_4都是这样,且使用bindiff发现函数都是一样的,猜测bindiff只比较两个库的函数,四个文件中在某些代码或数据并不同。使用010editor进行比较,大多都相同。打算先从main_1入手。

 换了个golang_loader_assist插件,找不到函数。打算先看看Go二进制文件逆向分析从基础到进阶(完结篇)

换成ida7.7,妈的竟然有作用了,也不知道为啥。

 将main_main_func1main_main_func2call runtime_morestacknop掉,这样就不会反汇编错误了。call runtime_morestack是为了扩展堆栈的,因为被调用函数的堆栈由调用者维护,所以要时常扩展。静态分析中没啥用,所以nop掉了。

0x00 main_1的分析

main_init_0

 首先分析main_init_0

image-20230705104012664

 此函数大概流程如下:

(1)设置命令行参数pidusernameaddrbookinfo,并分别赋值给变量var_passwdvar_idvar_usernamevar_addrbookvar_info

main_main

 之后,分析main_main函数:

(1)对命令行参数使用flag__FlagSet_Parse进行分割,并将参数个数赋值给变量zero(猜测),若参数个数小于5,则直接退出程序。

image-20230705104400792

(2)使用main_decodeStroff_8B3B80处长度为0x35的数据使用rc4解密。注意,main_decodeStr默认密钥为:[0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0]。并使用net_http__Client_Get函数对url=off_8B3B80_decode||var_username进行访问。

image-20230705104531996

(3)若返回值resp的状态码不为200,则直接进入解析addrbook环节。否则,则进入main_handleInfoOK函数,解析resp.body

image-20230705104910177

(4)判断是否有var_addrbook这个参数,如果没有,则进入(8);否则,进入(5),以解析addrbook参数。

image-20230705110745033

(5)使用main_decodeStr,对off_8B3B200x45个字节解密。这是一个格式字符串,在其中添加var_idvar_passwdvar_passwd取前5位),并将其作为一个url,使用net_http_Client_Get访问。

image-20230705111010789

(6)使用main_handleAddrBookOK函数对resp.body进行解析。

(7)运行main_main_func2函数,此函数运行逻辑如下:

  (a)执行resp_body_for_addrbook+18h

(8)运行main_main_func1函数。

  (a)执行resp_body_for_userInfo+18h

main_handleInfoOK

 此函数逻辑如下:

(1)调用main_decryptoJSONByteresp.body做解密。(下面子流程是main_decryptoJSONByte的步骤)

  (a)使用main_decodeStroff_8B3BA0处长度为0x8的数据使用rc4解密。

  (b)将步骤(a)解密出的数据当作rc4密钥,对resp.body做解密。

(2)将resp.body结构化,最终可以获得:{ID:xx,Name:xx,Phone:xxx,Msg:xxx}

image-20230705110121543

(3)使用main_decodeStr,分别对off_8B3BC0off_8B3BE0off_8B3C00off_8B3B60处长度为8101114的数据使用rc4解密,并将结果打印到终端。

main_handleAddrBookOK

 此函数逻辑如下:

(1)与main_handleInfoOK的第1步相同。

(2)将resp.body结构化,最终可以获得:{Phones:xxx}

(3)使用main_decodeStr,分别对off_8B3B40处长度为0x20的数据使用rc4解密,并将结果打印到终端。

0x01 题目思路

 可以通过A查到B,然后从B查到C。那么我们就能得到:A如何查找到C,即Sophia到Christopher。

1
2
3
4
5
6
7
# 输出st->ed的字节数据
import idc
st = 0x883C80
ed = 0x883CB5
for addr in range(st, ed):
byte_data = idc.get_wide_byte(addr)
print(hex(byte_data), end = " ")
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
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
# @Time : 2023/07/05 13:16:59
# @Author: wd-2711
'''

userinfo = [0xed,0x2f,0xd8,0x27,0xa8,0x8,0x4e,0xb4,0xc1,0x69,0x97,0x2,0xa7,0x6b,0x4f,0xa7,0x39,0xeb,0xa1,0x63,0x92,0x2d,0xb3,0x1e,0x58,0x91,0x24,0x10,0x3e,0x91,0x88,0x7a,0x2a,0xf5,0x9a,0xdc,0x75,0x45,0xe0,0x43,0xdf,0x11,0xf7,0x7d,0x58,0x14,0x19,0x8c,0x3a,0x40,0xd,0x6e,0x7e]
userinfo = bytes(bytearray(userinfo))

addrbook = [0xed,0x2f,0xd8,0x27,0xa8,0x8,0x4e,0xb4,0xc1,0x69,0x97,0x2,0xa7,0x6b,0x4f,0xa7,0x39,0xeb,0xa1,0x63,0x92,0x2d,0xb3,0x1e,0x58,0x91,0x24,0x1f,0x24,0x89,0x8f,0x32,0x30,0xf3,0xda,0xde,0x67,0x44,0xf6,0x58,0xd4,0x4,0xeb,0x20,0x42,0x8,0x17,0xc1,0x21,0x52,0x5,0x79,0x2a,0xdc,0x95,0x9d,0xc5,0x59,0x20,0xc0,0xc2,0xf4,0x69,0xaf,0x60,0x97,0x9f,0x76,0x25]
addrbook = bytes(bytearray(addrbook))


def main_decode_str(data):
key = [0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0]
key = key[::-1]
key = bytearray(key)
key = bytes(key)
enc = ARC4.new(key)
res = enc.decrypt(data)
res = str(res,'gbk')
return res

print(main_decode_str(userinfo))
print(main_decode_str(addrbook))

# http://190.92.230.233:8080/nologin/userinfo?username=
# http://190.92.230.233:8080/auth/showaddressbook?userid=%d&password=%s

 由于http://190.92.230.233:8080已无法访问,因此无法进行下去。考虑接下来的步骤:

(1)访问http://190.92.230.233:8080/nologin/userinfo?username=sophia,并解析,获得其ID,name,phone,Msg。之后,访问http://190.92.230.233:8080/auth/showaddressbook?userid=sophia.id&password=xxx,由于password共5位,因此可以遍历获得密码,从而获得sophiaaddrbook

(2)类似(1)的方法,最终找到christopher,猜测在其Msg信息中就有其flag

0x02 Nu1L’s WP

 真实的解题步骤更加复杂。

(1)使用sqlmap可以发现http://190.92.230.233:8080/nologin/userinfo?username=xx存在sql注入,爆破得到所有用户的信息(共50个)。

(2)爆破所有用户的密码,从而获得所有用户的addrbook

(3)使用nx.Graph,画出社交关系图。并使用nx.dijkstra_path找出最短路径。最终,flag=sctf{md5(path)}但是我猜测,其实就是用main1-4一个一个找。

checkFlow

1
2
Input the flow to test the channel.(输入流量以测试通道)
flag format:sctf{Caps MD5}(flag格式:`sctf{Caps MD5}`)

 64位ELF。显示为ABI。Linux ABI 4.4.0 定义了许多与操作系统交互的规则,包括系统调用(system call)的参数和返回值、函数调用的参数和返回值、异常处理、动态链接等方面的内容。它规定了二进制可执行文件应该如何与操作系统进行交互,从而实现二进制兼容性。猜测类似于一个linux的dll库(其实这种猜测是不准确的,具体看什么是ABI,简略说:ABI是一组规则,规定了数据类型占几个字节、函数调用传哪些参数等),运行之后则是一个真实程序(要求输入Flow,并作检查)。因此,猜测此文件与linux的交互都用api实现了,并静态编译到此文件中。

 经过好几天的分析,这个还挺麻烦的,主要是没有符号表,所有函数都得手动看。总结一下这个题目的流程:

(1)输入长度为12的整数倍的0/1字符串。字符串c='000000000000'

(2)每次取12位字符str,之后check_1检验,若函数返回0则失败。

(3)v23 = -1 or func_2(c)

(4)若v23 >= func_2(str),则失败。

(5)再次做检验,重点函数为sub_4070E1check_1。如下所示:

image-20230710204205159

(6)当马上到字符串结尾时,再次做检验,如下所示:

image-20230710204246862

 此题的逻辑不难,难的是函数的分析,其中sub_4070E1check_1函数很难分析,里面层层嵌套,非常容易令人混乱。wp如下:

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
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
# @Time : 2023/07/10 20:34:43
# @Author: wd-2711
'''

import hashlib

def func_2(str1):
res = 0
for i in range(0xC, 0, -1):
if str1[0xC-i] == '1':
res |= (1<<((i-1)&0x3F))
return res

def sub_4070E1(str1, j):
def count_head_zero(j):
if j == 0:
return 0xC
j = bin(j)[::-1]
cnt = 0
for jj in j:
if jj == '1':
break
elif jj == '0':
cnt += 1
return cnt
def sub_4087C6(j, i):
if i+1 > 0x3F:
return 0xC
if j>>(i+1) == 0:
return 0xC
return count_head_zero(j>>(i+1))+(i+1)

j = j & 0xfff
str1 = "000000000000"
result = count_head_zero(j)
i = result
while True:
if i > 0xB:
break
str1 = str1[:0xB-i] + '1' + str1[0xB-i+1:]
result = sub_4087C6(j, i)
i = result
return str1

def check_1(inp):
inp = [ord(i) - 48 for i in inp]
arr1 = [
[1, 1, 0, 0, 1, 0,],
[1, 0, 1, 1, 0, 0,],
[1, 0, 1, 0, 1, 0,],
[0, 1, 1, 0, 1, 0,],
[1, 1, 0, 0, 1, 0,],
[1, 0, 0, 1, 0, 1,],
[0, 1, 1, 1, 0, 0,],
[1, 1, 0, 0, 0, 1,],
[0, 0, 1, 1, 1, 0,],
[1, 1, 0, 1, 0, 0,],
[0, 0, 0, 1, 1, 1,],
[1, 0, 1, 0, 0, 1,],
]

out = [0] * 6
for j in range(6):
for k in range(0xC):
out[j] ^= (arr1[k][j]&inp[k])
if sum(out) == 0:
return 1
return 0

def generate_inps():
res = []
for i in range(0xfff+1):
binary_string = bin(i)[2:]
bit_array = [int(bit) for bit in binary_string]
if len(bit_array) < 0xC:
bit_array = [0]*(0xC-len(bit_array)) + bit_array
bit_array = "".join(str(i) for i in bit_array)
res.append(bit_array)
return res

def test_length(leng):
res = ""
inps = generate_inps()
c = "000000000000"
i = 0
while True:
for inp in inps:
f = i
if f >= leng:
return 1, res
a = inp
b = a
if check_1(a) == 0:
continue
if i:
g = func_2(c)
else:
g = -1
if g >= func_2(a):
continue
j = g + 1
wrong = 0
while True:
a = b
if j >= func_2(a):
break
a = sub_4070E1(a, j)
if check_1(a):
wrong = 1
break
j += 1
if wrong == 0:
res += inp
pass
else:
continue
if i + 12 == leng:
a = b
e = func_2(a) + 1
while True:
a = "111111111111"
if func_2(a) < e:
break
a = sub_4070E1(a, e)
if check_1(a):
return 0, None
e += 1
c = b
i += 12

if __name__ == "__main__":
for i in range(1, 1000):
suc, res = test_length(12 * i)
if suc == 1:
print("success length", 12 * i)
print("res", res)
m = hashlib.md5()
m.update(res.encode())
print("flag is", m.hexdigest())
# success 768
# res 000000000000000001011111000010100011000011111100000100000111000101011000000110100100000111111011001000110010001001101101001010010001001011001110001100110101001101101010001110010110001111001001010000010101010001001010010010110110010011101001010100010010010101001101010110110001010111101110011000100111011001111000011010000100011011011011011100100000011101111111011110000011011111011100100000100011100001111100100010000000100011011111100100100100100101111011100110000111100111011000101000010001101001001110101010110010101011101101101100010110101101001001101110110101101111101010110000110110110001101001110010010101110011001010110100110001110101101110110110010010110111001101111000000100111001011011111010100111111011111000111100000011111101011100111110100000111111111111
# flag is 7bc821b13f8f9f7c043012b6a2a2ece0

W&M没做出来,Nu1L是用frida做的,没太看懂。感觉是用frida动调,使用程序自己的函数。将字符串输入,检验flag是否正确:

image-20230710204725957

 可以发现,没有报错,因此0/1字符串正确。

SycLock

1
2
3
4
题目flag为:flag{password0+password1+password2}
这是一个恶意app,尽量跑在模拟器或调试机上(>=Android5.0),必要时通过adb uninstall packagename卸载。此app仅供本次比赛使用,不要随意转发给他人。
点击后等待30s左右便会开始锁机,测过Android7模拟器,Android10 pixel2真机,均可正常运行。
玩的愉快。

0x00 level0

 审计代码,其重点如下:

image-20230712215309522

 此程序逻辑为:将原始level0.jar中的数据,使用writeFileFromIS函数处理,并重新写入到level0.jar中,之后调用level0.jar中的Check.docheck函数,参数为password0=xxxwriteFileFromIS函数逻辑如下:

image-20230712215558686

 是一个简单的异或,最终可以得到level0_decode.jar。其重要代码如下:

image-20230712215822726

image-20230712215859429

 不知道是什么加密算法(RC4?),反正最终遍历就可以得到password0=good

0x01 level1

image-20230712220024834

 其中,level1check函数是从liblevel1.so中导入的。

image-20230712220306335

buildTree函数是构建一个哈夫曼树,其逻辑如下:

(1)统计reverseisfun字符串出现频次,并按照26个英文字母先后顺序来排序。

1
2
3
4
5
6
7
8
e:3
f:1
i:1
n:1
r:2
s:2
u:1
v:1

(2)之后将出现频次按顺序压入到一个sortqueue,最终可以得到:

1
v:1 -> u:1 -> n:1 -> i:1 -> f:1 -> s:2 -> r:2 -> e:3

(3)每次取出sortqueue的头两个元素,第一个元素为左节点,第二个元素为右节点,把这两个节点合并,并重新压入sortqueue

(4)最终可以得到哈夫曼树如下(其中traversetree用于给树的叶子节点添加诸如00/0110的路径符号):

image-20230712221853222

 得到此树之后,由于要匹配110111110001100,因此可以得到password1=userv

0x02 level2

image-20230712222253893

image-20230712222322098

 上述逻辑为:将输入写入到/level2input文件中,并通过check(并未体现),检查生成的/judge文件中是否包含success,若有则挑战成功。

 那check()体现在哪里呢?体现在LoadLibrary("level2")那里。在导入liblevel2.so库时,默认会自动运行JNI_OnLoad方法,如下图所示:

image-20230712222702441

 跟进socketThread,可以跟进到extractLevel2函数。

image-20230712222813332

 其中extract是提取level2.jar所用,跟进:

image-20230712223157860

 可以发现,此函数读取liblevel2.so的最后4个字节,作为长度size。读取最后20到最后4字节,作为xxtea解密的key。读取最后[-52-size, -52]的字节,作为要解密的数据。最终将解密后的数据存入到level2.jar中。

 按照上述流程进行操作,可以得到level2.jar,如下所示:

image-20230712223448237

 可以得到password2=4ndroidisfun

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
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
# @Time : 2023/07/12 21:39:58
# @Author: wd-2711
'''

import struct
from tqdm import tqdm
from ctypes import c_uint32

# challenge0
def challenge0():
def decode_level0():
with open("C:\\Users\\23957\\Desktop\\Syclock\\level0jar", "rb") as f:
d = f.read()

with open("C:\\Users\\23957\\Desktop\\Syclock\\level0_decode.jar", "wb") as f:
for dd in d:
inp = dd ^ 52
f.write(inp.to_bytes(1, 'little'))
def change(a1, a2):
return a2, a1
def get_flag():
plain = "flag{this_is_fake_flag}"
cipher = [24, 0xF8, 37, 0x86, 70, 16, 0x92, 0xDA, 0xD3, 0x89, 0xF4, 4, 0x7E, 0xB3, 0xF7, 92, 206, 77, 0xAF, 34, 0x7A, 14, 0x9E]
v6 = [i for i in range(0x100)]
for k0 in tqdm(range(32,128)):
for k1 in range(32,128):
for k2 in range(32,128):
for k3 in range(32,128):
k = [k0, k1, k2, k3]
tmp = 0
v3 = [k[i%4] for i in range(0x100)]
for i in range(256):
tmp = (v6[i] + tmp + v3[i]) & 0xff
v6[i], v6[tmp] = change(v6[i], v6[tmp])
i = 0
out = [0 for i in range(23)]
tmp = 0
for j in range(23):
i = (i + 1) & 0xff
tmp = (tmp + v6[i]) & 0xff
v6[i], v6[tmp] = change(v6[i], v6[tmp])
out[j] = ord(plain[j])^v6[(v6[i]+v6[tmp])&0xff]^18
eq = 1
for i in range(23):
if out[i] != cipher[i]:
eq = 0
break
if eq == 1:
print(chr(k), end = "")
v6 = [i for i in range(0x100)]
print("challenge0_flag:", end = "")
get_flag()
print("")

# challenge1
def challenge1():
print("challenge2_flag:", end = "")
print("userv")

# challenge2
DELTA = 0x9E3779B9
def encrypt(v, n, k):
rounds = 6 + int(52 / n)
sum = c_uint32(0)
z = v[n - 1].value
while rounds > 0:
sum.value += DELTA
e = (sum.value >> 2) & 3
p = 0
while p < n - 1:
y = v[p + 1].value
v[p].value += (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum.value ^ y) + (k[(p & 3) ^ e] ^ z)))
z = v[p].value
p += 1
y = v[0].value
v[n - 1].value += (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum.value ^ y) + (k[(p & 3) ^ e] ^ z)))
z = v[n - 1].value
rounds -= 1

def decrypt(v, n, k):
rounds = 6 + int(52 / n)
sum = c_uint32(rounds * DELTA)
y = v[0].value
while rounds > 0:
e = (sum.value >> 2) & 3
p = n - 1
while p > 0:
z = v[p - 1].value
v[p].value -= (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum.value ^ y) + (k[(p & 3) ^ e] ^ z)))
y = v[p].value
p -= 1
z = v[n - 1].value
v[0].value -= (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum.value ^ y) + (k[(p & 3) ^ e] ^ z)))
y = v[0].value
sum.value -= DELTA
rounds -= 1

def challenge2():
def get_level2jar():
with open("C:\\Users\\23957\\Desktop\\Syclock\\liblevel2.so", "rb") as f:
d = f.read()

# get size
size_tuple = (hex(d[-4]), hex(d[-3]), hex(d[-2]), hex(d[-1]))
size = ""
for s in size_tuple[::-1]:
size += s[2:]
size = int(size, 16)

# get md5
st = -20
md5 = []
for i in range(16):
md5.append(d[st+i])
md5_to_arr4 = []
for i in range(4):
tmp = md5[i*4:i*4+4]
tmp = tmp[::-1]
tmp = "".join(hex(t)[2:] for t in tmp)
md5_to_arr4.append(int(tmp, 16))

# get key
st = -52
key = []
for i in range(0x20):
key.append(d[st+i])

# get level2.jar
st = -52 - size
filedata = []
for i in range(size):
filedata.append(d[st+i])

# decrypt
cipher = []
for i in range(0, len(filedata), 4):
dd = [hex(filedata[i+j])[2:].zfill(2) for j in range(4)]
dd = dd[::-1]
n = "".join(dd)
n = int(n, 16)
cipher.append(c_uint32(n))
decrypt(cipher, len(cipher), md5_to_arr4)

# save to level2.jar
out = []
for i in range(len(cipher)):
dd = hex(cipher[i].value)[2:].zfill(8)
dd = [int(dd[j*2:j*2+2], 16) for j in range(4)]
dd = dd[::-1]
out += dd
with open("C:\\Users\\23957\\Desktop\\Syclock\\level2.jar", "wb") as f:
for dd in out:
f.write(dd.to_bytes(1, 'little'))

def get_flag():
target = [90, 80, 70, 91, 93, 80, 93, 71, 82, 65, 90, 110]
for i in range(11, 0, -1):
target[i] = target[i-1] ^ target[i]
for i in range(11, -1, -1):
target[i] = target[(i+1)%12] ^ target[i]
for t in target:
print(chr(t), end = "")

get_level2jar()
print("challenge2_flag:", end = "")
get_flag()
print("")

challenge0()
challenge1()
challenge2()
# flag{gooduserv4ndroidisfun}

SycGson

1
2
Find the correct route
找到正确的路由

 Go-64。

 可以总结出如下3种数据结构:

1
2
3
4
5
6
7
8
9
10
11
12
main_city(0x28):
name int 0x00
neighbours []main.neighbour_city 0x08
delta uint8 0x20
main.neighbour_city():
name int 0x00
distance int 0x08
cost main.cost 0x10
main.cost(0x18):
transportation int 0x00
time int 0x08
expense int 0x10

 程序逻辑如下:

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
valid_city_delta = 0
valid_city_delta_1 = 0
valid_city_index = 2
tmp1 = 0
tmp2 = 1
tmp5 = 0
target2 = 0x186A0
target2_add_8 = 0
str1 = "“fedcba9876543210"

while True:
if flag_len < valid_city_delta_1:
break
now_flag_char = flag[valid_city_delta_1]
valid_city_delta_add_1 = valid_city_delta_1 + 1
rax = tmp5&0xF
if rax%2 == 1:
rcx = 1
else:
rcx = 0
newstr = str1[rcx:0x10-rax]+str1[:rax]
rax = newstr.index(now_flag_str) # rax < 4
tmp1 ^= valid_city_index邻居列表的第rax个邻居的time
tmp2 = (tmp2*valid_city_index邻居列表的第rax个邻居的expense)&0xFFFFFFFF
target2 -= valid_city_index邻居列表的第rax个邻居的transportation
target2_add_8 += valid_city_index邻居列表的第rax个邻居的distance
valid_city_delta += valid_city_index的delta
if valid_city_index的邻居列表的第rax个邻居的name == 0x7C2:
target1 = valid_city_delta*tmp1
if valid_city_delta==0x83 and target1==0x1F370 and tmp2==0x0AA00000 and target2==0xC4E0 and target2_add_8==0xD898 and flag_len==0x1F:
print("good")
else:
tmp5 += valid_city_index的邻居数量
valid_city_index = valid_city_index邻居列表的第rax个邻居在main_citys中的索引
valid_city_delta_1 = valid_city_delta_add_1

 总结以下程序逻辑:找出城市2到城市0x7c2-1800之间长度为32的路径,且expensetransportationdistancedelta都要满足一定条件。

 由于路径遍历复杂度太高,因此进行一些约束,从而可以对路径树进行剪枝。

(1)路径点数为32。

(2)路径无环。

(3)由于transportation单调递减,distancedelta单调递增,因此路径的transportation少于某个值则不再考虑,distancedelta大于某个值则不再考虑。

(4)最重要的点:由于计算出平均每条边的transportation为1600,且图中transportation最小为1600,因此路径每条边应为transportation

 脚本如下:

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
#!/usr/bin/env python
# -*- encoding: utf-8 -*-
'''
# @Time : 2023/07/18 10:31:52
# @Author: wd-2711
'''


import time

class citys():
def __init__(self, data):
self.data = data

def get_neighbours_info_by_index(self, index):
info = self.data[index]
neighbours = info[1]
neighbours_ind = []
neighbours_distance = []
neighbours_cost = []
for n in neighbours:
neighbours_ind.append(n[0]-1800)
neighbours_distance.append(n[1])
neighbours_cost.append({
"transportation": n[2][0],
"time": n[2][1],
"expense": n[2][2],
})
return neighbours_ind, neighbours_distance, neighbours_cost

def get_delta_by_index(self, index):
info = self.data[index]
delta = info[2]
return delta

def __len__(self):
return len(self.data)

def parse_data():
path = "./gson/gson.json"
with open(path, 'r') as f:
lines = f.read().strip().replace(" ", ",").replace("{", "[").replace("}", "]")
lines = eval(lines)
return citys(lines)

expense_gate = 0x0AA000000
target2_gate = 0xC4E0
target2_add_8_gate = 0xD898
valid_city_delta_gate = 0x83
st_index = 2
ed_index = 0x7C2 - 1800
arr = "fedcba9876543210"
citys_info = parse_data()

def find_path():
ret = {}

path_seq = [[st_index]]
expense_seq = [1]
target2_seq = [0x186A0]
target2_add_8_seq = [0]
valid_city_delta_seq = [0]

st = time.time()
i = 0
while True:
i += 1
if i % 10000 == 0:
print("path_seq:{:<10}|time:{:<10}|max_path_len:{:<10}".format(len(path_seq), int(time.time()-st), len(path_seq[-1])))

head_path = path_seq[0]
head_expense = expense_seq[0]
head_target2 = target2_seq[0]
head_target2_add_8 = target2_add_8_seq[0]
head_valid_city_delta = valid_city_delta_seq[0]

if len(head_path) == 32:
break

now_index = head_path[-1]
neighbours_ind, neighbours_distance, neighbours_cost = citys_info.get_neighbours_info_by_index(now_index)
delta = citys_info.get_delta_by_index(now_index)
neighbours_len = len(neighbours_ind)

for j in range(neighbours_len):
new_path = head_path + [neighbours_ind[j]]
new_expense = (head_expense * neighbours_cost[j]['expense']) & 0xFFFFFFFF
new_target2 = head_target2 - neighbours_cost[j]['transportation']
new_target2_add_8 = head_target2_add_8 + neighbours_distance[j]
new_valid_city_delta = head_valid_city_delta + delta

if neighbours_cost[j]['transportation'] != 1600:
continue
if new_target2 < target2_gate or new_target2_add_8 > target2_add_8_gate or new_valid_city_delta > valid_city_delta_gate:
continue
if new_path[-1] in new_path[:-1]:
continue
if new_path[-1] == ed_index and len(new_path) < 32:
continue
if len(new_path) > 32:
continue
if new_path[-1] == ed_index and len(new_path) == 32 and new_target2 == target2_gate and new_target2_add_8 == target2_add_8_gate and new_valid_city_delta == valid_city_delta_gate:
if new_expense == expense_gate:
ret['path'] = new_path
return ret
continue

path_seq.append(new_path)
expense_seq.append(new_expense)
target2_seq.append(new_target2)
target2_add_8_seq.append(new_target2_add_8)
valid_city_delta_seq.append(new_valid_city_delta)

path_seq = path_seq[1:]
expense_seq = expense_seq[1:]
target2_seq = target2_seq[1:]
target2_add_8_seq = target2_add_8_seq[1:]
valid_city_delta_seq = valid_city_delta_seq[1:]

def get_flag():
p = find_path()
path = p['path']
tmp5 = 0
print("SCTF{", end = "")
for i in range(len(path)-1):
node_index = path[i]
next_node_index = path[i+1]
rax = tmp5 & 0xF
new_arr = arr[:0x10-rax] + arr[:rax]
neighbours, _, _ = citys_info.get_neighbours_info_by_index(node_index)
tmp = neighbours.index(next_node_index)
flag_c = new_arr[tmp]
print(flag_c, end = "")
tmp5 += 4
print("}")

if __name__ == "__main__":
get_flag()
# SCTF{dfedfcdefdecdefdccffeddceddecdf}

 做出来才发现,Nu1LW&M都没做出来,嘿嘿嘿,官方题解也没给答案,我得出的flag=SCTF{dfedfcdefdecdefdccffeddceddecdf}

SycTee

1
2
3
4
study OP tee
附件:链接:https://pan.baidu.com/s/1rPLyDrhzZJGilYgFQP6qoA?pwd=SCTF
提取码:SCTF
Google Drive:https://drive.google.com/file/d/1568TtX2DRrTg7jtIv3IaNd1MhVcEmhDS/view?usp=sharing

0x00 思路

 今天看了一天OP-TEE,搭建好了OP-TEE的环境,也能运行hello_world等示例程序。但是还是看不出来,只能运行bl1.bin,其余的bl2.bin/bl31.bin/bl32.bin/bl33.bin无法运行。

 发现这是realworld题目,直接打算看wp,复现一下,这样的话以后遇到了也可以解决(主要是浪费了3天时间了)。

0x01 My middle WP

 找到了某链接,可能是出题人学习optee的笔记。rootfs.cpio.gz是题目的文件系统,首先解压:

1
2
gzip -d root.cpio.gz
sudo cpio -id -D ./rootfs < rootfs.cpio

 在其中,发现optee的相关示例文件:

image-20230714181339639

 重点关注optee_example_bj666-888,因为这是普通示例文件没有的。经过实验,bj888带参数输入会输出wrong,猜测可能flag检验文件。

 分析bj888文件:

image-20230714181902928

 在此需要补充,由于optee是一个可信执行环境,因此,有TEE(可信环境)与REE(不可信环境)两种环境,并且在程序加载时,TEE将根据UUID从内存中取出UUID.ta文件,将其作为TEE系统的镜像文件。从上图可以看出,输入长度为27。

image-20230714182157993

UUID如上图红框所示,我们可以根据它找到此程序对应的UUID.ta文件。在本题中,UUID.ta文件存放在/lib/optee_armtz中,对应的具体文件为:/lib/optee_armtz/045ccc45-ee83-43ec-b69f-121819c1ba6b.ta

 之后,由于045ccc45-ee83-43ec-b69f-121819c1ba6b.ta是加密的,因此,我就不知道该如何做了。

0x02 W&M’s wp

 无论是官方的wp,还是Nu1Lwp,他们都没说一件事:xxx.ta文件并不是加密的,绝大部分情况下,将xxx.ta文件的头部字节删掉,即变成以.ELF字节开头,这样就可以直接使用IDA进行分析。因此,045ccc45-ee83-43ec-b69f-121819c1ba6b.ta不是加密的。

 补充:.ta文件的开头,也就是.ta_head:0000000000000000,一般是uuid。跟进start进入类似于下图的函数结构后,optee.ta有一个固定的模式(看了两个文件都是这样的模式),记住下图红框中的函数一般是主函数即可。

image-20230715113707186

 跟进sub_1DC函数后,发现:

image-20230715114714266

 其中sub_20可以当作是copy函数,因此有:

1
v21 = [0x25, 3, 0xA, 0x6C, 0xF8, 0xB1, 0xCE, 0x7F, 0xC9, 0x42, 0xC, 0xD, 0x68, 0xB3, 0x1C, 4, 0x64, 0xFA, 0xE5, 0xA4, 0x22, 0xD4, 0x2C, 0xFF, 0x4E, 0x36, 0x2A, 0]

 下面跟进v20,在此,说明一下.ta文件的某些函数:

image-20230715120030289

 如上图所示,sub_930函数结构出现过多次,此函数的功能是:收取来自CA(不可信环境)中的参数。

 因此,keyiv密文都是用户输入的。并且,可以发现字符串:Example of TA using an AES sequence,猜测是AES解密。在128位AES的情况下,keyiv都是16字节。再次看bj888,发现:

image-20230715120830295

 猜测key=snbjklefsdcvfsyciv=snbjklefsdcvfsyc。通过在线网站CyberChief可以获得flag

image-20230715121516685

留言

2023-06-17

© 2024 wd-z711

⬆︎TOP