由于篇幅原因,本篇文章旨在讲解应用层及应用层的各种协议、Linux下的抓包工具(tcpdump、wireshark)等,关于网络的其他层将会在后续的文章中介绍。
在应用层最重要的东西便当属于协议了,那么协议到底是什么东西呢?
原来在通过socket通讯时,发送和接收的数据都是以“字符串”的形式进行的,那么在接收方,如何才能从字符串中分离出有用的信息呢?于是便有了协议,协议便是一种约定,协议规定发送方按照固定格式将想要发送的数据组织成字符串进行发送(序列化),接收方按照相同的格式解析字符串取到有用的信息,以达到正确传输信息的目的。
应用层包含有各种各样的协议,包括标准协议(HTTP、DNS、Telnet等)、自定义协议(程序员编写的解决各种实际问题的网络程序用到的自定义协议)。
以下介绍几种常见的应用层标准协议:
URL:统一资源定位符是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它,其实所谓URL,就是我们口中常说的网址,格式如下:
https://www.baidu.com/?tn=96928074_hao_pg
说明:
https
:协议名,可以是各种传输层的标准协议名。://
:固定格式,前面为协议名,后面为域名或ip地址。www.baidu.com
域名,此处可以是域名,也可以是ip地址。域名后往往还会跟有端口号,形如:80
。/
:带层次的文件路径,此处为根目录,百度服务器会认为是请求根目录下某个默认文件(百度主页)。?
:固定格式,前面为文件路径,后面为参数。tn=96928074_hao_pg
:参数,每个参数是一个键值对,键和值用=
号分隔,参数可以有多个以&
分隔。urlencode和urldecode(URL编码、解码):
urlencode:
在URL中像:
、/
、?
、=
等这些字符已经有了固定的含义或者一些特殊的字符(那些不是简单的ascii字符,如+
、汉字等),这些字符在传输时不能直接传输,那么在参数中想要传递这些字符时应该怎么办呢? 如果想要在参数中传递这些特殊的字符就必须将这些字符进行转义
再进行传递,也就是URL编码,编码规则如下:
将这些字符的ascii码转为十六进制,若转换后的十六进制为两位,则直接在前面加上%
,若转换后的十六进制大于两位,则在每两位之前加%
,例如:+
编码后为%2B
,张
编码后为%e5%bc%a0
。
urldecode
便是urlencode
的逆过程,解码的规则。
其实在我们编程过程中不用太在意这些规则,因为网络上有好多在线的URL编码解码工具,当我们需要的时候就可以拿来用了。
当我们在浏览器中键入URL后,浏览器会解析URL并根据URL构造出相应的HTTP请求,发送给URL中指定的主机,那么接下来我们就来了解下HTTP报文吧。
HTTP报文格式:
在下面正式介绍http报文格式之前,我们先来了解两个工具:tcpdump、wireshark
。tcpdump是Linux下字符界面的抓包工具,所以我们应该重点掌握,而wireshark是一种图型界面的网络封包分析软件,它不仅可以分析封包也可以抓取封包,所以我们一般在Linux下利用tcpdump将抓取的封包保存下来,在图型界面的环境用wireshark分析抓到的封包。所以wireshark的分析封包功能我们也应该重点掌握。
tcpdump抓取封包:
tcpdump语法:
tcpdump [选项] [参数]
选项:
-A:以ASCII编码打印每个报文(不包括链路层的头),这对分析网页来说很方便;
-c<数据包数目>:在收到指定的包的数目后,tcpdump就会停止;
-C:用于判断用 -w 选项将报文写入的文件的大小是否超过这个值,如果超过了
//就新建文件(文件名后缀是1、2、3依次增加);
-D:列出当前主机的所有网卡编号和名称,可以用于选项 -i;
-e:在输出行打印出数据链路层的头部信息;
-f:将外部的Internet地址以数字的形式打印出来;
-F<表达文件>:从指定的文件中读取表达式,忽略其它的表达式;
-i<网络界面>:监听主机的该网卡上的数据流,如果没有指定,就会使用最小网卡
//编号的网卡(在选项-D可知道,但是不包括环路接口),linux 2.2 内核及之后
//的版本支持 any 网卡,用于指代任意网卡;
-l:如果没有使用 -w 选项,就可以将报文打印到 标准输出终端(此时这是默认);
-n:显示ip,而不是主机名;
-N:不列出域名;
-p:不让网络界面进入混杂模式;
-q:快速输出,仅列出少数的传输协议信息;
-r<数据包文件>:从指定的文件中读取包(这些包一般通过-w选项产生);
-s<数据包大小>:指定抓包显示一行的宽度,-s 0表示可按包长显示完整的包,
//经常和-A一起用,默认截取长度为60个字节,但一般ethernet MTU都是1500字
//节。所以,要抓取大于60字节的包时,使用默认参数就会导致包数据丢失;
-S:用绝对而非相对数值列出TCP关联数;
-t:在输出的每一行不打印时间戳;
-tt:在输出的每一行显示未经格式化的时间戳记;
-v:输出一个稍微详细的信息,例如在ip包中可以包括ttl和服务类型的信息;
-vv:输出详细的报文信息;
-x/-xx/-X/-XX:以十六进制显示包内容,几个选项只有细微的差别.
-w<数据包文件>:直接将包写入文件中,并不分析和打印出来;
参数:
实例:
说明:由于tcpdump需要将网络界面设置成混杂模式,普通用户不能正常执行,但具备root权限的用户可以直接执行它以获取网络上的信息。另外,如果直接运行tcpdump命令,默认会抓取第一个网卡上所有经过的数据包。
1.抓取指定网卡上的数据。
[root@localhost DELL]# tcpdump -i wlp7s0
结果:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
19:48:54.567801 IP localhost.localdomain.46572 > tm-in-f188.1e100.net.https: Flags [P.], seq 2042310756:2042310789, ack 879148209, win 337, options [nop,nop,TS val 19274583 ecr 620717912], length 33
19:48:54.569274 IP localhost.localdomain.58315 > gateway.domain: 13832+ PTR? 188.97.177.108.in-addr.arpa. (45)
19:48:54.625956 IP gateway.domain > localhost.localdomain.58315: 13832 1/0/0 PTR tm-in-f188.1e100.net. (79)
19:48:54.626794 IP localhost.localdomain.33960 > gateway.domain: 4697+ PTR? 71.43.168.192.in-addr.arpa. (44)
19:48:54.630728 IP gateway.domain > localhost.localdomain.33960: 4697 NXDomain 0/0/0 (44)
若以下没有说明抓取经过那个网卡上的数据包,默认抓取经过wlp7s0网卡上的数据
2.抓取经过wlp7s0网卡上,源或目的ip为192.168.43.71的数据包。
[root@localhost DELL]# tcpdump -i wlp7s0 host 192.168.43.71
结果:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
19:57:52.630422 IP localhost.localdomain.47824 > 38.143.1.154.https: Flags [.], ack 568483698, win 4123, options [nop,nop,TS val 19812646 ecr 403680230], length 0
19:57:52.631644 IP localhost.localdomain.43977 > gateway.domain: 63569+ PTR? 154.1.143.38.in-addr.arpa. (43)
19:57:52.676148 IP 38.143.1.154.https > localhost.localdomain.47824: Flags [.], ack 1, win 2025, options [nop,nop,TS val 403684743 ecr 18233570], length 0
19:57:53.095689 IP gateway.domain > localhost.localdomain.43977: 63569 NXDomain 0/1/0 (101)
19:57:53.097793 IP localhost.localdomain.47703 > gateway.domain: 38170+ PTR? 71.43.168.192.in-addr.arpa. (44)
19:57:53.100910 IP gateway.domain > localhost.localdomain.47703: 38170 NXDomain 0/0/0 (44)
3.抓取主机192.168.43.71与主机183.232.231.173或
主机47.95.164.112通讯的数据包,并以ip显示不转换为名字。
[root@localhost DELL]# tcpdump -n -i wlp7s0 host 192.168.43.71 and \(183.232.231.173 or 47.95.164.112\)
结果:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:09:50.296214 IP 192.168.43.71.37100 > 183.232.231.173.https: Flags [F.], seq 2787828002, ack 1416838278, win 1208, length 0
20:09:50.296244 IP 192.168.43.71.48048 > 47.95.164.112.https: Flags [R.], seq 3521333266, ack 860546579, win 501, length 0
20:09:50.296270 IP 192.168.43.71.48046 > 47.95.164.112.https: Flags [R.], seq 3856559806, ack 3613054605, win 1326, length 0
20:09:50.296304 IP 192.168.43.71.48050 > 47.95.164.112.https: Flags [R.], seq 62875069, ack 2425314016, win 3515, length 0
20:09:50.371705 IP 192.168.43.71.37200 > 183.232.231.173.https: Flags [S], seq 3089330567, win 29200, options [mss 1460,sackOK,TS val 20530387 ecr 0,nop,wscale 7], length 0
4.抓取主机192.168.43.71发出的数据包。
[root@localhost DELL]# tcpdump -n -i wlp7s0 src host 192.168.43.71
结果:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:17:36.482870 IP 192.168.43.71.39654 > 47.94.238.112.websm: Flags [S], seq 3588989576, win 29200, options [mss 1460,sackOK,TS val 20996498 ecr 0,nop,wscale 7], length 0
20:17:36.535339 IP 192.168.43.71.45298 > 106.185.47.219.afs3-fileserver: Flags [P.], seq 295387644:295387686, ack 355348308, win 2705, options [nop,nop,TS val 20996550 ecr 913761578], length 42
20:17:36.537292 IP 192.168.43.71.45298 > 106.185.47.219.afs3-fileserver: Flags [P.], seq 42:144, ack 1, win 2705, options [nop,nop,TS val 20996552 ecr 913761578], length 102
5.抓取源或目的ip为192.168.43.71的且源或目的端口为80的TCP报文,即HTTP报文。
[root@localhost DELL]# tcpdump -n -i wlp7s0 host 192.168.43.71 and tcp port 80
结果:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
20:57:59.515229 IP 112.34.111.72.http > 192.168.43.71.54910: Flags [F.], seq 561750399, ack 1629701056, win 65535, length 0
20:57:59.555377 IP 192.168.43.71.54910 > 112.34.111.72.http: Flags [.], ack 1, win 30016, length 0
20:58:00.052161 IP 112.34.111.72.http > 192.168.43.71.54908: Flags [F.], seq 3023768745, ack 611798210, win 65535, length 0
20:58:00.091422 IP 192.168.43.71.54908 > 112.34.111.72.http: Flags [.], ack 1, win 47600, length 0
6.只抓SYN包。
[root@localhost DELL]# tcpdump -n -i wlp7s0 'tcp[tcpflags]=tcp-syn'
结果:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
21:06:34.734361 IP 192.168.43.71.39974 > 47.94.238.112.websm: Flags [S], seq 1522620125, win 29200, options [mss 1460,sackOK,TS val 23934750 ecr 0,nop,wscale 7], length 0
21:06:34.734460 IP 192.168.43.71.39976 > 47.94.238.112.websm: Flags [S], seq 2405168365, win 29200, options [mss 1460,sackOK,TS val 23934750 ecr 0,nop,wscale 7], length 0
7.抓取端口为53的udp报文,即DNS报文。
[root@localhost DELL]# tcpdump -n -i wlp7s0 udp port 53
结果
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
21:10:48.937569 IP 192.168.43.71.49532 > 192.168.43.1.domain: 4908+ A? adservice.google.com. (38)
21:10:48.940675 IP 192.168.43.71.57088 > 192.168.43.1.domain: 39198+ A? clients5.google.com. (37)
21:10:48.955968 IP 192.168.43.71.43237 > 192.168.43.1.domain: 10263+ A? notifications.google.com. (42)
21:10:48.992694 IP 192.168.43.1.domain > 192.168.43.71.49532: 4908 4/0/0 CNAME pagead46.l.doubleclick.net., A 203.208.43.121, A 203.208.43.122, A 203.208.43.109 (126)
21:10:48.995970 IP 192.168.43.71.35272 > 192.168.43.1.domain: 7654+ A? play.google.com. (33)
21:10:49.070521 IP 192.168.43.1.domain > 192.168.43.71.35272: 7654 1/0/0 A 172.217.160.110 (49)
8.抓取arp报文。
[root@localhost DELL]# tcpdump -n -i wlp7s0 arp
结果:
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on wlp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
21:13:01.880551 ARP, Request who-has 192.168.43.71 tell 192.168.43.1, length 28
21:13:01.880592 ARP, Reply 192.168.43.71 is-at 2c:6e:85:53:b4:75, length 28
9.将第5步抓取的HTTP报文保存./tmp/tmp.log
在文件中
[root@localhost 桌面]# tcpdump -n -i wlp7s0 host 192.168.43.71 and tcp port 80 -w ./tmp/tmp.log
结果:
tcpdump: listening on wlp7s0, link-type EN10MB (Ethernet), capture size 262144 bytes
^C 18 packets captured
18 packets received by filter
0 packets dropped by kernel
此时,文件里已经有了抓到的数据了,只不过都是一些看不懂的数据需要用wireshark进一步解析才能成为人可以看懂的字符串便于分析,如下图:
wireshark分析封包:
当我们抓到了数据包,便可以用wireshark进行解析并分析了,在此处我们并不详细介绍wireshark的各个功能,我们只是简单的介绍用wireshark分析我们抓到的包的过程。
1.打开wireshark,从File->Open->tmp.log打开我们的文件后,界面如图所示:
2.右击窗口中任一个HTTP报文,选择Follow TCP Stream(追踪TCP流)便可看到相应的HTTP报文,红色部分表示请求,蓝色部分表示响应。如下图所示:
到此,我们不再深入的介绍tcpdump和wireshark的使用,更多关于这两个工具的使用,请读者自行百度:)。
抓到了HTTP报文,那么接下来我们便来看看HTTP报文究竟长什么,让我们来一探究竟吧。
请求报文:
GET /stat.htm?id=1257346249&r=&lg=zh-cn&ntime=1531053010&cnzz_eid=903834342-1523438485-&showp=1280x720&t=%E7%99%BE%E5%BA%A6%E4%B8%80%E4%B8%8B%EF%BC%8C%E4%BD%A0%E5%B0%B1%E7%9F%A5%E9%81%93&umuuid=162b40d4c86c5-08f6fef9b719e9-3b7c015b-e1000-162b40d4c87e80&h=1&rnd=1079476645 HTTP/1.1
Host: z11.cnzz.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36
Accept: image/webp,image/apng,image/*,*/*;q=0.8
Referer: http://ie.165123.com/baidu.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: cna=NT7VEnIzDiACAW8TKaz38U9L
说明:
在HTTP请求报文中,共可分为三部分有首行、Header及Body,分别解释如下:
响应报文:
HTTP/1.1 200 OK
Server: Tengine
Date: Sun, 08 Jul 2018 13:22:21 GMT
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Connection: close
Vary: Accept-Encoding
Content-Encoding: gzip
16
..............G..y....
0
说明:在HTTP响应报文中,也分为三部分分别为首行、Header、Body。解释如下:
HTTP方法:
方法 | 说明 | 支持的HTTP版本 |
---|---|---|
GET | 获取资源 | 1.0、1.1 |
POST | 传输实体主体 | 1.0、1.1 |
PUT | 传输文件 | 1.0、1.1 |
HEAD | 获得报文首部 | 1.0、1.1 |
DELETE | 删除文件 | 1.0、1.1 |
OPTIONS | 询问支持的方法 | 1.1 |
TRACE | 追踪路径 | 1.1 |
CONNECT | 要求用隧道协议连接代理 | 1.1 |
LINK | 建立和资源之间的联系 | 1.0 |
UNLINK | 断开连接关系 | 1.0 |
注:其中最常用的方法为GET和POST方法。
HTTP状态码:
状态码 | 类别 | 解释短语 |
---|---|---|
1xx | 信息性状态码 | 收到的请求正在处理 |
2xx | 成功状态码 | 请求正常处理完毕 |
3xx | 重定向状态码 | 需要进行附加操作来完成请求 |
4xx | 客户端错误状态码 | 服务器无法处理请求 |
5xx | 服务器端错误状态码 | 服务器处理请求出错 |
注:其中常见的状态码有200(OK)、404(Not Found)、403(Forbidden)、302(Redirect)、503(Bad Gateway)。
HTTP常见Header字段:
简单的HTTP服务器程序:
实现一个能在浏览器页面打印“Hello World”字符串的HTTP服务器程序。
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char *argv[]){
if(argc != 3){
printf("Usage:./server [ip] [port]\n");
return -1;
}
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0){
perror("socket");
return -2;
}
int opt = 1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
if(bind(sock,(struct sockaddr*)&serveraddr,sizeof(struct sockaddr_in)) < 0){
perror("bind");
return -3;
}
if(listen(sock,10) < 0){
perror("listen");
return -4;
}
while(1){
struct sockaddr_in clientaddr;
socklen_t len;
int client_socket = accept(sock,(struct sockaddr *)&clientaddr,&len);
if(client_socket < 0){
perror("accept");
continue;
}
char in_buf[1024*10]= {0};
ssize_t readsize = read(client_socket,in_buf,sizeof(in_buf)-1);
if(readsize <= 0){
close(client_socket);
continue;
}
in_buf[readsize] = '\0';
printf("[Request] %s\n",in_buf);
char out_buf[1024] = {0};
char* out = "hello world
";
sprintf(out_buf,"HTTP/1.0 504 OK\nContent-Length:%lu\n\n%s",strlen(out),out);
write(client_socket,out_buf,strlen(out_buf));
close(client_socket);
}
return 0;
}
注:在云服务器上编译、运行该程序,在浏览器中输入服务器的公网IP地址(必须为公网IP,要是拿两台自己的电脑测试,很有可能不能成功,因为大多数电脑的IP是私有IP)和端口号9090(需要添加云服务器的安全组规则,开放9090端口),就可以在浏览器界面看到“Hello World”字样,如下图:
服务器端:
浏览器端
应用层常见端口总结:
协议 | 传输层协议 | 端口号 |
---|---|---|
HTTP | TCP | 80 |
HTTPS | TCP | 443 |
DNS | UDP(DNS服务器间传输数据时为TCP) | 53 |
DHCP | UDP | 67(服务器回应)、68(服务器接收) |
SMTP | TCP | 25(服务器端接收) |
POP3 | TCP | 110(服务器端发送) |
FTP | TCP | 21(用于控制连接)、20(用于传输数据) |
TELNECT | TCP | 23 |
SSH | TCP | 22 |
注:可以通过cat /etc/services
命令查看知名端口号。