TGCTF 2025 RE部分WP

re部分除了最后一道index都做了,index附件本身都跑不起来,而且wasm不好玩,不补了

水果忍者



点进去直接就写明白了,iv,key和密文直接就有,cyberchef直接解
HZNUCTF{de20-70dd-4e62-b8d0-06e}

蛇年的本命语言1

先拿把exe解压一下拿到pyc

变量名混淆过,先整理一下

明显的z3,上面那个仔细观察后发现每个数字都对应一种字符的出现次数,结合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
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
from z3 import *
# v = [Int('v%d' % i) for i in range(6)]
final = [BitVec('final[%d]' % i, 8) for i in range(30)]
S = Solver()
mes = r"H1Z1N1U1C1T1F1{1a6b2c5d7e4f3}1"
for i in range(1, 30, 2):
S.add(final[i] == ord(mes[i]))
print(mes[i])
S.add([
7 * final[0] == 504,
9 * final[0] - 5 * final[1] == 403,
(2 * final[0] - 5 * final[1]) + 10 * final[2] == 799,
3 * final[0] + 8 * final[1] + 15 *
final[2] + 20 * final[3] == 2938,
(5 * final[0] + 15 * final[1] + 20 * final[2] -
19 * final[3]) + 1 * final[4] == 2042,
(7 * final[0] + 1 * final[1] + 9 * final[2] - 11 *
final[3]) + 2 * final[4] + 5 * final[5] == 1225,
11 * final[0] + 22 * final[1] + 33 * final[2] + 44 * final[3] +
55 * final[4] + 66 * final[5] -
77 * final[6] == 7975,
((21 * final[0] + 23 * final[1] + 3 * final[2] + 24 * final[3] -
55 * final[4]) + 6 * final[5] - 7 * final[6]) + 15 * final[7] == 229,
(2 * final[0] + 26 * final[1] + 13 * final[2] + 0 * final[3] - 65 * final[4]
) + 15 * final[5] + 29 * final[6] + 1 * final[7] + 20 * final[8] == 2107,
(10 * final[0] + 7 * final[1] + -9 * final[2] + 6 * final[3] + 7 * final[4] + 1 *
final[5] + 22 * final[6] + 21 * final[7] - 22 * final[8]) + 30 * final[9] == 4037,
(15 * final[0] + 59 * final[1] + 56 * final[2] + 66 * final[3] + 7 * final[4] + 1 * final[5] -
122 * final[6]) + 21 * final[7] + 32 * final[8] + 3 * final[9] - 10 * final[10] == 4950,
(((13 * final[0] + 66 * final[1] + 29 * final[2] + 39 * final[3] - 33 * final[4]) + 13 * final[5] - 2 *
final[6]) + 42 * final[7] + 62 * final[8] + 1 * final[9] - 10 * final[10]) + 11 * final[11] == 12544,
(((23 * final[0] + 6 * final[1] + 29 * final[2] + 3 * final[3] - 3 * final[4]) + 63 * final[5] - 25 * final[6]
) + 2 * final[7] + 32 * final[8] + 1 * final[9] - 10 * final[10]) + 11 * final[11] - 12 * final[12] == 6585,
((((223 * final[0] + 6 * final[1] - 29 * final[2] - 53 * final[3] - 3 * final[4]) + 3 * final[5] - 65 * final[6]) + 0 *
final[7] + 36 * final[8] + 1 * final[9] - 15 * final[10]) + 16 * final[11] - 18 * final[12]) + 13 * final[13] == 6893,
((((29 * final[0] + 13 * final[1] - 9 * final[2] - 93 * final[3]) + 33 * final[4] + 6 * final[5] + 65 * final[6] + 1 * final[7] -
36 * final[8]) + 0 * final[9] - 16 * final[10]) + 96 * final[11] - 68 * final[12]) + 33 * final[13] - 14 * final[14] == 1883,
(((69 * final[0] + 77 * final[1] - 93 * final[2] - 12 * final[3]) + 0 * final[4] + 0 * final[5] + 1 * final[6] + 16 * final[7] + 36 *
final[8] + 6 * final[9] + 19 * final[10] + 66 * final[11] - 8 * final[12]) + 38 * final[13] - 16 * final[14]) + 15 * final[15] == 8257,
((((23 * final[0] + 2 * final[1] - 3 * final[2] - 11 * final[3]) + 12 * final[4] + 24 * final[5] + 1 * final[6] + 6 * final[7] + 14 * final[8] -
0 * final[9]) + 1 * final[10] + 68 * final[11] - 18 * final[12]) + 68 * final[13] - 26 * final[14]) + 15 * final[15] - 16 * final[16] == 5847,
(((((24 * final[0] + 0 * final[1] - 1 * final[2] - 15 * final[3]) + 13 * final[4] + 4 * final[5] + 16 * final[6] + 67 * final[7] + 146 * final[8] - 50 *
final[9]) + 16 * final[10] + 6 * final[11] - 1 * final[12]) + 69 * final[13] - 27 * final[14]) + 45 * final[15] - 6 * final[16]) + 17 * final[17] == 18257,
((((25 * final[0] + 26 * final[1] - 89 * final[2]) + 16 * final[3] + 19 * final[4] + 44 * final[5] + 36 * final[6] + 66 * final[7] - 150 * final[8] - 250 * final[9]
) + 166 * final[10] + 126 * final[11] - 11 * final[12]) + 690 * final[13] - 207 * final[14]) + 46 * final[15] + 6 * final[16] + 7 * final[17] - 18 * final[18] == 12591,
(((((5 * final[0] + 26 * final[1] + 8 * final[2] + 160 * final[3] + 9 * final[4] - 4 * final[5]) + 36 * final[6] + 6 * final[7] - 15 * final[8] - 20 * final[9]) + 66 *
final[10] + 16 * final[11] - 1 * final[12]) + 690 * final[13] - 20 * final[14]) + 46 * final[15] + 6 * final[16] + 7 * final[17] - 18 * final[18]) + 19 * final[19] == 52041,
((((((29 * final[0] - 26 * final[1]) + 0 * final[2] + 60 * final[3] + 90 * final[4] - 4 * final[5]) + 6 * final[6] + 6 * final[7] - 16 * final[8] - 21 * final[9]) + 69 * final[10] +
6 * final[11] - 12 * final[12]) + 69 * final[13] - 20 * final[14] - 46 * final[15]) + 65 * final[16] + 0 * final[17] - 1 * final[18]) + 39 * final[19] - 20 * final[20] == 20253,
(((((((45 * final[0] - 56 * final[1]) + 10 * final[2] + 650 * final[3] - 900 * final[4]) + 44 * final[5] + 66 * final[6] - 6 * final[7] - 6 * final[8] - 21 * final[9]) + 9 * final[10] - 6 *
final[11] - 12 * final[12]) + 69 * final[13] - 2 * final[14] - 406 * final[15]) + 651 * final[16] + 2 * final[17] - 10 * final[18]) + 69 * final[19] - 0 * final[20]) + 21 * final[21] == 18768,
(((((555 * final[0] - 6666 * final[1]) + 70 * final[2] + 510 * final[3] - 90 * final[4]) + 499 * final[5] + 66 * final[6] - 66 * final[7] - 610 * final[8] - 221 * final[9]) + 9 * final[10] - 23 * final[11] -
102 * final[12]) + 6 * final[13] + 2050 * final[14] - 406 * final[15]) + 665 * final[16] + 333 * final[17] + 100 * final[18] + 609 * final[19] + 777 * final[20] + 201 * final[21] - 22 * final[22] == 111844,
(((((((1 * final[0] - 22 * final[1]) + 333 * final[2] + 4444 * final[3] - 5555 * final[4]) + 6666 * final[5] - 666 * final[6]) + 676 * final[7] - 660 * final[8] - 22 * final[9]) + 9 * final[10] - 73 * final[11] -
107 * final[12]) + 6 * final[13] + 250 * final[14] - 6 * final[15]) + 65 * final[16] + 39 * final[17] + 10 * final[18] + 69 * final[19] + 777 * final[20] + 201 * final[21] - 2 * final[22]) + 23 * final[23] == 159029,
(((520 * final[0] - 222 * final[1]) + 333 * final[2] + 4 * final[3] - 56655 * final[4]) + 6666 * final[5] + 666 * final[6] + 66 * final[7] - 60 * final[8] - 220 * final[9]) + 99 * final[10] + 73 * final[11] + 1007 * final[12] +
7777 * final[13] + 2500 * final[14] + 6666 * final[15] + 605 * final[16] + 390 * final[17] + 100 * final[18] + 609 *
final[19] + 99999 * final[20] + 210 * final[21] + 232 *
final[22] + 23 * final[23] -
24 * final[24] == 2762025,
((((1323 * final[0] - 22 * final[1]) + 333 * final[2] + 4 * final[3] - 55 * final[4]) + 666 * final[5] + 666 * final[6] + 66 * final[7] - 660 * final[8] - 220 * final[9]) + 99 * final[10] + 3 * final[11] + 100 * final[12] + 777 *
final[13] + 2500 * final[14] + 6666 * final[15] + 605 * final[16] + 390 * final[17] + 100 * final[18] + 609 * final[19] + 9999 * final[20] + 210 * final[21] + 232 * final[22] + 23 * final[23] - 24 * final[24]) + 25 * final[25] == 1551621,
(((((777 * final[0] - 22 * final[1]) + 6969 * final[2] + 4 * final[3] - 55 * final[4]) + 666 * final[5] - 6 * final[6]) + 96 * final[7] - 60 * final[8] - 220 * final[9]) + 99 * final[10] + 3 * final[11] + 100 * final[12] + 777 * final[13] +
250 * final[14] + 666 * final[15] + 65 * final[16] + 90 * final[17] + 100 * final[18] + 609 * final[19] + 999 * final[20] + 21 * final[21] + 232 * final[22] + 23 * final[23] - 24 * final[24]) + 25 * final[25] - 26 * final[26] == 948348,
((((((97 * final[0] - 22 * final[1]) + 6969 * final[2] + 4 * final[3] - 56 * final[4]) + 96 * final[5] - 6 * final[6]) + 96 * final[7] - 60 * final[8] - 20 * final[9]) + 99 * final[10] + 3 * final[11] + 10 * final[12] + 707 * final[13] + 250 *
final[14] + 666 * final[15] + -9 * final[16] + 90 * final[17] + -2 * final[18] + 609 * final[19] + 0 * final[20] + 21 * final[21] + 2 * final[22] + 23 * final[23] - 24 * final[24]) + 25 * final[25] - 26 * final[26]) + 27 * final[27] == 777044,
(((((177 * final[0] - 22 * final[1]) + 699 * final[2] + 64 * final[3] - 56 * final[4] - 96 * final[5] - 66 * final[6]) + 96 * final[7] - 60 * final[8] - 20 * final[9]) + 99 * final[10] + 3 * final[11] + 10 * final[12] + 707 * final[13] + 250 * final[14] +
666 * final[15] + -9 * final[16] + 0 * final[17] + -2 * final[18] + 69 * final[19] + 0 * final[20] + 21 * final[21] + 222 * final[22] + 23 * final[23] - 224 * final[24]) + 25 * final[25] - 26 * final[26]) + 27 * final[27] - 28 * final[28] == 185016,
((((((77 * final[0] - 2 * final[1]) + 6 * final[2] + 6 * final[3] - 96 * final[4] - 9 * final[5] - 6 * final[6]) + 96 * final[7] - 0 * final[8] - 20 * final[9]) + 99 * final[10] + 3 * final[11] + 10 * final[12] + 707 * final[13] + 250 * final[14] + 666 * final[15] + -9 * final[16] + 0 * final[17] + -2 * final[18] + 9 * final[19] + 0 * final[20] + 21 * final[21] + 222 * final[22] + 23 * final[23] - 224 * final[24]) + 26 * final[25] - -58 * final[26]) + 27 * final[27] - 2 * final[28]) + 29 * final[29] == 130106])
if S.check() == sat:
ans = S.model()
print(ans)
print(ans[final[14]].as_long())
for i in range(30):

