Scapy基础学习
Scapy入门
Scapy是一个Python程序,它允许用户发送、嗅探、分析和伪造网络包。这种能力允许构建能够探测、扫描或攻击网络的工具。
中文文档:
https://www.osgeo.cn/scapy/index.html
下载Scapy
1 | pip install scapy |
- Windows需要安装Npcap
- Linux确保安装了tcpdump在终端输入
1
yum install tcpdump
scapy
即可进入交互式命令行
Scapy基本操作
在Scapy中,ls()
函数用于列出当前可用的网络协议和数据包。
如果想查看可用的TCP选项,可以使用ls(TCP)
。
构建一个IP数据包
- 首先查看
IP()
类中的属性。1
2
3
4
5
6
7
8
9
10
11
12
13
14>> ls(IP)
version : BitField (4 bits) = ('4')
ihl : BitField (4 bits) = ('None')
tos : XByteField = ('0')
len : ShortField = ('None')
id : ShortField = ('1')
flags : FlagsField = ('<Flag 0 ()>')
frag : BitField (13 bits) = ('0')
ttl : ByteField = ('64')
proto : ByteEnumField = ('0')
chksum : XShortField = ('None')
src : SourceIPField = ('None')
dst : DestIPField = ('None')
options : PacketListField = ('[]') - 设置属性值
1
2>> IP(dst="192.168.0.1",ttl=64)
<IP ttl=64 dst=192.168.0.1 |>采用分层的形式来构造数据包
一个数据包是由多层协议组合而成,Scapy中实现多层协议通过符号/
分层实现,协议之间使用/
分开,按照从下往上从左往右排列,如Ether()/IP()/TCP()
。
构造一个完整的HTTP数据包:1
2
3
4
5>> a=Ether()/IP(dst="www.slashdot.org")/TCP()/"GET /index.html HTTP/1.0 \n\n"
>> b=raw(a)
>> c=Ether(b)
>> c
<Ether dst=cc:08:fb:ea:3f:71 src=f8:e4:3b:e3:98:17 type=IPv4 |<IP version=4 ihl=5 tos=0x0 len=67 id=1 flags= frag=0 ttl=64 proto=tcp chksum=0x34e6 src=192.168.0.190 dst=104.18.28.86 |<TCP sport=ftp_data dport=http seq=0 ack=0 dataofs=5 reserved=0 flags=S window=8192 chksum=0x77e3 urgptr=0 |<Raw load='GET /index.html HTTP/1.0 \n\n' |>>>>变量a是一个Scapy数据包对象,其中包含了以太网帧、IP数据包、TCP分段和HTTP请求的数据。
b = raw(a)从Scapy数据包对象a中提取原始字节流。raw()方法将数据包序列化为可以在网络上传输的字节流格式。
c = Ether(b)的目的是在序列化后的数据包b周围创建一个以太网帧。这是必要的,因为当我们在局域网上发送数据包时,它们需要封装在一个以太网 帧中,其中包括源设备和目标设备的硬件地址(MAC地址)。
因此,通过将序列化后的数据包b嵌入到以太网帧中创建了c。然后可以将结果数据包发送到网络上。
python代码示例:
1 | from scapy.all import * |
pcap文件读取和保存
1 | rdpcap("test.cap") |
发送和接受数据包
send()
函数工作在第三层,sendp()
函数工作在第二层,这两个函数都可以发送数据包,但是sendp()
函数可以发送以太网帧,而send()
函数只能发送IP数据包。sr()
函数用于发送一个或多个数据包,并等待响应。
1 | from scapy.all import * |
sr1()
函数用于发送一个数据包,并等待响应,但是只返回第一个响应。
1 | from scapy.all import * |
srloop()
函数在第三层连续发送数据包,有接收功能,且连续接收数据包。srp()
函数用于发送一个或多个以太网帧,并等待响应。
之前区别:
srp()函数发送数据包并返回收到的响应数据包列表,而srp1()函数只返回一个响应数据包。
srp()函数可以发送多个数据包,并通过过滤器指定要接收的响应数据包,而srp1()函数只能发送一个数据包并返回第一个响应数据包。
不同的是在sr()函数中,如果没有收到响应数据包,则会返回一个空列表,而在sr1()函数中,如果没有收到响应数据包,则会返回None。
1 | srp1(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(op=1,pdst="192.168.0.1"),timeout=1,iface="iface_name") |
在发送二层和三层数据包时,可以使用iface参数指定要使用的网络接口。如果没有指定网络接口,则会使用默认网络接口。默认网卡为电脑自带的网卡,而我使用的扩展网卡,通过srp()
函数发送数据包时,需要指定网卡名称,通过sr()
函数发送数据包时,需要配置路由表项。默认的网卡不需要配置这些。
“发送和接收”功能族 是 Scapy 的核心。他们返回了两个列表。第一个元素是成对的列表(发送数据包、应答),第二个元素是未应答数据包的列表。
1 | >> sr(IP(dst="192.168.0.1")/TCP(dport=[21,22,23])) |
三次握手原理:
第1次握手:客户端发送一个带有SYN(synchronize)标志的数据包给服务端;
第2次握手:服务端接收成功后,回传一个带有SYN/ACK标志的数据包传递确认信息,表示我收到了;
第3次握手:客户端再回传一个带有ACK标志的数据包,表示我知道了,握手结束。SYN
:开始会话ACK
: 应答RST
: 中断连接FIN
: 结束会话
ARP扫描
ARP请求返回的QueryAnswer对象如下:
1 | QueryAnswer(query=<Ether dst=ff:ff:ff:ff:ff:ff type=ARP | |
字段解释:
1 | 查询请求(Query):发送了一个以太网(Ether)帧,其目的地址(dst)为广播地址(ff:ff:ff:ff:ff:ff),帧类型(type)为ARP。ARP协议请求被用于查找目标IP地址所对应的MAC地址。 |
python实现:
1 | from scapy.all import * |
SYN扫描
1 | >> sr(IP(dst="192.168.0.1")/TCP(sport=RandShort(),dport=(78,100),flags="S"),timeout=1) |
python实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18# SYN scanning
def syn_scan(ip, port):
p = IP(dst=ip) / TCP(sport=RandShort(), dport=int(port), flags="S")
response = sr1(p, timeout=1)
if response == None:
pass
else:
if response[TCP].flags == 'SA':
print(ip, "port", port, "is open.")
elif response[TCP].flags == 'RA':
print(ip, "port", port, "is closed.")
else:
print(ip, "port", port, "is filtered.")
if __name__ == "__main__":
syn_scan("192.168.0.1", "80")
Scapy嗅探
使用Scapy进行嗅探网络数据包,可以捕获和分析传输过程中的所有数据包。
1
2
3
4
5
6
7
8 from scapy.all import *
def packet_handler(pkt):
print(pkt.show())
sniff(prn=packet_handler, count=10)
定义了一个名为packet_handler()的回调函数,用于处理每个捕获到的数据包。使用sniff()函数捕获前10个数据包,并将每个数据包传递给packet_handler()函数进行处理。在packet_handler()函数中,使用show()方法打印出每个数据包的详细信息。
sniff参数详解:
1 | count -- 要捕获的数据包数。0表示无穷大。 |
Scapy 中常用的一些协议层:
1 | Ether 协议层:以太网协议层,用于操作以太网数据帧。 |
获取Raw层的内容:
1 | from scapy.all import * |
haslayer()
:判断数据包是否包含指定协议层。getlayer()
:获取指定协议层的数据包对象。
使用
payload
就相当于向上解封装一次。
也可以直接通过packet[IP].src
直接获取想要的值。