NepCTF 部分REwp+复现

w1n9 Lv1

Realme

考点:新建节区的smc + 常见反调试 + RC4算法魔改

主函数看上去是个人畜无害的RC4魔改

KSA加了个异或,然后PRGA最后一步换成了模运算,尝试爆破无果,结合key和题目提示来看这估计不是真正的加密逻辑,翻看函数列表发现没有其他比较可疑加密函数,估计是对代码段进行了加密,但是又没有搜到对virtualprotect的引用,不过在函数列表发现了这个东西

这三个函数的节区是自定义的,那就合理了,说明不用通过修改权限也能smc,回溯找到对这些函数引用的地方,找到对应的加密部分

image-20250801231055935
image-20250801231055935

通过动调,绕过两个反调试,得到真正的RC4魔改

KSA:

PRGA:

脚本如下:

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
def rc4_modified_decrypt(key, ciphertext):
S = [0] * 256
T = [0] * 256

key_len = len(key)

for i in range(256):
S[i] = (i ^ 0xCF) & 0xFF
T[i] = key[i % key_len]

j = 0
for i in range(256):
j = (T[i] + j + S[i]) % 256
S[i], S[j] = S[j], S[i]
S[j] = (S[j] ^ 0xAD) & 0xFF

i_ptr = 0
j_ptr = 0

plaintext = bytearray()

for k in range(len(ciphertext)):
i_ptr = (i_ptr + 1) % 256
j_ptr = (j_ptr + i_ptr * S[i_ptr]) % 256

S[i_ptr], S[j_ptr] = S[j_ptr], S[i_ptr]

v4 = (S[i_ptr] + S[j_ptr]) % 256
keystream_byte = S[v4]

if k % 2 == 1:
decrypted_byte = (ciphertext[k] - keystream_byte) & 0xFF
else:
decrypted_byte = (ciphertext[k] + keystream_byte) & 0xFF

plaintext.append(decrypted_byte)

return plaintext

key = b"Y0u_Can't_F1nd_Me!"

ciphertext_hex = [
0x50, 0x59, 0xA2, 0x94, 0x2E, 0x8E, 0x5C, 0x95, 0x79, 0x16, 0xE5, 0x36,
0x60, 0xC7, 0xE8, 0x06, 0x33, 0x78, 0xF0, 0xD0, 0x36, 0xC8, 0x73, 0x1B,
0x65, 0x40, 0xB5, 0xD4, 0xE8, 0x9C, 0x65, 0xF4, 0xBA, 0x62, 0xD0
]

ciphertext = bytes(ciphertext_hex)

decrypted_data = rc4_modified_decrypt(key, ciphertext)

print(decrypted_data.decode('utf-8', errors='ignore'))
# NepCTF{Y0u_FiN1sH_Th1s_E3sy_Smc!!!}

Crackme

考点:魔改ollvm混淆 + 魔改AES算法

字符串定位到关键函数

sign和aesEncrypt是关键加密逻辑,位于libcrypto.dll中被调用,主要逻辑为用户名先经过第一次sign函数加密,作为aes加密的密钥;然后将用户名加上“showmaker”字符串再经过一次sign函数加密作为密文,aes就是加密password的函数

在libcrypto.dll中找到这两个函数,但是发现这两个函数都被进行了非常抽象的混淆,d810和脚本执行的去混淆都暂时失效了,不过整个过程我们只需要sign函数加密后的结果,因此sign函数不用逆,直接调用即可,关键就是看aes加密

image-20250828172411199
image-20250828172411199

魔改点只有列混淆处多了一个与0x55的异或

因为这里复现时环境已经关闭,因此脚本就省略了最后解密的部分(体会精神就行)

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

typedef struct
{
uint32_t eK[44], dK[44];
int Nr;
} AesKey;

#define BLOCKSIZE 16
#define LOAD32H(x, y)
do
{
(x) = ((uint32_t)((y)[0] & 0xff) << 24) | ((uint32_t)((y)[1] & 0xff) << 16) |
((uint32_t)((y)[2] & 0xff) << 8) | ((uint32_t)((y)[3] & 0xff));
} while (0)