print(chr(ans[final[i]].as_long()), end="")
# 1U1C1T1F1{1 a6 d2 75 f7 46 3 }1

finalans = [0 for i in range(30)]
finalans[0] = 72
finalans[1] = 49
finalans[2] = 90
finalans[3] = 49
finalans[4] = 78
finalans[5] = 49
finalans[6] = 85
finalans[7] = 49
finalans[8] = 67
finalans[9] = 49
finalans[10] = 84
finalans[11] = 49
finalans[12] = 70
finalans[13] = 49
finalans[14] = 123
finalans[15] = 49
finalans[16] = 97
finalans[17] = 54
finalans[18] = 100
finalans[19] = 50
finalans[20] = 55
finalans[21] = 53
finalans[22] = 102
finalans[23] = 55
finalans[24] = 13
finalans[25] = 52
finalans[26] = 54
finalans[27] = 51
finalans[28] = 125
finalans[29] = 49


for i in range(30):
print(chr(finalans[i]), end="")
# H1Z1N1U1C1T1F1{1 a6 d2 75 f7 x4 63}1

解完后发现出现4次的字符对应的是不可见字符,可能是z3有多解,尝试爆破,试出来那个位置还有一种解是-
HZNUCTF{ad7fa-76a7-ff6a-fffa-7f7d6a}

