前言
在正式開始本文前先來一段廢話.
工作中經(jīng)常會(huì)使用到自己沒有接觸過的技術(shù), 而后不斷的百度, 谷歌查詢, 不斷的摸索最終找到解決自己遇到的難題的解決方案. 每次都想寫一些總結(jié)分享一下, 但是總覺得自己文筆不佳, 而相關(guān)技術(shù)的文章網(wǎng)絡(luò)上多不勝數(shù), 也不缺自己寫的, 所以到最后總是不了了之
幾個(gè)月后回頭看一看自己做過的項(xiàng)目, 用過的技術(shù), 覺得又熟悉又陌生, 用過卻忘記了.
偶然聽同事說的一句話很受感觸: 寫文章寫總結(jié)并不一定要寫的多好, 更重要的是將自己遇到過的難題以及解決方案分享, 這樣當(dāng)別的 IT Guys在遇到相同難題時(shí), 能快速的定位和解決, 提高工作效率, 這才是分享精神. 想一下: 遇到個(gè)bug, 錯(cuò)誤的時(shí)候, 還不是從其他伙伴的總結(jié), 筆記里一個(gè)個(gè)方案摸索嘗試, 最后解決問題的呢?
以上總結(jié)一句話就是: 寫得不好, 不要吐槽我; 沒幫上你, 別罵我
說明
本文是講解如何以小白的姿勢(shì), 學(xué)習(xí)使用wireshark抓包, 本人是剛?cè)腴Twireshark的使用, 想更深入了解的請(qǐng)自行查詢其他資料;
本文重點(diǎn)講解的是Wireshark的一些簡(jiǎn)單操作, 例如:
- 如何抓取指定網(wǎng)絡(luò)的數(shù)據(jù)包
- 如果編寫過濾表達(dá)式
- 如何分析抓取到的數(shù)據(jù)
- 本文講解如何抓取TCP和UDP協(xié)議的數(shù)據(jù)包; 使用電腦端和手機(jī)端互發(fā)消息, 然后抓取它們之間數(shù)據(jù), 手機(jī)和電腦處于同一網(wǎng)段下(連著同一個(gè)路由器);
- 電腦端使用Java編寫的小程序, 用cmd 窗口做輸入輸出交互
- 手機(jī)端使用安卓手機(jī)
- 本文演示的環(huán)境是Windows操作系統(tǒng)
軟件安裝
可以到百度上搜索 Wireshark, 然后點(diǎn)擊下載即可
點(diǎn)擊安裝, 一路Next即可
界面操作
- 安裝好Wireshark后, 進(jìn)入軟件, 你看到的是如下界面
首先看到的是
VirtualBox host-Only Network
無(wú)線網(wǎng)絡(luò)連接
VirtualBox host-Only Network #2
VirtualBox host-Only Network #4
....
上面顯示的是當(dāng)前操作系統(tǒng)連接了哪些網(wǎng)絡(luò), 這些和你的電腦上
控制面板/網(wǎng)絡(luò)和Internet/網(wǎng)絡(luò)連接是一一對(duì)應(yīng)的
現(xiàn)在, 我的手機(jī)和電腦同處于 無(wú)線網(wǎng)絡(luò)連接 下, 我需要抓取這個(gè)網(wǎng)絡(luò)的數(shù)據(jù)包, 鼠標(biāo)選中 無(wú)線網(wǎng)絡(luò)連接 后, 軟件左上角出現(xiàn)了一個(gè)藍(lán)色的弧形圖標(biāo)(文件) 下, 這個(gè)圖片的含義就是開始捕獲
點(diǎn)擊開始捕獲后, 出現(xiàn)如下:
可以看到, 軟件顯示數(shù)據(jù)模塊被分成了三個(gè)部分, 我們可以稱為 Top, Center和Bottom
- Top顯示當(dāng)前網(wǎng)絡(luò)中傳遞數(shù)據(jù)的記錄的, 當(dāng)前網(wǎng)絡(luò)中每次傳遞的數(shù)據(jù)都會(huì)顯示到上面
- Center顯示的是Top某一條記錄的詳細(xì)信息
- Bottom則顯示Center中某一項(xiàng)數(shù)據(jù)的具體數(shù)據(jù)
還可以看到另外一個(gè)重要的輸入行: 在Top上 提示 "應(yīng)用顯示過濾器" 的輸入框, 用于輸入過濾表達(dá)式來從Top中海量的記錄過濾出我們想要的信息
來看一則抓取到的TCP消息
先來看看過濾表達(dá)式:
tcp and ((ip.src == 192.168.31.113 and ip.dst == 192.168.31.225)or(ip.dst == 192.168.31.113 and ip.src == 192.168.31.225))
PS: 上面的過濾表達(dá)式等同于:
tcp && ((ip.src == 192.168.31.113 && ip.dst == 192.168.31.225) || (ip.dst == 192.168.31.113 && ip.src == 192.168.31.225))
含義是:
1. 當(dāng)協(xié)議是TCP, 并且源地址IP是192.168.31.113, 目標(biāo)地址IP是192.168.31.225時(shí), 滿足過濾條件
2. 當(dāng)協(xié)議是TCP, 并且源地址IP是192.168.31.225, 目標(biāo)地址IP是192.168.31.113時(shí), 滿足過濾條件
過濾出來的記錄確實(shí)如此, Top中顯示的記錄:
Sourece表示是源地址IP
Destination表示的是目標(biāo)地址IP
Protocol表示的是通信協(xié)議
Length表示的是數(shù)據(jù)長(zhǎng)度(字節(jié)個(gè)數(shù))
Info 表示的是數(shù)據(jù)的詳細(xì)信息, 包含了協(xié)議的一些參數(shù), 通信雙方的端口號(hào), 我們發(fā)送的數(shù)據(jù)等等
另外, 本次抓取到的數(shù)據(jù), 其實(shí)是連接 TCP 的時(shí)候抓取到的, 我們都聽聞過TCP是經(jīng)過三次握手才建立連接, 今天終于見到它的廬山真面目
抓取TCP數(shù)據(jù)
本節(jié)來講解如何抓取TCP協(xié)議的數(shù)據(jù)
1. 將我的電腦作為服務(wù)端, 手機(jī)作為客戶端, 手機(jī)去連接電腦, 連接成功后, 手機(jī)和電腦就可以相互發(fā)送消息了
2. 電腦的IP是192.168.23.225, 端口號(hào)是23233
3. 手機(jī)的IP是192.168.23.113, 隨機(jī)分配端口
在Wireshark中添加過濾表達(dá)式
tcp && ((ip.src == 192.168.31.113 && ip.dst == 192.168.31.225) || (ip.dst == 192.168.31.113 && ip.src == 192.168.31.225))
首先, 要使用TCP傳遞數(shù)據(jù), 第一步是創(chuàng)建連接, PS: 以下代碼只是演示使用
- 首先需要先運(yùn)行服務(wù)端程序
int serverPort = 23233;
ServerSocket serverSocket = new ServerSocket(serverPort);
Socket client = serverSocket.accept(); // 阻塞等待, 直到客戶端連接成功
- 客戶端
Socket socket = new Socket();
String serverIp = "192.168.31.225";
InetAddress serverHost = InetAddress.getByName(serverIp);
int serverPort = 23233;
SocketAddress serverAddress = new InetSocketAddress(serverHost,serverPort);
socket.connect(serverAddress); // 執(zhí)行這個(gè)方法后, 如果不拋出異常, 則連接服務(wù)端成功,
- 如果連接服務(wù)端程序成功, 則Wireshark抓取到的消息如下
看到了建立TCP連接的時(shí)候的三次握手, 來看看有什么數(shù)據(jù)可能是我們需要的呢?
1. 看第一次握手的發(fā)起方是192.168.31.113(手機(jī)端), 接收方是192.168.31.225(電腦端)
2. 看第二次握手的時(shí)候, Center里的消息, 看到Src Port是23233(上面說了設(shè)置給服務(wù)端的端口號(hào)), Dst Port是客戶端的端口號(hào)
3. 連接tcp的時(shí)候, 我們并沒有去發(fā)送什么數(shù)據(jù), 全部是協(xié)議底層自己實(shí)現(xiàn)的一些數(shù)據(jù)交互, 而這些數(shù)據(jù)我們不關(guān)心, 因此三次握手抓包意義應(yīng)該不大
客戶端給服務(wù)端發(fā)送 "hello"
- 客戶端發(fā)送數(shù)據(jù)
// 獲取與服務(wù)端的輸入流
OutputStream os = socket.getOutputStream();
// 準(zhǔn)備發(fā)送的信息
String msg = "hello";
// 給準(zhǔn)備發(fā)送的數(shù)據(jù)加一個(gè)換行符, 服務(wù)端以 '行' 的方式來讀取數(shù)據(jù)
msg += "\n";
// 以u(píng)tf-8的編碼寫出數(shù)據(jù)
os.write(msg.getBytes("UTF-8"));
// 將數(shù)據(jù)刷進(jìn)流中
os.flush();
- 服務(wù)端讀取數(shù)據(jù)
// 獲取與客戶端的字節(jié)輸入流
// 將字節(jié)輸入流轉(zhuǎn)成為編碼為utf-8的字符輸入流
BufferedReader reader = new BufferedReader(new InputStreamReader(
client.getInputStream(), "UTF-8"));
String line = null;
// 讀取數(shù)據(jù)
while ((line = reader.readLine()) != null) {
// 將接收到的數(shù)據(jù)打印到cmd控制臺(tái)上
System.out.println("receive msg: "+line);
}
- 抓取到的包
1. 從top可看出第一份數(shù)據(jù)是客戶端(ip:192.168.31.113,端口: 48983)發(fā)給服務(wù)端(ip:192.168.31.225,端口: 23233);
服務(wù)端收到來自客戶端的消息后也客戶端反饋了一份數(shù)據(jù)(第二份數(shù)據(jù))
2. 選中top中的第一份數(shù)據(jù), 可以在center中看到數(shù)據(jù)的詳細(xì)信息:
a. src.ip, dst.ip
b. src.port, dst.port
c. data, 數(shù)據(jù)(6個(gè)字節(jié), 數(shù)據(jù)內(nèi)容為6865566c6c6f0a)
拆成字節(jié)數(shù)組就是[68,65,56,6c,6c,6f,0a], 在這里字節(jié)是以十六進(jìn)制表示的, 轉(zhuǎn)成十進(jìn)制, 再轉(zhuǎn)成對(duì)應(yīng)的ascii碼表上的字符, 數(shù)據(jù)就是['h','e','l','l','o','\n']
總結(jié)一下: data: 6865566c6c6f0a --每?jī)晌粸橐粋€(gè)字節(jié), 拆成字節(jié)數(shù)組-->[68,65,56,6c,6c,6f,0a]--將每個(gè)字節(jié)轉(zhuǎn)成十進(jìn)制, 再轉(zhuǎn)成對(duì)應(yīng)的ascii字符-->['h','e','l','l','o','\n']
是不是抓取到了TCP數(shù)據(jù)了呢?
抓取UDP數(shù)據(jù):
本節(jié)來講解如何抓取UDP協(xié)議的數(shù)據(jù)
UDP和TCP的傳輸方式不一樣, 但是抓取的方式卻是一樣的, 只需要修改一下過濾表達(dá)式, 就可以帥選出UDP協(xié)議的數(shù)據(jù)包
UDP協(xié)議面向無(wú)連接, 所謂面向無(wú)連接, 也就是說不管對(duì)方存不存在, 它都可以向?qū)Ψ桨l(fā)送數(shù)據(jù), 當(dāng)然, 如果接收方不在, 數(shù)據(jù)就會(huì)被丟失了, 所以, 使用UDP發(fā)送數(shù)據(jù), 沒有三次握手, 也沒有想TCP那樣, 接收到消息后, 對(duì)方還會(huì)反饋一個(gè)數(shù)據(jù)
- 首先來編寫一個(gè)過濾表達(dá)式;
我要過濾所有發(fā)送給本機(jī)(ip:192.168.31.225, 端口號(hào)為10086)的udp數(shù)據(jù)
udp && ip.dst == 192.168.31.225 && udp.port == 10086
- 接收端程序
int port = 10086;
DatagramSocket datagramSocket = new DatagramSocket(port);
byte[] buffer = new byte[1024 * 8];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
datagramSocket.receive(packet); // 這個(gè)方法會(huì)阻塞直到接收到一個(gè)udp消息
byte[] data = packet.getData();
int length = packet.getLength();
int offset = packet.getOffset();
InetAddress remoteAddress = packet.getAddress(); // 遠(yuǎn)程發(fā)送端的ip地址
int remotePort = packet.getPort(); // 遠(yuǎn)程發(fā)送端的端口
System.out.println("remoteIp: " + remoteAddress.getHostAddress()
+ ", remotePort: " + remotePort
+ ", Msg: " + (new String(data, offset, length)));
- 發(fā)送端程序
DatagramSocket datagramSocket = new DatagramSocket();
String msg = "hello udp";
String toIp = "192.168.31.225";
int toPort = 10086;
byte[] bytes = msg.getBytes();
DatagramPacket packet = new DatagramPacket(
bytes,0,bytes.length, // 要發(fā)送的數(shù)據(jù)
InetAddress.getByName(toIp), // 要發(fā)送到哪個(gè)ip
toPort); // 要發(fā)送到哪個(gè)端口上
datagramSocket.send(packet);
- 抓包
可以看到,
- top區(qū)顯示的僅一條數(shù)據(jù)記錄
- udp發(fā)送端: ip: 192.168.31.214, 端口:16166
- udp接收端: ip: 192.168.31.225, 端口:10086
- center區(qū)顯示接收到的數(shù)據(jù)具有9個(gè)字節(jié), 數(shù)據(jù)是[68656c6c6f20756470]
- bottom區(qū)可以看到數(shù)據(jù)是"hello udp"
編寫過濾表達(dá)式
Wireshark的過濾表達(dá)式, 就和我們一般寫的if語(yǔ)句的條件表達(dá)式一致
其中,
and 和 && 含義一致, 表示 '并且';
or 和 || 含義一致, 表示 '或者';
udp 表示 udp協(xié)議
tcp 表示 tcp協(xié)議
udp.port == 10086 表示udp端口為10086
tcp.port == 23233 表示tcp端口為23233
ip.src == 192.168.31.225 數(shù)據(jù)發(fā)送端ip是192.168.31.225
ip.dst == 192.168.31.113 數(shù)據(jù)接收端ip是192.168.31.113
來看幾個(gè)例子
- 過濾ip為192.168.31.225設(shè)備, 端口為23233, 協(xié)議為tcp的數(shù)據(jù)包和端口為10086, 協(xié)議為udp的數(shù)據(jù)包
(ip.src == 192.168.31.225 or ip.dst == 192.168.31.225) and ( ((tcp) and tcp.port == 23233) or((udp) and udp.port == 10086))
注意事項(xiàng)
需要注意的是:
Wireshark抓取的是經(jīng)過本機(jī)網(wǎng)卡的數(shù)據(jù), 無(wú)論tcp還是udp, 如果接收端和發(fā)送端都是本機(jī), 那么是抓不到包的, 當(dāng)然解決方案是有的,解決方案:
使用管理員權(quán)限運(yùn)行cmd, 執(zhí)行命令
route add 本機(jī)ip mask 255.255.255.255 網(wǎng)關(guān)ip
發(fā)送數(shù)據(jù)的時(shí)候, ip地址必須添加本機(jī)的ip, 而不應(yīng)該是127.0.0.1或localhost
當(dāng)結(jié)束調(diào)試的時(shí)候, 需要將靜態(tài)ip刪除
route delete 本機(jī)ip mask 網(wǎng)關(guān)ip
總結(jié)
- 本文介紹了Wireshark的簡(jiǎn)單用法, 并分別演示了抓取TCP, UDP數(shù)據(jù)
- 本文從另一個(gè)角度去驗(yàn)證了TCP的三次握手連接, 以及發(fā)送消息時(shí)的連接(TCP發(fā)送消息時(shí), 接收端也有反饋), 驗(yàn)證了UDP的面向無(wú)連接
后記
使用Wireshark抓包并不是新技術(shù), 也不是什么黑科技. 看似能抓取到數(shù)據(jù), 不過現(xiàn)在傳遞的數(shù)據(jù)都是經(jīng)過加密的, 即便抓取到數(shù)據(jù)了, 也無(wú)法解析出來.
比較有用的場(chǎng)景, 例如你在研究一個(gè)設(shè)備(例如攝像頭)與它的控制軟件之間的通訊協(xié)議, 你使用Wireshark抓包, 然后反推出它們之間的通訊協(xié)議; 再實(shí)現(xiàn)自己的需求.