前言

比赛时间:2025.3.2 - 2025.3.8

官方WP:GHCTF 2025 Reverse Offical WP

质量非常好的新生赛!!Liv师傅好强

Reverse

ASM?Signin!

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

// 数据部分
unsigned char welcome_msg[] = "Welcome to GHCTF!\r\n";
unsigned char input_msg[] = "Input your flag:";
unsigned char wrong_msg[] = "Wrong!\r\n";
unsigned char right_msg[] = "Right!\r\n";

unsigned char 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};

unsigned char 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};

unsigned char buffer1[33]; // 用户输入缓冲区
unsigned char buffer2[33]; // 处理后数据缓冲区

// DO1 函数:交换 DATA1 中的某些字节
void do1()
{
int cx, di, si = 0;

for (int i = 0; i < 8; i++)
{
di = si;
di += 4;
if (di >= 28)
di -= 28;
do2(si, di);
si += 4;
}
}

// DO2 函数:交换 DATA1[BX] 和 DATA1[DI] 的 4 个字节
void do2(int bx, int di)
{
unsigned char temp[4];
memcpy(temp, &data1[bx], 4);
memcpy(&data1[bx], &data1[di], 4);
memcpy(&data1[di], temp, 4);
}

// ENC 函数:对 BUFFER1 中的数据进行某种处理
void enc()
{
int si, di, i;
unsigned char *buffer_ptr = buffer1 + 2;
unsigned char *data1_ptr = data1;

for (si = 0, di = 0; si < 8 * 4; si += 4, di += 4)
{
// XOR 操作
*(unsigned short *)(buffer_ptr + si) ^= *(unsigned short *)(data1_ptr + di + 1);
*(unsigned short *)(buffer_ptr + si + 2) ^= *(unsigned short *)(data1_ptr + di + 2);
}
}

void dec()
{
int si, di, i;
unsigned char *buffer_ptr = data2;
unsigned char *data1_ptr = data1;

for (si = 0, di = 0; si < 8 * 4; si += 4, di += 4)
{
// XOR 操作
*(unsigned short *)(buffer_ptr + si) ^= *(unsigned short *)(data1_ptr + di + 1);
*(unsigned short *)(buffer_ptr + si + 2) ^= *(unsigned short *)(data1_ptr + di + 2);
}
}

int main()
{
do1();
dec();
printf("%s\n", data2); // NSSCTF{W0w_y0u're_g00d_@t_@5M!!}
return 0;
}

FishingKit

先求解bait

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
from sympy import symbols, Eq, solve

# 定义符号变量
variables = symbols("x_0 x_1 x_2 x_3 x_4 x_5 x_6 x_7 x_8 x_9")
x_0, x_1, x_2, x_3, x_4, x_5, x_6, x_7, x_8, x_9 = variables

equations = [
Eq(202 * x_8 + 216 * x_5 - 4 * x_4 - 330 * x_9 - 13 * x_4 - 268 * x_6, -14982),
# Eq(325 * x_8 + 195 * x_0 + 229 * x_1 - 121 * x_6 - 409 * x_6 - (x_1 << 7), 22606),
Eq(489 * x_1 + 480 * x_6 + 105 * x_2 + 367 * x_3 - 135 * x_4 - 482 * x_9, 63236),
Eq(493 * x_1 - 80 * x_4 - 253 * x_8 - 121 * x_2 - 177 * x_0 - 243 * x_9, -39664),
Eq(275 * x_4 + 271 * x_6 + 473 * x_7 - 72 * x_5 - 260 * x_4 - 367 * x_4, 14255),
Eq(286 * x_0 + 196 * x_7 + 483 * x_2 + 442 * x_1 - 495 * x_8 - 351 * x_4, 41171),
Eq(212 * x_2 + 283 * x_7 - 329 * x_8 - 429 * x_9 - 362 * x_2 - 261 * x_6, -90284),
Eq(456 * x_5 + 244 * x_7 + 92 * x_4 + 348 * x_7 - 225 * x_1 - 31 * x_2, 88447),
Eq(238 * x_9 + 278 * x_7 + 216 * x_6 + 237 * x_0 + 8 * x_2 - 17 * x_9, 83838),
# Eq(323 * x_9 + 121 * x_1 + 370 * x_7 - (x_4 << 6) - 196 * x_9 - 422 * x_0, 26467),
Eq(166 * x_9 + 90 * x_1 + 499 * x_2 + 301 * x_8 - 31 * x_2 - 206 * x_2, 88247),
Eq(355 * x_0 + 282 * x_4 + 44 * x_9 + 359 * x_8 - 167 * x_5 - 62 * x_3, 76658),
Eq(488 * x_6 + 379 * x_9 + 318 * x_2 - 85 * x_1 - 357 * x_2 - 277 * x_5, 35398),
Eq(40 * x_0 + 281 * x_4 + 217 * x_5 - 241 * x_1 - 407 * x_7 - 309 * x_7, -35436),
Eq(429 * x_3 + 441 * x_3 + 115 * x_1 + 96 * x_8 + 464 * x_1 - 133 * x_7, 157448),
]


solutions = solve(equations, variables)

print([solutions[variables[i]] for i in range(10)])
print("".join(chr(solutions[variables[i]]) for i in range(10)))
# [68, 101, 108, 117, 120, 101, 66, 97, 105, 116]
# DeluxeBait

乍一看是个普通逆向,此时的strcmp还很正常,RC4解密即可。

造密码的,给我一个假flag,NSSCTF{Fake!Fake!Fake!}

经过我的动态调试,发现在之前程序做了一些鬼鬼祟祟的事情,看见了strcmp的字段。

果然,原来是给我搞了个偷天换日,步进到strcmp里面,竟别有洞天。

把tea加密给逆了就好了。

Ture flag: NSSCTF{Wh@t_@_b1g_F1sh}

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define _BYTE unsigned char

unsigned __int8 byte_1400060C0[256];

__int64 __fastcall init(const char *a1)
{
__int64 result; // rax
int k; // [rsp+20h] [rbp-138h]
unsigned int i; // [rsp+24h] [rbp-134h]
int j; // [rsp+28h] [rbp-130h]
int v5; // [rsp+2Ch] [rbp-12Ch]
unsigned int v6; // [rsp+30h] [rbp-128h]
unsigned __int8 v7[256]; // [rsp+40h] [rbp-118h] BYREF

for (i = 0; i < 0x100; ++i)
byte_1400060C0[i] = i;
v6 = strlen(a1);
result = 0LL;
memset(v7, 0, sizeof(v7));
for (j = 0; j < 256; ++j)
{
v7[j] = a1[j % v6];
result = (unsigned int)(j + 1);
}
v5 = 0;
for (k = 0; k < 256; ++k)
{
v5 = ((unsigned __int8)v7[k] + (unsigned __int8)byte_1400060C0[k] + v5) % 256;
_BYTE temp = byte_1400060C0[k];
byte_1400060C0[k] = byte_1400060C0[v5];
byte_1400060C0[v5] = temp;
result = (unsigned int)(k + 1);
}
return result;
}

__int64 __fastcall de(char *a1, __int64 a2)
{
__int64 result; // rax
unsigned __int8 v4; // [rsp+20h] [rbp-18h]
unsigned __int8 v5; // [rsp+21h] [rbp-17h]
unsigned int i; // [rsp+24h] [rbp-14h]
unsigned int v7; // [rsp+28h] [rbp-10h]

v4 = 23;
v5 = 32;
v7 = strlen(a1);
for (i = 22; (int)i >= 0; --i)
{
a1[i] = 0x14u ^ *(_BYTE *)(a2 + i) ^ byte_1400060C0[(unsigned __int8)(byte_1400060C0[v5] + byte_1400060C0[v4])];

_BYTE temp = byte_1400060C0[v4];
byte_1400060C0[v4] = byte_1400060C0[v5];
byte_1400060C0[v5] = temp;
v5 -= byte_1400060C0[v4--];
}
result = v7;
*(_BYTE *)(a2 + v7) = 0;
return result;
}

void tea(unsigned int *a1, unsigned int *a2, unsigned int *key)
{
unsigned int v6 = *a1, v9 = *a2;

unsigned int v3 = 0;

for (int k = 0; k < 24; ++k)
{
v6 += (key[v3 & 3] + v3) ^ (v9 + ((v9 >> 5) ^ (16 * v9)));
v3 += 1719109785;
v9 += (key[(v3 >> 11) & 3] + v3) ^ (v6 + ((v6 >> 5) ^ (16 * v6)));
printf("%x %x %x %x %x\n", v6, v9, v3, key[v3 & 3], key[(v3 >> 11) & 3]);
}
*a1 = v6;
*a2 = v9;
}

void de_tea(unsigned int *a1, unsigned int *a2, unsigned int *key)
{
unsigned int v6 = *a1, v9 = *a2;

unsigned int v3 = 2603929176;

for (int k = 0; k < 24; ++k)
{
v9 -= (key[(v3 >> 11) & 3] + v3) ^ (v6 + ((v6 >> 5) ^ (16 * v6)));
v3 -= 1719109785;
v6 -= (key[v3 & 3] + v3) ^ (v9 + ((v9 >> 5) ^ (16 * v9)));
}
*a1 = v6;
*a2 = v9;
}

int main()
{
unsigned char bait[] = {68, 101, 108, 117, 120, 101, 66, 97, 105, 116, 0, 0, 0, 0, 0, 0};
unsigned int *key = (unsigned int *)bait;
unsigned char test_flag[] = "NSSCTF{}";
printf("%s\n", bait);

unsigned char fake_flag[] = {
0xE9, 0x37, 0xF8, 0xE2, 0x0C, 0x0F, 0x3D, 0xB9,
0x5C, 0xA3, 0xDE, 0x2D, 0x55, 0x96, 0xDF, 0xA2,
0x35, 0xFE, 0xB3, 0xDD, 0x7F, 0x91, 0x3C, 0x00};

unsigned char enc[] = {
0x21, 0x56, 0x97, 0xA6, 0x1A, 0xD5, 0xC4, 0xDE,
0xA4, 0x9C, 0x82, 0x4D, 0xD1, 0x45, 0xC8, 0x56,
0xA7, 0xB4, 0x96, 0x5C, 0x4D, 0x49, 0x87, 0x20};

unsigned char ture_flag[] = {
0x21, 0x56, 0x97, 0xA6, 0x1A, 0xD5, 0xC4, 0xDE,
0xA4, 0x9C, 0x82, 0x4D, 0xD1, 0x45, 0xC8, 0x56,
0xA7, 0xB4, 0x96, 0x5C, 0x4D, 0x49, 0x87, 0x20};

unsigned __int8 v4 = 0;
unsigned __int8 v5 = 0;
init(bait);

for (int i = 0; i < 23; ++i)
{
v5 += byte_1400060C0[++v4];
_BYTE temp = byte_1400060C0[v4];
byte_1400060C0[v4] = byte_1400060C0[v5];
byte_1400060C0[v5] = temp;
}

de(fake_flag, (__int64)&fake_flag);
printf("Fake flag: %s\n", fake_flag);

for (int i = 0; i < 24; i++)
{
ture_flag[i] = enc[i];
}
key = (unsigned int *)bait;
for (int i = 0; i < 3; ++i)
{
de_tea((unsigned int *)(ture_flag + 8 * i), (unsigned int *)(ture_flag + 8 * i + 4), key);
}
printf("Ture flag: %s\n", ture_flag);
// DeluxeBait
// Fake flag: NSSCTF{Fake!Fake!Fake!}
// Ture flag: NSSCTF{Wh@t_@_b1g_F1sh}

return 0;
}

LockedSecret

脱壳,逆向。程序的逻辑:确保flag长度为32,生成dword_3543D8(推测是rand()),再根据它生成key,经过恶心的加密,最后比较加密后的flag。

加密函数大概长这个样子

现在我掌握了动态调试之力,程序能自己跑的一定不会自己算。经过我的不懈的坐牢,关键参数也是挖出来了。

1
2
3
4
v20 = 0x423DF72D;
v21 = 0x05F59A01;
v22 = 0x633FCF1D;
v23 = 0x77D19122;

再花亿点时间构造调试解密函数。

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

#define LOBYTE(x) ((unsigned char)(x))
#define _DWORD unsigned int

int dword_3543D8[8] = {0x31323334, 0x35363738, 0x39414243, 0x44454647, 0x48494A4B, 0x4C4D4E4F, 0x50515253, 0x54555657};

int __cdecl sub_351190(int a1)
{
unsigned int v1; // ecx
unsigned int v2; // eax
int result; // eax
int v4; // [esp+0h] [ebp-7Ch]
unsigned int v5; // [esp+4h] [ebp-78h]
unsigned int v6; // [esp+14h] [ebp-68h]
unsigned int v7; // [esp+18h] [ebp-64h]
unsigned int v8; // [esp+1Ch] [ebp-60h]
unsigned int v9; // [esp+20h] [ebp-5Ch]
unsigned int v10; // [esp+24h] [ebp-58h]
unsigned int v11; // [esp+28h] [ebp-54h]
unsigned int v12; // [esp+30h] [ebp-4Ch]
unsigned int v13; // [esp+38h] [ebp-44h]
unsigned int v14; // [esp+3Ch] [ebp-40h]
unsigned int v15; // [esp+40h] [ebp-3Ch]
unsigned int v16; // [esp+44h] [ebp-38h]
unsigned int v17; // [esp+48h] [ebp-34h]
int i; // [esp+4Ch] [ebp-30h]
int v19; // [esp+50h] [ebp-2Ch]
int v20; // [esp+54h] [ebp-28h] BYREF
int v21; // [esp+58h] [ebp-24h]
int v22; // [esp+5Ch] [ebp-20h]
int v23; // [esp+60h] [ebp-1Ch]

v20 = 0x423DF72D;
v21 = 0x05F59A01;
v22 = 0x633FCF1D;
v23 = 0x77D19122;

v19 = 4;
do
{
printf("%d %x %x\n", v19, *(_DWORD *)(a1 + 8 * (4 - v19)), *(_DWORD *)(a1 + 8 * (4 - v19) + 4));
v17 = *(_DWORD *)(a1 + 8 * (4 - v19) + 4);
v16 = *(_DWORD *)(a1 + 8 * (4 - v19)) + ((v21 + (v17 >> 5)) ^ (v17 + 1579382783) ^ (v20 + 16 * v17));
v15 = v17 + ((v23 + (v16 >> 5)) ^ (v16 + 1579382783) ^ (v22 + 16 * v16));
v14 = v16 + ((v21 + (v15 >> 5)) ^ (v15 - 1136201730) ^ (v20 + 16 * v15));
v13 = v15 + ((v23 + (v14 >> 5)) ^ (v14 - 1136201730) ^ (v22 + 16 * v14));
v12 = v13 + ((v23 + ((v14 + ((v21 + (v13 >> 5)) ^ (v13 + 443181053) ^ (v20 + 16 * v13))) >> 5)) ^ (v14 + ((v21 + (v13 >> 5)) ^ (v13 + 443181053) ^ (v20 + 16 * v13)) + 443181053) ^ (v22 + 16 * (v14 + ((v21 + (v13 >> 5)) ^ (v13 + 443181053) ^ (v20 + 16 * v13)))));
v1 = v14 + ((v21 + (v13 >> 5)) ^ (v13 + 443181053) ^ (v20 + 16 * v13)) + ((v21 + (v12 >> 5)) ^ (v12 + 2022563836) ^ (v20 + 16 * v12));
v11 = v12 + ((v23 + (v1 >> 5)) ^ (v1 + 2022563836) ^ (v22 + 16 * v1));
v10 = v1 + ((v21 + (v11 >> 5)) ^ (v11 - 693020677) ^ (v20 + 16 * v11));
v9 = v11 + ((v23 + (v10 >> 5)) ^ (v10 - 693020677) ^ (v22 + 16 * v10));
v8 = v10 + ((v21 + (v9 >> 5)) ^ (v9 + 886362106) ^ (v20 + 16 * v9));
v7 = v9 + ((v23 + (v8 >> 5)) ^ (v8 + 886362106) ^ (v22 + 16 * v8));
v6 = v8 + ((v21 + (v7 >> 5)) ^ (v7 - 1829222407) ^ (v20 + 16 * v7));
v2 = v7 + ((v23 + (v6 >> 5)) ^ (v6 - 1829222407) ^ (v22 + 16 * v6));
v5 = v2 + ((v23 + ((v6 + ((v21 + (v2 >> 5)) ^ (v2 - 249839624) ^ (v20 + 16 * v2))) >> 5)) ^ (v6 + ((v21 + (v2 >> 5)) ^ (v2 - 249839624) ^ (v20 + 16 * v2)) - 249839624) ^ (v22 + 16 * (v6 + ((v21 + (v2 >> 5)) ^ (v2 - 249839624) ^ (v20 + 16 * v2)))));
// *(_DWORD *)(a1 + 8 * (4 - v19)) = (v6 + ((v21 + (v2 >> 5)) ^ (v2 - 249839624) ^ (v20 + 16 * v2))) ^ 0xF;
// *(_DWORD *)(a1 + 8 * (4 - v19) + 4) = v5 ^ 0xF;
printf("%d %x %x %x\n", v19, v17, v6, v5);
v4 = v19;
// printf("%s", a1);
result = --v19;
} while (v4 > 1);
return result;
}