randomsystem

点开有花,拿脚本去一去

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
from binaryninja import *
import bisect


def matchPatterns(idx: int, bv: BinaryView, instructions: List[Tuple[List[InstructionTextToken], int]], address: List[int]):
curInstruction = instructions[idx]
if curInstruction[0][0].text == 'jz': # jz jnz
if idx + 1 < len(instructions):
nextInstruction = instructions[idx + 1]
if nextInstruction[0][0].text == 'jnz':
if curInstruction[0][2].text == nextInstruction[0][2].text:
# print(curInstruction[0], nextInstruction[0])
print("find jz jnz")
return (curInstruction[1], curInstruction[0][2].value-1)
if curInstruction[0][0].text == 'jnz': # jnz jz
if idx + 1 < len(instructions):
nextInstruction = instructions[idx + 1]
if nextInstruction[0][0].text == 'jz':
if curInstruction[0][2].text == nextInstruction[0][2].text:
# print(curInstruction[0], nextInstruction[0])
print("find jnz jz")
return (curInstruction[1], curInstruction[0][2].value-1)
if curInstruction[0][0].text == 'call': # call-return
nextInstructionAddr = instructions[idx + 1][1]
# print(curInstruction[0])
if len(curInstruction[0][2].text) and curInstruction[0][2].text[0] == '$':
idx = find_instruction_idx(address, curInstruction[0][2].value)
if idx != None:
callInstruction: List[InstructionTextToken] = instructions[idx][0]
if callInstruction[0].text == 'add' and len(callInstruction) == 11 and (callInstruction[8].text == r'%esp' or callInstruction[8].text == r'%rsp'):
print("find call-return")
print("nextInstructionAddr", nextInstructionAddr)
print("callInstructionOffset", callInstruction[2].value)
return (curInstruction[1], nextInstructionAddr+callInstruction[2].value-1)