#define STORE32H(x, y)
do
{
(y)[0] = (uint8_t)(((x) >> 24) & 0xff);
(y)[1] = (uint8_t)(((x) >> 16) & 0xff);
(y)[2] = (uint8_t)(((x) >> 8) & 0xff);
(y)[3] = (uint8_t)((x) & 0xff);
} while (0)

#define BYTE(x, n) (((x) >> (8 * (n))) & 0xff)
#define MIX(x) (((S[BYTE(x, 2)] << 24) & 0xff000000) ^ ((S[BYTE(x, 1)] << 16) & 0xff0000) ^
((S[BYTE(x, 0)] << 8) & 0xff00) ^ (S[BYTE(x, 3)] & 0xff))
#define ROF32(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
#define ROR32(x, n) (((x) >> (n)) | ((x) << (32 - (n))))
static const uint32_t rcon[10] = {
0x01000000UL, 0x02000000UL, 0x04000000UL, 0x08000000UL, 0x10000000UL,
0x20000000UL, 0x40000000UL, 0x80000000UL, 0x1B000000UL, 0x36000000UL};

unsigned char S[256] = {
0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16};

unsigned char inv_S[256] = {
0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D};

int LoadStateArray(uint8_t state[4][4], const uint8_t *in){
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
state[j][i] = *in++;
}
}
return 0;
}

int storeStateArray(uint8_t state[4][4], uint8_t *out){
for(int i = 0; i < 4; i++){
for(int j = 0; j < 4; j++){
*out++ = state[j][i];
}
}
return 0;
}

int keyExpansion(const uint8_t *key, uint32_t keyLen, AesKey *aesKey)
{

if (NULL == key || NULL == aesKey)
{
printf("keyExpansion param is NULL\n");
return -1;
}

if (keyLen != 16)
{
printf("keyExpansion keyLen = %d, Not support.\n", keyLen);
return -1;
}

uint32_t *w = aesKey->eK;
uint32_t *v = aesKey->dK;

for (int i = 0; i < 4; ++i)
{
LOAD32H(w[i], key + 4 * i);
}


for (int i = 0; i < 10; ++i)
{
w[4] = w[0] ^ MIX(w[3]) ^ rcon[i];
w[5] = w[1] ^ w[4];
w[6] = w[2] ^ w[5];
w[7] = w[3] ^ w[6];
w += 4;
}

w = aesKey->eK + 44 - 4;

for (int j = 0; j < 11; ++j)
{
for (int i = 0; i < 4; ++i)
{
v[i] = w[i];
}
w -= 4;
v += 4;
}

return 0;
}


int addRoundKey(uint8_t (*state)[4], const uint32_t *key)
{
uint8_t k[4][4];


for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
k[i][j] = (uint8_t)BYTE(key[j], 3 - i);
}
}

return 0;
}


int subBytes(uint8_t (*state)[4])
{

for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
state[i][j] = S[state[i][j]];
}
}

return 0;
}


int invSubBytes(uint8_t (*state)[4])
{

for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
state[i][j] = inv_S[state[i][j]];
}
}
return 0;
}


int shiftRows(uint8_t (*state)[4])
{
uint32_t block[4] = {0};

for (int i = 0; i < 4; ++i)
{

LOAD32H(block[i], state[i]);
block[i] = ROF32(block[i], 8 * i);
STORE32H(block[i], state[i]);
}
return 0;
}


int invShiftRows(uint8_t (*state)[4])
{
uint32_t block[4] = {0};


for (int i = 0; i < 4; ++i)
{
LOAD32H(block[i], state[i]);
block[i] = ROR32(block[i], 8 * i);
STORE32H(block[i], state[i]);
}

return 0;
}

uint8_t GMul(uint8_t u, uint8_t v)
{
uint8_t p = 0;

for (int i = 0; i < 8; ++i)
{
if (u & 0x01)
{
p ^= v;
}

int flag = (v & 0x80);
v <<= 1;
if (flag)
{
v ^= 0x1B;
}

u >>= 1;
}

return p;
}


int mixColumns(uint8_t (*state)[4])
{
uint8_t tmp[4][4];
uint8_t M[4][4] = {{0x02, 0x03, 0x01, 0x01},
{0x01, 0x02, 0x03, 0x01},
{0x01, 0x01, 0x02, 0x03},
{0x03, 0x01, 0x01, 0x02}};


for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
tmp[i][j] = state[i][j];
}
}