int __cdecl de(int a1)
{
unsigned int v1; // ecx
unsigned int v2; // eax
int result; // eax
int v4; // [esp+0h] [ebp-7Ch]
unsigned int v5; // [esp+4h] [ebp-78h]
unsigned int v6; // [esp+14h] [ebp-68h]
unsigned int v7; // [esp+18h] [ebp-64h]
unsigned int v8; // [esp+1Ch] [ebp-60h]
unsigned int v9; // [esp+20h] [ebp-5Ch]
unsigned int v10; // [esp+24h] [ebp-58h]
unsigned int v11; // [esp+28h] [ebp-54h]
unsigned int v12; // [esp+30h] [ebp-4Ch]
unsigned int v13; // [esp+38h] [ebp-44h]
unsigned int v14; // [esp+3Ch] [ebp-40h]
unsigned int v15; // [esp+40h] [ebp-3Ch]
unsigned int v16; // [esp+44h] [ebp-38h]
unsigned int v17; // [esp+48h] [ebp-34h]
int i; // [esp+4Ch] [ebp-30h]
int v19; // [esp+50h] [ebp-2Ch]
int v20; // [esp+54h] [ebp-28h] BYREF
int v21; // [esp+58h] [ebp-24h]
int v22; // [esp+5Ch] [ebp-20h]
int v23; // [esp+60h] [ebp-1Ch]
unsigned int temp;

v20 = 0x423DF72D;
v21 = 0x05F59A01;
v22 = 0x633FCF1D;
v23 = 0x77D19122;

v19 = 1;
printf("%x %x\n", *(_DWORD *)(a1 + 8 * 0 + 4), *(_DWORD *)(a1 + 8 * 0));
do
{

v5 = *(_DWORD *)(a1 + 8 * (4 - v19) + 4) ^ 0xF;
temp = *(_DWORD *)(a1 + 8 * (4 - v19)) ^ 0xF;
v2 = v5 - ((v23 + (temp >> 5)) ^ (temp - 249839624) ^ (v22 + 16 * temp));
v6 = temp - ((v21 + (v2 >> 5)) ^ (v2 - 249839624) ^ (v20 + 16 * v2));
v7 = v2 - ((v23 + (v6 >> 5)) ^ (v6 - 1829222407) ^ (v22 + 16 * v6));
v8 = v6 - ((v21 + (v7 >> 5)) ^ (v7 - 1829222407) ^ (v20 + 16 * v7));
v9 = v7 - ((v23 + (v8 >> 5)) ^ (v8 + 886362106) ^ (v22 + 16 * v8));
v10 = v8 - ((v21 + (v9 >> 5)) ^ (v9 + 886362106) ^ (v20 + 16 * v9));
v11 = v9 - ((v23 + (v10 >> 5)) ^ (v10 - 693020677) ^ (v22 + 16 * v10));
v1 = v10 - ((v21 + (v11 >> 5)) ^ (v11 - 693020677) ^ (v20 + 16 * v11));
v12 = v11 - ((v23 + (v1 >> 5)) ^ (v1 + 2022563836) ^ (v22 + 16 * v1));

temp = v1 - ((v21 + (v12 >> 5)) ^ (v12 + 2022563836) ^ (v20 + 16 * v12));
v13 = v12 - ((v23 + ((temp) >> 5)) ^ (temp + 443181053) ^ (v22 + 16 * temp));
v14 = temp - ((v21 + (v13 >> 5)) ^ (v13 + 443181053) ^ (v20 + 16 * v13));

v15 = v13 - ((v23 + (v14 >> 5)) ^ (v14 - 1136201730) ^ (v22 + 16 * v14));
v16 = v14 - ((v21 + (v15 >> 5)) ^ (v15 - 1136201730) ^ (v20 + 16 * v15));
v17 = v15 - ((v23 + (v16 >> 5)) ^ (v16 + 1579382783) ^ (v22 + 16 * v16));
*(_DWORD *)(a1 + 8 * (4 - v19) + 4) = v17;
*(_DWORD *)(a1 + 8 * (4 - v19)) = v16 - ((v21 + (v17 >> 5)) ^ (v17 + 1579382783) ^ (v20 + 16 * v17));

// printf("%d %x %x %x\n", v19, v17, v6, v5);
// printf("%d %x %x\n", v19, *(_DWORD *)(a1 + 8 * (4 - v19)), *(_DWORD *)(a1 + 8 * (4 - v19) + 4));
v4 = v19;
result = ++v19;
} while (v4 < 4);
return result;
}

int main()
{
unsigned char flag2[] = {
0xdc, 0x45, 0x1e, 0x03, 0x89, 0xe9, 0x76, 0x27,
0x47, 0x48, 0x23, 0x01, 0x70, 0xd2, 0xce, 0x64,
0xda, 0x7f, 0x46, 0x33, 0xb1, 0x03, 0x49, 0xa3,
0x27, 0x00, 0xd1, 0x2c, 0x37, 0xb3, 0xbd, 0x75};

de((int)flag2);
printf("%s\n", flag2);
// NSSCTF{!!!Y0u_g3t_th3_s3cr3t!!!}

return 0;
}

Mio?Ryo?Soyo?

直接在在线网站上就能反编译得到python源码,但是SecretEncrypt有错误,不知道是出题者故意混淆的还是反编译出错。总是程序逻辑是对输入的flag进行进行R(base45)、S(key=7)、M(base85)、S(key=9)加密与s比较。

下面从base45,base85解密使用CyberChef算的,注意把base45的编码表复制上去。

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
def de_S(Src, key):
result = []
for c in Src:
if "a" <= c and c <= "z":
temp = ((ord(c) - ord("a")) - key) % 26
result.append(chr(ord("a") + temp))
continue
if "0" <= c and c <= "9":
temp = (ord(c) - ord("0") + key) % 10
result.append(chr(ord("0") + temp))
continue
result.append(c)
continue
return "".join(result)

s = '9v!rD8us"44_N(1;_U?z6!Mn16"mjz<\\l[=3*>#&4C>zt0L2C3;)z--3Z'
print(s)
s = de_S(s, 9)
print(s)
# 8m!iD7lj"33_N(0;_U?q5!Me05"daq<\c[=2*>#&3C>qk9L1C2;)q--2Z
s = "JX2NG:CM:KJ?S0=:>?NC>K5<V29Z5<Y:9C=;LA1RQ9G:7"
print(s)
s = de_S(s, 7)
print(s)
# JX9NG:CM:KJ?S7=:>?NC>K2<V96Z2<Y:6C=;LA8RQ6G:4
s = "NSSCTF{Th3y'r3_a11_p1aY_Ba5e!}"

TimeSpaceRescue

程序先生成了一个Src,作为加密的密钥对Str加密。加密函数是AES,好像有魔改,不过对re手来说已经是家常便饭,到最后都是把程序复制下来一点点调试。

第一阶段,对加密函数逆向,这部分就不罗嗦了,虽然多,但大部分还是比较好逆向的。有一个困难一点的是我重命名为hard_enc的这个,还是写一下吧。

先来理解加密,这个sub_511100函数姑且认为是一种特殊的乘法,下面的推导中将直接用乘法表示,如2*x。左边的乘数v6总是1、2、3中的一个,这里只需要关注二进制形式01、10、11。

这个sub_511100函数,对不同状态的a2进行异或叠加,参数a1的二进制形式则体现了叠加了哪几次循环,如,显然第一次循环的a2就是原数,第二次循环则是a2<<1,在a2有最高位时,额外异或0x1B。解密的关键就在于这个乘法满足了分配律(n*x)^(n*y)=n*(x^y)。

回过头看加密函数,把v6当作矩阵,以列为单位加密。例如,设第一列为x1 x2 x3 x4,其余列同理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 加密后
a1 = 10*x1 ^ 11*x2 ^ 01*x3 ^ 01*x4
a2 = 01*x1 ^ 10*x2 ^ 11*x3 ^ 01*x4
a3 = 01*x1 ^ 01*x2 ^ 10*x3 ^ 11*x4
a4 = 11*x1 ^ 01*x2 ^ 01*x3 ^ 10*x4
// 解密过程
a1 ^ a2 = 11*x1 ^ 01*x2 ^ 10*x3 ^ 00*x4
a1 ^ a3 = 11*x1 ^ 10*x2 ^ 11*x3 ^ 10*x4
t1 = a1 ^ a2 ^ a3 ^ a4 = 01*x1 ^ 01*x2 ^ 01*x3 ^ 01*x4
t2 = 10*t1 ^ a1 ^ a3 = 01*x1 ^ 00*x2 ^ 01*x3 ^ 00*x4
t3 = 10*t2 ^ a1 ^ a2 = 01*x1 ^ 01*x2 ^ 00*x3 ^ 00*x4
x1 = a1 ^ t1 ^ 10*t3
x2 = x1 ^ t3
x3 = x1 ^ t2
x4 = x1 ^ x2 ^ x3 ^ t1

第二阶段,复刻程序到自己的代码里,调试程序,看看有没有隐藏bug,毕竟是Liv出的题再小心也不为过。

第一处断点,修改eax的值,不然直接exit了。

第二处断点,可以下前两个或最后一个,Src的三个值分别对应tm类型中的day,month,year,用来控制穿越的时间。

第三处断点,检测v9(拓展后的密钥)是否正常。

第四处断点,验证加密完成后的结果跟自己跑的是否一致,顺便验证byte_516104是否被篡改了(并没有)。

经过测试,发现的坑是,这里不是有个异或0x14吗,由于反编译出错,还有一个异或0x11。

然后就是解密了,循环爆破2024的每一天,最后得到的日期是15 06 7c,即2024年7月21日。

flag: NSSCTF{W0w_Y0u’re_@n_AE5_M@5t3r}

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
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define _BYTE unsigned char
#define _DWORD unsigned int
#define BYTE unsigned char
#define HIBYTE(x) ((unsigned char)((x) >> 24)) // 提取最高字节
#define BYTE1(x) ((unsigned char)((x) >> 8)) // 提取次高字节
#define BYTE2(x) ((unsigned char)((x) >> 16)) // 提取次低字节

#define LODWORD(x) (*((DWORD *)&x))
#define HIDWORD(x) (*((DWORD *)&x + 1))
#define __ROL4__(value, shift) (((value) << (shift)) | ((value) >> (32 - (shift))))