def find_instruction_idx(address, addr: int):
# print(addr)
idx = bisect.bisect_left(address, addr) # 二分说是
if idx < len(address) and addr == address[idx]:
return idx
else:
return None


def scanAllInstruction(bv: BinaryView):
instructions = [ins for ins in bv.instructions]
instructions = sorted(instructions, key=lambda x: x[1])
address = [addr[1] for addr in instructions]
for i in range(0, len(instructions)):
nopRange = matchPatterns(i, bv, instructions, address)
if nopRange:
bv.write(nopRange[0], b'\x90' * (nopRange[1] - nopRange[0] + 1))
print(f"Found NOP range: {hex(nopRange[0])} to {hex(nopRange[1])}")


scanAllInstruction(bv)

点开里面一大坨,但发现前面乱七八杂糟的都在初始化dest这个量,而这个量的预期值直接明文给出了,直接动调patch进入下面的逻辑

下面那个rand也不看,反正都是初始化,动调抓
最后是一个异或,key直接动调拿到

倒数第二个是一个矩阵乘,用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
65
66
67
68
69
70
71
72
73
74
75
from z3 import *
# v = [Int('v%d' % i) for i in range(6)]
final = [Int('final[%d]' % i) for i in range(64)]
S = Solver()
S.add(298 == final[0] + final[8] + final[24] + final[48])
S.add(257 == final[1] + final[9] + final[25] + final[49])
S.add(255 == final[2] + final[10] + final[26] + final[50])
S.add(400 == final[3] + final[11] + final[27] + final[51])
S.add(327 == final[4] + final[12] + final[28] + final[52])
S.add(300 == final[5] + final[13] + final[29] + final[53])
S.add(216 == final[6] + final[14] + final[30] + final[54])
S.add(307 == final[7] + final[15] + final[31] + final[55])
S.add(302 == final[8] + final[16] + final[40] + final[56])
S.add(264 == final[9] + final[17] + final[41] + final[57])
S.add(244 == final[10] + final[18] + final[42] + final[58])
S.add(359 == final[11] + final[19] + final[43] + final[59])
S.add(303 == final[12] + final[20] + final[44] + final[60])
S.add(310 == final[13] + final[21] + final[45] + final[61])
S.add(264 == final[14] + final[22] + final[46] + final[62])
S.add(342 == final[15] + final[23] + final[47] + final[63])
S.add(341 == final[16] + final[24] + final[40] + final[48])
S.add(258 == final[17] + final[25] + final[41] + final[49])
S.add(244 == final[18] + final[26] + final[42] + final[50])
S.add(385 == final[19] + final[27] + final[43] + final[51])
S.add(356 == final[20] + final[28] + final[44] + final[52])
S.add(296 == final[21] + final[29] + final[45] + final[53])
S.add(265 == final[22] + final[30] + final[46] + final[54])
S.add(310 == final[23] + final[31] + final[47] + final[55])
S.add(196 == final[24] + final[40] + final[56])
S.add(167 == final[25] + final[41] + final[57])
S.add(249 == final[26] + final[42] + final[58])
S.add(317 == final[27] + final[43] + final[59])
S.add(204 == final[28] + final[44] + final[60])
S.add(159 == final[29] + final[45] + final[61])
S.add(211 == final[30] + final[46] + final[62])
S.add(202 == final[31] + final[47] + final[63])
S.add(204 == final[8] + final[32] + final[48])
S.add(200 == final[9] + final[33] + final[49])
S.add(206 == final[10] + final[34] + final[50])
S.add(286 == final[11] + final[35] + final[51])
S.add(204 == final[12] + final[36] + final[52])
S.add(321 == final[13] + final[37] + final[53])
S.add(209 == final[14] + final[38] + final[54])
S.add(264 == final[15] + final[39] + final[55])
S.add(151 == final[40] + final[56])
S.add(113 == final[41] + final[57])
S.add(147 == final[42] + final[58])
S.add(196 == final[43] + final[59])
S.add(148 == final[44] + final[60])
S.add(111 == final[45] + final[61])
S.add(156 == final[46] + final[62])
S.add(145 == final[47] + final[63])
S.add(152 == final[48] + final[56])
S.add(107 == final[49] + final[57])
S.add(154 == final[50] + final[58])
S.add(213 == final[51] + final[59])
S.add(147 == final[52] + final[60])
S.add(154 == final[53] + final[61])
S.add(112 == final[54] + final[62])
S.add(98 == final[55] + final[63])
S.add(204 == final[8] + final[16] + final[56])
S.add(208 == final[9] + final[17] + final[57])
S.add(199 == final[10] + final[18] + final[58])
S.add(260 == final[11] + final[19] + final[59])
S.add(203 == final[12] + final[20] + final[60])
S.add(256 == final[13] + final[21] + final[61])
S.add(165 == final[14] + final[22] + final[62])
S.add(242 == final[15] + final[23] + final[63])

