2025 GHCTF RE wp

前言:RE的最后一题没有写 混淆和花太多 而且出题人的wp写的十分详细 我就不献丑了

ASM?Signin!

直接扔给deekseek 得到c语言代码

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
#include <stdio.h>

#include <stdint.h>

#include <string.h>



// 初始化数据段

uint8_t DATA1[] = {

    0x26, 0x27, 0x24, 0x25, 0x2A, 0x2B, 0x28, 0x00,

    0x2E, 0x2F, 0x2C, 0x2D, 0x32, 0x33, 0x30, 0x00,

    0x36, 0x37, 0x34, 0x35, 0x3A, 0x3B, 0x38, 0x39,

    0x3E, 0x3F, 0x3C, 0x3D, 0x3F, 0x27, 0x34, 0x11

};



uint8_t DATA2[] = {

    0x69, 0x77, 0x77, 0x66, 0x73, 0x72, 0x4F, 0x46,

    0x03, 0x47, 0x6F, 0x79, 0x07, 0x41, 0x13, 0x47,

    0x5E, 0x67, 0x5F, 0x09, 0x0F, 0x58, 0x63, 0x7D,

    0x5F, 0x77, 0x68, 0x35, 0x62, 0x0D, 0x0D, 0x50

};



uint8_t BUFFER1[35] = {0}; // 33字节缓冲区+2字节头信息



// 数据交换函数

void swap_blocks(int src, int dest) {

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

        uint8_t tmp = DATA1[src + i];

        DATA1[src + i] = DATA1[dest + i];

        DATA1[dest + i] = tmp;

    }

}



// DATA1预处理函数

void preprocess_data1() {

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

        int src = i * 4;

        int dest = src + 4;

        if (dest >= 28) {

            dest -= 28;

        }

        swap_blocks(src, dest);

    }

}



// 加密函数

void encrypt_buffer() {

    uint8_t *buf = BUFFER1 + 2; // 跳过输入头信息

    uint8_t *key = DATA1;



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

        // 构造16位密钥字(小端模式)

        uint16_t key1 = (key[2] << 8) | key[1];

        uint16_t key2 = (key[3] << 8) | key[2];



        // 处理前两个字节

        uint16_t val = *((uint16_t*)buf);

        val ^= key1;

        memcpy(buf, &val, sizeof(val));



        // 处理后两个字节

        val = *((uint16_t*)(buf + 2));

        val ^= key2;

        memcpy(buf + 2, &val, sizeof(val));



        buf += 4;

        key += 4;

    }

}



int main() {

    printf("Welcome to GHCTF!\nInput your flag:");

    // 模拟DOS 0AH功能调用

    fgets((char*)BUFFER1 + 2, sizeof(BUFFER1) - 2, stdin);

    size_t len = strlen((char*)BUFFER1 + 2);

    if (len > 0 && BUFFER1[len + 1] == '\n')

        BUFFER1[len + 1] = 0;



    preprocess_data1(); // 预处理DATA1

    encrypt_buffer();   // 加密输入



    // 验证加密结果

    if (memcmp(BUFFER1 + 2, DATA2, 32) == 0) {

        printf("\nRight!\n");

    } else {

        printf("\nWrong!\n");

    }



    return 0;

}

直接对照着写解密即可

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
key = bytearray([

    0x26, 0x27, 0x24, 0x25, 0x2A, 0x2B, 0x28, 0x00,

    0x2E, 0x2F, 0x2C, 0x2D, 0x32, 0x33, 0x30, 0x00,

    0x36, 0x37, 0x34, 0x35, 0x3A, 0x3B, 0x38, 0x39,

    0x3E, 0x3F, 0x3C, 0x3D, 0x3F, 0x27, 0x34, 0x11

])



def swap_blocks(data, src, dest):

    data[src:src+4], data[dest:dest+4] = data[dest:dest+4], data[src:src+4]



for i in range(8):

    src = i * 4

    dest = src + 4

    if dest >= 28:

        dest -= 28

    swap_blocks(key, src, dest)