_DWORD dword_7342D0[] = {
0xD76AA478,
0xE8C7B756,
0x242070DB,
0xC1BDCEEE,
0xF57C0FAF,
0x4787C62A,
0xA8304613,
0xFD469501,
0x698098D8,
0x8B44F7AF,
0xFFFF5BB1,
0x895CD7BE,
0x6B901122,
0xFD987193,
0xA679438E,
0x49B40821,
0xF61E2562,
0xC040B340,
0x265E5A51,
0xE9B6C7AA,
0xD62F105D,
0x02441453,
0xD8A1E681,
0xE7D3FBC8,
0x21E1CDE6,
0xC33707D6,
0xF4D50D87,
0x455A14ED,
0xA9E3E905,
0xFCEFA3F8,
0x676F02D9,
0x8D2A4C8A,
0xFFFA3942,
0x8771F681,
0x6D9D6122,
0xFDE5380C,
0xA4BEEA44,
0x4BDECFA9,
0xF6BB4B60,
0xBEBFBC70,
0x289B7EC6,
0xEAA127FA,
0xD4EF3085,
0x04881D05,
0xD9D4D039,
0xE6DB99E5,
0x1FA27CF8,
0xC4AC5665,
0xF4292244,
0x432AFF97,
0xAB9423A7,
0xFC93A039,
0x655B59C3,
0x8F0CCC92,
0xFFEFF47D,
0x85845DD1,
0x6FA87E4F,
0xFE2CE6E0,
0xA3014314,
0x4E0811A1,
0xF7537E82,
0xBD3AF235,
0x2AD7D2BB,
0xEB86D391};
_DWORD dword_7343D0[] = {
0x07, 0x0C, 0x11, 0x16,
0x07, 0x0C, 0x11, 0x16,
0x07, 0x0C, 0x11, 0x16,
0x07, 0x0C, 0x11, 0x16,
0x05, 0x09, 0x0E, 0x14,
0x05, 0x09, 0x0E, 0x14,
0x05, 0x09, 0x0E, 0x14,
0x05, 0x09, 0x0E, 0x14,
0x04, 0x0B, 0x10, 0x17,
0x04, 0x0B, 0x10, 0x17,
0x04, 0x0B, 0x10, 0x17,
0x04, 0x0B, 0x10, 0x17,
0x06, 0x0A, 0x0F, 0x15,
0x06, 0x0A, 0x0F, 0x15,
0x06, 0x0A, 0x0F, 0x15,
0x06, 0x0A, 0x0F, 0x15};
int __cdecl sub_7324A0(int a1, _DWORD *a2)
{
*a2 = a1;
return 3;
}
int __cdecl sub_732500(unsigned __int16 *a1)
{
return (*((unsigned __int8 *)a1 + 3) << 24) | (*((unsigned __int8 *)a1 + 2) << 16) | *a1;
}
int __cdecl sub_7321A0(void *Src, size_t Size, int a3)
{
size_t v3; // edx
int v4; // eax
_DWORD v6[16]; // [esp+4h] [ebp-7Ch]
int v7; // [esp+44h] [ebp-3Ch]
unsigned int v8; // [esp+48h] [ebp-38h]
unsigned int v9; // [esp+4Ch] [ebp-34h]
unsigned int v10; // [esp+50h] [ebp-30h]
unsigned int v11; // [esp+54h] [ebp-2Ch]
int v12; // [esp+58h] [ebp-28h]
unsigned int v13; // [esp+5Ch] [ebp-24h]
int v14; // [esp+60h] [ebp-20h]
int v15; // [esp+64h] [ebp-1Ch]
void *Block; // [esp+68h] [ebp-18h]
int v17; // [esp+6Ch] [ebp-14h]
unsigned int i; // [esp+70h] [ebp-10h]
int v19; // [esp+74h] [ebp-Ch]
unsigned int j; // [esp+78h] [ebp-8h]
unsigned int k; // [esp+7Ch] [ebp-4h]

Block = 0;
v11 = 1732584193;
v10 = -271733879;
v9 = -1732584194;
v8 = 271733878;
for (i = Size + 1; i % 0x40 != 56; ++i)
;
v3 = i + 8;
if (i >= 0xFFFFFFF8)
v3 = -1;
Block = malloc(v3);
memcpy(Block, Src, Size);
*((_BYTE *)Block + Size) = 0x80;
for (j = Size + 1; j < i; ++j)
*((_BYTE *)Block + j) = 0;
sub_7324A0(8 * Size, (char *)Block + i);
sub_7324A0(Size >> 29, (char *)Block + i + 4);
for (j = 0; j < i; j += 64)
{
for (k = 0; k < 0x10; ++k)
{
v4 = sub_732500((char *)Block + 4 * k + j);
v6[k] = v4;
}
v12 = v11;
v19 = v10;
v15 = v9;
v17 = v8;
for (k = 0; k < 0x40; ++k)
{
if (k >= 0x10)
{
if (k >= 0x20)
{
if (k >= 0x30)
{
v14 = v15 ^ (v19 | ~v17);
v13 = 7 * k % 0x10;
}
else
{
v14 = v17 ^ v15 ^ v19;
v13 = (3 * k + 5) % 0x10;
}
}
else
{
v14 = v15 & ~v17 | v19 & v17;
v13 = (5 * k + 1) % 0x10;
}
}
else
{
v14 = v17 & ~v19 | v15 & v19;
v13 = k;
}
v7 = v17;
v17 = v15;
v15 = v19;
v19 += __ROL4__(v6[v13] + dword_7342D0[k] + v14 + v12, dword_7343D0[k]);
v12 = v7;
}
v11 += v12;
v10 += v19;
v9 += v15;
v8 += v17;
}
free(Block);
sub_7324A0(v11, a3);
sub_7324A0(v10, a3 + 4);
sub_7324A0(v9, a3 + 8);
return sub_7324A0(v8, a3 + 12);
}
_BYTE byte_A46000[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};
int dword_734148[] = {
0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000};
char byte_736000[] = {
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};
int __cdecl sub_731490(int a1, int a2, _DWORD *a3)
{
_DWORD *v4; // [esp+4h] [ebp-18h]
int k; // [esp+8h] [ebp-14h]
int j; // [esp+Ch] [ebp-10h]
int m; // [esp+10h] [ebp-Ch]
int i; // [esp+14h] [ebp-8h]
_DWORD *v9; // [esp+18h] [ebp-4h]
_DWORD *v10; // [esp+18h] [ebp-4h]

if (!a1 || !a3)
return -1;
if (a2 != 16)
return -1;
v9 = a3;
v4 = a3 + 44;
for (i = 0; i < 4; ++i)
a3[i] = _byteswap_ulong(*(_DWORD *)(a1 + 4 * i));
for (j = 0; j < 10; ++j)
{

v9[4] = dword_734148[j] ^ *v9 ^ (unsigned __int8)byte_736000[HIBYTE(v9[3])] ^ (unsigned __int16)((unsigned __int8)byte_736000[(unsigned __int8)v9[3]] << 8) ^ ((unsigned __int8)byte_736000[BYTE1(v9[3])] << 16) & 0xFF0000 ^ ((unsigned __int8)byte_736000[(unsigned __int8)BYTE2(v9[3])] << 24);
v9[5] = v9[4] ^ v9[1];
v9[6] = v9[5] ^ v9[2];
v9[7] = v9[6] ^ v9[3];
v9 += 4;
}
v10 = a3 + 40;
for (k = 0; k < 11; ++k)
{
for (m = 0; m < 4; ++m)
v4[m] = v10[m];
v10 -= 4;
v4 += 4;
}
return 0;
}
unsigned int __cdecl re_xorF(int a1)
{
unsigned int result; // eax
unsigned int i; // [esp+0h] [ebp-Ch]
unsigned int v3; // [esp+4h] [ebp-8h]
char v4; // [esp+Bh] [ebp-1h]

v3 = 0;
for (i = 15;; --i)
{
result = v3;
if (v3 >= i)
break;
v4 = *(_BYTE *)(v3 + a1) ^ 0xF;
*(_BYTE *)(v3 + a1) = *(_BYTE *)(i + a1) ^ 0xF;
*(_BYTE *)(i + a1) = v4;
++v3;
}
return result;
}
int __cdecl toMatrix(int a1, char *a2)
{
int i; // [esp+0h] [ebp-Ch]
int j; // [esp+4h] [ebp-8h]
char v5; // [esp+Bh] [ebp-1h]

for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
v5 = *a2++;
*(_BYTE *)(a1 + 4 * j + i) = v5;
}
}
return 0;
}
int __cdecl fromMatrix(int a1, _BYTE *a2)
{
int i; // [esp+0h] [ebp-8h]
int j; // [esp+4h] [ebp-4h]

for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
*a2++ = *(_BYTE *)(a1 + 4 * j + i);
}
return 0;
}
int __cdecl tran_enc(int a1)
{
int i; // [esp+4h] [ebp-18h]
_DWORD v3[4]; // [esp+8h] [ebp-14h] BYREF
_BYTE temp;

memset(v3, 0, sizeof(v3));
for (i = 0; i < 4; ++i)
{
for (int j = 0; j < i; j++)
{
temp = *(_BYTE *)(a1 + 4 * i);
*(_BYTE *)(a1 + 4 * i) = *(_BYTE *)(a1 + 4 * i + 1);
*(_BYTE *)(a1 + 4 * i + 1) = *(_BYTE *)(a1 + 4 * i + 2);
*(_BYTE *)(a1 + 4 * i + 2) = *(_BYTE *)(a1 + 4 * i + 3);
*(_BYTE *)(a1 + 4 * i + 3) = temp;
}
}

return 0;
}
int __cdecl tran_dec(int a1)
{
int i; // [esp+4h] [ebp-18h]
_DWORD v3[4]; // [esp+8h] [ebp-14h] BYREF
_BYTE temp;

memset(v3, 0, sizeof(v3));
for (i = 0; i < 4; ++i)
{
for (int j = 0; j < i; j++)
{
temp = *(_BYTE *)(a1 + 4 * i + 3);
*(_BYTE *)(a1 + 4 * i + 3) = *(_BYTE *)(a1 + 4 * i + 2);
*(_BYTE *)(a1 + 4 * i + 2) = *(_BYTE *)(a1 + 4 * i + 1);
*(_BYTE *)(a1 + 4 * i + 1) = *(_BYTE *)(a1 + 4 * i);
*(_BYTE *)(a1 + 4 * i) = temp;
}
}

return 0;
}
int __cdecl key_enc(int a1, int a2)
{
_DWORD v3[4]; // [esp+0h] [ebp-18h] BYREF
int j; // [esp+10h] [ebp-8h]
int i; // [esp+14h] [ebp-4h]

for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
*((_BYTE *)&v3[i] + j) = *(_DWORD *)(a2 + 4 * j) >> (8 * (3 - i));
*(_BYTE *)(a1 + 4 * i + j) ^= *((_BYTE *)&v3[i] + j);
}
}
return 0;
}
int __cdecl key_dec(int a1, int a2)
{
_DWORD v3[4]; // [esp+0h] [ebp-18h] BYREF
int j; // [esp+10h] [ebp-8h]
int i; // [esp+14h] [ebp-4h]

for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
*((_BYTE *)&v3[i] + j) = *(_DWORD *)(a2 + 4 * j) >> (8 * (3 - i));
*(_BYTE *)(a1 + 4 * i + j) ^= *((_BYTE *)&v3[i] + j);
}
}
return 0;
}
int __cdecl box_enc(int a1)
{
int j; // [esp+0h] [ebp-8h]
int i; // [esp+4h] [ebp-4h]

for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
*(_BYTE *)(a1 + 4 * i + j) = byte_A46000[*(unsigned __int8 *)(a1 + 4 * i + j)];
}
return 0;
}
int __cdecl box_dec(int a1)
{
int j; // [esp+0h] [ebp-8h]
int i; // [esp+4h] [ebp-4h]
_BYTE box[256];
for (int i = 0; i < 256; i++)
{
box[byte_A46000[i]] = i;
}

for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
*(_BYTE *)(a1 + 4 * i + j) = box[*(unsigned __int8 *)(a1 + 4 * i + j)];
}
return 0;
}
unsigned int __cdecl swap_xor5(int a1)
{
unsigned int result; // eax
unsigned int i; // [esp+0h] [ebp-8h]
char v3; // [esp+7h] [ebp-1h]

for (i = 0; i < 0x10; i += 2)
{
v3 = *(_BYTE *)(i + a1) ^ 5;
*(_BYTE *)(i + a1) = *(_BYTE *)(i + a1 + 1) ^ 5;
*(_BYTE *)(i + a1 + 1) = v3;
result = i + 2;
}
return result;
}
int __cdecl sub_A41100(unsigned __int8 a1, char a2)
{
int v3; // [esp+0h] [ebp-Ch]
int i; // [esp+4h] [ebp-8h]
unsigned __int8 v5; // [esp+Bh] [ebp-1h]

v5 = 0;
for (i = 0; i < 8; ++i)
{
if ((a1 & 1) != 0) // 奇数
v5 ^= a2;
v3 = a2 & 0x80;
a2 *= 2;
if (v3) // a2 的最高位有值
a2 ^= 0x1Bu;
a1 >>= 1;
}
return v5;
}
int __cdecl hard_enc(int a1)
{
char v1; // bl
char v2; // bl
char v3; // bl
char v4; // al
_BYTE v6[16]; // [esp+8h] [ebp-30h] BYREF
_BYTE v7[16]; // [esp+18h] [ebp-20h] BYREF
int j; // [esp+28h] [ebp-10h]
int i; // [esp+2Ch] [ebp-Ch]
int m; // [esp+30h] [ebp-8h]
int k; // [esp+34h] [ebp-4h]

v7[0] = 2;
v7[1] = 3;
v7[2] = 1;
v7[3] = 1;
v7[4] = 1;
v7[5] = 2;
v7[6] = 3;
v7[7] = 1;
v7[8] = 1;
v7[9] = 1;
v7[10] = 2;
v7[11] = 3;
v7[12] = 3;
v7[13] = 1;
v7[14] = 1;
v7[15] = 2;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
v6[4 * i + j] = *(_BYTE *)(a1 + 4 * i + j);
}
for (k = 0; k < 4; ++k)
{
for (m = 0; m < 4; ++m)
{
v1 = sub_A41100(v7[4 * k], v6[m]);
v2 = sub_A41100(v7[4 * k + 1], v6[m + 4]) ^ v1;
v3 = sub_A41100(v7[4 * k + 2], v6[m + 8]) ^ v2;
v4 = sub_A41100(v7[4 * k + 3], v6[m + 12]);
*(_BYTE *)(a1 + 4 * k + m) = v4 ^ v3;
}
}
return 0;
}
int __cdecl hard_dec(int a1)
{
char v1; // bl
char v2; // bl
char v3; // bl
char v4; // al
_BYTE v6[16]; // [esp+8h] [ebp-30h] BYREF
_BYTE v7[16]; // [esp+18h] [ebp-20h] BYREF
int j; // [esp+28h] [ebp-10h]
int i; // [esp+2Ch] [ebp-Ch]
int m; // [esp+30h] [ebp-8h]
int k; // [esp+34h] [ebp-4h]
_BYTE a_1, a_2, a_3, a_4;
_BYTE t_1, t_2, t_3;

v7[0] = 2;
v7[1] = 3;
v7[2] = 1;
v7[3] = 1;
v7[4] = 1;
v7[5] = 2;
v7[6] = 3;
v7[7] = 1;
v7[8] = 1;
v7[9] = 1;
v7[10] = 2;
v7[11] = 3;
v7[12] = 3;
v7[13] = 1;
v7[14] = 1;
v7[15] = 2;

for (int i = 0; i < 4; i++)
{
a_1 = *(_BYTE *)(a1 + i);
a_2 = *(_BYTE *)(a1 + 4 + i);
a_3 = *(_BYTE *)(a1 + 8 + i);
a_4 = *(_BYTE *)(a1 + 12 + i);
t_1 = a_1 ^ a_2 ^ a_3 ^ a_4;
t_2 = sub_A41100(2, t_1) ^ a_1 ^ a_3;
t_3 = sub_A41100(2, t_2) ^ a_1 ^ a_2;
*(_BYTE *)(a1 + i) = sub_A41100(2, t_3) ^ a_1 ^ t_1;
*(_BYTE *)(a1 + 4 + i) = *(_BYTE *)(a1 + i) ^ t_3;
*(_BYTE *)(a1 + 8 + i) = *(_BYTE *)(a1 + i) ^ t_2;
*(_BYTE *)(a1 + 12 + i) = *(_BYTE *)(a1 + i) ^ *(_BYTE *)(a1 + 4 + i) ^ *(_BYTE *)(a1 + 8 + i) ^ t_1;
}

return 0;
}
int __cdecl decrypt(void *Src, size_t Size, int a3, unsigned int a4)
{
int j; // [esp+10h] [ebp-19Ch]
unsigned int i; // [esp+14h] [ebp-198h]
int v7; // [esp+18h] [ebp-194h]
_BYTE *v8; // [esp+20h] [ebp-18Ch]
_DWORD v10[4]; // [esp+188h] [ebp-24h] BYREF
_DWORD v11[4]; // [esp+198h] [ebp-14h] BYREF
_DWORD v9[88];

v7 = a3; // flag
v8 = v9; // const
memset(v10, 0, sizeof(v10));
memset(v11, 0, sizeof(v11));
memcpy(v10, Src, Size);

swap_xor5((int)v10);
sub_731490((int)v10, 16, v9);
v8 += 0x90;
for (i = 0; i < (int)a4; i += 16)
{
swap_xor5((int)v7);
toMatrix((int)v11, (int)v7); // copy v7->v10 4*4mode
key_dec((int)v11, (int)(v8 + 16));
tran_dec((int)v11);
box_dec((int)v11);
for (j = 1; j < 10; j++)
{
key_dec((int)v11, (int)v8);
hard_dec((int)v11);
tran_dec((int)v11);
box_dec((int)v11);
v8 -= 16;
}
key_dec((int)v11, (int)v9);
fromMatrix((int)v11, (int)v7); // copy v11->v7 4*4mode
re_xorF(a3);
v7 += 16;
a3 += 16;
v8 = v9;
v8 += 0x90;
}
return 39;
}
int __cdecl encrypt(void *Src, size_t Size, int a3, unsigned int a4)
{
int j; // [esp+10h] [ebp-19Ch]
unsigned int i; // [esp+14h] [ebp-198h]
int v7; // [esp+18h] [ebp-194h]
_BYTE *v8; // [esp+20h] [ebp-18Ch]
_DWORD v10[4]; // [esp+188h] [ebp-24h] BYREF
_DWORD v11[4]; // [esp+198h] [ebp-14h] BYREF
_DWORD v9[88];

v7 = a3; // flag
v8 = v9; // const
memset(v10, 0, sizeof(v10));
memset(v11, 0, sizeof(v11));
memcpy(v10, Src, Size);
swap_xor5((int)v10);
sub_731490((int)v10, 16, v9);

for (i = 0; i < (int)a4; i += 16)
{

re_xorF(a3); // reverse then xor 0xF
toMatrix((int)v11, a3); // copy a3->v11 4*4mode
key_enc((int)v11, (int)v9);
for (j = 1; j < 10; ++j)
{
v8 += 16;
box_enc((int)v11); // <- byte_A46000 <- v11
tran_enc((int)v11);
hard_enc((int)v11);
key_enc((int)v11, (int)v8);
}
box_enc((int)v11);
tran_enc((int)v11);
key_enc((int)v11, (int)(v8 + 16));
fromMatrix((int)v11, (int)v7); // copy v11->v7 4*4mode
swap_xor5((int)v7);
v7 += 16;
a3 += 16;
v8 = v9;
}
return 39;
}
int __cdecl make_key(int a1, int date, int month, int year)
{
_DWORD Src[3]; // [esp+18h] [ebp-10h] BYREF
int k;

Src[0] = date;
Src[1] = month;
Src[2] = year;
printf("%02x %02x %02x \n", Src[0], Src[1], Src[2]);
sub_7321A0(Src, 0xCu, a1);
for (k = 0; k < 4; ++k)
printf("%x ", *(_DWORD *)(a1 + 4 * k));
printf("\n");

for (k = 0; k < 16; ++k)
*(_BYTE *)(k + a1) ^= 0x14u ^ 0x11u;

return 0;
}
int main()
{
_DWORD Src[4];
unsigned char flag[] = {
0xCD, 0x16, 0xDB, 0xB5, 0xD1, 0x02, 0xA4, 0x82,
0x8E, 0x59, 0x73, 0x9E, 0x96, 0x26, 0x56, 0xF2,
0x16, 0x8E, 0x46, 0xF2, 0x55, 0x7B, 0x92, 0x31,
0x30, 0xDC, 0xAA, 0x8A, 0xF3, 0x1C, 0xA0, 0xAA};
unsigned char flag_copy[] = {
0xCD, 0x16, 0xDB, 0xB5, 0xD1, 0x02, 0xA4, 0x82,
0x8E, 0x59, 0x73, 0x9E, 0x96, 0x26, 0x56, 0xF2,
0x16, 0x8E, 0x46, 0xF2, 0x55, 0x7B, 0x92, 0x31,
0x30, 0xDC, 0xAA, 0x8A, 0xF3, 0x1C, 0xA0, 0xAA};

// char test_flag[16] = "NSSCTF{fuck_Liv}";
// make_key((int)Src, 28, 0, 0x7d);
// for (int i = 0; i < 4; i++)
// {
// printf("%x ", Src[i]);
// }
// printf("\n");
// encrypt(Src, 0x10u, test_flag, 16);
// putFlag(test_flag);

for (int j = 0; j <= 11; j++)
{
for (int i = 1; i <= 31; i++)
{
memcpy(flag, flag_copy, 32);
make_key((int)Src, i, j, 0x7c);
decrypt(Src, 0x10u, flag, 32);
if ((flag[0] == 'N' && flag[1] == 'S') || (flag[0] == 'n' && flag[1] == 's'))
{
printf("flag: %s\n", flag);
return 0;
}
}
}
return 0;
}

Canon

程序逻辑,准备了7种带key的加密,实际用到5种,flag分成了3份,每次加密一部分为另一部分的密钥。需要把这些加密函数做出解密函数。

顺便说说调试的逻辑吧,我用了三个C文件。

主要的自然是完整的解密脚本,一般做出来后就只保留这一个文件了,但只靠这个还不够。

一个复制源代码,自己编一个flag跟程序跑出来的对比,防止出题人偷偷做手脚,这道题的base64表就被偷偷替换成"stuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr"了。

另一个对每个加密函数逐个调试,输出-加密-输出-加密-解密,这样调试更方便,如果在解密脚本发现BUG,就把参数调过来在这里面调试,虽说该坐的牢一个也逃不掉,至少不至于头脑混乱。

NSSCTF{P4ch3Lbel’s_C@n0n_1n_D_mAjOr}

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
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define _DWORD unsigned int
#define _BYTE unsigned char
#define _QWORD unsigned __int64
#define BYTE4(x) ((unsigned char)((x) >> 24))