if S.check() == sat:
ans = S.model()
# print(ans)
for i in range(64):
print(chr(ans[final[i]].as_long()), end="")

解出fd11v56d454r6f4acb-1ea8d-6fy80795af83zfrb8-cd6cdc24tca7559fa099-
再往前就是交换混淆,这个直接往里面输入1,2,3,4,5…爆破交换映射


然后重新映射回去即可解出flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <bits/stdc++.h>
using namespace std;
int enc[] = {376, 356, 169, 501, 277, 329, 139, 342, 380, 365, 162, 258, 381, 339, 347, 307, 263, 359, 162, 484, 310, 333, 346, 339, 150, 194, 175, 344, 158, 250, 128, 175, 158, 173, 152, 379, 158, 292, 130, 365, 197, 20, 197, 161, 198, 10, 207, 244, 202, 14, 204, 176, 193, 255, 35, 7, 158, 181, 145, 353, 153, 357, 246, 151};
unsigned char key[] =
{
0x52, 0x65, 0x56, 0x65, 0x52, 0x65, 0x53, 0x65};
int dw[64] =
{1, 1, 0, 1, 0, 0, 1, 0,
0, 1, 1, 0, 0, 1, 0, 1,
0, 0, 1, 1, 0, 1, 1, 0,
0, 0, 0, 1, 0, 1, 0, 1,
0, 1, 0, 0, 1, 0, 1, 0,
0, 0, 0, 0, 0, 1, 0, 1,
0, 0, 0, 0, 0, 0, 1, 1,
0, 1, 1, 0, 0, 0, 0, 1};