enc = bytearray([

    0x69, 0x77, 0x77, 0x66, 0x73, 0x72, 0x4F, 0x46,

    0x03, 0x47, 0x6F, 0x79, 0x07, 0x41, 0x13, 0x47,

    0x5E, 0x67, 0x5F, 0x09, 0x0F, 0x58, 0x63, 0x7D,

    0x5F, 0x77, 0x68, 0x35, 0x62, 0x0D, 0x0D, 0x50

])



flag = bytearray()

for i in range(8):

    block = enc[i*4:(i+1)*4]

    k = key[i*4:(i+1)*4]



    key1 = (k[2] << 8) | k[1]  

    key2 = (k[3] << 8) | k[2]



    enc1 = block[0] | (block[1] << 8)

    dec1 = enc1 ^ key1

    flag.append(dec1 & 0xFF)

    flag.append((dec1 >> 8) & 0xFF)

    # 解密后两个字节

    enc2 = block[2] | (block[3] << 8)

    dec2 = enc2 ^ key2

    flag.append(dec2 & 0xFF)

    flag.append((dec2 >> 8) & 0xFF)



print(flag.decode('latin-1').strip('\x00'))
#NSSCTF{W0w_y0u're_g00d_@t_@5M!!}

FishingKit

ida打开附件 逻辑很清晰

FishingKit

z3可以求出key

FishingKit-1

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

a1 = [BitVec(f'a1[{i}]',8) for i in range(10)]
solver = Solver()
solver.add(202 * a1[8] + 216 * a1[5] - 4 * a1[4] - 330 * a1[9] - 13 * a1[4] - 268 * a1[6] == 0xFFFFC57A)
solver.add(325 * a1[8] + 195 * a1[0] + 229 * a1[1] - 121 * a1[6] - 409 * a1[6] - (a1[1] << 7) == 0x584E)
solver.add(489 * a1[1] + 480 * a1[6] + 105 * a1[2] + 367 * a1[3] - 135 * a1[4] - 482 * a1[9] == 0xF704)
solver.add(493 * a1[1] - 80 * a1[4] - 253 * a1[8] - 121 * a1[2] - 177 * a1[0] - 243 * a1[9] == 0xFFFF6510)
solver.add(275 * a1[4] + 271 * a1[6] + 473 * a1[7] - 72 * a1[5] - 260 * a1[4] - 367 * a1[4] == 0x37AF)
solver.add(286 * a1[0] + 196 * a1[7] + 483 * a1[2] + 442 * a1[1] - 495 * a1[8] - 351 * a1[4] == 0xA0D3)
solver.add(212 * a1[2] + 283 * a1[7] - 329 * a1[8] - 429 * a1[9] - 362 * a1[2] - 261 * a1[6] == 0xFFFE9F54)
solver.add(456 * a1[5] + 244 * a1[7] + 92 * a1[4] + 348 * a1[7] - 225 * a1[1] - 31 * a1[2] == 0x1597F)
solver.add(238 * a1[9] + 278 * a1[7] + 216 * a1[6] + 237 * a1[0] + 8 * a1[2] - 17 * a1[9] == 0x1477E)
solver.add(323 * a1[9] + 121 * a1[1] + 370 * a1[7] - (a1[4] << 6) - 196 * a1[9] - 422 * a1[0] == 0x6763)
solver.add(166 * a1[9] + 90 * a1[1] + 499 * a1[2] + 301 * a1[8] - 31 * a1[2] - 206 * a1[2] == 0x158B7)
solver.add(355 * a1[0] + 282 * a1[4] + 44 * a1[9] + 359 * a1[8] - 167 * a1[5] - 62 * a1[3] == 0x12B72)
solver.add(488 * a1[6] + 379 * a1[9] + 318 * a1[2] - 85 * a1[1] - 357 * a1[2] - 277 * a1[5] == 0x8A46)
solver.add(40 * a1[0] + 281 * a1[4] + 217 * a1[5] - 241 * a1[1] - 407 * a1[7] - 309 * a1[7] == 0xFFFF7594)
solver.add(429 * a1[3] + 441 * a1[3] + 115 * a1[1] + 96 * a1[8] + 464 * a1[1] - 133 * a1[7] == 0x26708)
solutions = []
while solver.check() == sat:
model = solver.model()
solution = [model[a1[i]].as_long() for i in range(10)]
solutions.append(solution)
solver.add(Or([a1[i] != solution[i] for i in range(10)]))
for sol in solutions:
for i in range(10):
print(chr(sol[i]),end='')
#DeluxeBait

