点开这个文件

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
.text:0000000000401550                 push    rbp
.text:0000000000401551 mov rbp, rsp
.text:0000000000401554 sub rsp, 90h
.text:000000000040155B call __main
.text:0000000000401560 lea rcx, Buffer ; "input your flag:"
.text:0000000000401567 call puts
.text:000000000040156C lea rax, [rbp+Str]
.text:0000000000401570 mov rdx, rax
.text:0000000000401573 lea rcx, Format ; "%s"
.text:000000000040157A call scanf
.text:000000000040157F lea rax, [rbp+Str]
.text:0000000000401583 mov rcx, rax ; Str
.text:0000000000401586 call strlen
.text:000000000040158B cmp rax, 23h ; '#'
.text:000000000040158F jz short loc_40159B
.text:0000000000401591 mov eax, 0
.text:0000000000401596 jmp loc_40162B
.text:000000000040159B ; ---------------------------------------------------------------------------
.text:000000000040159B
.text:000000000040159B loc_40159B: ; CODE XREF: main+3F↑j
.text:000000000040159B mov [rbp+var_4], 0
.text:00000000004015A2
.text:00000000004015A2 loc_4015A2: ; CODE XREF: main+C8↓j
.text:00000000004015A2 cmp [rbp+var_4], 22h ; '"'
.text:00000000004015A6 jg short loc_40161A
.text:00000000004015A8 mov eax, [rbp+var_4]
.text:00000000004015AB cdqe
.text:00000000004015AD movzx eax, [rbp+rax+Str]
.text:00000000004015B2 xor eax, 52h
.text:00000000004015B5 mov edx, eax
.text:00000000004015B7 mov eax, [rbp+var_4]
.text:00000000004015BA cdqe
.text:00000000004015BC mov [rbp+rax+Str], dl
.text:00000000004015C0 mov eax, [rbp+var_4]
.text:00000000004015C3 cdqe
.text:00000000004015C5 movzx eax, [rbp+rax+Str]
.text:00000000004015CA add eax, 5
.text:00000000004015CD mov edx, eax
.text:00000000004015CF mov eax, [rbp+var_4]
.text:00000000004015D2 cdqe
.text:00000000004015D4 mov [rbp+rax+Str], dl
.text:00000000004015D8 mov eax, [rbp+var_4]
.text:00000000004015DB cdqe
.text:00000000004015DD movzx eax, [rbp+rax+Str]
.text:00000000004015E2 movsx eax, al
.text:00000000004015E5 mov edx, [rbp+var_4]
.text:00000000004015E8 movsxd rdx, edx
.text:00000000004015EB lea rcx, ds:0[rdx*4]
.text:00000000004015F3 lea rdx, res
.text:00000000004015FA mov edx, [rcx+rdx]
.text:00000000004015FD cmp eax, edx
.text:00000000004015FF jz short loc_401614
.text:0000000000401601 lea rcx, aWrong ; "Wrong!"
.text:0000000000401608 call puts
.text:000000000040160D mov eax, 0
.text:0000000000401612 jmp short loc_40162B
.text:0000000000401614 ; ---------------------------------------------------------------------------
.text:0000000000401614
.text:0000000000401614 loc_401614: ; CODE XREF: main+AF↑j
.text:0000000000401614 add [rbp+var_4], 1
.text:0000000000401618 jmp short loc_4015A2
.text:000000000040161A ; ---------------------------------------------------------------------------
.text:000000000040161A
.text:000000000040161A loc_40161A: ; CODE XREF: main+56↑j
.text:000000000040161A lea rcx, aGood ; "Good!"
.text:0000000000401621 call puts
.text:0000000000401626 mov eax, 0
.text:000000000040162B
.text:000000000040162B loc_40162B: ; CODE XREF: main+46↑j
.text:000000000040162B ; main+C2↑j
.text:000000000040162B add rsp, 90h
.text:0000000000401632 pop rbp
.text:0000000000401633 retn
.text:0000000000401633 main endp


hint = 0x21,0x6,0x6,0x16,0xb,0x19,0x2e,0x65,0x35,0x6a,0x6f,0x38,0x36,0x84,0x70,0x3b,0x39,0x65,0x38,0x35,0x84,0x6f,0x36,0x3c,0x6a,0x38,0x68,0x84,0x66,0x70,0x3b,0x38,0x6a,0x36,0x34,