int main()
{
for (int i = 0; i < 64; i++)
{
enc[i] ^= key[i % 8]; // get final
}
int k; // [esp+D0h] [ebp-20h]
int j; // [esp+DCh] [ebp-14h]
int i; // [esp+E8h] [ebp-8h]

// for (i = 0; i < 8; ++i)
// {
// for (j = 0; j < 8; ++j)
// {
// // final[8 * i + j] = 0;
// printf("S.add(%d == ", enc[8 * i + j]);
// for (k = 0; k < 8; ++k)
// if (dw[8 * i + k])
// {
// printf("final[%d] +", 8 * k + j);
// }
// printf(")\n");
// // final[8 * i + j] += intDest[8 * k + j] * dw[8 * i + k];
// }
// // result = i + 1;
// }
unsigned char mappingTable[] =
{
37, 38, 39, 41, 36, 63, 58, 54, 44, 57,
49, 50, 33, 46, 45, 43, 55, 34, 42, 40,
56, 62, 35, 61, 52, 53, 47, 48, 64, 51,
59, 60, 13, 18, 23, 5, 1, 2, 3, 20,
4, 19, 16, 9, 15, 14, 27, 28, 11, 12,
30, 25, 26, 8, 17, 21, 10, 7, 31, 32,
24, 22, 6, 29};
char final[] = "fd11v56d454r6f4acb-1ea8d-6fy80795af83zfrb8-cd6cdc24tca7559fa099-";
char ans[65];
for (int i = 1; i <= 64; i++)
{
ans[i] = final[mappingTable[i - 1] - 1];
}
for (int i = 1; i <= 64; i++)
printf("%c", ans[i]);
return 0;
// fd11v56d454r6f4acb-1ea8d-6fy80795af83zfrb8-cd6cdc24tca7559fa099-
}

HZNUCTF{3zfb899ac5c256d-7a8r59f0tccd-4fa6b8vfd111-a44ffy4r0-6dce5679da58}

exchange

打开有一个很奇怪的算法,对比下输入输出


可以看到是把输入以4个16进制位为一组,交换中间的两位并转字符串

里面就是很明显的一个des,findCrypto可以找到盒
然后这个des是魔改过的,工具没法解,众所周知Fesitel网络密码只要把轮函数反过来跑就能解密,des就是把key倒过来用

动调抓一下key,发现这个key似乎是对称的,而且组数也太多了点,合理怀疑这是一个加解密一体的算法,后半部分就是解密用的反key
直接把key的指针+32 (看一下当前传参时key的地址通过哪个寄存器转递,直接改寄存器值)
然后把密文patch进输入里跑

可以看到跑出来的结果十分好看
然后写脚本把换位改回去转成hex

1
2
3
4
5
6
7
8
9
char mes[] = "333936147332632923d96353321d3345636826d26314621d3349330463126348";
for (int i = 0; i < 64; i += 4)
{
swap(mes[i + 1], mes[i + 2]);
}
for (int i = 0; i < 64; i += 2)
{
printf("%c%c ", mes[i], mes[i + 1]);
}


直接解出

xtea

cbc模式的xtea,直接进去抄源码