随后是一个异或0x14的rc4

FishingKit-2

写脚本求出的是fake的flag
NSSCTF{Fake!Fake!Fake!}
随后看了看左边的函数 发现了一个函数很奇怪

FishingKit-3

随后打个断点慢慢调试 发现在main函数调用之前进行了一个跳转

FishingKit-4

继续跟进就是之前那个奇怪的函数 交叉引用函数以及变量发现了三个tea的加密

FishingKit-5

密文就是之前奇怪的函数里的值
0xA6975621, 0xDEC4D51A, 0x4D829CA4, 0x56C845D1, 0x5C96B4A7, 0x2087494D
key就是最开始z3求出来的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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <stdio.h>

#include <stdint.h>

#include <string.h>



void tea_decrypt(uint32_t *v0, uint32_t *v1, uint32_t key[4]) {

    uint32_t delta = 1719109785;

    uint32_t sum = delta * 24;

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

        *v1 -= ((key[(sum >> 11) & 3] + sum) ^ (*v0 + ((*v0 >> 5) ^ (*v0 * 16))));

        sum -= delta;

        *v0 -= ((key[sum & 3] + sum) ^ (*v1 + ((*v1 >> 5) ^ (*v1 * 16))));

    }

}



int main() {

    uint32_t enc[6] = {0xA6975621, 0xDEC4D51A, 0x4D829CA4, 0x56C845D1, 0x5C96B4A7, 0x2087494D};

    uint32_t key[4] = {0x756C6544, 0x61426578, 0x7469, 0};

    uint32_t flag[6];

    tea_decrypt(&enc[0], &enc[1], key);

    tea_decrypt(&enc[2], &enc[3], key);

    tea_decrypt(&enc[4], &enc[5], key);

    memcpy(flag, enc, sizeof(enc));

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

        uint8_t *bytes = (uint8_t *)&flag[i];

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

            printf("%c", bytes[j]);  // 按小端序输出字符

        }

    }

    return 0;

}
//NSSCTF{Wh@t_@_b1g_F1sh}

LockedSecret

首先查壳 修改一下特征值 即可upx -d脱掉

LockedSecret

ida打开 逻辑很清晰 flag长度32

LockedSecret-1

跟进encry函数 进行了很多自定义的加密 带有混淆 很难看

LockedSecret-2

随后一点一点分析下面的逻辑 类似于tea加密 扔给deepseek 简化一下

LockedSecret-6

把这些看成一个整体 分为flag1和2

LockedSecret-8

随后对照着加密 写出解密脚本即可 需要注意这个地方多处理一下即可

LockedSecret-7

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
#include <stdio.h>

#include <stdint.h>

#include <string.h>



uint32_t key[4] = {0x423DF72D, 0x05F59A01, 0x633FCF1D, 0x77D19122};

uint32_t enc[8] = {0x031e45dc, 0x2776e989, 0x01234847, 0x64ced270,

                   0x33467fda, 0xa34903b1, 0x2cd10027, 0x75bdb337};



uint32_t tea1(uint32_t m, int32_t sum) {

    m &= 0xffffffff;

    return ((key[1] + (m >> 5)) & 0xffffffff) ^ ((m + sum) & 0xffffffff) ^ ((key[0] + 16 * m) & 0xffffffff);

}



uint32_t tea2(uint32_t m, int32_t sum) {

    m &= 0xffffffff;

    return ((key[3] + (m >> 5)) & 0xffffffff) ^ ((m + sum) & 0xffffffff) ^ ((key[2] + 16 * m) & 0xffffffff);

}



