1. 项目背景与核心挑战
在移动应用安全研究领域,即时通讯协议的逆向分析一直是个热门方向。这次我选择微信作为研究对象,主要因为它在国内市场的占有率超过90%,其通信协议设计代表了行业顶尖水平。微信从早期版本开始就采用了多层加密机制,最新版本更是将加密逻辑分散在Native层和Java层,给分析工作带来了不小难度。
这次我重点关注的是Java层实现的AES和RC4加密流量还原。选择Java层入手有几个考虑:首先Java代码相对容易逆向,可以快速定位关键算法;其次Java层通常负责业务逻辑相关的加解密,对理解协议结构更有帮助;最重要的是,Java层算法还原后可以直接用Python或Java复现,方便后续自动化工具开发。
2. 逆向工程环境搭建
2.1 设备与工具选型
我使用的主力设备是Google Pixel 3,选择这款机型主要因为:
- 原生Android系统便于root
- 处理器性能足够实时抓包分析
- 物理设备比模拟器更能反映真实场景
工具链配置如下:
- Frida 14.2.18(动态注入)
- Jadx 1.4.5(静态反编译)
- Burp Suite 2023.8(流量拦截)
- Wireshark 4.0.8(原始流量捕获)
- IDA Pro 7.7(辅助分析Native层)
注意:微信有较强的反调试检测,建议使用定制版Xposed框架配合HideMyApplist模块来隐藏调试环境特征。
2.2 微信样本准备
从APKMirror下载了三个历史版本:
- 8.0.32(2023年4月)
- 8.0.25(2022年12月)
- 7.0.21(2020年6月)
多版本对比可以观察加密方案的演进过程。安装时需要注意:
- 关闭Google Play自动更新
- 使用adb install -d参数允许降级安装
- 安装后立即冻结应用更新组件
3. Java层加密定位技巧
3.1 关键类识别方法
通过以下特征快速定位加密相关类:
- 导入javax.crypto包的类
- 包含Cipher.getInstance()调用的方法
- 类名含AES/RC4/DES等关键词
- 常量表中出现算法标识(如"AES/CBC/PKCS5Padding")
实际分析中发现微信的加密类主要分布在两个包:
- com.tencent.mm.protocal.protobuf(协议基础类)
- com.tencent.mm.sdk.platformtools(工具类)
3.2 动态Hook实战
使用Frida对Cipher类进行动态监控:
Java.perform(function() { let Cipher = Java.use("javax.crypto.Cipher"); Cipher.getInstance.overload('java.lang.String').implementation = function(algorithm) { console.log("Cipher.getInstance: " + algorithm); return this.getInstance(algorithm); }; Cipher.doFinal.overload('[B').implementation = function(input) { let result = this.doFinal(input); console.log(hexdump(input) + "\n=>\n" + hexdump(result)); return result; }; });这个脚本帮我们捕获到关键信息:
- 文本消息使用AES/CBC/PKCS5Padding
- 文件传输使用RC4
- 语音消息采用自定义的AES变种
4. AES算法还原细节
4.1 密钥生成机制
微信的AES密钥不是简单硬编码,而是动态生成的复合密钥:
- 设备指纹(IMEI+MAC地址的SHA256)
- 用户身份Token(从服务器下发)
- 会话随机数(每次登录重新生成)
实际密钥生成代码还原如下:
public static byte[] generateAESKey(String imei, String token, byte[] nonce) { MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); byte[] part1 = sha256.digest((imei + token).getBytes()); byte[] part2 = sha256.digest(nonce); byte[] finalKey = new byte[32]; for (int i = 0; i < 32; i++) { finalKey[i] = (byte)(part1[i] ^ part2[i]); } return finalKey; }4.2 CBC模式特殊处理
微信对标准AES/CBC做了两处修改:
- IV向量不是随机生成,而是取自密钥的后16字节
- 在加密前会附加4字节的消息长度头
解密时需要特别注意这个细节,正确的解密流程应该是:
- 取密文前4字节作为长度标识
- 用密钥后16字节作为IV
- 剩余数据用AES/CBC解密
- 根据长度标识验证数据完整性
5. RC4算法变种分析
5.1 密钥调度调整
标准RC4的KSA算法被修改为:
def modified_KSA(key): S = list(range(256)) j = 0 for i in range(256): j = (j + S[i] + key[i % len(key)] + i) & 0xFF # 额外加了i S[i], S[j] = S[j], S[i] return S这个改动使得密钥与位置相关,提高了抗暴力破解能力。
5.2 文件传输协议分析
文件传输采用RC4加密,但协议结构很有特点:
+----------+------------+-----------+ | 2字节类型 | 4字节文件ID | 加密数据流 | +----------+------------+-----------+解密时需要:
- 提取前6字节明文头
- 用文件ID作为RC4密钥种子
- 对剩余数据流解密
6. 流量解密实战
6.1 解密工具实现
基于分析结果,我用Python实现了通用解密工具:
class WeChatDecryptor: def __init__(self, device_info, user_token): self.aes_key = self._gen_aes_key(device_info, user_token) self.rc4_seed = hashlib.md5(user_token.encode()).digest() def decrypt_aes(self, ciphertext): iv = self.aes_key[-16:] cipher = AES.new(self.aes_key[:32], AES.MODE_CBC, iv) plaintext = cipher.decrypt(ciphertext[4:]) length = int.from_bytes(ciphertext[:4], 'big') return plaintext[:length] def decrypt_rc4(self, ciphertext): file_id = ciphertext[2:6] key = self._gen_rc4_key(file_id) rc4 = ARC4.new(key) return ciphertext[:6] + rc4.decrypt(ciphertext[6:])6.2 典型问题排查
在实际解密过程中遇到过几个典型问题:
问题1:解密后数据乱码
- 检查密钥生成是否使用了正确的设备信息
- 确认是否混淆了AES和RC4加密类型
- 验证消息是否完整(微信经常分片传输)
问题2:偶尔解密成功偶尔失败
- 可能是会话密钥过期(微信每2小时更换一次)
- 检查网络包顺序是否正确(TCP重组问题)
- 确认没有遗漏协议头(微信有些版本会加0x00前缀)
7. 防护方案与对抗思路
微信在较新版本中增加了以下防护措施:
- 关键算法迁移到Native层(通过JNI调用)
- 运行时校验类文件哈希值
- 对Hook框架进行动态检测
对应的对抗方法:
- 对于Native层分析需要结合IDA动态调试
- 使用内存dump获取解密后的类文件
- 在系统内核层隐藏调试痕迹(如修改procfs)
8. 延伸应用场景
这套分析方法不仅适用于微信,稍作调整也可用于:
- 其他即时通讯App的协议分析
- 银行类App的加密通信破解
- 物联网设备的加密固件解析
关键是要掌握算法识别、动态Hook、协议逆向这三项核心技能。在实际项目中,我通常会先花70%时间做逆向分析,30%时间写自动化工具,这样的效率最高。