for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j]) ^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]) ^ 0x55;
}
}

return 0;
}

int invMixColumns(uint8_t (*state)[4])
{
uint8_t tmp[4][4];
uint8_t M[4][4] = {{0x0E, 0x0B, 0x0D, 0x09},
{0x09, 0x0E, 0x0B, 0x0D},
{0x0D, 0x09, 0x0E, 0x0B},
{0x0B, 0x0D, 0x09, 0x0E}};


for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
tmp[i][j] = state[i][j];
}
}

for (int i = 0; i < 4; ++i)
{
for (int j = 0; j < 4; ++j)
{
state[i][j] = GMul(M[i][0], tmp[0][j]) ^ GMul(M[i][1], tmp[1][j]) ^ GMul(M[i][2], tmp[2][j]) ^ GMul(M[i][3], tmp[3][j]) ^ 0x55;
}
}

return 0;
}


int aesEncrypt(const uint8_t *key, uint32_t keyLen, const uint8_t *pt, uint8_t *ct, uint32_t len)
{
AesKey aesKey;
uint8_t *pos = ct;
const uint32_t *rk = aesKey.eK;
uint8_t out[BLOCKSIZE] = {0};
uint8_t actualKey[16] = {0};
uint8_t state[4][4] = {0};

if (NULL == key || NULL == pt || NULL == ct)
{
printf("param err.\n");
return -1;
}

if (keyLen > 16)
{
printf("keyLen must be 16.\n");
return -1;
}

if (len % BLOCKSIZE)
{
printf("inLen is invalid.\n");
return -1;
}

memcpy(actualKey, key, keyLen);
keyExpansion(actualKey, 16, &aesKey);


for (int i = 0; i < len; i += BLOCKSIZE)
{

loadStateArray(state, pt);

addRoundKey(state, rk);

for (int j = 1; j < 10; ++j)
{
rk += 4;
subBytes(state);
shiftRows(state);
mixColumns(state);
addRoundKey(state, rk);
}

subBytes(state);
shiftRows(state);

addRoundKey(state, rk + 4);


storeStateArray(state, pos);

pos += BLOCKSIZE;
pt += BLOCKSIZE;
rk = aesKey.eK;
}
return 0;
}


int aesDecrypt(const uint8_t *key, uint32_t keyLen, const uint8_t *ct, uint8_t *pt, uint32_t len)
{
AesKey aesKey;
uint8_t *pos = pt;
const uint32_t *rk = aesKey.dK;
uint8_t out[BLOCKSIZE] = {0};
uint8_t actualKey[16] = {0};
uint8_t state[4][4] = {0};

if (NULL == key || NULL == ct || NULL == pt)
{
printf("param err.\n");
return -1;
}

if (keyLen > 16)
{
printf("keyLen must be 16.\n");
return -1;
}

if (len % BLOCKSIZE)
{
printf("inLen is invalid.\n");
return -1;
}

memcpy(actualKey, key, keyLen);
keyExpansion(actualKey, 16, &aesKey);

for (int i = 0; i < len; i += BLOCKSIZE)
{

loadStateArray(state, ct);

addRoundKey(state, rk);

for (int j = 1; j < 10; ++j)
{
rk += 4;
invShiftRows(state);
invSubBytes(state);
addRoundKey(state, rk);
invMixColumns(state);
}

invShiftRows(state);
invSubBytes(state);

addRoundKey(state, rk + 4);

storeStateArray(state, pos);
pos += BLOCKSIZE;
ct += BLOCKSIZE;
rk = aesKey.dK;
}
return 0;
}

