17、网络协议逆向:Wireshark抓包分析、自定义协议识别、加密算法定位与Hook
网络协议逆向,说白了就是跟数据包「打交道」。你拿到一个黑盒软件,不知道它怎么跟服务器通信,不知道它传了什么数据,更不知道它有没有加密。怎么办?抓包看呗。
我个人习惯,拿到一个待分析的软件,第一件事就是开Wireshark。不是直接上IDA,也不是急着跑调试器。先看看它到底在「说」什么,比什么都重要。
17.1 Wireshark抓包基础与技巧
Wireshark这东西,大家都会用。但真正用好的人不多。我见过太多人打开Wireshark,看到满屏花花绿绿的包就懵了。
其实核心就三点:过滤、追踪、统计。
17.1.1 抓包前的准备
先确定你要抓哪个网卡。别抓错了,抓了半天发现是虚拟机网卡,白费功夫。
- 本地回环:如果软件和服务器都在本机,用
loopback或Npcap的环回抓包 - 物理网卡:软件连远程服务器,选你上网的那张卡
- 无线网卡:注意混杂模式,有些网卡不支持
17.1.2 过滤表达式
Wireshark的过滤语法,我建议你背下来几个常用的。别每次都去查手册,效率太低。
| 过滤表达式 | 作用 |
|---|---|
ip.addr == 192.168.1.100 |
只看某个IP的流量 |
tcp.port == 443 |
只看某个端口的流量 |
http.request |
只看HTTP请求 |
tcp contains "login" |
查找包含特定字符串的包 |
!arp and !dns |
排除ARP和DNS噪音 |
我曾经遇到一个软件,它发的包全是乱码。我一开始以为是加密了,后来用tcp contains "0x"一搜,发现是自定义的二进制协议。嗯,这里要注意,不是所有乱码都是加密。
17.2 自定义协议识别
很多商业软件不会用HTTP这种公开协议。它们自己定义一套二进制协议,包头、包体、校验码,全自己来。
怎么识别?我一般分三步走。
17.2.1 找特征码
先抓一批包,看看有没有固定的字节序列。比如每个包的前4个字节是不是固定的魔数?
// 假设抓到的包长这样
// 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
// 0xAB 0xCD 0x00 0x01 0x00 0x2A ... 数据 ...
// 0xAB 0xCD 0x00 0x02 0x00 0x1E ... 数据 ...
// 前两个字节 0xAB 0xCD 就是魔数
// 第3-4字节是命令字
// 第5-6字节是包长度
你想想看,如果每个包都以0xABCD开头,那这基本就是协议魔数了。接下来就是分析每个字段的含义。
17.2.2 字段边界分析
我习惯用「差分法」。就是对比两个相似但不同的包,看哪些字节变了,哪些没变。
- 长度字段:包长度变化时,总有一个字段跟着变
- 序列号:连续发包,某个字段递增
- 校验和:改一个字节,整个包尾部某个字段就变了
- 命令字:不同操作,某个字段值不同
17.2.3 实战:识别一个自定义协议
我记得有一次分析一个游戏外挂。它跟服务器通信,包结构是这样的:
// 包结构
// [魔数:2字节] [命令:2字节] [包体长度:2字节] [包体:可变] [校验:4字节]
// 魔数固定为 0xFE 0xED
// 命令字:0x0001=登录,0x0002=心跳,0x0003=数据上报
// 包体长度 = 包体实际字节数
// 校验 = CRC32(魔数+命令+长度+包体)
怎么发现的?我改了包里的一个字节,服务器就断开了。然后我对比了正常包和异常包的尾部,发现最后4个字节不一样。一算,是CRC32。嗯,这里要注意,很多协议会用CRC做校验,但不是所有CRC都是标准的。
17.3 加密算法定位
协议识别完了,发现数据是加密的。怎么办?找加密算法。
我一般从两个方向入手:静态分析和动态调试。
17.3.1 静态定位:找常量
很多加密算法有固定的常量。比如AES的S盒、MD5的初始向量、Base64的字母表。在IDA里搜这些常量,一搜一个准。
| 算法 | 特征常量 |
|---|---|
| AES | 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, ...(S盒) |
| MD5 | 0x67452301, 0xEFCDAB89, ...(初始向量) |
| Base64 | ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef...(字母表) |
| CRC32 | 0xEDB88320(多项式) |
我个人习惯,先在IDA里搜0x63, 0x7C,如果搜到了,基本就是AES。然后顺着交叉引用找到加密函数。
17.3.2 动态定位:Hook加密函数
静态分析找不到?那就动态调。用Frida或者API Monitor,Hook常见的加密API。
// Frida Hook Windows CryptoAPI
// 目标:定位加密函数调用点
Interceptor.attach(Module.findExportByName("advapi32.dll", "CryptEncrypt"), {
onEnter: function(args) {
console.log("[CryptEncrypt] called");
console.log(" hKey:", args[0]);
console.log(" dwDataLen:", args[2].toInt32());
// 打印加密前的数据
var dataPtr = args[1];
var dataLen = args[2].toInt32();
console.log(" Data:", hexdump(dataPtr, {length: dataLen}));
},
onLeave: function(retval) {
console.log("[CryptEncrypt] returned:", retval);
}
});
我曾经遇到一个软件,它没用Windows的CryptoAPI,而是自己实现了一个XOR加密。我Hook了memcpy和memmove,发现它每次拷贝数据前后都会调用一个内部函数。跟进去一看,就是个简单的XOR循环。说白了,很多所谓的「加密」其实就是XOR,别想复杂了。
17.4 加密算法Hook实战
定位到加密算法后,下一步就是Hook它,拿到明文。
17.4.1 思路
核心思路就一句话:在加密前拿到明文,在解密后拿到明文。
- 加密函数:Hook入口,参数里就是明文
- 解密函数:Hook返回,返回值里就是明文
- 密钥:如果密钥是动态生成的,Hook密钥生成函数
17.4.2 实战:Hook一个自定义AES
假设我们找到了一个自定义的AES加密函数,签名大概是:
void MyAESEncrypt(unsigned char* input, int inputLen, unsigned char* output, unsigned char* key);
用Frida Hook它:
// 找到目标函数地址
var targetAddr = Module.findExportByName("target.exe", "MyAESEncrypt");
// 或者用偏移
// var targetAddr = Module.findBaseAddress("target.exe").add(0x12345);
Interceptor.attach(targetAddr, {
onEnter: function(args) {
console.log("[MyAESEncrypt] called");
console.log(" Input length:", args[1].toInt32());
console.log(" Input data:", hexdump(args[0], {length: args[1].toInt32()}));
console.log(" Key:", hexdump(args[3], {length: 16})); // AES-128
},
onLeave: function(retval) {
// 输出是第二个参数?还是返回值?要看具体实现
// 这里假设输出是 args[2]
console.log("[MyAESEncrypt] output:", hexdump(this.context.r2, {length: 32}));
}
});
你想想看,一旦你拿到了加密前的明文和加密后的密文,再加上密钥,这个加密对你来说就是透明的了。你可以自己写个脚本,随时加解密。
17.4.3 避坑指南
我曾经踩过一个坑。有个软件用了动态密钥,每次启动都不一样。我Hook了加密函数,拿到了明文和密文,但密钥是空的。后来发现,它的密钥是在另一个线程里生成的,我Hook早了。
解决办法:先Hook密钥生成函数,等密钥生成了,再Hook加密函数。或者用Frida的Stalker跟踪代码执行流,找到密钥的传递路径。
17.5 知识体系总览
下面这张图,是我对网络协议逆向整个流程的总结。从抓包开始,到协议识别,再到加密定位和Hook,每一步都有对应的技术和工具。
这张图把整个流程串起来了。你从左上角的Wireshark开始,抓到包后识别协议,发现加密了就定位算法,最后用Hook拿到明文。每一步都有对应的工具和方法。
说白了,网络协议逆向没那么神秘。它就是「观察-分析-验证」的循环。你观察数据包,分析规律,验证你的猜测。错了就重来,对了就继续往下走。
我做了这么多年逆向,最大的体会就是:耐心比技术更重要。有时候一个协议要分析好几天,但只要你坚持,总能找到规律。