char Str[] = "stuvwxyz0123456789+/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr";
_BYTE *__fastcall b64encode(__int64 a1, signed int a2)
{
size_t v2; // rax
int v4; // [rsp+20h] [rbp-28h]
int v5; // [rsp+24h] [rbp-24h]
signed int i; // [rsp+28h] [rbp-20h]
int v7; // [rsp+2Ch] [rbp-1Ch]
_BYTE *v8; // [rsp+30h] [rbp-18h]

v2 = 2 * a2;
v8 = malloc(v2);
v7 = 0;
v5 = -6;
v4 = 0;
for (i = 0; i < a2; ++i)
{
v7 = *(_DWORD *)(a1 + 4LL * i) + (v7 << 8);
for (v5 += 8; v5 >= 0; v5 -= 6)
{
v8[v4++] = Str[(v7 >> v5) & 0x3F];
}
}
if (v5 > -6)
v8[v4++] = Str[(v7 << 8 >> (v5 + 8)) & 0x3F];
while (v4 % 4)
v8[v4++] = 61;
v8[v4] = 0;
return v8;
}
_BYTE *__fastcall b64decode(unsigned char *a1, signed int *a2)
{
int v4, v5, v7, i;
_BYTE *v8 = malloc(*a2);

// 计算解码后数据的长度

v4 = 0; // 用于索引解码后的数据
v5 = -8; // 用于控制位操作
v7 = 0; // 用于累积位数据

for (i = 0; i < *a2; ++i)
{
if (a1[i] == '=') // 处理填充字符
v7 = (v7 << 6);
else
{
// 查找当前字符在Base64编码表中的位置
int val = 0;
while (val < 64 && Str[val] != a1[i])
++val;
// printf("%c %d\n", a1[i], val);

// 如果字符不在编码表中,忽略
if (val == 64)
continue;

// 将当前字符的6位累积到 v7 中
v7 = (v7 << 6) | val;
}

// 每累积16位(4个字符),解码为2个字节
if ((i + 1) % 4 == 0)
{
v8[v4++] = (v7 >> 16) & 0xFF;
v8[v4++] = (v7 >> 8) & 0xFF;
v8[v4++] = v7 & 0xFF;
v7 = 0; // 重置 v7
}
}

v8[v4] = 0; // 添加字符串终止符
*a2 = v4;
return v8;
}
__int64 __fastcall RC4(__int64 a1, const char *a2, __int64 a3)
{
__int64 v3; // kr00_8
__int64 result; // rax
int i; // [rsp+20h] [rbp-438h]
int j; // [rsp+20h] [rbp-438h]
int v7; // [rsp+20h] [rbp-438h]
int v8; // [rsp+24h] [rbp-434h]
int v9; // [rsp+24h] [rbp-434h]
int k; // [rsp+28h] [rbp-430h]
int v11; // [rsp+30h] [rbp-428h]
int v12; // [rsp+34h] [rbp-424h]
_DWORD v13[258]; // [rsp+50h] [rbp-408h]

v8 = 0;
for (i = 0; i < 256; ++i)
v13[i] = i;
for (j = 0; j < 256; ++j)
{
v3 = a2[j % strlen(a2)] + v13[j] + v8;
v8 = (unsigned __int8)(BYTE4(v3) + v3) - BYTE4(v3);
v11 = v13[j];
v13[j] = v13[v8];
v13[v8] = v11;
}

v9 = 0;
v7 = 0;
for (k = 0;; ++k)
{
result = (unsigned int)*(char *)(a1 + k);
if (!*(_BYTE *)(a1 + k))
break;
v7 = (v7 + 1) % 256;
v9 = (v13[v7] + v9) % 256;
v12 = v13[v7];
v13[v7] = v13[v9];
v13[v9] = v12;
*(_DWORD *)(a3 + 4LL * k) = ((v13[(v13[v9] + v13[v7]) % 256] ^ *(char *)(a1 + k)) + 57) % 256;
}
return result;
}
__int64 __fastcall de_RC4(__int64 a1, const char *a2, __int64 a3)
{
__int64 v3; // kr00_8
__int64 result; // rax
int i; // [rsp+20h] [rbp-438h]
int j; // [rsp+20h] [rbp-438h]
int v7; // [rsp+20h] [rbp-438h]
int v8; // [rsp+24h] [rbp-434h]
int v9; // [rsp+24h] [rbp-434h]
int k; // [rsp+28h] [rbp-430h]
int v11; // [rsp+30h] [rbp-428h]
int v12; // [rsp+34h] [rbp-424h]
_DWORD v13[258]; // [rsp+50h] [rbp-408h]

v8 = 0;
for (i = 0; i < 256; ++i)
v13[i] = i;
for (j = 0; j < 256; ++j)
{
v3 = a2[j % strlen(a2)] + v13[j] + v8;
v8 = (unsigned __int8)(BYTE4(v3) + v3) - BYTE4(v3);
v11 = v13[j];
v13[j] = v13[v8];
v13[v8] = v11;
}

v9 = 0;
v7 = 0;
for (k = 0;; ++k)
{
result = (unsigned int)*(char *)(a3 + k);
if (!*(_BYTE *)(a3 + k))
break;
v7 = (v7 + 1) % 256;
v9 = (v13[v7] + v9) % 256;
v12 = v13[v7];
v13[v7] = v13[v9];
v13[v9] = v12;
}

for (k = strlen(a3) - 1; k >= 0; --k)
{
*(char *)(a1 + k) = v13[(v13[v9] + v13[v7]) % 256] ^ ((*(unsigned char *)(a3 + k) - 57 + 256) % 256);

v12 = v13[v7];
v13[v7] = v13[v9];
v13[v9] = v12;

v9 = (v9 - v13[v7]) % 256;
v7 = (v7 - 1) % 256;
}
*(char *)(a1 + strlen(a3)) = 0;
return result;
}
void __fastcall en(char *a1, const char *a2, int a3)
{
size_t v3; // rax
unsigned int v4; // eax
size_t v5; // rax
unsigned int v6; // eax
int i; // [rsp+20h] [rbp-F8h]
int j; // [rsp+24h] [rbp-F4h]
int v9; // [rsp+28h] [rbp-F0h]
int v10; // [rsp+2Ch] [rbp-ECh]
char v11; // [rsp+30h] [rbp-E8h]
int m; // [rsp+34h] [rbp-E4h]
int n; // [rsp+38h] [rbp-E0h]
int i1; // [rsp+3Ch] [rbp-DCh]
int k; // [rsp+40h] [rbp-D8h]
int ii; // [rsp+44h] [rbp-D4h]
int jj; // [rsp+48h] [rbp-D0h]
int v18; // [rsp+4Ch] [rbp-CCh]
int nn; // [rsp+50h] [rbp-C8h]
int kk; // [rsp+54h] [rbp-C4h]
int v21; // [rsp+58h] [rbp-C0h]
int v22; // [rsp+5Ch] [rbp-BCh]
int mm; // [rsp+60h] [rbp-B8h]
_QWORD *Block; // [rsp+68h] [rbp-B0h]
int v25; // [rsp+7Ch] [rbp-9Ch]
_DWORD *v26; // [rsp+88h] [rbp-90h]
void *v27; // [rsp+90h] [rbp-88h]
void *v28; // [rsp+98h] [rbp-80h]
char *Source; // [rsp+A0h] [rbp-78h]
char *v30; // [rsp+A8h] [rbp-70h]
char *v31; // [rsp+B0h] [rbp-68h]
_DWORD v32[12]; // [rsp+B8h] [rbp-60h]
__int64 v33; // [rsp+E8h] [rbp-30h]
__int64 v34; // [rsp+F0h] [rbp-28h]

v9 = strlen(a1);
v21 = strlen(a2);
switch (a3)
{
// 凯撒
case 1:
for (i = 0; i < v9; ++i)
{
v22 = a2[i % v21];
if (a1[i] < 65 || a1[i] > 90)
{
if (a1[i] < 97 || a1[i] > 122)
{
if (a1[i] >= 48 && a1[i] <= 57)
a1[i] = (a1[i] + v22 - 48 + 10) % 10 + 48;
}
else
{
a1[i] = (a1[i] + v22 - 97 + 26) % 26 + 97;
}
}
else
{
a1[i] = (a1[i] + v22 - 65 + 26) % 26 + 65;
}
}
break;
// 换位
case 3:
v10 = *a2 % 10 + 2;
Block = malloc(v10 * 8);
for (k = 0; k < v10; ++k)
{
Block[k] = malloc(v9 + 1);
memset((void *)Block[k], 0, v9 + 1);
}
for (m = 0; v10 * m < v9; ++m)
{
for (n = 0; n < v10 && n + v10 * m < v9; ++n)
*(_BYTE *)(Block[n] + m) = a1[n + v10 * m];
}
v18 = 0;
for (ii = 0; ii < v10; ++ii)
{
for (jj = 0; jj < m; ++jj)
{
if (*(_BYTE *)(Block[ii] + jj) && v18 < v9)
{
a1[v18++] = *(_BYTE *)(Block[ii] + jj);
}
}
}
a1[v18] = 0;
for (kk = 0; kk < v10; ++kk)
free((void *)Block[kk]);
free(Block);
break;
// 移位
case 4:
v25 = *a2 % 10 + 2;
for (mm = 0; mm < v25; ++mm)
{
v11 = a1[v9 - 1];
for (nn = v9 - 1; nn > 0; --nn)
a1[nn] = a1[nn - 1];
*a1 = v11;
}
break;
// Base64
case 5:
v34 = v9;
v26 = malloc(v9 * 4);
for (i1 = 0; i1 < v9; ++i1)
v26[i1] = (a2[i1 % v21] + 57) ^ a1[i1];

Source = (char *)b64encode(v26, (unsigned int)v9);
strcpy(a1, Source);
free(v26);
free(Source);
break;
// RC4+Base64
case 6:
v3 = strlen(a1) * 4;
v27 = malloc(v3);
RC4(a1, a2, v27);
v4 = strlen(a1);
v30 = (char *)b64encode(v27, v4);
strcpy(a1, v30);
free(v27);
free(v30);
break;
}
}
void de(char *a1, const char *a2, int a3)
{
size_t v3; // rax
unsigned int v4; // eax
size_t v5; // rax
unsigned int v6; // eax
int i; // [rsp+20h] [rbp-F8h]
int j; // [rsp+24h] [rbp-F4h]
int v9; // [rsp+28h] [rbp-F0h]
int v10; // [rsp+2Ch] [rbp-ECh]
char v11; // [rsp+30h] [rbp-E8h]
int m; // [rsp+34h] [rbp-E4h]
int n; // [rsp+38h] [rbp-E0h]
int i1; // [rsp+3Ch] [rbp-DCh]
int k; // [rsp+40h] [rbp-D8h]
int ii; // [rsp+44h] [rbp-D4h]
int jj; // [rsp+48h] [rbp-D0h]
int v18; // [rsp+4Ch] [rbp-CCh]
int nn; // [rsp+50h] [rbp-C8h]
int kk; // [rsp+54h] [rbp-C4h]
int v21; // [rsp+58h] [rbp-C0h]
int v22; // [rsp+5Ch] [rbp-BCh]
int mm; // [rsp+60h] [rbp-B8h]
_QWORD *Block; // [rsp+68h] [rbp-B0h]
int v25; // [rsp+7Ch] [rbp-9Ch]
_DWORD *v26; // [rsp+88h] [rbp-90h]
void *v27; // [rsp+90h] [rbp-88h]
void *v28; // [rsp+98h] [rbp-80h]
char *Source; // [rsp+A0h] [rbp-78h]
char *v30; // [rsp+A8h] [rbp-70h]
char *v31; // [rsp+B0h] [rbp-68h]
_DWORD v32[12]; // [rsp+B8h] [rbp-60h]
__int64 v33; // [rsp+E8h] [rbp-30h]
__int64 v34; // [rsp+F0h] [rbp-28h]

v9 = strlen(a1);
v21 = strlen(a2);
switch (a3)
{
case 1:
for (i = 0; i < v9; ++i)
{
v22 = a2[i % v21];
if (a1[i] < 65 || a1[i] > 90)
{
if (a1[i] < 97 || a1[i] > 122)
{
if (a1[i] >= 48 && a1[i] <= 57)
a1[i] = (a1[i] - v22 - 48 + 1000) % 10 + 48;
}
else
{
a1[i] = (a1[i] - v22 - 97 + 260) % 26 + 97;
}
}
else
{
a1[i] = (a1[i] - v22 - 65 + 260) % 26 + 65;
}
}
break;
case 3:
v10 = *a2 % 10 + 2;
Block = malloc(v10 * 8);
for (k = 0; k < v10; ++k)
{
Block[k] = malloc(v9 + 1);
memset((void *)Block[k], 0, v9 + 1);
}
for (m = 0; v10 * m < v9; ++m)
{
for (n = 0; n < v10 && n + v10 * m < v9; ++n)
{
*(_BYTE *)(Block[n] + m) = a1[n + v10 * m];
}
}

v18 = 0;
for (ii = 0; ii < v10; ++ii)
{
for (jj = 0; jj < m; ++jj)
{
if (*(_BYTE *)(Block[ii] + jj) && v18 < v9)
{
*(_BYTE *)(Block[ii] + jj) = a1[v18++];
}
}
}

for (m = 0; v10 * m < v9; ++m)
{
for (n = 0; n < v10 && n + v10 * m < v9; ++n)
{
a1[n + v10 * m] = *(_BYTE *)(Block[n] + m);
}
}
a1[v18] = 0;
for (kk = 0; kk < v10; ++kk)
free((void *)Block[kk]);
free(Block);
break;
case 4:
v25 = *a2 % 10 + 2;
for (mm = 0; mm < v25; ++mm)
{

v11 = *a1;
for (nn = 1; nn < v9; ++nn)
a1[nn - 1] = a1[nn];
a1[v9 - 1] = v11;
}
break;
case 5:
v34 = v9;
Source = (char *)b64decode(a1, &v34);
// printf("%d\n", strlen(Source));
for (i1 = 0; i1 < v34; ++i1)
a1[i1] = (a2[i1 % v21] + 57) ^ Source[i1];
a1[v34] = 0;

free(Source);
break;
case 6:
v3 = strlen(a1) * 4;
v27 = malloc(v3);
v4 = strlen(a1);
v27 = (char *)b64decode(a1, &v4);
de_RC4(a1, a2, v27);
free(v27);
break;
}
}
int __fastcall main()
{
int j;
int i;
int v6;
_DWORD v7[4];
__int64 v8;
__int64 v9;
__int64 v10;
_DWORD v11[8] = {1, 5, 6, 3, 4, 1, 4, 5};
_DWORD v12[4] = {0, 1, 2};
char Str[12];
char Source[12];
char v15[16];
char flag1[112] = "WgvDmssEvcY326bHo3nNro3vXvvfmgrz";
char flag2[112] = "gX+Ri9PG=bt5=00B6hscPQOL";
char flag3[112] = "T6bHgUPL2gXUd=xT=FNHtPzV";
// char flag1[112] = "1cCNdnuHe7LnteULhojtnZDJ2YXdnszK";
// char flag2[112] = "U0VHH=O2=cIHmIlx/0EGqPSW";
// char flag3[112] = "fmb=ihK=YVGnSWc4D8MR4n/o";

v7[0] = 7;
v7[1] = 6;
v7[2] = 5;
for (i = 7; i >= 0; --i)
{
for (j = 2; j >= 0; --j)
{
if (i >= v12[j])
{
v6 = v7[j];
// printf("%d %d %d\n", i, j, v6);
if (v6 < 8)
{
// printf("%d %d %d\n", i, j, (unsigned int)v11[v6]);
if (j)
{
if (j == 1)
{
de(flag2, flag3, (unsigned int)v11[v6]);
}
else if (j == 2)
{
de(flag3, flag1, (unsigned int)v11[v6]);
}
}
else
{
de(flag1, flag2, (unsigned int)v11[v6]);
}
// printf("%s\n", flag1);
// printf("%s\n", flag2);
// printf("%s\n", flag3);
--v7[j];
}
}
}
}
printf("%s", flag1);
printf("%s", flag2);
printf("%s\n", flag3);
return 0;
}

Room0 | Review

如果直接分析得到的flag是NSSCTF{FAKE_FAKE_FAKE_FAKE_FAKE},看看汇编会发现这里有个异常处理机制,不难定位到检测key的函数中,可能触发除零异常。

image-20250318220449319

为了反编译异常处理的代码,直接把中间的这部分代码nop掉,这样原来程序的结尾就能接上异常处理的代码。

image-20250318220818442

我把第一个函数重命名为unhex,例如把字符串"12345678"变成0x12345678返回。

第二个函数(SMC)手动去掉3个花指令,第三个函数是等待解密的函数。

