1. 吾爱破解红包题逆向分析实战
作为一名逆向分析爱好者,我最近参加了吾爱破解的春节红包挑战赛。虽然最终没能全部完成,但在这个过程中积累了不少实战经验。本文将详细记录其中几个题目的解题思路和过程,希望能给同样对逆向分析感兴趣的朋友一些参考。
2. Windows初级题解析
2.1 题目初步分析
首先我们来看Windows初级题。打开程序后,通过简单的字符串搜索就能发现许多敏感字符串,这给了我们很好的切入点。
使用IDA Pro等工具进行静态分析时,重点关注以下几个关键函数:
- 输入校验函数
- 字符串处理函数
- 加密/解密函数
- 输出函数
2.2 核心解密函数分析
在本题中,我们发现了一个关键的解密函数init_block,它负责初始化数据块并进行异或解密:
c复制_BYTE *__cdecl init_block(int a1) {
_BYTE *result; // eax
*a1 = 758280311;
*(a1 + 4) = 1663511336;
*(a1 + 8) = 1880974179;
*(a1 + 12) = 494170226;
*(a1 + 16) = 842146570;
*(a1 + 20) = 657202491;
*(a1 + 24) = 658185525;
*(a1 + 30) = 99;
*(a1 + 28) = 12323;
result = a1;
do
*result++ ^= 0x42u;
while (result != (a1 + 31));
*(a1 + 31) = 0;
return result;
}
这个函数的主要逻辑是:
- 初始化一个32字节的数据块
- 对前28字节使用4字节整型赋值
- 对第28-30字节使用短整型和字节赋值
- 对整个数据块(除最后一个字节)进行异或0x42操作
- 最后一个字节置0
2.3 Python实现解密脚本
根据上述分析,我们可以用Python编写解密脚本:
python复制import struct
data = bytearray(32)
# 使用struct.pack_into将整数按小端序写入字节数组
struct.pack_into('<I', data, 0, 758280311) # 写入4字节无符号整数
struct.pack_into('<I', data, 4, 1663511336)
struct.pack_into('<I', data, 8, 1880974179)
struct.pack_into('<I', data, 12, 494170226)
struct.pack_into('<I', data, 16, 842146570)
struct.pack_into('<I', data, 20, 657202491)
struct.pack_into('<I', data, 24, 658185525)
# 写入2字节无符号短整数和1字节无符号字符
struct.pack_into('<H', data, 28, 12323) # H表示2字节无符号短整数
struct.pack_into('<B', data, 30, 99) # B表示1字节无符号字符
flag = ""
for i in range(31):
flag += chr(data[i] ^ 0x42) # 逐字节异或0x42
print(f"解密后的结果为: {flag}")
2.4 解题关键点
- 数据结构识别:正确识别出原始数据是混合了4字节整数、2字节短整数和单字节的数据结构
- 字节序处理:使用
<表示小端序,确保多字节数据正确解析 - 异或操作范围:注意循环终止条件是31而不是32,因为最后一个字节不需要异或
- 类型转换:使用
chr()将解密后的字节转换为ASCII字符
3. Android初级题解析
3.1 题目概述
这是一个拼图游戏,通关后应该会显示flag。由于我不擅长游戏操作,决定采用静态分析的方法来获取flag。
3.2 代码混淆处理
首先注意到代码被严重混淆,关键逻辑不在MainActivity中。我们需要:
- 使用jadx或apktool等工具反编译APK
- 搜索"flag"等关键词定位关键代码
- 分析游戏通关后的处理逻辑
3.3 关键代码分析
在代码中找到了一个关键类w1.g,它处理游戏状态和flag生成。重点关注以下几个部分:
- 游戏状态判断:通过
b7.c判断游戏是否通关 - 拼图顺序校验:
k.a(list1, b.h)检查当前拼图顺序是否正确 - 哈希校验:一个简单的哈希算法防止直接修改内存作弊
java复制long v17 = 0L;
int v18 = 0;
for(Object object5: list1) {
v17 = v17 * 0x1FL + ((long)(((Number)object5).intValue() * (v18 + 1)));
++v18;
}
if((v17 ^ 305419896L) == 0xE30FE54D0L) { ... }
3.4 Flag生成机制
当所有校验通过后,程序会解密flag:
- 从
r1.a.a获取密文(一个6行的二维整数数组) - 使用密钥
[54, 1, 22, 28]进行异或解密 - 拼接解密结果得到最终flag
解密逻辑在case 25中实现:
java复制case 25: {
k.e(((int[])object0), "part");
StringBuilder stringBuilder0 = new StringBuilder();
while(v < ((int[])object0).length) {
stringBuilder0.append(((char)(((int[])object0)[v] ^ ((byte[])this.k)[v % ((byte[])this.k).length] & 0xFF)));
++v;
}
return stringBuilder0.toString();
}
3.5 Python解密脚本
根据分析,我们可以编写Python脚本来解密flag:
python复制# 出题人硬编码的密钥
key = [54, 1, 22, 28]
# r1.a.a 密文数组
arr = [
[80, 109, 119, 123, 77], # 第1行
[97, 116, 34, 45, 105], # 第2行
[102, 49, 124, 45, 5, 94], # 第3行
[4, 49, 36, 42, 105], # 第4行
[101, 113, 100, 45, 88, 102, 73], # 第5行
[112, 50, 101, 104, 7, 119, 34, 112, 75] # 第6行
]
flag = ""
for row in arr:
for i, val in enumerate(row):
# 异或解密:密文 ^ Key[i % 4]
flag += chr(val ^ key[i % 4])
print("解密成功!最终FLAG为:")
print(flag)
3.6 解题技巧
- 代码搜索技巧:善用"flag"、"success"等关键词快速定位关键代码
- 动态难度识别:注意游戏可能会根据时间或步数动态调整难度
- 加密算法分析:识别简单的异或加密模式
- 数据提取:从代码中直接提取密文和密钥
4. Python逆向题解析
4.1 题目识别
通过程序图标很容易判断这是一个Python打包的程序。我们可以使用pyinstxtractor等工具进行解包,获取pyc文件。
4.2 反编译过程
使用在线的pylingual.io服务对pyc文件进行反编译,得到以下关键代码:
python复制import hashlib
import base64
import sys
def xor_decrypt(data, key):
"""XOR解密"""
result = bytearray()
for i, byte in enumerate(data):
result.append(byte ^ key ^ i & 255)
return result.decode('utf-8', errors='ignore')
def get_encrypted_flag():
"""获取加密的flag"""
enc_data = 'e3w+fiRvfW18fnx4ZAZ6Pj43YwB9OWMXfXo8Dg4O'
return base64.b64decode(enc_data)
def generate_flag():
"""动态生成flag"""
encrypted = get_encrypted_flag()
key = 78
result = bytearray()
for i, byte in enumerate(encrypted):
re"
4.3 加密算法分析
从反编译结果可以看出:
- Flag经过Base64编码存储
- 使用异或加密,密钥为78
- 加密算法还包含了索引i的参与
4.4 Python解密脚本
根据分析,我们可以补全解密脚本:
python复制import base64
enc_data = 'e3w+fiRvfW18fnx4ZAZ6Pj43YwB9OWMXfXo8Dg4O'
encrypted = base64.b64decode(enc_data)
key = 78
result = bytearray()
for i, byte in enumerate(encrypted):
result.append(byte ^ key ^ i & 255)
print(result.decode('utf-8', errors='ignore'))
4.5 注意事项
- Base64解码:注意处理可能的填充字符
- 异或操作顺序:确保与加密时的操作顺序一致
- 字符编码:使用UTF-8解码时注意处理可能的错误
- 反编译限制:在线工具可能无法完整反编译,需要人工分析补全逻辑
5. 逆向分析经验总结
5.1 常用工具链
-
静态分析工具:
- IDA Pro/Ghidra:二进制分析
- jadx/jeb:Android逆向
- pyinstxtractor:Python打包程序解包
-
动态分析工具:
- Frida:动态插桩
- Xposed:Android hook框架
- GDB/LLDB:调试工具
-
辅助工具:
- Binwalk:固件分析
- Apktool:APK反编译
- CyberChef:编解码和加密操作
5.2 解题通用思路
-
信息收集:
- 文件类型识别
- 字符串搜索
- 导入表分析
-
关键点定位:
- 输入输出函数
- 加密解密函数
- 校验逻辑
-
算法分析:
- 识别常见加密模式
- 跟踪数据流
- 验证猜测
-
脚本编写:
- 复现加密逻辑
- 自动化处理
- 结果验证
5.3 常见加密模式识别
-
异或加密:
- 特征:数据与密钥逐字节操作
- 破解:寻找重复模式或已知明文
-
Base64:
- 特征:字符集A-Za-z0-9+/,常以=结尾
- 工具:标准库即可解码
-
哈希校验:
- 特征:固定长度输出
- 识别:MD5(32)、SHA1(40)等
-
对称加密:
- AES:固定块大小,常见模式ECB/CBC
- DES:8字节块,弱密钥
5.4 避坑指南
-
代码混淆:
- 保持耐心,逐层分析
- 关注关键字符串和系统调用
- 动态调试辅助理解
-
反调试:
- 识别反调试技术(时间检查、调试器检测)
- 使用隐蔽的调试方法
- 修改二进制绕过检测
-
多平台差异:
- Windows/Linux/macOS调用约定
- ARM/x86指令集差异
- 字节序问题
-
环境依赖:
- 注意库版本兼容性
- 准备虚拟环境
- 记录环境配置
逆向分析是一项需要耐心和经验的工作。通过不断练习和总结,我们能够更好地理解程序的工作原理,提高分析效率。希望本文的分析过程和经验总结能对大家有所帮助。在实际操作中,最重要的是保持好奇心和解决问题的毅力,每个挑战都是学习新知识的机会。