2025-09-25
逆向工程
00
请注意,本文编写于 71 天前,最后修改于 71 天前,其中某些信息可能已经过时。

目录

第一步:获取目标程序
第二步:理解程序结构
2.1 找出程序的主要功能
2.2 识别关键逻辑
2.3 重要的提示信息
第三步:分析复杂的判断逻辑
第四步:还原缺失的函数
第五步:创建分析工具
第六步:测试不同参数组合
第七步:成功破解!
第八步:获取密码并验证

第一步:获取目标程序

我们拿到了一个反编译后的C代码文件(challenge.c),这是逆向工程中最常见的起点:

c
int __fastcall main(int argc, const char **argv, const char **envp) { bool v3; _QWORD *v4; __int64 v6; __int64 v7; _QWORD v8[2]; char v9; _QWORD v10[7]; _main(argc, argv, envp); std::ios_base::sync_with_stdio(0LL, v3); // ... 更多复杂的代码 }

** 困惑点**:

  • 为什么变量名都是 v3, v4, v6 这样?
  • _QWORD 是什么意思?
  • __fastcall 是什么?

解答

  • 变量名混乱:反编译器无法知道原始变量名,所以用 v1, v2 等占位
  • _QWORD:表示8字节(64位)的数据类型,相当于 unsigned long long
  • __fastcall:调用约定,表示函数参数如何传递

第二步:理解程序结构

2.1 找出程序的主要功能

通过阅读代码,我们发现:

c
std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "========== Reverse Practice Challenge ==========\n"); std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "Enter passphrase: ");

翻译成人话:程序在输出欢迎信息,要求用户输入密码。

2.2 识别关键逻辑

c
if ((unsigned __int8)verify_passphrase(v8)) { decrypt_blob[abi:cxx11](v10, &enc_flag); // 显示解密结果 } else { std::operator<<<std::char_traits<char>>(refptr__ZSt4cout, "Access denied.\n"); std::operator<<<std::char_traits<char>>( refptr__ZSt4cout, "Hint: check rotated-xor layers and index-dependent offset.\n"); }

程序逻辑

  1. 验证用户输入的密码
  2. 如果密码正确,解密flag并显示
  3. 如果密码错误,显示提示信息

2.3 重要的提示信息

"Hint: check rotated-xor layers and index-dependent offset."

这个提示告诉我们加密算法可能包含:

  • rotated(旋转操作)
  • xor(异或运算)
  • index-dependent offset(索引相关的偏移)

第三步:分析复杂的判断逻辑

代码中有一行特别复杂的判断:

c
if ((*((_BYTE *)v4 + *(_QWORD *)(*v4 - 24LL) + 32) & 5) == 0)

** 这行代码在做什么?**

让我们一步步分解:

  1. v4:指向输入流对象(std::cin
  2. *v4 - 24LL:访问对象内部的虚函数表
  3. + 32:偏移到状态标志位的位置
  4. & 5:检查特定的状态位
  5. == 0:判断输入是否成功

** 简化理解**:这相当于检查 std::cin.good()!std::cin.fail()

第四步:还原缺失的函数

程序中有两个重要函数没有实现:

  • verify_passphrase() - 验证密码
  • decrypt_blob() - 解密flag

由于我们没有源代码,需要通过逆向分析来推断这些函数的实现。

第五步:创建分析工具

基于提示信息,我们创建一个暴力破解工具来测试不同的解密算法:

cpp
#include <iostream> #include <vector> #include <string> #include <cstdint> using namespace std; // 8位右旋转函数 inline uint8_t rotateRight8(uint8_t value, unsigned rotation) { rotation &= 7; // 限制在0-7范围内 if (rotation == 0) return value; return (uint8_t)((value >> rotation) | (value << (8 - rotation))); } // 尝试解密函数 string tryDecryption(const vector<uint8_t>& data, int multiplier, int rotation_mod, uint8_t xor_key) { string result; result.reserve(data.size()); for (size_t i = 0; i < data.size(); ++i) { uint8_t byte = data[i]; // 步骤1:减去索引偏移 byte = (uint8_t)((byte - (uint8_t)((i * multiplier) & 0xFF)) & 0xFF); // 步骤2:右旋转 if (rotation_mod > 0) { byte = rotateRight8(byte, (unsigned)(i % rotation_mod)); } // 步骤3:异或 byte = (uint8_t)(byte ^ xor_key); result.push_back((char)byte); } return result; }

第六步:测试不同参数组合

我们需要测试不同的参数组合:

cpp
// 加密数据 vector<uint8_t> sample_data = { 60, 115, 250, 254, 46, 75, 168, 225, 49, 193, 111, 203, 104, 20, 85, 110, 70, 24, 201, 247 }; // 尝试不同的参数组合 vector<tuple<int, int, uint8_t>> patterns = { {7, 5, 0x5A}, // 乘数7, 旋转模5, 异或0x5A {3, 4, 0x5A}, {5, 3, 0x5A}, // ... 更多组合 }; for (auto& pattern : patterns) { int mult = get<0>(pattern); int rot_mod = get<1>(pattern); uint8_t xor_key = get<2>(pattern); string result = tryDecryption(sample_data, mult, rot_mod, xor_key); printf("乘数:%d, 旋转模:%d, 异或:0x%02x -> '%s'\n", mult, rot_mod, xor_key, result.c_str()); // 检查是否包含flag模式 if (result.find("flag") != string::npos) { cout << "*** 可能找到flag! ***\n"; } }

第七步:成功破解!

运行我们的分析工具后,得到了结果:

乘数:7, 旋转模:5, 异或:0x5a -> 'flag{reverse_me_123}'

** 找到了正确的解密参数**:

  • 索引偏移乘数: 7 (即 i * 7)
  • 旋转模数: 5 (即 i % 5)
  • 异或密钥: 0x5A

第八步:获取密码并验证

使用相同的算法解密密码数据:

cpp
// 密码数据 vector<uint8_t> enc_pass_data = { 53, 91, 10, 182, 147, 76, 168, 213, 17, 178, 133 }; string password = decryptData(enc_pass_data); cout << "正确密码: " << password << endl; // 输出: open-sesame

最终验证:

bash
$ echo "open-sesame" | ./challenge_working.exe ========== Reverse Practice Challenge ========== Enter passphrase: Access granted. Here is the secret: flag{reverse_me_123}

本文作者:晏秋

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!