分析

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
.text:0000000000401550 push rbp 
.text:0000000000401551 mov rbp, rsp
.text:0000000000401554 sub rsp, 90h
.text:000000000040155B call __main
#对应伪代码
int main() {
// 保存旧的栈帧指针,设置新的栈帧,为局部变量分配空间
// push rbp; mov rbp, rsp; sub rsp, 90h
// 调用初始化函数(可能是C标准库的初始化)
__main();

.text:0000000000401560 lea rcx, Buffer ;"input your flag:"
.text:0000000000401567 call puts
#对应伪代码
puts("input your flag:");

.text:000000000040156C lea rax, [rbp+Str] .text:0000000000401570 mov rdx, rax .text:0000000000401573 lea rcx, Format ; "%s" .text:000000000040157A call scanf
#对应伪代码
// 读取用户输入到局部变量Str中
char Str[/* 大小取决于栈空间分配 */];
scanf("%s", Str);

.text:000000000040157F lea rax, [rbp+Str] .text:0000000000401583 mov rcx, rax ; Str .text:0000000000401586 call strlen .text:000000000040158B cmp rax, 23h ; '#' .text:000000000040158F jz short loc_40159B .text:0000000000401591 mov eax, 0 .text:0000000000401596 jmp loc_40162B
#对应伪代码
// 检查输入长度是否为35(0x23)
if (strlen(Str) != 35) {
return 0; // 长度不符则退出
}

.text:000000000040159B mov [rbp+var_4], 0 ; i = 0 .text:00000000004015A2 loc_4015A2: ; 循环开始 .text:00000000004015A2 cmp [rbp+var_4], 22h ; i <= 34?
.text:00000000004015A6 jg short loc_40161A .text:00000000004015A8 mov eax, [rbp+var_4] .text:00000000004015AB cdqe ; 符号扩展 .text:00000000004015AD movzx eax, [rbp+rax+Str] ; 取Str[i]
.text:00000000004015B2 xor eax, 52h ; Str[i] ^= 0x52 #xor异或运算
.text:00000000004015B5 mov edx, eax .text:00000000004015B7 mov eax, [rbp+var_4] .text:00000000004015BA cdqe .text:00000000004015BC mov [rbp+rax+Str], dl ; 写回Str[i]
.text:00000000004015C0 mov eax, [rbp+var_4] .text:00000000004015C3 cdqe .text:00000000004015C5 movzx eax, [rbp+rax+Str] .text:00000000004015CA add eax, 5 ; Str[i] += 5 .text:00000000004015CD mov edx, eax .text:00000000004015CF mov eax, [rbp+var_4] .text:00000000004015D2 cdqe .text:00000000004015D4 mov [rbp+rax+Str], dl ; 写回Str[i]
#对应伪代码
// 初始化循环变量i=0
int i = 0;
// 循环处理每个字符,直到i > 34 (0x22)
while (i <= 34) {
// 对Str[i]进行变换:先异或0x52
Str[i] ^= 0x52;
Str[i] += 5;// 再加上5

.text:00000000004015D8 mov eax, [rbp+var_4] .text:00000000004015DB cdqe .text:00000000004015DD movzx eax, [rbp+rax+Str] .text:00000000004015E2 movsx eax, al ; 符号扩展 .text:00000000004015E5 mov edx, [rbp+var_4] .text:00000000004015E8 movsxd rdx, edx .text:00000000004015EB lea rcx, ds:0[rdx*4] ; hint[i*4]?
.text:00000000004015F3 lea rdx, res ; 可能是hint数组基址
.text:00000000004015FA mov edx, [rcx+rdx] ; 取hint[i]
.text:00000000004015FD cmp eax, edx ; 比较Str[i]与hint[i]
.text:00000000004015FF jz short loc_401614 ; 相等则继续
.text:0000000000401601 lea rcx, aWrong ; "Wrong!" .text:0000000000401608 call puts .text:000000000040160D mov eax, 0 .text:0000000000401612 jmp short loc_40162B
#对应伪代码
// 检查变换后的Str[i]是否等于hint[i]
if (Str[i] != hint[i]) {
puts("Wrong!"); // 不相等则输出错误信息
return 0; // 并退出程序
}
i++; // 循环变量递增
}

.text:000000000040161A loc_40161A: ; 循环结束后执行 .text:000000000040161A lea rcx, aGood ; "Good!" .text:0000000000401621 call puts .text:0000000000401626 mov eax, 0
#对应伪代码
// 所有字符验证通过,输出"Good!"
puts("Good!");
return 0;

.text:000000000040162B loc_40162B: .text:000000000040162B add rsp, 90h ; 释放局部变量空间
.text:0000000000401632 pop rbp ; 恢复旧的栈帧指针 .text:0000000000401633 retn ; 返回
#对应伪代码
// 函数结束,栈帧恢复
} // 对应main函数结束

总结

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
char Str[/* 长度动态 */]; 
printf("input your flag:"); // puts("input your flag:")
scanf("%s", Str); // 读取用户输入到 Str
if (strlen(Str) != 0x23) { // 0x23 = 35,检查输入长度是否为 35
printf("Wrong!"); // 长度不对直接报错
return 0;
}
// 第一步:异或 0x52
Str[i] ^= 0x52;
// 第二步:加 5
Str[i] += 5;
// 第三步:与 hint 数组对比
if (Str[i] != hint[i]) {
printf("Wrong!");
return 0;
}

解题思路:逆向推导 Flag

已知 hint 数组,需反向推导原始输入 Str(即 Flag )。
逆向推导原始 Str[i]

1
Str[i] = (hint[i] - 5) ^ 0x52  

编写逆向计算脚本
用 Python 反向推导每个 Str[i]

1
2
3
4
5
6
hint = [0x21,0x6,0x6,0x16,0xb,0x19,0x2e,0x65,0x35,0x6a,0x6f,0x38,0x36,0x84,0x70,0x3b,0x39,0x65,0x38,0x35,0x84,0x6f,0x36,0x3c,0x6a,0x38,0x68,0x84,0x66,0x70,0x3b,0x38,0x6a,0x36,0x34]
flag = []
for h in hint:
temp = (h - 5) ^ 0x52 #逆向计算:先减5,再异或 0x52
flag.append(chr(temp))
print(''.join(flag))# 拼接字符得到 Flag

NSSCTF{2b78ac-9df2ab-8ce7a1-39da7c}