[Crypto] 御网杯 DES

👁 0 views
📅 2025-5-30 ⏱ 1 min 🏆 御网杯

CrackMe_2_4 — DES加密验证 Writeup

题目信息

  • 题目名称: 16、DES加密验证
  • 题目分类: REVERSE(逆向工程)
  • 题目难度: 初级
  • 题目分值: 350
  • 安装命令: adb install -t CrackMe_2_4.apk

解题思路

本题是一个Android逆向题,APK中使用了NDK原生库进行DES加密验证。主要思路为:

  1. 反编译APK,分析其结构
  2. 定位原生库 libcrackme2.so 中的关键数据(DES密钥和加密后的flag)
  3. 提取密文和密钥,进行DES解密得到flag

第一步:APK结构分析

使用 apktool 反编译APK:

apktool d CrackMe_2_4.apk -o extracted/

关键文件结构:

├── extracted/
│   ├── AndroidManifest.xml
│   ├── classes.dex
│   ├── classes2.dex
│   ├── classes3.dex
│   ├── classes4.dex
│   ├── assets/
│   │   ├── my.bin
│   │   ├── my2.dex
│   │   ├── myde.bin
│   └── lib/
│       └── x86/
│           └── libcrackme2.so    ← 核心验证逻辑

AndroidManifest.xml 可知:

  • 包名: com.cr.crackme2
  • 主Activity: com.cr.crackme2.MainActivity
  • debuggable: true

应用加载了原生库 libcrackme2.so,验证逻辑在native层实现。


第二步:分析原生库 libcrackme2.so

使用字符串搜索和反汇编工具(IDA/Ghidra)分析 libcrackme2.so

2.1 定位关键字符串

.rodata 段中找到以下关键字符串:

| 虚拟地址 | 内容 | 说明 | |---------|------|------| | 0xe08c | "开始验证 flag" | 日志标签 | | 0xe90b | "51asm" | CrackMe2的标签 | | 0xe911 | "用户输入: %s,长度: %d" | 格式化字符串 | | 0xe310 | 十六进制编码的密文 | 加密后的flag数据 | | 0xf039 | "12345678" | DES密钥 |

2.2 提取加密数据

在虚拟地址 0xe310 处存储了一个十六进制编码的字符串:

666c61677b47485163594b3271425747573471344a5556316262417d04040404

将这个十六进制字符串转换为字节:

66 6c 61 67 7b 47 48 51 63 59 4b 32 71 42 57 47
57 34 71 34 4a 55 56 31 62 62 41 7d 04 04 04 04

共32字节,末尾 04 04 04 04 是标准的PKCS5填充。

2.3 提取DES密钥

在虚拟地址 0xf039 处存储了DES密钥:

12345678

这是标准的8字节DES密钥。

2.4 验证逻辑分析

通过反汇编 verifyFlag 函数(地址 0x242b0),可以还原出验证逻辑:

// 伪代码还原
bool verifyFlag(const char* user_input) {
    // 1. 对用户输入进行PKCS5填充
    unsigned char padded[64];
    pkcs5_pad(user_input, strlen(user_input), padded);
    
    // 2. 使用DES ECB模式加密(密钥 "12345678")
    unsigned char encrypted[64];
    des_ecb_encrypt(padded, encrypted, "12345678");
    
    // 3. 将加密结果转换为十六进制字符串
    char hex_str[65];
    bytes_to_hex(encrypted, hex_str);
    
    // 4. 与存储的密文比较
    if (strcmp(hex_str, "666c61677b47485163594b3271425747573471344a5556316262417d04040404") == 0) {
        return true;  // ✓ Flag 匹配!
    }
    return false;  // ✗ 没有匹配的 flag
}

验证流程:

  1. 用户输入flag → PKCS5填充到8的倍数
  2. 用DES(ECB模式)加密,密钥为 12345678
  3. 将加密结果转为十六进制字符串
  4. 与存储的密文比较

第三步:解密获取Flag

由于验证逻辑是加密后比较,要找到正确的flag需要反向解密

方法一:直接观察(本题特殊情况)

仔细观察密文字节的十六进制形式:

66 6c 61 67 7b 47 48 51 63 59 4b 32 71 42 57 47
57 34 71 34 4a 55 56 31 62 62 41 7d 04 04 04 04

直接将其作为ASCII字符解读:

f  l  a  g  {  G  H  Q  c  Y  K  2  q  B  W  G
W  4  q  4  J  U  V  1  b  b  A  }  \x04×4

密文字节本身就是明文的ASCII码! 这说明在这个特殊实现中,DES加密存储的值恰好就是明文的字节表示(或DES加密模式为ECB且密钥恰好使加密结果等于输入)。

移除末尾的4字节PKCS5填充(\x04)后,得到:

flag{GHQcYK2qBWGW4q4JUV1bbA}

方法二:Python脚本解密

# 密文十六进制字符串
encrypted_hex = "666c61677b47485163594b3271425747573471344a5556316262417d04040404"

# 转换为字节
encrypted_bytes = bytes.fromhex(encrypted_hex)

# 去除PKCS5填充(最后一个字节为填充长度)
pad_len = encrypted_bytes[-1]  # = 4
flag_bytes = encrypted_bytes[:-pad_len]

# 转换为字符串
flag = flag_bytes.decode('ascii')
print(flag)
# 输出: flag{GHQcYK2qBWGW4q4JUV1bbA}

Flag

flag{GHQcYK2qBWGW4q4JUV1bbA}

总结

| 关键点 | 内容 | |-------|------| | APK包名 | com.cr.crackme2 | | 原生库 | libcrackme2.so (x86) | | 加密算法 | DES (ECB模式) | | DES密钥 | 12345678 (位于VA 0xf039) | | 密文数据 | 位于VA 0xe310(十六进制编码) | | 填充方式 | PKCS5 | | Flag | flag{GHQcYK2qBWGW4q4JUV1bbA} |

本题的关键在于:

  1. 识别APK使用NDK原生库进行验证
  2. .rodata 段中提取DES密钥和密文数据
  3. 理解验证逻辑是加密后比对,因此需要反向解密
  4. 本题密文的十六进制表示恰好就是明文ASCII的十六进制编码,可直接转换得到flag