【攻防世界】reverse | Reversing-x64Elf-100 详细题解 WP
下载附件

放进 ida pro进行逆向分析
main函数伪代码:
c
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char s[264]; // [rsp+0h] [rbp-110h] BYREF
unsigned __int64 v5; // [rsp+108h] [rbp-8h]
v5 = __readfsqword(0x28u);
printf("Enter the password: ");
if ( !fgets(s, 255, stdin) )
return 0LL;
if ( (unsigned int)sub_4006FD(s) )
{
puts("Incorrect password!");
return 1LL;
}
else
{
puts("Nice!");
return 0LL;
}
}
sub_4006FD函数伪代码:
c
void __fastcall sub_4006FD(__int64 a1)
{
int i; // [rsp+14h] [rbp-24h]
__int64 v2[4]; // [rsp+18h] [rbp-20h]
v2[0] = (__int64)"Dufhbmf";
v2[1] = (__int64)"pG`imos";
v2[2] = (__int64)"ewUglpt";
for ( i = 0; i <= 11 && *(char *)(v2[i % 3] + 2 * (i / 3)) - *(char *)(i + a1) == 1; ++i )
;
}
exp:
python
def reverse_password():
"""
逆向生成正确password:根据校验规则推导输入字符
"""
# 1. 从校验函数中提取的目标字符串数组(v3)
target_strings = [
"Dufhbmf", # v3[0]
"pG`imos", # v3[1]
"ewUglpt" # v3[2]
]
password = []
# 2. 模拟循环i从0到11(共12次)
for i in range(12):
# 计算目标字符串索引(i%3)和偏移(2*(i//3))
str_idx = i % 3 # 选择哪个目标字符串
char_offset = 2 * (i // 3) # 字符串内的字符偏移(整数除法//)
# 提取目标字符
target_char = target_strings[str_idx][char_offset]
# 逆向公式:输入字符 = 目标字符ASCII - 1
input_char_ascii = ord(target_char) - 1
input_char = chr(input_char_ascii)
# 拼接输入字符
password.append(input_char)
return ''.join(password)
def verify_password(input_pass):
"""
正向验证:模拟sub_4006FD校验函数,验证输入是否正确
"""
target_strings = [
"Dufhbmf",
"pG`imos",
"ewUglpt"
]
# 输入长度必须为12(否则循环会越界,程序实际运行可能崩溃,但校验失败)
if len(input_pass) != 12:
return False
for i in range(12):
str_idx = i % 3
char_offset = 2 * (i // 3)
target_char = target_strings[str_idx][char_offset]
# 正向校验条件:目标字符 - 输入字符 == 1
if ord(target_char) - ord(input_pass[i]) != 1:
return False
return True
def simulate_main():
"""
模拟main函数流程:读取输入→校验→输出结果
"""
print("Enter the password: ")
user_input = input().strip() # strip()去除换行符和首尾空格(fgets会读入换行符,需处理)
if verify_password(user_input):
print("Nice!")
else:
print("Incorrect password!")
# 执行解题流程
if __name__ == "__main__":
# 步骤1:逆向生成正确password
correct_password = reverse_password()
print(f"逆向推导的正确password:{correct_password}")
# 步骤2:正向验证password正确性
if verify_password(correct_password):
print(f"✅ 正向验证通过!password有效")
else:
print(f"❌ 正向验证失败!password无效")
# 步骤3:模拟程序交互
print("\n--- 模拟程序运行 ---")
simulate_main()
运行脚本:
flag:Code_Talkers(只是字符串)
【攻防世界】reverse | Reversing-x64Elf-100 详细题解 WP 原理深度解析:
CTF 逆向实战:字符偏移校验题深度解析(伪代码拆解 + Python 破解全流程)
本题是典型的 "字符偏移校验类" 逆向题 ,核心逻辑是通过自定义校验函数(sub_4006FD)对用户输入进行逐字符校验,校验规则隐藏在循环索引计算和字符偏移中。解题关键是拆解循环逻辑、还原校验公式,从目标字符反向推导正确输入(password)。以下以专家视角,逐行解析伪代码、推导核心公式、实现 Python 破解,并总结实战技巧。
一、程序核心流程拆解(从 main 到校验函数)
先明确整体执行链路,定位核心校验环节:
1.1 main 函数逻辑解析(输入→校验→输出)
c
__int64 __fastcall main(int a1, char **a2, char **a3)
{
char s[264]; // [rsp+0h] [rbp-110h] BYREF → 用户输入缓冲区(264字节,足够容纳长输入)
unsigned __int64 v5; // [rsp+108h] [rbp-8h] → 栈保护变量(Canary),解题无关
v5 = __readfsqword(0x28u); // 读取Canary值(栈溢出防护,忽略)
printf("Enter the password: "); // 提示输入密码
if ( !fgets(s, 255, stdin) ) // 读取输入:最多254字符(fgets留1字节存\0),包含换行符
return 0LL;
if ( (unsigned int)sub_4006FD(s) ) // 调用校验函数:返回1→错误,返回0→正确
{
puts("Incorrect password!"); // 校验失败
return 1LL;
}
else
{
puts("Nice!"); // 校验成功(正确password)
return 0LL;
}
}
关键结论:
- 输入读取:
fgets(s,255,stdin)最多读取 254 个可见字符,输入末尾的换行符(\n)会被存入s,需注意后续校验是否包含换行符(本题校验 12 个字符,换行符不参与,需手动去除); - 核心依赖:
sub_4006FD是唯一校验函数,返回值决定输入是否正确。
1.2 校验函数 sub_4006FD 概述(核心逻辑所在地)
函数参数 a1 是用户输入字符串s的地址,函数内部通过循环逐字符校验输入,校验通过返回0,失败返回1。核心是解析循环中的索引计算和字符偏移规则。
二、核心突破:sub_4006FD 逻辑深度拆解
2.1 伪代码逐行解析(定位校验公式)
c
__int64 __fastcall sub_4006FD(__int64 a1)
{
int i; // [rsp+14h] [rbp-24h] → 循环索引(0~11,共12次循环)
__int64 v3[4]; // [rsp+18h] [rbp-20h] → 目标字符串数组(3个固定字符串)
// 初始化目标字符串数组(核心比对基准)
v3[0] = (__int64)"Dufhbmf"; // 字符串1:索引0→"Dufhbmf"(长度7,字符索引0~6)
v3[1] = (__int64)"pG`imos"; // 字符串2:索引1→"pG`imos"(长度7,字符索引0~6)
v3[2] = (__int64)"ewUglpt"; // 字符串3:索引2→"ewUglpt"(长度7,字符索引0~6)
// 核心循环:i从0到11(共12次,输入需至少12个字符,否则越界)
for ( i = 0; i <= 11; ++i )
{
// 校验条件:目标字符 - 输入字符 != 1 → 不满足则返回1(失败)
if ( *(char *)(v3[i % 3] + 2 * (i / 3)) - *(char *)(i + a1) != 1 )
return 1LL;
}
return 0LL; // 所有字符校验通过,返回0(成功)
}
2.2 关键公式推导(逆向核心)
2.2.1 校验条件变形(输入字符 = 目标字符 - 1)
原校验条件:
目标字符 - 输入字符 == 1
→ 逆向推导(求输入字符):(通过移项可直接得到逆向公式)
输入字符 = 目标字符 - 1(字符按 ASCII 码计算)
例如:若目标字符是 'D'(ASCII 68),则输入字符为 68-1=67,即 'C'。
2.2.2 循环索引计算(定位目标字符)
循环中i从 0 到 11(共 12 次),每次通过 i%3 和 2*(i/3) 计算目标字符串的索引和偏移,需拆解清楚:
v3[i % 3]:选择目标字符串(i%3结果为 0、1、2 循环,对应 v3 的 3 个字符串);2*(i / 3):目标字符串内的字符偏移(i/3是整数除法,结果为 0、1、2、3,乘以 2 后偏移为 0、2、4、6,刚好对应每个字符串的偶数索引位置);*(char *)(v3[...]+...):取出目标字符串中对应偏移的字符(目标字符);*(char *)(i + a1):取出用户输入中第i位的字符(输入字符)。
2.2.3 逐次计算目标字符(12 次循环完整拆解)
为清晰展示每一步计算,整理i从 0 到 11 的完整索引映射和目标字符:
| 循环索引 i | i%3(v3 索引) | i/3(整数除法) | 偏移 = 2*(i/3) | 目标字符串 | 目标字符(字符串 [偏移]) | 目标字符 ASCII | 输入字符 = 目标字符 - 1 | 输入字符 ASCII |
|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | "Dufhbmf" | 'D'(索引 0) | 68 | 'C' | 67 |
| 1 | 1 | 0 | 0 | "pG`imos" | 'p'(索引 0) | 112 | 'o' | 111 |
| 2 | 2 | 0 | 0 | "ewUglpt" | 'e'(索引 0) | 101 | 'd' | 100 |
| 3 | 0 | 1 | 2 | "Dufhbmf" | 'f'(索引 2) | 102 | 'e' | 101 |
| 4 | 1 | 1 | 2 | "pG`imos" | '`'(索引 2) | 96 | '_' | 95 |
| 5 | 2 | 1 | 2 | "ewUglpt" | 'U'(索引 2) | 85 | 'T' | 84 |
| 6 | 0 | 2 | 4 | "Dufhbmf" | 'b'(索引 4) | 98 | 'a' | 97 |
| 7 | 1 | 2 | 4 | "pG`imos" | 'm'(索引 4) | 109 | 'l' | 108 |
| 8 | 2 | 2 | 4 | "ewUglpt" | 'l'(索引 4) | 108 | 'k' | 107 |
| 9 | 0 | 3 | 6 | "Dufhbmf" | 'f'(索引 6) | 102 | 'e' | 101 |
| 10 | 1 | 3 | 6 | "pG`imos" | 's'(索引 6) | 115 | 'r' | 114 |
| 11 | 2 | 3 | 6 | "ewUglpt" | 't'(索引 6) | 116 | 's' | 115 |
2.2.4 正确输入拼接
将 12 个输入字符按顺序拼接,得到正确 password:Code_Talkers(验证:C o d e _ T a l k e r s → 长度 12,符合循环次数)。
三、Python 代码实现(逆向生成 + 正向验证)
3.1 核心逻辑:逆向生成正确 password
根据上述推导,编写代码自动计算正确输入,同时实现正向校验函数模拟程序逻辑,确保结果正确。
3.2 完整 Python 脚本
python
def reverse_password():
"""
逆向生成正确password:根据校验规则推导输入字符
"""
# 1. 从校验函数中提取的目标字符串数组(v3)
target_strings = [
"Dufhbmf", # v3[0]
"pG`imos", # v3[1]
"ewUglpt" # v3[2]
]
password = []
# 2. 模拟循环i从0到11(共12次)
for i in range(12):
# 计算目标字符串索引(i%3)和偏移(2*(i//3))
str_idx = i % 3 # 选择哪个目标字符串
char_offset = 2 * (i // 3) # 字符串内的字符偏移(整数除法//)
# 提取目标字符
target_char = target_strings[str_idx][char_offset]
# 逆向公式:输入字符 = 目标字符ASCII - 1
input_char_ascii = ord(target_char) - 1
input_char = chr(input_char_ascii)
# 拼接输入字符
password.append(input_char)
return ''.join(password)
def verify_password(input_pass):
"""
正向验证:模拟sub_4006FD校验函数,验证输入是否正确
"""
target_strings = [
"Dufhbmf",
"pG`imos",
"ewUglpt"
]
# 输入长度必须为12(否则循环会越界,程序实际运行可能崩溃,但校验失败)
if len(input_pass) != 12:
return False
for i in range(12):
str_idx = i % 3
char_offset = 2 * (i // 3)
target_char = target_strings[str_idx][char_offset]
# 正向校验条件:目标字符 - 输入字符 == 1
if ord(target_char) - ord(input_pass[i]) != 1:
return False
return True
def simulate_main():
"""
模拟main函数流程:读取输入→校验→输出结果
"""
print("Enter the password: ")
user_input = input().strip() # strip()去除换行符和首尾空格(fgets会读入换行符,需处理)
if verify_password(user_input):
print("Nice!")
else:
print("Incorrect password!")
# 执行解题流程
if __name__ == "__main__":
# 步骤1:逆向生成正确password
correct_password = reverse_password()
print(f"逆向推导的正确password:{correct_password}")
# 步骤2:正向验证password正确性
if verify_password(correct_password):
print(f"✅ 正向验证通过!password有效")
else:
print(f"❌ 正向验证失败!password无效")
# 步骤3:模拟程序交互
print("\n--- 模拟程序运行 ---")
simulate_main()
3.3 逆向生成 flag 的代码
python
def reverse_password():
# 目标字符串数组(从sub_4006FD提取)
target_strings = [
"Dufhbmf", # v3[0]
"pG`imos", # v3[1]
"ewUglpt" # v3[2]
]
password = []
for i in range(12): # 循环i=0到11
str_idx = i % 3 # 选择目标字符串
char_offset = 2 * (i // 3) # 计算字符偏移
target_char = target_strings[str_idx][char_offset]
input_char = chr(ord(target_char) - 1) # 应用逆向公式
password.append(input_char)
return ''.join(password)
3.4 正向验证代码(模拟 sub_4006FD)
python
def verify_password(input_pass):
target_strings = ["Dufhbmf", "pG`imos", "ewUglpt"]
if len(input_pass) != 12: # 输入必须为12字符
return False
for i in range(12):
str_idx = i % 3
char_offset = 2 * (i // 3)
target_char = target_strings[str_idx][char_offset]
if ord(target_char) - ord(input_pass[i]) != 1: # 正向校验条件
return False
return True
3.5 运行结果
plaintext
逆向推导的正确password:Code_Talkers
✅ 正向验证通过!password有效
--- 模拟程序运行 ---
Enter the password:
Code_Talkers
Nice!
四、原理深度解析与实战技巧
4.1 题目核心考点
4.1.1 循环索引计算拆解
本题的核心难点是 "双重索引映射" (i%3 选择字符串,2*(i/3) 选择字符偏移),这类 "循环步长 + 索引运算" 是 CTF 逆向的高频考点,解题时需:
- 列出循环变量的所有取值(如 i=0~11);
i%3使 3 个字符串循环被使用(0→1→2→0→...);2*(i//3)确保每个字符串只使用偶数索引(0、2、4、6),而目标字符串长度为 7(索引 0~6),刚好避免越界。- 逐次计算索引和偏移,避免凭直觉猜测;
- 关注字符串的长度和索引范围(本题目标字符串长度 7,偏移 0、2、4、6 均在有效范围内)。
这种设计是 CTF 中 "资源循环利用" 的典型思路,通过简单运算实现复杂的字符选取逻辑。
4.1.2 可逆字符偏移的本质
校验规则使用 "固定偏移(-1)",属于线性可逆运算,这类运算的逆向逻辑直接且唯一:
- 正向:
input → input+1 → target; - 逆向:
target → target-1 → input; - 扩展:若偏移为变量(如
i+1),则逆向时需按相同变量计算偏移。(如目标字符 - (i+1)),核心逻辑一致。
4.2 实战避坑指南
4.2.1 处理fgets的换行符
fgets会读取用户输入时的换行符(\n),若输入Code_Talkers后按回车,s中会包含"Code_Talkers\n"(长度 13),导致校验时第 12 位字符是\n,而非预期的s,最终校验失败。解决方法:
- 输入时不加换行符(直接粘贴字符串);
- 程序中用
strip()去除首尾空白字符(如 Python 脚本中的处理)。
4.2.2 输入长度校验
循环执行 12 次,输入必须为 12 个字符:
- 长度不足 12:循环中
i+a1会访问输入字符串的越界位置(垃圾数据),校验失败; - 长度超过 12:
fgets会读取前 254 个字符,但循环仅校验前 12 个,后续字符不影响结果(但题目隐含输入为 12 字符)。
4.3 IDA 实战操作技巧
4.3.1 快速提取目标字符串
- 方法 1:在
sub_4006FD伪代码中,右键"Dufhbmf"→Jump to xref→直接查看字符串内容; - 方法 2:按
Shift+F12打开 Strings 窗口,搜索长度为 7 的字符串(本题 3 个目标字符串长度均为 7),快速定位。
4.3.2 分析循环逻辑的高效方法
- 关注循环变量的初始化(
i=0)、终止条件(i<=11)和步长(++i); - 对循环内的复杂索引运算(如
v3[i%3] + 2*(i/3)),在 IDA 中按F5重新反编译,确保伪代码可读性; - 若索引运算复杂,可在 IDA 中设置断点,动态调试观察
i和目标字符的取值(适合复杂循环)。
五、总结
本题的正确 password 为 Code_Talkers,解题核心是 "拆解循环索引 + 还原可逆偏移"。这类题目虽涉及循环和索引计算,但逻辑直接,无复杂加密算法,属于 CTF 逆向的基础题型。
核心解题流程可概括为:
- 定位校验函数,明确输入→校验→输出的链路;
- 拆解循环逻辑,逐次计算目标字符的索引和偏移;
- 逆向推导输入字符(如目标字符 - 1);
- 正向验证结果,确保逻辑正确。
这类题目虽逻辑直接,但需细心处理索引计算与输入细节(如换行符)。掌握其思路后,可轻松应对 "变量偏移"" 多轮循环 " 等进阶题型,为复杂逆向题打下基础。