image-20250318220859648

注意到SMC函数需要key作为参数。如果这时候回头利用除零异常直接爆破会得到大量解。

看看SMC函数,将key逆序后对代码段异或解密,注意到 KEY = 加密代码 XOR 解密代码,加密代码有现成的,如果我猜出来代码段部分字节,就能推导出部分KEY。

image-20250318221811952

如果了解函数调用的堆栈原理的话可能会想到,正常的函数一开始都有这两行汇编。

1
2
55      push    ebp
8B EC mov ebp, esp

这样KEY的三个字节都有了,因为SMC有个KEY逆序,未知字节是原来KEY第一个字节,即KEY个位。

1
20 D4 1C XOR 55 8B EC == 75 5F F0

那就爆破 0x755FF000 ~ 0x755FF0FF 即可,改一下检测密钥函数,去掉unhex直接接受int。

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
int __cdecl sub_402000(int v2)
{
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; // [esp+18h] [ebp-4h]

v6 = 0;
v9 = v2;
v8 = HIBYTE(v2);
v5 = BYTE2(v2);
v4 = BYTE1(v2);
for (i = 0; i < 32; ++i)
{
v7 = v6 * (v8 + 1415881080) * (v9 - 1467486175) * ((v8 - v9) ^ (v8 >> 4));
v5 = (v9 + v5) ^ (8 * v4);
v4 = (v9 + v8) ^ (8 * v5);
v8 = (v9 + v4) ^ (8 * v5);
v9 -= v4 + v5 + v8;
if (v9 == 1415881080)
{
printf("key found: %x\n", v2);
return 0;
}
v6 = v7 + (v8 + 1467486175) * (((v8 - v9) ^ (unsigned __int64)(v8 >> 4)) / (unsigned int)(v9 - 1415881080));
}
return v9 ^ v6;
}

得到密钥755FF0D3,接下来就输入密钥,在SMC函数结束后的地方下断点,动态调试直到解密完成。

1
2
3
4
Welcome to the hostel!
Please input your informatioin to enter your room.
Input your flag:1111111111111111111111111111111111111111111111111
Input your Key:755FF0D3

image-20250318230333451

把这几个花指令处理一下

image-20250318230551824

image-20250318230649013

image-20250318230708755

image-20250318230954726

去除后就能看到代码了,又是异或,那可真是太棒了,到这里读一下a2的值0xF86D35D4,直接照抄代码,Str替换成密文,异或出来就是明文了。

NSSCTF{Int3r3st1ng_5MC_Pr0gr@m?}

image-20250318231128328

完整脚本

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

#define _BYTE unsigned char
#define _DWORD unsigned int
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define BYTE1(x) ((unsigned char)((x) >> 8))
#define BYTE2(x) ((unsigned char)((x) >> 16))
#define HIBYTE(x) ((unsigned char)((x) >> 24))

int __cdecl sub_402000(int v2)
{
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; // [esp+18h] [ebp-4h]

v6 = 0;
v9 = v2;
v8 = HIBYTE(v2);
v5 = BYTE2(v2);
v4 = BYTE1(v2);
for (i = 0; i < 32; ++i)
{
v7 = v6 * (v8 + 1415881080) * (v9 - 1467486175) * ((v8 - v9) ^ (v8 >> 4));
v5 = (v9 + v5) ^ (8 * v4);
v4 = (v9 + v8) ^ (8 * v5);
v8 = (v9 + v4) ^ (8 * v5);
v9 -= v4 + v5 + v8;
if (v9 == 1415881080)
{
printf("key: %x\n", v2);
return 0;
}
v6 = v7 + (v8 + 1467486175) * (((v8 - v9) ^ (unsigned __int64)(v8 >> 4)) / (unsigned int)(v9 - 1415881080));
}
return v9 ^ v6;
}

unsigned __int8 __cdecl sub_401000(char *Str, unsigned int a2)
{
unsigned __int8 result; // al
size_t v3; // [esp+Ch] [ebp-234h]
char v4; // [esp+10h] [ebp-230h]
int v5; // [esp+14h] [ebp-22Ch]
int ii; // [esp+18h] [ebp-228h]
size_t n; // [esp+1Ch] [ebp-224h]
int k; // [esp+20h] [ebp-220h]
int j; // [esp+24h] [ebp-21Ch]
int m; // [esp+28h] [ebp-218h]
int i; // [esp+2Ch] [ebp-214h]
char v12; // [esp+30h] [ebp-210h]
char v13; // [esp+31h] [ebp-20Fh]
unsigned __int8 v14; // [esp+32h] [ebp-20Eh]
unsigned __int8 v15; // [esp+33h] [ebp-20Dh]
_BYTE v16[256]; // [esp+34h] [ebp-20Ch] BYREF
_BYTE v17[256]; // [esp+134h] [ebp-10Ch] BYREF
_DWORD v18[2]; // [esp+234h] [ebp-Ch]

v18[0] = 0;
v18[1] = 0;
result = (unsigned __int8)memset(v17, 0, sizeof(v17));
for (i = 0; i < 8; ++i)
{
if (i >= 4)
v4 = 7 - i;
else
v4 = i;
result = i;
*((_BYTE *)v18 + i) = a2 >> (8 * v4);
}
for (j = 0; j < 256; ++j)
{
result = j;
v17[j] = j;
}
if (j == 256)
{
memset(v16, 0, sizeof(v16));
for (k = 0; k < 256; ++k)
v16[k] = *((_BYTE *)v18 + k % 8);
v5 = 0;
for (m = 0; m < 256; ++m)
{
v5 = ((unsigned __int8)v16[m] + v5 + (unsigned __int8)v17[m]) % 256;
v13 = v17[m];
v17[m] = v17[v5];
v17[v5] = v13;
}
v15 = 0;
v14 = 0;
v3 = 32;
for (n = 0; n < v3; ++n)
{
v14 += v17[++v15];
v12 = v17[v15];
v17[v15] = v17[v14];
v17[v14] = v12;
Str[n] ^= *((_BYTE *)v18 + (v15 & 7)) ^ v17[((unsigned __int8)v17[v14] + (unsigned __int8)v17[v15]) % 256];
}
}

printf("%s\n", Str);
return result;
}

int main()
{
char Str[32] = {
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};

int i, key;
for (i = 0x755FF000; i <= 0x755FF0FF; i++)
{
sub_402000(i);
}
sub_401000(Str, 0xF86D35D4);
return 0;
}

ezObfus | Review

进去先去除一大堆的花指令,把main反编译出来。

代码中有很多混淆手段:

  1. 每个字符串常量如"Password"都被加密了,只能动态调试。
  2. 有很多关于小数的判断,实际上都是里面的核心代码都能正常运行。
  3. 许多条件判断、整数都被复杂化,0~9等常量需要重命名,条件、循环的判断语句加入了函数、三元运算符复杂化代码。
    例如 i += sub_9E4190(1),函数返回 0 * (2 * 1 - 6) + a1 * 1,还是1本身。

image-20250402195304893

提取核心代码就是:

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
int main()
{
// 00 00 00 00 54 55 79 9E A8 E1 1C DA 04 1D C1 6E
// 80 82 0D 8A 4C 65 E1 46 71 31 ED D2 14 C5 39 B5
// 49 E2 04 A9 00 00 00 00 00 00 00 00 00 00 00 00
unsigned char enc_flag[33] = {
0x54, 0x55, 0x79, 0x9E, 0xA8, 0xE1, 0x1C, 0xDA,
0x04, 0x1D, 0xC1, 0x6E, 0x80, 0x82, 0x0D, 0x8A,
0x4C, 0x65, 0xE1, 0x46, 0x71, 0x31, 0xED, 0xD2,
0x14, 0xC5, 0x39, 0xB5, 0x49, 0xE2, 0x04, 0xA9, 0x00};
unsigned char flag[0x100];
unsigned int key;
unsigned char v76;

printf("Password:");
scanf("%d", &key);
printf("%d\n", key);

unsigned int key_hash = 0x811C9DC5;
for (int i = 0; i < 4; i++)
{
v76 = key >> (i * 8);
if (v76 % 2)
key_hash ^= v76;
else
key_hash *= 0x1000193;

key_hash = (key_hash >> 25) | (key_hash << 7);
key_hash -= v76;
}

if (key_hash != 1172912374) // 0x45E938F6
{
printf("Wrong Password!");
return 0;
}

memset(flag, 0, 0x100u);
printf("Flag:");
scanf("%s", flag);

for (i = 0; i < 32; i++)
{
flag[i] ^= 0x48;
}

for (i = 0; i < 32; i++)
{
flag[i] ^= i ^ (key >> ((3 - i % 4) * 8));
flag[i] = (flag[i] >> 5) | (flag[i] << 3);
flag[i] += i;
}

if (!strcmp(enc_flag, flag))
{
printf("Correct Flag");
}
else
{
printf("Wrong Flag");
}

return 0;
}

把key先爆破出来,0x8c90f77b,%d格式就是-1936656517。

程序逻辑+逆向代码:

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

int main_0()
{
// 00 00 00 00 54 55 79 9E A8 E1 1C DA 04 1D C1 6E
// 80 82 0D 8A 4C 65 E1 46 71 31 ED D2 14 C5 39 B5
// 49 E2 04 A9 00 00 00 00 00 00 00 00 00 00 00 00
unsigned char enc_flag[33] = {
0x54, 0x55, 0x79, 0x9E, 0xA8, 0xE1, 0x1C, 0xDA,
0x04, 0x1D, 0xC1, 0x6E, 0x80, 0x82, 0x0D, 0x8A,
0x4C, 0x65, 0xE1, 0x46, 0x71, 0x31, 0xED, 0xD2,
0x14, 0xC5, 0x39, 0xB5, 0x49, 0xE2, 0x04, 0xA9, 0x00};
unsigned char flag[0x100];
unsigned int key;
unsigned char v76;
int i;

printf("Password:");
scanf("%d", &key);
printf("%d\n", key);

unsigned int key_hash = 0x811C9DC5;
for (i = 0; i < 4; i++)
{
v76 = key >> (i * 8);
if (v76 % 2)
key_hash ^= v76;
else
key_hash *= 0x1000193;

key_hash = (key_hash >> 25) | (key_hash << 7);
key_hash -= v76;
}

if (key_hash != 1172912374) // 0x45E938F6
{
printf("Wrong Password!");
return 0;
}

memset(flag, 0, 0x100u);
printf("Flag:");
scanf("%s", flag);

for (i = 0; i < 32; i++)
{
flag[i] ^= 0x48;
}

for (i = 0; i < 32; i++)
{
flag[i] ^= i ^ (key >> ((3 - i % 4) * 8));
flag[i] = (flag[i] >> 5) | (flag[i] << 3);
flag[i] += i;
}

if (!strcmp(enc_flag, flag))
{
printf("Correct Flag");
}
else
{
printf("Wrong Flag");
}

return 0;
}

int main()
{

unsigned char flag[33] = {
0x54, 0x55, 0x79, 0x9E, 0xA8, 0xE1, 0x1C, 0xDA,
0x04, 0x1D, 0xC1, 0x6E, 0x80, 0x82, 0x0D, 0x8A,
0x4C, 0x65, 0xE1, 0x46, 0x71, 0x31, 0xED, 0xD2,
0x14, 0xC5, 0x39, 0xB5, 0x49, 0xE2, 0x04, 0xA9, 0x00};

unsigned int key = 0x8c90f77b;
int i;

// key = 0x8c90f77b; // -1936656517
for (i = 0; i < 32; i++)
{
flag[i] -= i;
flag[i] = (flag[i] >> 3) | (flag[i] << 5);
flag[i] ^= i ^ (key >> ((3 - i % 4) * 8));
flag[i] ^= 0x48;
}

printf("%s\n", flag);
// NSSCTF{NSSCTF{NSSCTF{NSSCTF{}}}}

return 0;
}

腐蚀 | Review

加密图片的题目,程序还是比较麻烦的。

image-20250402234117467

从后面反向追踪变量,这个带v44 v45 v47的应该就是加密函数了,v44是输出,v45是key,v47是输入的图片。

v33在之前虽然多次出现,但看代码不像是做了加密,key需要动态调试获得。

在函数调试里面得到 60 82 AE 42 4E 44 49 45 1A 0A 0D 0A 4E 47 89 50

rust的内存分配机制可能跟C语言不太一样,有的变量找起来挺麻烦的。

image-20250402234325948

这个加密函数是RC4加密,这是初始化S盒。

image-20250402235457004

后面的是加密部分,还异或了0x1f。

image-20250402235557992

这个加密函数后面还有个东西

image-20250402235642519

查看细节发现,是逆向字节

image-20250402235752653

解密脚本:

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
// 60 E3 22 D8 2A 73 C2 0E  30 B7 95 04 15 DA AD 56
// 7C D5 CF 76 86 DF 1A 83 E7 EF AE 3B DC 58 A0 11
// 89 32 29 61 9D 2E D9 0F A1 81 92 6B 45 D3 10 EB
// 53 44 0C 34 72 A2 98 C0 0A C5 9F FA BB D4 FB 69
// DD D1 4C 21 A3 41 68 47 A9 FC 23 9E 28 2C 87 3D
// 4D C3 82 39 0D F4 9B AB F9 36 CD DB A6 08 1B E0
// 4E 67 55 8D 8A 62 E2 5D F0 88 B6 C7 5B 49 51 2F
// 16 70 75 BC 7E 37 C1 59 D6 C4 8E D0 6C F2 19 BE
// AF 93 6E 9A 05 B2 25 26 BD B3 17 B4 2B 99 F5 1F
// 57 96 6F 1E 5C 5A 84 14 EA 03 B1 8C 00 AA 7A BF
// 06 E5 4A 46 0B 1D 85 DE 8F EC C8 FE F8 01 3E E1
// FD CE 5E 64 2D 97 1C 43 91 13 A4 CA E9 F7 3C 27
// 38 D2 C6 33 77 80 54 20 A5 E4 94 5F 90 F6 4B 24
// EE 78 E8 50 BA 09 B5 B9 66 4F 6A 7D 42 ED 31 FF
// 7F 7B C9 AC 63 40 48 74 B0 35 B8 07 E6 9C CC 79
// 18 F3 6D 12 71 A8 8B 02 3F D7 65 52 A7 F1 CB 3A
unsigned char S_box[256] = {
0x60, 0xE3, 0x22, 0xD8, 0x2A, 0x73, 0xC2, 0x0E,
0x30, 0xB7, 0x95, 0x04, 0x15, 0xDA, 0xAD, 0x56,
0x7C, 0xD5, 0xCF, 0x76, 0x86, 0xDF, 0x1A, 0x83,
0xE7, 0xEF, 0xAE, 0x3B, 0xDC, 0x58, 0xA0, 0x11,
0x89, 0x32, 0x29, 0x61, 0x9D, 0x2E, 0xD9, 0x0F,
0xA1, 0x81, 0x92, 0x6B, 0x45, 0xD3, 0x10, 0xEB,
0x53, 0x44, 0x0C, 0x34, 0x72, 0xA2, 0x98, 0xC0,
0x0A, 0xC5, 0x9F, 0xFA, 0xBB, 0xD4, 0xFB, 0x69,
0xDD, 0xD1, 0x4C, 0x21, 0xA3, 0x41, 0x68, 0x47,
0xA9, 0xFC, 0x23, 0x9E, 0x28, 0x2C, 0x87, 0x3D,
0x4D, 0xC3, 0x82, 0x39, 0x0D, 0xF4, 0x9B, 0xAB,
0xF9, 0x36, 0xCD, 0xDB, 0xA6, 0x08, 0x1B, 0xE0,
0x4E, 0x67, 0x55, 0x8D, 0x8A, 0x62, 0xE2, 0x5D,
0xF0, 0x88, 0xB6, 0xC7, 0x5B, 0x49, 0x51, 0x2F,
0x16, 0x70, 0x75, 0xBC, 0x7E, 0x37, 0xC1, 0x59,
0xD6, 0xC4, 0x8E, 0xD0, 0x6C, 0xF2, 0x19, 0xBE,
0xAF, 0x93, 0x6E, 0x9A, 0x05, 0xB2, 0x25, 0x26,
0xBD, 0xB3, 0x17, 0xB4, 0x2B, 0x99, 0xF5, 0x1F,
0x57, 0x96, 0x6F, 0x1E, 0x5C, 0x5A, 0x84, 0x14,
0xEA, 0x03, 0xB1, 0x8C, 0x00, 0xAA, 0x7A, 0xBF,
0x06, 0xE5, 0x4A, 0x46, 0x0B, 0x1D, 0x85, 0xDE,
0x8F, 0xEC, 0xC8, 0xFE, 0xF8, 0x01, 0x3E, 0xE1,
0xFD, 0xCE, 0x5E, 0x64, 0x2D, 0x97, 0x1C, 0x43,
0x91, 0x13, 0xA4, 0xCA, 0xE9, 0xF7, 0x3C, 0x27,
0x38, 0xD2, 0xC6, 0x33, 0x77, 0x80, 0x54, 0x20,
0xA5, 0xE4, 0x94, 0x5F, 0x90, 0xF6, 0x4B, 0x24,
0xEE, 0x78, 0xE8, 0x50, 0xBA, 0x09, 0xB5, 0xB9,
0x66, 0x4F, 0x6A, 0x7D, 0x42, 0xED, 0x31, 0xFF,
0x7F, 0x7B, 0xC9, 0xAC, 0x63, 0x40, 0x48, 0x74,
0xB0, 0x35, 0xB8, 0x07, 0xE6, 0x9C, 0xCC, 0x79,
0x18, 0xF3, 0x6D, 0x12, 0x71, 0xA8, 0x8B, 0x02,
0x3F, 0xD7, 0x65, 0x52, 0xA7, 0xF1, 0xCB, 0x3A};
#include <stdio.h>
#include <stdlib.h>