int main() {

    uint32_t flag[8];

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

        uint32_t flag_1 = enc[2 * i] ^ 0xf;

        uint32_t flag_2 = enc[2 * i + 1] ^ 0xf;



        uint32_t v2 = (flag_2 - tea2(flag_1, -249839624)) & 0xffffffff;

        uint32_t v6 = (flag_1 - tea1(v2, -249839624)) & 0xffffffff;

        uint32_t v7 = (v2 - tea2(v6, -1829222407)) & 0xffffffff;

        uint32_t v8 = (v6 - tea1(v7, -1829222407)) & 0xffffffff;

        uint32_t v9 = (v7 - tea2(v8, 886362106)) & 0xffffffff;

        uint32_t v10 = (v8 - tea1(v9, 886362106)) & 0xffffffff;

        uint32_t v11 = (v9 - tea2(v10, -693020677)) & 0xffffffff;

        uint32_t v1 = (v10 - tea1(v11, -693020677)) & 0xffffffff;

        uint32_t v12 = (v11 - tea2(v1, 2022563836)) & 0xffffffff;

        uint32_t v100 = (v1 - tea1(v12, 2022563836)) & 0xffffffff;

        uint32_t v13 = (v12 - tea2(v100, 443181053)) & 0xffffffff;

        uint32_t v14 = (v100 - tea1(v13, 443181053)) & 0xffffffff;

        uint32_t v15 = (v13 - tea2(v14, -1136201730)) & 0xffffffff;

        uint32_t v16 = (v14 - tea1(v15, -1136201730)) & 0xffffffff;

        uint32_t v17 = (v15 - tea2(v16, 1579382783)) & 0xffffffff;

        flag[2 * i] = (v16 - tea1(v17, 1579382783)) & 0xffffffff;

        flag[2 * i + 1] = v17;

    }



    uint8_t byte_data[32];

    memcpy(byte_data, flag, sizeof(flag));



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

        if (byte_data[i] >= 32 && byte_data[i] <= 126) {

            printf("%c", byte_data[i]);

        }

    }

    return 0;

}

//NSSCTF{!!!Y0u_g3t_th3_s3cr3t!!!}

Mio?Ryo?Soyo?

python的exe 使用pyinstxtractor解包 注意版本是3.8
随后找个在线网站反编译 看反编译出来的代码 变量名很长 先修改一下

MioRyoSoyo1

主要就是标准的base85 修改了码表的base45 还有凯撒
随后看密文以及加密过程

MioRyoSoyo

加密是base45-凯撒移位7-base85-凯撒移位9
直接写逆向脚本即可

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
def caesar(enc, key):

    bbb = []

    for a in enc:

        if "a" <= a <= "z":

            bbb.append(chr(ord("a")+(ord(a) - ord("a") - key) % 26))

        elif "0" <= a <= "9":

            bbb.append(chr(ord("0") + (ord(a) - ord("0") + key) % 10))

        else:

            bbb.append(a)

    return "".join(bbb)

def base45_decode(encoded_str, custom_alphabet):

    if len(custom_alphabet) != 45:

        raise ValueError("Base45 码表长度必须为 45")

    char_to_index = {char: idx for idx, char in enumerate(custom_alphabet)}

    n = len(encoded_str)

    decoded_bytes = []

    i = 0



    while i < n:

        if i + 3 <= n:

            c0, c1, c2 = encoded_str[i], encoded_str[i + 1], encoded_str[i + 2]

            value = (char_to_index[c0] + char_to_index[c1] * 45 + char_to_index[c2] * 45 * 45)

            decoded_bytes.extend(value.to_bytes(2, "big"))

            i += 3

        elif i + 2 <= n:

            c0, c1 = encoded_str[i], encoded_str[i + 1]

            value = char_to_index[c0] + char_to_index[c1] * 45

            if value > 0xFF:

                raise ValueError("无效的 Base45 编码:剩余字符解码后超出 1 字节范围")

            decoded_bytes.extend(value.to_bytes(1, "big"))

            i += 2

        else:

            raise ValueError("无效的 Base45 编码:字符串长度不符合要求")

    return bytes(decoded_bytes)

base45_table = "0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\"



import base64

enc = '9v!rD8us"44_N(1;_U?z6!Mn16"mjz<\l[=3*>#&4C>zt0L2C3;)z--3Z'

enc1 = base64.a85decode(caesar(enc,9)).decode()

