题目信息

题目信息

解题步骤

DIE分析

分析main

IDA分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int __fastcall __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax
char v4; // [rsp+Fh] [rbp-1h]

while ( 1 )
{
while ( 1 )
{
printf("Welcome to CTF game!\nPlease input d/D to start or input q/Q to quit this program: ");
v4 = getchar();
if ( v4 != 100 && v4 != 68 )
break;
Decry("Welcome to CTF game!\nPlease input d/D to start or input q/Q to quit this program: ", argv);// 非常明显,这道题应该主要是看Decry
}
if ( v4 == 113 || v4 == 81 )
Exit("Welcome to CTF game!\nPlease input d/D to start or input q/Q to quit this program: ", argv);
puts("Input fault format!");
v3 = getchar();
putchar(v3);
}
}

分析Decry

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
unsigned __int64 Decry()
{
char v1; // 临时字符(输入)
int v2; // str2 的当前索引(输出字符串的空格分组计数)
int v3; // key 的索引(Vigenère 密钥流)
int i; // 循环变量
int v5; // key 的长度
char src[8]; // 临时字符数组
__int64 v7; // 未使用
int v8; // 未使用
_QWORD v9[2]; // 存储 "wodah"
int v10; // 未使用
unsigned __int64 v11; // Canary,用于栈保护

v11 = __readfsqword(0x28u); // 保存栈 canary

*(_QWORD *)src = 'SLCDN'; // 读为 'SLCDN'

v7 = 0;
v8 = 0;
v9[0] = 'wodah'; // 读为 "wodah"
v9[1] = 0;
v10 = 0;


text = (char *)join(key3, v9); // key3 = 'kills'
strcpy(key, key1); // key1 = 'ADSFK'
strcat(key, src); // src = "SLCDN" → key = "ADSFKSLCDN"
// 所以最终 key = "ADSFKSLCDN",长度 v5 = 10

v2 = 0;
v3 = 0;
getchar(); // 吃掉一个换行符(可能是前面输入留下的)

v5 = strlen(key); // v5 = 10
1
2
3
4
5
6
for ( i = 0; i < v5; ++i )
{
if ( key[v3 % v5] > 64 && key[v3 % v5] <= 90 ) // 如果是大写字母 'A'-'Z'
key[i] = key[v3 % v5] + 32; // 转为小写
++v3;
}

用户输入处理

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
printf("Please input your flag:");
while ( 1 )
{
v1 = getchar(); // 读一个字符
if ( v1 == 10 ) // 换行符(Enter),结束输入
break;
if ( v1 == 32 ) // 空格
{
++v2; // v2 是输出字符串 str2 的索引(也用于分组)
}
else
{
// 判断字符是否为字母
if ( v1 <= 96 || v1 > 122 ) // 不是小写字母 a-z
{
if ( v1 > 64 && v1 <= 90 ) // 是大写字母 A-Z
{
// 解密公式
str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
++v3;
}
}
else // 是小写字母 a-z
{
str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;
++v3;
}

// 每当密钥用完一轮(v3 % v5 == 0),输出一个空格
if ( !(v3 % v5) )
putchar(32); // 输出空格

++v2; // str2 索引前进
}
}

解密公式分析

1
str2[v2] = (v1 - 39 - key[v3 % v5] + 97) % 26 + 97;

简化:

1
2
= (v1 - 39 - key_char + 97) % 26 + 97
= (v1 - key_char + 58) % 26 + 97

更清晰的写法是:

1
2
3
4
5
6
7
// 假设 v1 是密文字符(A-Z 或 a-z)
// key_char 是密钥字符('a'-'z')

int c = tolower(v1); // 统一转为小写
int k = key[v3 % v5] - 'a'; // 密钥偏移 0-25
int p = (c - 'a' - k + 26) % 26; // Vigenère 解密
str2[v2] = p + 'a';

这是 类似维吉尼亚密码的解密过程

因此,text = join(key3, v9) = "kills" + "wodah" = "killshadow"

同时,key1 = "ADSFK"src = "SLCDN",所以:

1
key = "ADSFK" + "SLCDN" = "ADSFKSLCDN"

解题脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
# 已知信息
plaintext = "killshadow"
key = "adsfkndcls"

# 加密:生成密文
ciphertext = ""
for i, char in enumerate(plaintext):
p = ord(char) - ord('a')
k = ord(key[i % len(key)]) - ord('a')
c = (p + k) % 26
ciphertext += chr(c + ord('A'))

print("Input this:", ciphertext) # 输出: KLDQCUDFZO

flag:flag{KLDQCUDFZO}