adb install -t CrackMe_2_4.apk本题是一个Android逆向题,APK中使用了NDK原生库进行DES加密验证。主要思路为:
libcrackme2.so 中的关键数据(DES密钥和加密后的flag)使用 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.crackme2com.cr.crackme2.MainActivity应用加载了原生库 libcrackme2.so,验证逻辑在native层实现。
使用字符串搜索和反汇编工具(IDA/Ghidra)分析 libcrackme2.so。
在 .rodata 段中找到以下关键字符串:
| 虚拟地址 | 内容 | 说明 |
|---------|------|------|
| 0xe08c | "开始验证 flag" | 日志标签 |
| 0xe90b | "51asm" | CrackMe2的标签 |
| 0xe911 | "用户输入: %s,长度: %d" | 格式化字符串 |
| 0xe310 | 十六进制编码的密文 | 加密后的flag数据 |
| 0xf039 | "12345678" | DES密钥 |
在虚拟地址 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填充。
在虚拟地址 0xf039 处存储了DES密钥:
12345678
这是标准的8字节DES密钥。
通过反汇编 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
}
验证流程:
12345678由于验证逻辑是加密后比较,要找到正确的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}
# 密文十六进制字符串
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{GHQcYK2qBWGW4q4JUV1bbA}
| 关键点 | 内容 |
|-------|------|
| APK包名 | com.cr.crackme2 |
| 原生库 | libcrackme2.so (x86) |
| 加密算法 | DES (ECB模式) |
| DES密钥 | 12345678 (位于VA 0xf039) |
| 密文数据 | 位于VA 0xe310(十六进制编码) |
| 填充方式 | PKCS5 |
| Flag | flag{GHQcYK2qBWGW4q4JUV1bbA} |
本题的关键在于:
.rodata 段中提取DES密钥和密文数据