tcpdump抓取指定返回码的http网络包
tcpdump -i any -A -s 0 -n 'port 80 and (tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x48545450) and (tcp[((tcp[12:1] & 0xf0) >> 2)+4:4] = 0x2f312e31) and (tcp[((tcp[12:1] & 0xf0) >> 2)+8:4] = 0x20323030)'
前面的参数比较简单,就不再详细说明了,我们关注下后面被单引号包裹起来的那串长长的让人有点懵逼的过滤表达式:
port 80 and (tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x48545450) and (tcp[((tcp[12:1] & 0xf0) >> 2)+4:4] = 0x2f312e31) and (tcp[((tcp[12:1] & 0xf0) >> 2)+8:4] = 0x20323030)
其中and起到的作用是逻辑运算里面的与操作符,我们把这个表达式分成四个部分:
port 80
这个比较好理解,意思就是说抓取发往80端口和从80端口回来的包;
tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x48545450
这段等式的前半段比较复杂,等式左边表达的意思是tcp数据包开头的四个字节,等式右边其实是ascii字符编码,查阅下ascii字符编码表(在linux系统可以直接通过man ascii查阅ascii字符编码表),0x48545450其实就是HTTP
那么这个等式的意思就是:tcp数据段的头四个字节 = HTTP,也就是说,我们抓取的是HTTP包;
tcp[((tcp[12:1] & 0xf0) >> 2)+4:4] = 0x2f312e31
类似于前面的表达式,表达的意思是:tcp数据段的头四个字节之后出现的四个字节 = /1.1,即http的版本,上面解析出来的HTTP连起来就是HTTP/1.1
tcp[((tcp[12:1] & 0xf0) >> 2)+8:4] = 0x20323030
这段表达式的意思是:tcp数据段的头八个字节之后出现的四个字节 = ‘ 200’
那现在我们来总结下,前面那段垄长的表达式的意思其实比较简单:tcp数据段的前12个字节 = HTTP/1.1 200
这其实就是HTTP响应报文的起始行。
那么tcp[((tcp[12:1] & 0xf0) >> 2):4]
这种表达式该怎么理解呢?
我们对这段表达式做一个拆分:
tcp[12:1]
tcp[12:1] & 0xf0
(tcp[12:1] & 0xf0) >> 2
((tcp[12:1] & 0xf0) >> 2):4
第一,tcp[12:1]
,参考tcp表达式的语法解释,tcp[x:y]
表示的意思是:从tcp报文的第x字节(不包括第x字节)之后,读取y个字节,那么tcp[12:1]
的意思就是说:读取tcp报文的第12个字节后的一个字节,也就是第13个字节,那么tcp报文的第13个字节是什么呢?来看下tcp报文的结构图:
上面的每一行是32位,也就是4个字节,第13个字节,也就是:4位头部长度 + 6位保留的前四位;
第二,tcp[12:1] & 0xf0
, 其实就是把tcp报文的第13个字节读取出来,前四位也就是tcp头部里面的4位头部长度,后四位填充4个0,也就是左移了四位的tcp报文的头部长度,因为我们多读取了后面四位;
第三,(tcp[12:1] & 0xf0) >> 2
,为什么要右移两位呢?如果我们要得到tcp报文头部的长度,应该要相应地右移四位对不对,因为前面我们多读了四位。但是,tcp报文头部里面的4位头部长度其实是tcp有多少个4字节。那么,如果我们要得到tcp报文到底有多少个字节的话,要再左移两位
所以,先右移四位去掉向后多读的四位,再左移两位从有多少个4字节换算成有多少个字节,总共就是右移两位。
也就是,(tcp[12:1] & 0xf0) >> 2
tcp头部有多少个字节
第四,((tcp[12:1] & 0xf0) >> 2):4
,结合1里面的表达式来理解,意思比较清楚了,读取tcp头部之后的数据段的前四个字节!
那么,tcp数据段开头的四个字节是什么?我们来看下tcp/ip四层协议下的包结构:
在这里,我们记住一点就是,下层的协议数据是在上层的整个包体结构上加上自己的头部,类似于下面的等式关系:
tcp = http + tcp_header
ip = tcp + ip_header
ip = http + tcp_header + ip_header
如果最上层的应用层协议是http协议的话,那么参考http响应包报文:
前12个字节恰好就是HTTP的版本+状态码
下面是使用上面的tcpdump命令示例抓取到的测试机响应报文:
说了这么多,是不是觉得根据http的返回码来抓包还是有点复杂呢,要每次手撸那串表达式估计也是蛮蛋疼的。其实我们可以用shell脚本对tcpdump再进一步封装成httpdump,输入参数就是你想抓取的返回码:
比如这个脚本文件名叫dump.sh,执行
./dump.sh 200
脚本内容参考:
#!/bin/sh
#将输入的http返回码转换成ascii码
code=`echo 1 | hexdump -C | awk '{print234}' | grep -Ev "^"`
#抓包
sudo tcpdump -i any -A -s 0 -n "port 80 and (tcp[((tcp[12:1]&0xf0) >> 2):4] = 0x48545450) and (tcp[((tcp[12:1]&0xf0) >> 2)+4:4] = 0x2f312e31) and (tcp[((tcp[12:1]&0xf0) >> 2)+8:4] = 0x20code)"