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

【攻防世界】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%32*(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. 定位校验函数,明确输入→校验→输出的链路;
  2. 拆解循环逻辑,逐次计算目标字符的索引和偏移;
  3. 逆向推导输入字符(如目标字符 - 1);
  4. 正向验证结果,确保逻辑正确。

这类题目虽逻辑直接,但需细心处理索引计算与输入细节(如换行符)。掌握其思路后,可轻松应对 "变量偏移"" 多轮循环 " 等进阶题型,为复杂逆向题打下基础。

相关推荐
FeiHuo565151 小时前
微信个人号开发中如何高效实现API二次开发
java·开发语言·python·微信
love530love1 小时前
【保姆级教程】Windows + Podman 从零部署 Duix-Avatar 数字人项目
人工智能·windows·笔记·python·数字人·podman·duix-avatar
zmzb01031 小时前
C++课后习题训练记录Day33
开发语言·c++
HKT_China1 小时前
香港电讯与Fortinet推出100G高效加密网络托管服务,迈进量子安全新时代
网络·安全
csbysj20201 小时前
Bootstrap 折叠
开发语言
JaguarJack1 小时前
PHP Fiber 优雅协作式多任务
php·服务端
Want5951 小时前
C/C++贪吃蛇小游戏
c语言·开发语言·c++
豆浆whisky2 小时前
Go并发模式选择指南:找到最适合你项目的并发方案|Go语言进阶(19)
开发语言·后端·golang
草莓熊Lotso2 小时前
《算法闯关指南:动态规划算法--斐波拉契数列模型》--01.第N个泰波拉契数,02.三步问题
开发语言·c++·经验分享·笔记·其他·算法·动态规划