int main()
{
FILE *input_file, *output_file;
long file_size; // 文件大小
unsigned char byte;

input_file = fopen("enc", "rb");
output_file = fopen("Input.png", "wb");

// 获取文件大小
fseek(input_file, 0, SEEK_END); // 移动到文件末尾
file_size = ftell(input_file); // 获取文件指针的位置(即文件大小)

int i = 0, j = 0, i0, j0, t = 0;
for (i0 = 0xC695; i0 >= 0; i0--)
{
fseek(input_file, i0, SEEK_SET);
fread(&byte, 1, 1, input_file);

i = (i + 1) % 256;
j = (j + S_box[i]) % 256;
unsigned char temp = S_box[i];
S_box[i] = S_box[j];
S_box[j] = temp;
byte ^= S_box[(S_box[i] + S_box[j]) % 256] ^ 0x1F;
fwrite(&byte, 1, 1, output_file);
}

fclose(input_file);
fclose(output_file);

printf("处理完成!结果已保存到 %s\n", "Input.png");
return 0;
}

看出题人的WP,学了一手CyberChef。

image-20250403004844738

Crypto | Review

baby_signin

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Crypto.Util.number import getPrime, bytes_to_long
p=getPrime(128)
q=getPrime(128)
n=p*q
phi=(p-1)*(q-1)
flag="NSSCTF{xxxxxx}"
print("p=",p)
print("q=",q)
m=bytes_to_long(flag.encode())
e=4
c=pow(m,e,n)
print("c=",c)
print("n=",n)
'''
p= 182756071972245688517047475576147877841
q= 305364532854935080710443995362714630091
c= 14745090428909283741632702934793176175157287000845660394920203837824364163635
n= 55807222544207698804941555841826949089076269327839468775219849408812970713531
'''

e = 4 跟 phi 不互素,那就直接开根了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from Crypto.Util.number import *
from sympy.ntheory.residue_ntheory import sqrt_mod
from sympy.ntheory.modular import crt

p = 182756071972245688517047475576147877841
q = 305364532854935080710443995362714630091
c = 14745090428909283741632702934793176175157287000845660394920203837824364163635
n = 55807222544207698804941555841826949089076269327839468775219849408812970713531
e = 4
n = p * q
phi = (p - 1) * (q - 1)
M4 = c

for M2_p in sqrt_mod(M4, p, all_roots=True):
for m1 in sqrt_mod(M2_p, p, all_roots=True):
for M2_q in sqrt_mod(M4, q, all_roots=True):
for m2 in sqrt_mod(M2_q, q, all_roots=True):
m = crt([p, q], [m1, m2])[0]
m = long_to_bytes(m)
if m.startswith(b"NSSCTF{"):
print(m)
# b'NSSCTF{4MM_1s_so_e4s7!}'

baby_factor

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from Crypto.Util.number import *
def create():
pl = []
for i in range(3):
pl.append(getPrime(1024))
return sorted(pl)
pl = create()
m=b'NSSCTF{xxx}'
p,q,r = pl[0],pl[1],pl[2]
e = 65537
n = p*q*r
phi = (p-1)*(q-1)*(r-1)
c=pow(bytes_to_long(m),e,n)
print(f'n={n}')
print(f'phi={phi}')
print(f'c={c}')
"""
n=2741832985459799195551463586200496171706401045582705736390510500694289553647578857170635209048629428396407631873312962021354740290808869502374444435394061448767702908255197762575345798570340246369827688321483639197634802985398882606068294663625992927239602442735647762662536456784313240499437659967114509197846086151042512153782486075793224874304872205720564733574010669935992016367832666397263951446340260962650378484847385424893514879629196181114844346169851383460163815147712907264437435463059397586675769959094397311450861780912636566993749356097243760640620004707428340786147078475120876426087835327094386842765660642186546472260607586011343238080538092580452700406255443887820337778505999803772196923996033929998741437250238302626841957729397241851219567703420968177784088484002831289722211924810899441563382481216744212304879717297444824808184727136770899310815544776369231934774967139834384853322157766059825736075553
phi=2741832985459799195551463586200496171706401045582705736390510500694289553647578857170635209048629428396407631873312962021354740290808869502374444435394061448767702908255197762575345798570340246369827688321483639197634802985398882606068294663625992927239602442735647762662536456784313240499437659967114509197784246608456057052779643060628984335578973450260519106769911425793594847759982583376628098472390090331415895352869275325656949958242181688663465437185437198392460569653734315961071709533645370007008616755547195108861900432818710027794402838336405197750190466425895582236209479543326147804766393022786785337752319686125574507066082357748118175068545756301823381723776525427724798780890160482013759497102382173931716030992837059880049832065500252713739288235410544982532170147652055063681116147027591678349638753796122845041417275362394757384204924094885233281257928031484806977974575497621444483701792085077113227851520
c=2675023626005191241628571734421094007494866451142251352071850033504791090546156004348738217761733467156596330653396106482342801412567035848069931148880296036606611571818493841795682186933874790388789734748415540102210757974884805905578650801916130709273985096229857987312816790471330181166965876955546627327549473645830218664078284830699777113214559053294592015697007540297033755845037866295098660371843447432672454589238297647906075964139778749351627739005675106752803394387612753005638224496040203274119150075266870378506841838513636541340104864561937527329845541975189814018246183215952285198950920021711141273569490277643382722047159198943471946774301837440950402563578645113393610924438585345876355654972759318203702572517614743063464534582417760958462550905093489838646250677941813170355212088529993225869303917882372480469839803533981671743959732373159808299457374754090436951368378994871937358645247263240789585351233
"""

信息都透完了,直接算。

1
2
3
4
5
6
7
8
9
10
from Crypto.Util.number import *

n = ...
phi = ...
c = ...
e = 65537
d = inverse(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))
# b'NSSCTF{W0W!!_Y0u_4r3_g00d_G03!!!}'

EZ_Fermat

题目:

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
from Crypto.Util.number import getPrime, bytes_to_long
from secret import f

flag = b'NSSCTF{test_flag}'
p = getPrime(512)
q = getPrime(512)
n = p*q

m = bytes_to_long(flag)
e = 65537
c = pow(m,e,n)

R.<x> = ZZ[]
f = R(str(f))

w = pow(2,f(p),n)


print(f'{n = }\n')
print(f'{e = }\n')
print(f'{c = }\n')
print(f'{f = }\n')
print(f'{w = }\n')


'''
n = 101780569941880865465631942473186578520071435753163950944409148606282910806650879176280021512435190682009749926285674412651435782567149633130455645157688819845748439487113261739503325065997835517112163014056297017874761742768297646567397770742374004940360061700285170103292360590891188591132054903101398360047
e = 65537
c = 77538275949900942020886849496162539665323546686749270705418870515132296087721218282974435210763225488530925782158331269160555819622551413648073293857866671421886753377970220838141826468831099375757481041897142546760492813343115244448184595644585857978116766199800311200819967057790401213156560742779242511746
f = 2*x^332 - x^331 + x^329 + 3*x^328 - x^327 - 3*x^325 + x^323 - 3*x^322 - x^321 - 3*x^320 + x^319 + 2*x^318 - 4*x^317 - 3*x^315 - 2*x^314 + x^313 + x^312 + 2*x^311 + 2*x^309 + 2*x^308 + 5*x^307 + 2*x^306 + 3*x^305 + 5*x^304 + 4*x^303 + x^302 - x^301 - x^300 - 2*x^299 - 2*x^298 + x^297 + 3*x^296 - x^295 - 4*x^292 - x^290 + 4*x^289 - x^287 - 3*x^286 + x^285 - 2*x^284 + x^283 - x^282 - 2*x^281 + x^280 - 2*x^279 + x^278 + 2*x^277 - 3*x^276 - x^275 - 4*x^274 - 3*x^273 - 5*x^272 - 2*x^271 - 3*x^270 + 2*x^269 + 2*x^268 - x^267 - 2*x^266 + x^265 + x^264 - 3*x^262 - 3*x^259 + 2*x^258 - x^257 + 2*x^256 + 2*x^255 - x^254 - 2*x^253 - x^252 + 2*x^251 - x^250 + x^249 + 2*x^247 + 2*x^246 + 2*x^245 - 2*x^244 - 3*x^243 + 2*x^242 - 3*x^241 - x^240 - 3*x^239 - x^236 - 3*x^235 - 2*x^234 - x^233 - 2*x^232 - x^231 - 3*x^230 - 2*x^229 - 4*x^228 - 2*x^227 - 3*x^226 + 2*x^225 + x^224 - x^223 - 2*x^221 + 3*x^219 - x^217 - 2*x^216 + x^215 + 2*x^213 - x^212 + 3*x^211 + x^210 + 4*x^209 + x^208 - x^206 - x^205 - x^204 + 2*x^203 - 3*x^202 + 2*x^199 - x^198 + 2*x^196 - 2*x^195 + 3*x^194 + 3*x^193 - x^192 + 4*x^191 + 2*x^189 + x^186 - x^185 - x^184 + 3*x^183 + x^182 + 2*x^181 - 2*x^180 + x^177 + x^175 - x^173 + 3*x^172 + x^170 + x^169 - x^167 - 2*x^166 - x^165 - 4*x^164 - 2*x^163 + 2*x^162 + 4*x^161 - 2*x^160 - 3*x^159 - 2*x^158 - 2*x^157 + x^156 - x^155 + 3*x^154 - 4*x^153 + x^151 + 2*x^150 + x^149 - x^148 + 2*x^147 + 3*x^146 + 2*x^145 - 4*x^144 - 4*x^143 + x^142 - 2*x^140 - 2*x^139 + 2*x^138 + 3*x^137 + 3*x^136 + 3*x^135 + x^134 - x^133 + 2*x^132 + 3*x^130 - 3*x^129 - 2*x^128 - x^127 - 2*x^126 + x^125 + x^124 - 2*x^123 + x^122 - x^121 + 3*x^120 - x^119 - 2*x^118 - x^117 - x^116 - 2*x^115 + 2*x^114 + 2*x^113 - 3*x^112 - x^111 - 4*x^110 + x^109 + x^108 + x^106 - 4*x^105 + x^104 - x^103 - x^101 + x^100 - 2*x^99 + x^98 - x^97 + 3*x^96 + 3*x^94 - x^93 - x^92 + x^91 - 2*x^90 + x^89 - x^88 + x^87 - x^86 + x^85 + x^84 - x^83 + x^79 - 3*x^78 - 2*x^77 + x^74 + 3*x^73 - x^72 - 3*x^71 - 2*x^70 + x^69 - 3*x^66 + x^65 + x^64 - 4*x^62 - x^61 + x^60 - x^59 + 3*x^58 - x^57 - x^54 + 3*x^53 + x^51 - 3*x^50 - x^49 + 2*x^47 - x^46 - x^44 + x^43 - x^42 - 4*x^41 - 3*x^39 - x^37 - x^36 - 3*x^35 + x^34 + x^33 - 2*x^32 + 2*x^31 - x^30 + 2*x^29 - 2*x^28 - 2*x^27 - x^24 + x^22 - 5*x^21 + 3*x^20 + 2*x^19 - x^18 + 2*x^17 + x^16 - 2*x^15 - 2*x^14 + x^13 + x^12 + 2*x^11 - 3*x^10 + 3*x^9 + 2*x^8 - 4*x^6 - 2*x^5 - 4*x^4 + x^3 - x^2 - 1
w = 32824596080441735190523997982799829197530203904568086251690542244969244071312854874746142497647579310192994177896837383837384405062036521829088599595750902976191010000575697075792720479387771945760107268598283406893094243282498381006464103080551366587157561686900620059394693185990788592220509670478190685244
'''

根据题意 w2f(p)modNw \equiv 2^{f(p)} \mod N ,那如果我能根据 f(p)f(p) ,找到一个常数C,使得 w2cmodpw \equiv 2^c \mod p 取最大公因数就能解出p了。

根据欧拉定理:f(p)C=k(p1)f(p)-C=k(p-1) ,设 C=f(x)C=f(x) 就,f(p)f(x)=an(pnx)f(p)-f(x)= \sum a_n(p^n-x),不难发现 x=1x=1 时因式分解就是p-1的倍数,所以 C=f(1)C=f(1) 是符合要求的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from Crypto.Util.number import *
n = ...
e = 65537
c = ...
w = ...

# 定义多项式环
f = ...

R.<x> = ZZ[]
f = R(str(f))
C = f(1)
print(C)

p = gcd(pow(2,int(C),n)-w, n)
q = n // int(p)
assert p*q==n
phi = (p-1)*(q-1)
d = inverse(e, int(phi))
m = pow(c, d, n)
print(long_to_bytes(int(m)))
# b'NSSCTF{8d1e3405044a79b23a44a43084bd994b}'

baby_factor_revenge

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from Crypto.Util.number import *
def create():
pl = []
for i in range(3):
pl.append(getPrime(1024))
return sorted(pl)
pl = create()
m=b'NSSCTF{xxxxxx}'
p,q,r = pl[0],pl[1],pl[2]
n = p*q*r
phi = (p-1)*(q-1)*(r-1)
e=65537
phi_2=(p-1)*(q-1)
n2=p*q
c=pow(bytes_to_long(m),e,n2)
print(f'n={n}')
print(f'phi={phi}')
print(f'c={c}')
"""
n=3191868707489083296976422171754481125088448532695639929013026951283334085716937496519972309690132954050242378974370025245594553866043111294840209514577676946872746793700126873931085112786381515154186105142460622301297252278473097650013016482539838576476763183025029834004241446095147665598581368214114851984460699747964946764645986828307675081596907634022110868102739948513844625534865764252668312850364286204872187001344218083941399088833989233474318289529103178632284291007694811574023047207470113594082533713524606268388742119103653587354956091145288566437795469230667897089543048576812362251576281067933183713438502813206542834734983616378764909202774603304124497453696792428111112644362307853143219890039129054302905340695668256116233515323529918746264727874817221051242387145263342018617858562987223211598238069486447049955021864781104312134816578626968386395835285074116149472750100154961405785440009296096563521430833
phi=3191868707489083296976422171754481125088448532695639929013026951283334085716937496519972309690132954050242378974370025245594553866043111294840209514577676946872746793700126873931085112786381515154186105142460622301297252278473097650013016482539838576476763183025029834004241446095147665598581368214114851984394758254181484105857103844940487787404078873566779953101987404891507588290232992132681729619718279684673827347612899406697514777723904351697638562060304399923174376216080338949397741477013367831377040866937433520175862575061413321076151761545984886547872427147498175814451096795344136954743868643889768901204954902708679102384061694877757565486240670882343628571424084461972849147495569088820011108794930593172573959423278140327579049114196086428504291102619820322231225943837444001821535593671764186251713714593498207219093585758479440828038119079608764008747539277397742897542501803218788455452391287578171880267200
c=8847973599594272436100870059187158819529199340583461915617467299706215012295598155778224026186157290320191983062022702191439286020733625396165573681688842631368993650799220713225485752608650482408353598320160571916055498330875851476520668973214124194890108144336715482373743731578734960096351460142579903010557821654345995923836938260379746304222820835040419844947019844885128550552066290798665884099701340641403329066058638137944934073185448687990744852400616823426082588916251127609191094346267837812018236673478691437630461425526779014305216914035039981685211625653600564431704400207095883904994772993227506462664
"""

参考了博客:https://blog.csdn.net/AxuAxuA123/article/details/141454579

二元的pq和phi解方程就行,那怎么解三元的呢?根据欧拉定理 aϕ(N)10modNa^{\phi(N)} - 1 \equiv 0 \mod N \\ ,我们尝试对 aϕ(N)1a^{\phi(N)} - 1因式分解,这些因式一定包括了p、q、r,而且很有可能p、q、r不同时出现在同一因式,这就意味着如果其中存在一个因式仅包含了r不包括pq,只要把这个因式和N取GCD就解出r了。ϕ(N)=2ts\phi(N)=2^t \cdot s,s为奇数,利用平方差公式就可以将其分解为多个因式。

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
from Crypto.Util.number import *
import random
from math import gcd, isqrt
from sympy import symbols, Eq, solve