void sign(unsigned char *in, int len, unsigned char *out)
{
HINSTANCE hAes = LoadLibrary("libcrypto.dll");
auto encrypt = (void (*)(unsigned char *, int, unsigned char *))GetProcAddress(hAes, "sign");
if (!encrypt)
{
printf("GetProcAddress failed\n");
}

encrypt(in, len, out);
}
int main()
{
uint8_t key[16] = {0x24, 0x25, 0x8A, 0x5B, 0x6A, 0x20, 0x62, 0x5D, 0xD2, 0x71, 0x64, 0x32, 0xFD, 0xE7, 0x5E, 0xC4};
uint8_t pt[16] = {0xca, 0x02, 0xa1, 0xd4, 0x73, 0x4b, 0x36, 0x8f, 0x8a, 0x1d, 0x78, 0x5e, 0xdf, 0x13, 0xa4, 0xe2};
uint8_t ct[16] = {0x22, 0xEB, 0xB5, 0x40, 0x91, 0x62, 0x9C, 0xF7, 0xE2, 0x13, 0xFF, 0xA8, 0x8C, 0x54, 0xD9, 0x80};
uint8_t plain[16] = {0};
unsigned char out[16] = {0};
int len = 0;
char raw[][100] = {...};
char raw1[][100] = {...};
for (int i = 0; i < 100; i++){
len = strlen(raw[i]);
sign(raw[i], len, out);
sign(raw1[i], strlen(raw1[i]), key);
memset(plain, 0, 16);
aesDecrypt(key, 16, out, plain, 16);
for (int j = 0; j < 16; j++){
printf("%02x", plain[j]);
}
printf("\n");
}

return 0;
}

QRS

程序运行的结果是起了一个服务器并监听,访问结果说的是缺少参数input,这里的参数就是我们需要的flag

image-20250825160217570
image-20250825160217570

image-20250825160231037
image-20250825160231037

通过字符串搜索missing field或者input等字眼,可以定位到如下函数

image-20250825210033271
image-20250825210033271

函数功能我推测估计就是判断参数,发现参数不正确然后报错的,跟着这个函数往上回溯发现了handler函数

image-20250825210237958
image-20250825210237958

根据官方wp中的提示,axum框架中,handler模块是用于接收并处理http请求的异步函数。因此这个地方应该就是涉及到接收并处理input参数的关键函数

通过给input参数随便赋一个值然后起调试,可以看到v16存储着输入的参数值

image-20250826094841739
image-20250826094841739

image-20250826103114465
image-20250826103114465

追踪该值发现在QRS::e12c96f7a24fc73e1::axum_extract::b2a92c317a3cdec81::h3500f6e3d3784860函数中被调用

image-20250826113153734
image-20250826113153734

可以看到有段很像tea的加密,只不过这里的delta值是通过GetTickOut获取,不过通过多次调试发现值好像是固定的为0x68547369

image-20250826114604087
image-20250826114604087

这里是cmp的逻辑,那很显然v20存储的就是密文

image-20250826172905706
image-20250826172905706

image-20250826172952300
image-20250826172952300

密钥为加密函数传入的第一个参数,得到密文密钥delta后,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
#include <stdio.h>
#include <stdint.h>

#define delta 0x68547369
#define rounds 48


uint32_t cipher[8] = {0x083EA621, 0xC745973C, 0xE3B77AE8, 0xCDEE8146, 0x7DC86B96, 0x6B8C9D3B, 0x79B14342, 0x2ECF0F0D};

void tea_decrypt(uint32_t cipher[8]) {
uint32_t key[4] = {0x01234567, 0x89ABCDEF, 0xFEDCBA98, 0x76543210};
for (int i = 0; i < 8; i += 2) {
uint32_t v0 = cipher[i];
uint32_t v1 = cipher[i + 1];
uint32_t sum = delta * rounds;

for (int j = 0; j < rounds; j++) {
sum -= delta;
v1 -= (v0 + ((v0 << 4) ^ (v0 >> 5))) ^ (sum + delta + key[(((sum + delta) >> 9) & 0xC) / 4]);
v0 -= (v1 + ((v1 << 4) ^ (v1 >> 5))) ^ (sum + key[(sum & 3)]);
}

cipher[i] = v0;
cipher[i + 1] = v1;
}
}

int main() {
tea_decrypt(cipher);
printf("%32s\n", cipher);

return 0;
}
//NepCTF{a4747f82be106d3f8c4d747c744d7ee5}

这次主要是记录一下自己当时在比赛过程中做出来和快做出来有思路的题,其余比赛当时还没有什么思路,后面再来慢慢复现吧QAQ…

  • 标题: NepCTF 部分REwp+复现
  • 作者: w1n9
  • 创建于 : 2025-08-01 22:23:11
  • 更新于 : 2025-09-03 21:53:40
  • 链接: https://vv1n9.github.io/2025/08/01/NepCTF RE复现/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
NepCTF 部分REwp+复现