flag = base45_decode(caesar(enc1, 7),base45_table).decode()

print(flag)
#NSSCTF{Th3y'r3_a11_p1aY_Ba5e!}

TimeSpaceRescue

根据题目描述 猜测需要时间戳爆破 范围是整个2024年
ida打开附件 有反调试 nop掉即可

TimeSpaceRescue-1

进入主函数 逻辑很清晰

TimeSpaceRescue

输入flag 根据当前时间的年月日 md5之后生成key 再根据key解密魔改的AES
跟进key_encry函数 有花 nop掉

TimeSpaceRescue-2

随后看sub_211210函数 找到魔改点

TimeSpaceRescue-3

10A0就是xor 0x5然后交换位置 1030就是xor 0xf然后交换位置 直接根据这些约束 写脚本爆破即可 爆破思路是 先爆破前16位 如果有NSSCTF字样则是正确的key 再根据这个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
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
import hashlib  
from Crypto.Cipher import AES

def sub_2110A0(data):
data = bytearray(data)
for i in range(0, 16, 2):
if i + 1 >= len(data):
break
a = data[i] ^ 0x05
b = data[i + 1] ^ 0x05
data[i], data[i + 1] = b, a
return bytes(data)


def sub_211030(data):
data = bytearray(data)
n = len(data)
for i in range(n // 2):
j = n - 1 - i
a = data[i] ^ 0x0F
b = data[j] ^ 0x0F
data[i], data[j] = b, a
return bytes(data)


def generate_key(input_data):
md5 = hashlib.md5(input_data).digest()
key = bytes([(b ^ 0x14) ^ 0x11 for b in md5])
return key


def aes_decrypt(key, ciphertext):
cipher = AES.new(key, AES.MODE_ECB)
try:
plaintext = cipher.decrypt(ciphertext)
except ValueError as e:
print(f"Decryption error: {e}")
return b''
return plaintext


enc1 = bytes([0xcd, 0x16, 0xdb, 0xb5, 0xd1, 0x02, 0xa4, 0x82, 0x8e, 0x59, 0x73, 0x9e, 0x96, 0x26, 0x56, 0xf2])
days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
for month in range(12):
for day in range(1, days_in_month[month] + 1):
input_data = bytearray(12)
input_data[0:4] = day.to_bytes(4, 'little')
input_data[4:8] = month.to_bytes(4, 'little')
input_data[8:12] = (124).to_bytes(4, 'little')
key = generate_key(bytes(input_data))
encrypted_key = sub_2110A0(key)
processed_cipher = sub_2110A0(enc1)
decrypted = aes_decrypt(encrypted_key, processed_cipher)
if not decrypted:
continue
flag1 = sub_211030(decrypted)
if b'NSSCTF' in flag1:
print(day, month + 1)
print(encrypted_key)
print(flag1.decode('latin-1')) # NSSCTF{W0w_Y0u'r

enc = bytes([
0x16, 0x8e, 0x46, 0xf2, 0x55, 0x7b, 0x92, 0x31,
0x30, 0xdc, 0xaa, 0x8a, 0xf3, 0x1c, 0xa0, 0xaa
])
key = b'\n\xe4\xfbFF+YTF+k\x87t&\x91\x07'
processed_cipher = sub_2110A0(enc)
decrypted = aes_decrypt(key, processed_cipher)
flag2 = sub_211030(decrypted)
print(flag2.decode('latin-1',(errors='replace'))
#NSSCTF{W0w_Y0u're_@n_AE5_M@5t3r}

Room 0

ida打开附件 有花 先去掉

Room

Room0

Room0-1

retn jzjnz花 直接nop掉 随后看主函数 逻辑很清晰

Room0-2

首先计算key key需要爆破 运算后的smc是一个函数 开头就是 push ebp mov ebp esp
所以前三位固定 爆破第四位 后来根据hint 触发除数为零的异常去爆破

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
#include <stdio.h>

#include <stdlib.h>

int main()

{

    int v2;          // [esp+0h] [ebp-1Ch]

    int i;           // [esp+4h] [ebp-18h]

    int v4;          // [esp+8h] [ebp-14h]

    int v5;          // [esp+Ch] [ebp-10h]

    int v6;          // [esp+10h] [ebp-Ch]

    int v7;          // [esp+10h] [ebp-Ch]

    unsigned int v8; // [esp+14h] [ebp-8h]

    int v9;

    int key;

    v9 = 0x755ff000;

    for (i = 0; i < 0xff, v9 < 0x755ff0ff; i++, v9++)

    {

        v6 = 0;

        v8 = 0x75;

        v5 = 0x5f;

        v4 = 0xf0;

        key = v9;

        for (int j = 0; j < 32; ++j)

        {

            v5 = (key + v5) ^ (8 * v4);

            v4 = (key + v8) ^ (8 * v5);

            v8 = (key + v4) ^ (8 * v5);

            key -= v4 + v5 + v8;

            if ((key - 1415881080) == 0)

            {

                printf("%x", v9);

            }

        }

    }

}//755ff0d3

得到key之后 打个断点 触发异常跳到smc段 也是retn jzjnz花 去掉即可

Room0-3

得到这么一个函数

Room0-4

像是个魔改的rc4 最后的加密就是个异或 所以提取一下输入的密文加密后的值 得到密钥流异或密文即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
print("A" * 32)  
get = [0x2D, 0xD6, 0xB2, 0x58, 0xCB, 0xEA, 0x58, 0x56, 0x0A, 0xD7,
0x1F, 0x95, 0x77, 0x95, 0x15, 0xFD, 0x52, 0xBF, 0x4C, 0x4A,
0x80, 0xA5, 0x61, 0xEB, 0x3A, 0xA9, 0x44, 0xE8, 0x01, 0xAC,
0xBC, 0x95]
enc = [0x22, 0xC4, 0xA0, 0x5A, 0xDE, 0xED, 0x62, 0x5E, 0x25, 0xE2,
0x6D, 0xA6, 0x05, 0xA7, 0x20, 0x8D, 0x7D, 0x99, 0x52, 0x3E,
0x8C, 0xA7, 0x7F, 0xFA, 0x09, 0xD8, 0x62, 0xDB, 0x00, 0x80,
0xC2, 0xA9]
keystream = [(ord("A") ^ i) & 0xff for i in get]
for i in range(len(enc)):
enc[i] ^= keystream[i]
enc[i] &= 0xff
print("".join(map(chr, enc)))
#NSSCTF{Int3r3st1ng_5MC_Pr0gr@m?}

Canon

ida打开附件 逻辑很清晰 直接起调试dump下来加密的各个case 没用到case2 7 所以不用分析

Canon

注意加密的base64是换表的

Canon-4

起调试dump下来

Canon-2

stuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr
逆回去写解密即可

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
import base64  
import math

def rc4_decrypt(data, key):
key = key.encode()
S = list(range(256))
j = 0
out = []

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

i = j = 0
for char in data:
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
out.append(((char - 0x39) ^ S[(S[i] + S[j]) % 256]) % 256)
return bytes(out)


def decrypt(a1, a2, a3):
v9 = len(a1)
v21 = len(a2)
if a3 == 1:
output = ''
for i in range(v9):
v22 = ord(a2[i % v21])
if 'A' <= a1[i] <= 'Z':
offset = (ord(a1[i]) - ord('A') - v22) % 26
output += chr(offset + ord('A'))
elif 'a' <= a1[i] <= 'z':
offset = (ord(a1[i]) - ord('a') - v22) % 26
output += chr(offset + ord('a'))
elif '0' <= a1[i] <= '9':
offset = (ord(a1[i]) - ord('0') - v22) % 10
output += chr(offset + ord('0'))
else:
output += a1[i]
return output


elif a3 == 3:
Ek = ord(a2[0]) % 10 + 2
cipher_len = v9
Dk = cipher_len // Ek
remainder = cipher_len % Ek
if remainder == 0:
steps = [Dk] * Ek
else:
big_step = math.ceil(cipher_len / Ek)
small_step = cipher_len // Ek
steps = [big_step] * remainder + [small_step] * (Ek - remainder)
output = []
max_columns = math.ceil(cipher_len / Ek)
for n_column in range(max_columns):
count = 0
for step in steps:
pos = n_column + count
if pos >= cipher_len:
break
output.append(a1[pos])
count += step
return ''.join(output)

elif a3 == 4:
v25 = ord(a2[0]) % 10 + 2
output = a1[v25:] + a1[:v25]
return output


elif a3 == 5:
tableBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
tableNew = "stuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr"
decoded = base64.b64decode(a1.translate(str.maketrans(tableNew, tableBase64)))
output = ''
for i, char in enumerate(decoded):
output += chr(char ^ ord(a2[i % v21]) + 57)
return output

elif a3 == 6:
tableBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
tableNew = "stuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr"
decoded = base64.b64decode(a1.translate(str.maketrans(tableNew, tableBase64)))
output = rc4_decrypt(decoded, a2)
return output.decode()


enc1 = "WgvDmssEvcY326bHo3nNro3vXvvfmgrz"
enc2 = "gX+Ri9PG=bt5=00B6hscPQOL"
enc3 = "T6bHgUPL2gXUd=xT=FNHtPzV"

enc3 = decrypt(enc3, enc1, 1)
enc2 = decrypt(enc2, enc3, 4)
enc1 = decrypt(enc1, enc2, 5)

enc3 = decrypt(enc3, enc1, 4)
enc2 = decrypt(enc2, enc3, 1)
enc1 = decrypt(enc1, enc2, 4)

enc3 = decrypt(enc3, enc1, 3)
enc2 = decrypt(enc2, enc3, 4)
enc1 = decrypt(enc1, enc2, 1)

enc3 = decrypt(enc3, enc1, 6)
enc2 = decrypt(enc2, enc3, 3)
enc1 = decrypt(enc1, enc2, 4)

enc3 = decrypt(enc3, enc1, 5)
enc2 = decrypt(enc2, enc3, 6)
enc1 = decrypt(enc1, enc2, 3)

enc3 = decrypt(enc3, enc1, 1)
enc2 = decrypt(enc2, enc3, 5)
enc1 = decrypt(enc1, enc2, 6)

enc2 = decrypt(enc2, enc3, 1)
enc1 = decrypt(enc1, enc2, 5)
enc1 = decrypt(enc1, enc2, 1)
#print(enc1)
#print(enc2)
#print(enc3)
print(enc1 + enc2 + enc3)
#NSSCTF{P4ch3Lbel's_C@n0n_1n_D_mAjOr}

腐蚀

ida打开附件 查看字符串 发现input.png 所以可以知道 是对enc进行了一些操作 得到png文件 根据字符串定位到函数

腐蚀

跟进sub_7FF7354652E0函数 发现256 最后还有xor 猜测是魔改的rc4 多xor了个0x1f

腐蚀-1

起个调试取出key
0x60, 0x82, 0xAE, 0x42, 0x4E, 0x44, 0x49, 0x45, 0x1A, 0x0A, 0x0D, 0x0A, 0x4E, 0x47, 0x89, 0x50
随后在内存中看见 对输入进行了倒叙操作 所以加密流程就是 rc4 倒叙 写逆向脚本即可

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
def rc4_decrypt(data, key):  
S = list(range(256))
j = 0
key_len = len(key)

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

i = j = 0
output = []

for k in range(len(data)):
i = (i + 1) % 256
j = (j + S[i]) % 256
S[i], S[j] = S[j], S[i]
output.append((data[k] ^ S[(S[i] + S[j]) % 256]) ^ 0x1F)

return bytes(output)

def decrypt_file(filename, key):
with open(filename, "rb") as f:
enc_data = f.read()
enc_data = enc_data[::-1]
decrypted_data = rc4_decrypt(enc_data, key)
return decrypted_data

filename = "./enc"
key = bytes([0x60, 0x82, 0xAE, 0x42, 0x4E, 0x44, 0x49, 0x45, 0x1A, 0x0A, 0x0D, 0x0A, 0x4E, 0x47, 0x89, 0x50])

decrypted_data = decrypt_file(filename, key)

with open("./1.png", "wb") as f:
f.write(decrypted_data)

打开图片得到flag

腐蚀-2