def factor_n_with_phi(n: int, phi_n: int, max_attempts=10):
t = 0
s = phi_n
while s % 2 == 0:
s = s // 2
t += 1
for _ in range(max_attempts):
a = random.randint(0, n - 1)
current = pow(a, s, n)
for n_exp in range(t):
exponent = s * (2 ** (t - n_exp))
current = pow(a, exponent, n)
r = gcd(current + 1, n)
if r == 1:
continue
elif r >= n:
break
else:
return r
return None


n = ...
phi = ...
c = ...

r = factor_n_with_phi(n, phi, 10)
assert n % r == 0
n //= r
phi //= r - 1

x, y = symbols("p q")
solutions = solve((Eq(x + y, n - phi + 1), Eq(x * y, n)), (x, y))
p, q = solutions[0]
p, q = int(p), int(q)

p, q, r = sorted([p, q, r])
n = p * q
phi = (p - 1) * (q - 1)
e = 65537
d = inverse(e, phi)
m = pow(c, d, n)
print(long_to_bytes(m))
# b'NSSCTF{D0_Y0u_knnn0www_p71!!!}'

RSA_and_DSA

题目:

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
from random import getrandbits, randint
from secrets import randbelow
from Crypto.Util.number import *
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
import hashlib
import random
import gmpy2

ink = getPrime(20)
p1 = getPrime(512)
q1 = getPrime(512)
N = p1 * q1
phi = (p1 - 1) * (q1 - 1)
while True:
d1 = getRandomNBitInteger(200)
if GCD(d1, phi) == 1:
e = inverse(d1, phi)
break
c_ink = pow(ink, e, N)
print(f"c_ink=", c_ink)
print(f"e=", e)
print(f"N=", N)

k = getPrime(64)
q = getPrime(160)


def sign(msg, pub, pri, k):
(p, q, g, y) = pub
x = pri
r = int(pow(g, k, p) % q)
h = int(hashlib.sha256(msg).digest().hex(), 16)
s = int((h + x * r) * gmpy2.invert(k, q) % q)
return (r, s)