注意到这里有一个反调试,如果直接动调拿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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <bits/stdc++.h>
using namespace std;
void xteaDecrypt(uint32_t *v_0, uint32_t *v_1, uint32_t *k)
{
uint32_t sum = 0;
// printf("%u %u\n", v0, v1);
uint32_t v0 = *v_0;
uint32_t v1 = *v_1;
uint32_t delta = 2654435769u;
for (int i = 0; i < 32; i++)
sum -= delta;
for (int i = 0; i < 32; i++)
{
v1 -= (k[(sum >> 11) & 3] + sum) ^ (v0 + ((v0 >> 5) ^ (16 * v0)));
sum += delta;
v0 -= (k[sum & 3] + sum) ^ (v1 + ((v1 >> 5) ^ (16 * v1)));
}
*v_0 = v0;
*v_1 = v1;
}
int main()
{
srand(0x7e8);
uint32_t key[4] = {6651, 15290, 20313, 4631};
for (int i = 0; i < 4; i++)
key[i] = rand();
uint32_t enc[8] =
{
2362123044u,
161969178u,
4215039885u,
4127734393u,
4056687643u,
972708338u,
4071350732u,
387489650u};
for (int j = 6; j >= 0; j--)
xteaDecrypt(&enc[j], &enc[j + 1], key);
for (int j = 0; j < 32; j += 4)
{
for (int i = j + 3; i >= j; i--)
printf("%c", ((char *)enc)[i]);
}

return 0;
}

HZNUCTF{ae6-9f57-4b74-b423-98eb}

conforand

die检测有ollvm,先尝试用d810解一下
解完还是很丑,每个函数都有几百行,但是出题人没删符号表,定位几个关键点

众所周知rc4跑两次等于没跑,所以我们尝试用frida去手动调用rc4
检查initbox的部分,发现引入了随机数种子,且种子未给出




但注意到这个随机数的范围被限定在了0~255(S盒里的元素是uint8),且rand只调用了这一次
所以我们可以hook rand的返回值,爆破所有情况
写frida脚本

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
var lib = Process.findModuleByName("conforand");
var sym = lib.enumerateSymbols();
for (var i = 0; i < sym.length; i++) {
console.log(sym[i].name);
}
var randPtr = Module.findExportByName("libc.so.6", "rand");
console.log("randPtr: " + randPtr);

var rc4Ptr = lib.findSymbolByName("rc4");
var rand = new NativeFunction(randPtr, 'int', []);
var rc4 = new NativeFunction(rc4Ptr, 'void', ['pointer', 'int', 'pointer', 'int']);
var rc4Key = Memory.alloc(9);
rc4Key.writeUtf8String("JustDoIt!");
let randRet = 0
Interceptor.attach(randPtr, {
onLeave: function (retval) {
retval.replace(randRet);
}
})
for (let i = 0; i < 256; i++) {
randRet = i;
// console.log(rand());
var Input = Memory.alloc(42);
Input.writeByteArray([0x83, 0x1e, 0x9c, 0x48, 0x7a, 0xfa, 0xe8, 0x88,
0x36, 0xd5, 0x0a, 0x08, 0xf6, 0xa7, 0x70, 0x0f,
0xfd, 0x67, 0xdd, 0xd4, 0x3c, 0xa7, 0xed, 0x8d,
0x51, 0x10, 0xce, 0x6a, 0x9e, 0x56, 0x57, 0x83,
0x56, 0xe7, 0x67, 0x9a, 0x67, 0x22, 0x24, 0x6e,
0xcd, 0x2f]);
rc4(Input, 42, rc4Key, 9);
try {
console.log(Input.readUtf8String(41));
} catch (e) {
console.log("Error: " + e);
}
}
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
import frida
import sys
import time


def onMessage(message, data):
if message['type'] == 'send':
print("[!!] Message from target: ", message['payload'])
else:
print("Not a send type Message", message['stack'])


with open('FridaScript.js', "r", encoding="utf-8") as f:
jscode = f.read()

targetProcessPackName = "./conforand"
device = frida.get_local_device()
try:
pid = device.spawn(targetProcessPackName)
session = device.attach(pid)
print(pid)
except frida.ProcessNotFoundError:
print("No such process")
sys.exit(0)

device.resume(pid)
time.sleep(2)
# input("continue")
script = session.create_script(jscode)
script.on('message', onMessage)

script.load()
# device.resume(pid)

sys.stdin.read()

解出HZNUCTF{489b88-1305-411e-b1f4-88a3070a73}

Author

SGSG

Posted on

2025-04-14

Updated on

2025-04-18

Licensed under