while True:
temp = q * getrandbits(864)
if isPrime(temp + 1):
p = temp + 1
break
assert p % q == 1
h = randint(1, p - 1)
g = pow(h, (p - 1) // q, p)
y = pow(g, k, p)
pub = (p, q, g, y)
pri = random.randint(1, q - 1)
print(f"(r1,s1)=", sign(b"GHCTF-2025", pub, pri, k))
print(f"(r2,s2)=", sign(b"GHCTF-2025", pub, pri, k + ink))
print(f"{g= }")
print(f"{q= }")
print(f"{p= }")
print(f"{y= }")
key = hashlib.sha1(str(pri).encode()).digest()[:16]
cipher = AES.new(key, AES.MODE_ECB)
flag = "NSSCTF{xxxxxxxxx}"
ciphertext = cipher.encrypt(pad(flag.encode(), 16))
print(f"{ciphertext = }")
"""
c_ink= 84329531596553394336538987023357227935440127545924398750500007122949822951975451942488164538560925222694222413022235832336439700420379598454619959178424907616592885325169668838139433265501326382467741883799799897305247164532663683724926267222341485376684034461780316163663624769479766276645610470850267093664
e= 100797590979191597676081881632112443200677974501832055481332601002844223186483558337099380805371010917502984674789369037985572270571944684404114475915036053451756526659905789324413633016308331745100752282051937597697581233757669107763643041665187533373053952694612521031477624363476981177214961821456672635823
N= 133020919573254586736009662994351483197630110046444622015176359062686053521475990861985101412597512894313048001198942449066636145265799205815566892581351543233960812384316942438814742826123037762680960898927252792974233266551853930274479435403549161383103059746381782668941421906340168652870371226382805032027
(r1,s1)= (105538622724986198173818280402723234123231812870, 165871242333491991006684781121637801537623792920)
(r2,s2)= (895673018852361693797535983771888430717799939767, 511956887428148277909616673338517730698888202223)
g= 97444164915108666817264719918456841236668149777715575246719562319277238318814584882880249446488655758781498681349330709135670188875982069778879957837454582916193915374305422049064769688749957611500682447936476425649642359105731049262259786188565867271216015835626264543593116387612078934710741467063982007499
q= 974306102330898613562307019447798934376234044213
p= 113996945917185663452903189185812083054654586038361814576057637684218572059191009152754335053396974825607186512631652893899380922217026759410880236546966561476761050482902589270845489570126254333374605973087540746242818447451510386137109253463070487353845675998098620056687507969012229115435439218407426962991
y= 8015503667614219250943034151839311927430676423719991507127801373333532219335171760992873121586820712328636972152697436159934583810723294897449200937370031784164230148453787378834760102389031574149857480339843366568164403131143385627621208571673677878768568991050568882099039880976450795530322753270408770484
ciphertext = b'\xb0\ra\x9c\xeb9y\xd5k\xfde\xdfJ\xba\n\xce^u\xae\x81J8\xe4\x8da\xdf;H@WV5'
"""

先用低解密指数攻击(wiener攻击或BD攻击 )获取ink,再破掉DSA的私钥。

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
from random import getrandbits, randint
from secrets import randbelow
from Crypto.Util.number import *
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
import hashlib
import random
import gmpy2

c_ink = ...
e = ...
N = ...
(r1, s1) = ...
(r2, s2) = ...
g = ...
q = ...
p = ...
y = ...

d = 1051176891915773585468122074232642482031663118639646043742447
ink = pow(c_ink, d, N)
print(pow(ink, e, N) == c_ink)

msg = b"GHCTF-2025"
h = int(hashlib.sha256(msg).digest().hex(), 16)
pri = (h * s1 - h * s2 - ink * s1 * s2) * inverse(r1 * s2 - r2 * s1, q) % q

ciphertext = (
b"\xb0\ra\x9c\xeb9y\xd5k\xfde\xdfJ\xba\n\xce^u\xae\x81J8\xe4\x8da\xdf;H@WV5"
)
key = hashlib.sha1(str(pri).encode()).digest()[:16]
cipher = AES.new(key, AES.MODE_ECB)
flag = "NSSCTF{xxxxxxxxx}"
plaintext = cipher.decrypt(ciphertext)
print(plaintext)
# b'NSSCTF{n0_RRRrs4_or_DDDS4????}\x02\x02'

baby_lattice

题目:

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
from Crypto.Util.number import *
from Crypto.Cipher import AES
import os
from Crypto.Util.Padding import pad
from secret import flag
miku = 30
p = getPrime(512)
key = getPrime(512)
while key> p:
key= getPrime(512)
ts = []
gs = []
zs = []
for i in range(miku):
t = getPrime(512)
z = getPrime(400)
g= (t * key + z) % p
ts.append(t)
gs.append(g)
zs.append(z)
print(f'p = {p}')
print(f'ts = {ts}')
print(f'gs = {gs}')
iv= os.urandom(16)
cipher = AES.new(str(key).encode()[:16], AES.MODE_CBC,iv)
ciphertext=cipher.encrypt(pad(flag.encode(),16))
print(f'iv={iv}')
print(f'ciphertext={ciphertext}')


LHNP问题,脚本直接抄https://hasegawaazusa.github.io/hidden-number-problem.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from Crypto.Util.number import *
from Crypto.Cipher import AES

p = ...
ts = ...
gs = ...

p = p
rs = ts
cs = gs
t = len(rs)
kbits = 400
K = 2 ** kbits

P = identity_matrix(t) * p
RC = matrix([[-1, 0], [0, 1]]) * matrix([rs, cs])
KP = matrix([[K / p, 0], [0, K]])
M = block_matrix([[P, 0], [RC, KP]], subdivide=False)
shortest_vector = M.LLL()
x = shortest_vector[1, -2] / K * p % p

iv=b'\x88\x0c\x7f\x92\xd7\xb7\xaf4\xe4\xfb\xd1_\xab\xff)\xb8'
ciphertext=b'\x94\x198\xd6\xa2mK\x00\x06\x7f\xad\xa0M\xf7\xadV;EO$\xee\xcdB0)\xfb!&8%,M'
cipher = AES.new(str(x).encode()[:16], AES.MODE_CBC,iv)
plaintext=cipher.decrypt(ciphertext)
print(plaintext)
# b'NSSCTF{F@@@un7_L4444t1c3333!!}\x02\x02'

MIMT_RSA

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 Crypto.Util.number import *
from hashlib import md5
from secret import KEY, flag


assert int(KEY).bit_length() == 36
assert not isPrime(KEY)

p = getPrime(1024)
q = getPrime(1024)
n = p * q
e = 0x10001

ck = pow(KEY, e, n)


assert flag == b"NSSCTF{" + md5(str(KEY).encode()).hexdigest().encode() + b"}"

print(f"{n = }")
print(f"{e = }")
print(f"{ck = }")

"""
n = 26563847822899403123579768059987758748518109506340688366937229057385768563897579939399589878779201509595131302887212371556759550226965583832707699167542469352676806103999861576255689028708092007726895892953065618536676788020023461249303717579266840903337614272894749021562443472322941868357046500507962652585875038973455411548683247853955371839865042918531636085668780924020410159272977805762814306445393524647460775620243065858710021030314398928537847762167177417552351157872682037902372485985979513934517709478252552309280270916202653365726591219198063597536812483568301622917160509027075508471349507817295226801011
e = 65537
ck = 8371316287078036479056771367631991220353236851470185127168826270131149168993253524332451231708758763231051593801540258044681874144589595532078353953294719353350061853623495168005196486200144643168051115479293775329183635187974365652867387949378467702492757863040766745765841802577850659614528558282832995416523310220159445712674390202765601817050315773584214422244200409445854102170875265289152628311393710624256106528871400593480435083264403949059237446948467480548680533474642869718029551240453665446328781616706968352290100705279838871524562305806920722372815812982124238074246044446213460443693473663239594932076

"""

题目名字打错了,应该是MITM中间攻击的思路,就是对于 f(m)=cf(m)=c 爆破m很困难,如果能把m分成两段,令 m=m1m2m=m_1\cdot m_2,并且能把问题转化为 g(m1)=h(m2)g(m_1)=h(m_2) 的形式,那么只需要先爆破m~1~,把所有可能的 g(m1)g(m_1) 存起来,再爆破m~2~,看看 h(m1)h(m_1) 是不是在里面。时间复杂度缩减为 O(n)O(\sqrt n)

KEY虽然小,但e大,也没法用小明文攻击。这道题提示很明显,KEY不是质数,那不就是分解KEY吗

m1eckm2emodnm_1^e \equiv ck\cdot m_2^{-e} \mod n

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
from Crypto.Util.number import *
import time
from hashlib import md5

n = 26563847822899403123579768059987758748518109506340688366937229057385768563897579939399589878779201509595131302887212371556759550226965583832707699167542469352676806103999861576255689028708092007726895892953065618536676788020023461249303717579266840903337614272894749021562443472322941868357046500507962652585875038973455411548683247853955371839865042918531636085668780924020410159272977805762814306445393524647460775620243065858710021030314398928537847762167177417552351157872682037902372485985979513934517709478252552309280270916202653365726591219198063597536812483568301622917160509027075508471349507817295226801011
e = 65537
ck = 8371316287078036479056771367631991220353236851470185127168826270131149168993253524332451231708758763231051593801540258044681874144589595532078353953294719353350061853623495168005196486200144643168051115479293775329183635187974365652867387949378467702492757863040766745765841802577850659614528558282832995416523310220159445712674390202765601817050315773584214422244200409445854102170875265289152628311393710624256106528871400593480435083264403949059237446948467480548680533474642869718029551240453665446328781616706968352290100705279838871524562305806920722372815812982124238074246044446213460443693473663239594932076
KEY = 0
e_inv = inverse(e, n)

start_time = time.time()
dict_temp = {}
for m1 in range(262144):
dict_temp[pow(m1, e, n)] = m1
t1 = time.time() - start_time
print(f"Time taken to create dictionary: {t1} seconds")

start_time = time.time()
for m2 in range(262144, 68719476736):
try:
temp = (ck * pow(m2, -e, n)) % n
if temp in dict_temp:
KEY = m2 * dict_temp[temp]
print(f"Found key: {KEY}")
break
except:
continue
t2 = time.time() - start_time
print(f"Time taken to brute force: {t2} seconds")

# KEY = 62495925932
assert ck == pow(KEY, e, n)
flag = b"NSSCTF{" + md5(str(KEY).encode()).hexdigest().encode() + b"}"
print(flag)
# b'NSSCTF{14369380f677abec84ed8b6d0e3a0ba9}'

这是我跑的时间:

1
2
3
4
# Time taken to create dictionary: 17.384088039398193 seconds
# Found key: 62495925932
# Time taken to brute force: 57.97086429595947 seconds
# b'NSSCTF{14369380f677abec84ed8b6d0e3a0ba9}'

交换一下m~1~ m~2~的位置,用时差不多。

m2eckm1emodnm_2^e \equiv ck\cdot m_1^{-e} \mod n

1
2
3
4
Time taken to create dictionary: 38.08794832229614 seconds
Found key: 62495925932
Time taken to brute force: 34.84964156150818 seconds
b'NSSCTF{14369380f677abec84ed8b6d0e3a0ba9}'

Sin

题目:

1
2
3
4
5
6
7
8
9
10
11
12
13
from Crypto.Util.number import bytes_to_long

print(
(2 * sin((m := bytes_to_long(b"NSSCTF{test_flag}"))) - 2 * sin(m) * cos(2 * m)).n(
1024
)
)

"""
m的值即为flag
0.002127416739298073705574696200593072466561264659902471755875472082922378713642526659977748539883974700909790177123989603377522367935117269828845667662846262538383970611125421928502514023071134249606638896732927126986577684281168953404180429353050907281796771238578083386883803332963268109308622153680934466412
"""

先在高精度下求m的一个解mf,根据公式 m=mf+2kπm=mf+2k\pi​ 构造格:

[m1k][10K210K1K2mf00K22π]=[mK10]\begin{bmatrix} m & -1 & -k \\ \end{bmatrix}\cdot \begin{bmatrix} 1 & 0 & K_2 \cdot 1 \\ 0 & K_1 & K_2 \cdot mf \\ 0 & 0 & K_2 \cdot 2\pi \\ \end{bmatrix} =\begin{bmatrix} m & -K_1 & 0 \\ \end{bmatrix}

受限于计算过程中的精度损失,这里的0不是真正的0,而是在相对于K2的数量级下的一个较小的数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Util.number import *

R = RealField(1024)
r = R(0.002127416739298073705574696200593072466561264659902471755875472082922378713642526659977748539883974700909790177123989603377522367935117269828845667662846262538383970611125421928502514023071134249606638896732927126986577684281168953404180429353050907281796771238578083386883803332963268109308622153680934466412)

mf = arcsin((r / 4) ** (1 / 3))
p = mf.precision()
K1 = 2**400
K2 = 2**p
M = matrix(QQ, [[1, 0, K2 * 1], [0, K1, K2 * mf], [0, 0, K2 * 2 * pi.n(p)]])

for line in M.LLL():
if abs(line[1]) == K1:
print(float(line[2]))
m = abs(int(line[0]))
print(long_to_bytes(m))
# b'NSSCTF{just_make_a_latter_and_LLL_is_OK_padpad}'

EZ_Fermat_bag_PRO

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
from Crypto.Util.number import getPrime, bytes_to_long
from random import *
from secret import f, flag

assert len(flag) == 88
assert flag.startswith(b'NSSCTF{')
assert flag.endswith(b'}')

p = getPrime(512)
q = getPrime(512)
n = p*q

P.<x,y> = ZZ[]
f = P(str(f))

w = pow(2,f(p,q),n)
assert all(chr(i) in ''.join(list(set(str(p)))) for i in flag[7:-1:])
c = bytes_to_long(flag) % p



print(f'{n = }\n')
print(f'{f = }\n')
print(f'{w = }\n')
print(f'{c = }\n')

'''
n = 95656952327201449381426394713246214670537600365883923624876350719801926817916514429721785287844335184715049179879891389941974481490433975689601829920289485889138252888029716516069912637121531561601839948367426922036690701168975937162280451323099126372019216020898338909808577022618554997063496690156977790629
f = x^31 - x^30*y - 2*x^29*y^2 + 7*x^28*y^3 + 2*x^27*y^4 - 4*x^24*y^7 + 3*x^23*y^8 - x^20*y^11 - 4*x^19*y^12 + x^18*y^13 - 5*x^17*y^14 - 4*x^16*y^15 - x^15*y^16 + x^14*y^17 + x^13*y^18 + x^12*y^19 - 2*x^11*y^20 - 3*x^9*y^22 + 5*x^7*y^24 + x^6*y^25 + 6*x^4*y^27 + x^3*y^28 + 2*x*y^30 + y^31 - 2*x^30 - 3*x^29*y + 2*x^28*y^2 + 2*x^27*y^3 - x^26*y^4 - x^25*y^5 - 2*x^24*y^6 - 3*x^23*y^7 - 3*x^22*y^8 - 3*x^20*y^10 - 4*x^19*y^11 + 2*x^18*y^12 + x^15*y^15 - x^14*y^16 - 2*x^12*y^18 - 3*x^11*y^19 - x^10*y^20 + x^9*y^21 + 2*x^8*y^22 + x^7*y^23 + x^5*y^25 - x^4*y^26 - 2*x^3*y^27 - 2*x^2*y^28 - y^30 - 2*x^29 - x^28*y + 3*x^26*y^3 - x^25*y^4 - 2*x^24*y^5 + x^23*y^6 - x^22*y^7 - x^20*y^9 + 2*x^19*y^10 + 2*x^18*y^11 + x^16*y^13 + x^15*y^14 + x^14*y^15 + x^13*y^16 + x^12*y^17 + 5*x^11*y^18 - x^9*y^20 - 2*x^8*y^21 - 5*x^7*y^22 - 2*x^6*y^23 + 3*x^5*y^24 - 5*x^3*y^26 - x^2*y^27 + 2*x*y^28 - y^29 + 3*x^28 + 3*x^27*y - 2*x^26*y^2 + x^25*y^3 + 2*x^24*y^4 - x^23*y^5 - 2*x^22*y^6 - 3*x^20*y^8 - 3*x^19*y^9 + 4*x^17*y^11 - x^16*y^12 - 3*x^15*y^13 - 2*x^14*y^14 + x^13*y^15 + 2*x^12*y^16 - 2*x^11*y^17 + x^10*y^18 - 2*x^9*y^19 + x^8*y^20 - 2*x^7*y^21 - x^6*y^22 + x^5*y^23 - x^4*y^24 + x^3*y^25 + x^2*y^26 - x*y^27 - y^28 + x^27 + x^26*y - 2*x^24*y^3 + x^23*y^4 - 3*x^22*y^5 - 2*x^21*y^6 - 2*x^20*y^7 - 5*x^19*y^8 + 2*x^18*y^9 - 5*x^17*y^10 + x^16*y^11 - 3*x^15*y^12 - 4*x^14*y^13 - x^13*y^14 + x^12*y^15 + 3*x^11*y^16 + 2*x^10*y^17 - 4*x^9*y^18 - 2*x^6*y^21 + x^5*y^22 + 4*x^3*y^24 + 2*x^2*y^25 + 2*x*y^26 - 2*y^27 + x^25*y + x^24*y^2 + x^23*y^3 + 5*x^22*y^4 + x^20*y^6 - 3*x^19*y^7 + x^18*y^8 - x^17*y^9 + 2*x^15*y^11 - x^14*y^12 + 2*x^13*y^13 - x^12*y^14 + 4*x^11*y^15 - x^10*y^16 - 2*x^6*y^20 - x^5*y^21 + 3*x^3*y^23 + x^2*y^24 - 3*x*y^25 - 3*y^26 + 3*x^25 - 2*x^23*y^2 - x^21*y^4 + x^17*y^8 + 2*x^16*y^9 - x^15*y^10 - 2*x^14*y^11 - x^13*y^12 + 2*x^12*y^13 - 2*x^11*y^14 - x^9*y^16 - x^8*y^17 - x^6*y^19 - x^5*y^20 + x^4*y^21 + x^3*y^22 + 5*x*y^24 - 2*y^25 - x^24 + 2*x^23*y + x^22*y^2 - x^21*y^3 - x^19*y^5 + x^18*y^6 - x^17*y^7 + 2*x^16*y^8 - 4*x^15*y^9 - x^14*y^10 - x^13*y^11 - x^12*y^12 + 4*x^10*y^14 + 2*x^9*y^15 - x^8*y^16 - 2*x^7*y^17 - 2*x^6*y^18 + 4*x^5*y^19 + x^4*y^20 + 2*x^2*y^22 - 5*x*y^23 - y^24 + x^23 - x^22*y + 2*x^21*y^2 - x^20*y^3 - x^18*y^5 - x^17*y^6 - 5*x^15*y^8 + x^14*y^9 - 3*x^13*y^10 + 3*x^12*y^11 + 2*x^11*y^12 - 2*x^10*y^13 - 2*x^9*y^14 - x^8*y^15 + 2*x^7*y^16 - 2*x^6*y^17 - 4*x^5*y^18 - 5*x^3*y^20 - x^2*y^21 - x*y^22 - 4*y^23 - x^22 + 2*x^21*y - 2*x^20*y^2 - 2*x^19*y^3 - 3*x^17*y^5 - x^16*y^6 - x^15*y^7 + 4*x^13*y^9 + 2*x^12*y^10 + 3*x^11*y^11 + 2*x^10*y^12 - x^9*y^13 - x^7*y^15 + 2*x^6*y^16 + x^3*y^19 + 2*x^2*y^20 + 2*x*y^21 + 3*y^22 - 3*x^21 - x^20*y - x^19*y^2 + 2*x^17*y^4 - x^16*y^5 - x^15*y^6 + x^14*y^7 - 5*x^12*y^9 - 2*x^11*y^10 + x^10*y^11 + x^6*y^15 + x^5*y^16 + x^4*y^17 - 3*x^2*y^19 - 2*x*y^20 - 2*y^21 + x^20 + 2*x^19*y - 2*x^17*y^3 + 2*x^16*y^4 - 3*x^15*y^5 + 4*x^14*y^6 + 2*x^13*y^7 - x^12*y^8 - 2*x^11*y^9 + x^10*y^10 + 6*x^9*y^11 + x^8*y^12 + x^7*y^13 + 2*x^5*y^15 + 4*x^4*y^16 + x^3*y^17 - x^2*y^18 + 3*x*y^19 - x^17*y^2 + 2*x^16*y^3 + 3*x^14*y^5 - x^13*y^6 + 2*x^11*y^8 + x^10*y^9 + 3*x^9*y^10 - x^7*y^12 - x^6*y^13 + 3*x^5*y^14 - 4*x^4*y^15 + x^2*y^17 + 2*y^19 - x^18 - x^16*y^2 - 2*x^14*y^4 - 2*x^13*y^5 - 2*x^12*y^6 + 2*x^11*y^7 + 3*x^9*y^9 + 3*x^8*y^10 + x^6*y^12 - x^4*y^14 + 2*x^3*y^15 + 2*x^2*y^16 - 2*x*y^17 - x^17 - 4*x^16*y - 2*x^15*y^2 + 2*x^14*y^3 - x^13*y^4 + x^12*y^5 - 2*x^11*y^6 - 3*x^10*y^7 - x^9*y^8 - 5*x^8*y^9 + 2*x^7*y^10 + 2*x^6*y^11 - x^5*y^12 + x^4*y^13 - 3*x^2*y^15 + x*y^16 - 3*x^16 + x^15*y - 3*x^14*y^2 - x^13*y^3 - x^12*y^4 + 2*x^11*y^5 - x^10*y^6 + 5*x^8*y^8 + 3*x^7*y^9 + 3*x^6*y^10 + 2*x^5*y^11 + 4*x^4*y^12 + 2*x^3*y^13 + x^2*y^14 - 3*x*y^15 - x^15 + 3*x^14*y + x^13*y^2 - x^12*y^3 - 3*x^11*y^4 + x^10*y^5 - x^9*y^6 + 2*x^8*y^7 - x^7*y^8 + 4*x^5*y^10 - 2*x^4*y^11 + x^3*y^12 - x^14 + x^13*y + 2*x^12*y^2 + x^11*y^3 - 5*x^10*y^4 - x^9*y^5 - 3*x^8*y^6 - 2*x^7*y^7 + x^6*y^8 + 3*x^5*y^9 + x^4*y^10 + 2*x^3*y^11 - x^2*y^12 - 4*x*y^13 + 3*y^14 + x^12*y - 2*x^11*y^2 - x^9*y^4 - x^8*y^5 + 5*x^7*y^6 - 4*x^6*y^7 + 3*x^5*y^8 + 4*x^4*y^9 - 3*x^3*y^10 - x^2*y^11 - 2*x*y^12 - 3*y^13 + 3*x^12 + x^11*y + x^10*y^2 + x^9*y^3 + x^8*y^4 - x^6*y^6 - x^5*y^7 - 4*x^3*y^9 - x^2*y^10 - 3*x*y^11 - 2*y^12 + x^10*y + 5*x^9*y^2 + x^8*y^3 + 3*x^5*y^6 + x^4*y^7 + 2*x^3*y^8 - 4*x^2*y^9 + 2*x*y^10 + 3*y^11 - x^10 - 2*x^9*y - 2*x^7*y^3 - x^6*y^4 + x^5*y^5 + 3*x^4*y^6 - 2*x^2*y^8 - x*y^9 + 4*x^9 - 3*x^8*y - 3*x^6*y^3 + x^5*y^4 - x^4*y^5 - 2*x^3*y^6 - 2*x^2*y^7 + x*y^8 + 4*y^9 + 2*x^8 - x^7*y - 2*x^5*y^3 - 4*x^4*y^4 + 3*x^3*y^5 + 4*x^2*y^6 + 2*x*y^7 - 2*y^8 + 2*x^7 + 3*x^5*y^2 + 3*x^2*y^5 - x*y^6 - 4*x^6 + 6*x^3*y^3 + 2*x^2*y^4 - 2*x*y^5 - 3*y^6 + x^5 - 3*x^4*y + x^3*y^2 + x^2*y^3 - 2*x*y^4 + 2*x^4 - 2*x^3*y + 6*x^2*y^2 - 3*x*y^3 - 2*y^4 - 5*x^3 - 2*x^2*y - 2*x*y^2 + 3*y^3 + 2*x^2 - x*y + y^2 - 2*x + 2*y - 2
w = 12796020294902567574981427270787776254781813995526831579805652479456168245098217943847166109912113827479436654134179666391771173421469188197935460525521295192736123648410762964187396897298542198935971755852754544978564521188423737649175136194386664628304164316905741781089536713701674793641345344818309314224
c = 10266913434526071998707605266130137733134248608585146234981245806763995653822203763396430876254213500327272952979577138542487120755771047170064775346450942
'''

和之前的差不多,不妨先设 C=f(x,y)C=f(x,y) ,则 f(p,q)f(x,y)=an(pmqnxmyn)f(p,q)-f(x,y)= \sum a_n(p^mq^n-x^my^n),首先得想办法把这个q提出去,y必须是q的倍数,我们能选用的就只有N了,pmqnxmNn=(pmxmpn)qnp^mq^n-x^mN^n=(p^m-x^mp^n)q^n,显然只要令 x=1x=1,问题就解决了,所以C=f(1,n)C=f(1, n)​。

算出p、q还没完,根据题目,c只是模p得到的,而flag确定共88位,全是数字,整理一下:m=kp+C=mmin+256ixim=kp+C=m_{min}+\sum256^{i} \cdot x_i

m~min~表示全取零的m,x~i~ 是每位数字,根据这个等式 256ixi+(mminC)kp=0\sum256^{i} \cdot x_i + (m_{min}-C)- kp=0 构造格:

[x80x791k][1000256800100256790010256780001mminC0000p]=[x80x79x110]\begin{bmatrix} x_{80} & x_{79} & \cdots & 1 & k \\ \end{bmatrix}\cdot \begin{bmatrix} 1 & 0 & 0 & \cdots & 0 & 256^{80} \\ 0 & 1 & 0 & \cdots & 0 & 256^{79} \\ 0 & 0 & 1 & \cdots & 0 & 256^{78} \\ \vdots & \vdots & \vdots & \ddots & \vdots & \vdots \\ 0 & 0 & 0 & \cdots & 1 & m_{min}-C \\ 0 & 0 & 0 & \cdots & 0 & -p \\ \end{bmatrix} =\begin{bmatrix} x_{80} & x_{79} & \cdots & x_{1} & 1 & 0 \\ \end{bmatrix}

按照预期,格基规约后会得到一串前80位小于10,后两位依次取1和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
from Crypto.Util.number import *

e = 65537
n = ...
w = ...
c = ...

P.<x,y> = ZZ[]
f = ...

C = f(1, n)

p = int(gcd(pow(2,int(C),n)-w, n))
q = n // int(p)
print(p, q, int(p*q))
assert p*q==n

flag_min = b"NSSCTF{" + b"0" * 80 + b"}"
m_min = bytes_to_long(flag_min)

# print(m_min , c)
M = matrix.identity(82)
for i in range(80):
M[i, -1] = int(256**(80-i))
M[-1, -1] = p
M[-2, -1] = m_min - c
# print(M)
for line in M.LLL():
if abs(line[-1])==0 and abs(line[-2])==1:
flag = "NSSCTF{"
for i in range(80):
flag += str(line[-2]*line[i])
flag += "}"
if len(flag) == 88:
print(flag)
# NSSCTF{38886172735077060750460332815973614272222523052135584902884007925985948919714862}

river

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
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from hashlib import md5
from secret import flag, seed, mask


class 踩踩背:
def __init__(self, n, seed, mask, lfsr=None):
self.state = [int(b) for b in f"{seed:0{n}b}"]
self.mask_bits = [int(b) for b in f"{mask:0{n}b}"]
self.n = n
self.lfsr = lfsr

def update(self):
s = sum([self.state[i] * self.mask_bits[i] for i in range(self.n)]) & 1
self.state = self.state[1:] + [s]

def __call__(self):
if self.lfsr:
if self.lfsr():
self.update()
return self.state[-1]
else:
self.update()
return self.state[-1]


class 奶龙(踩踩背):
def __init__(self, n, seed, mask):
super().__init__(n, seed, mask, lfsr=None)


n = 64
assert seed.bit_length == mask.bit_length == n
lfsr1 = 奶龙(n, seed, mask)
lfsr2 = 踩踩背(n, seed, mask, lfsr1)
print(f"mask = {mask}")
print(f"output = {sum(lfsr2() << (n - 1 - i) for i in range(n))}")
print(
f"enc = {AES.new(key=md5(str(seed).encode()).digest(), mode=AES.MODE_ECB).encrypt(pad(flag, 16))}"
)
# mask = 9494051593829874780
# output = 13799267741689921474
# enc = b'\x03\xd1#\xb9\xaa5\xff3y\xba\xcb\x91`\x9d4p~9r\xf6i\r\xca\x03dW\xdb\x9a\xd2\xa6\xc6\x85\xfa\x19=b\xb2)5>]\x05,\xeb\xa0\x12\xa9\x1e'

本来是想利用算法的性质,直接推出一些位置的,发现这个方法好笨。看了一眼官方WP,学了一个叫剪枝的玩意。前几位是否符合要求是可以判断出来的。满足条件的有9000多,填满64位后,逆推一下seed,看看解出来的是不是符合要求。

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 Crypto.Cipher import AES
from hashlib import md5

Init_pattern = "011______1______1_11_1__1__1_1_1__1________11___1_1_______1___11"
Result = "1011111110000000110110001110011000111111111011110011111111000010"
enc = b"\x03\xd1#\xb9\xaa5\xff3y\xba\xcb\x91`\x9d4p~9r\xf6i\r\xca\x03dW\xdb\x9a\xd2\xa6\xc6\x85\xfa\x19=b\xb2)5>]\x05,\xeb\xa0\x12\xa9\x1e"

mask = 9494051593829874780
output = 13799267741689921474
mask = [int(i) for i in bin(mask)[2:]]
current = []
Count = 0


def reState(current: list[64]):
l = current[-1]
for i in range(63):
l ^= mask[i + 1] & current[i]
current = [l] + current[:-1]
return current


def cut(s: list):
s_out = []
p = -1
for i in range(len(s)):
if i == 0:
s_out = [1]
else:
if s[i] == 1:
p += 1
s_out.append(s[p])
elif s[i] == 0:
s_out.append(s[p])
s_out = "".join(str(i) for i in s_out)
if s_out == Result[0 : len(s)]:
return True
else:
return False


def check(current: list[64]):
global Count
Count += 1
seed = current.copy()
for _ in range(64):
seed = reState(seed)
seed = int("".join(str(i) for i in seed), 2)

m = AES.new(key=md5(str(seed).encode()).digest(), mode=AES.MODE_ECB).decrypt(enc)
if m.isascii():
print(m)
exit()


def dfs(position):
if position == 64:
if cut(current):
check(current)
return

current.append(0)
if cut(current):
dfs(position + 1)
current.pop()

current.append(1)
if cut(current):
dfs(position + 1)
current.pop()


current = []
dfs(len(current))
# b'flag{5b322a2b-8d15-43b3-88f0-ee1586f1cf4f}\x06\x06\x06\x06\x06\x06'