• 网络抓包技术: scapy


    概念

    Scapy是一个 Python程序,它允许用户发送、嗅探、分析和伪造网络包。这种能力允许构建能够探测、扫描或攻击网络的工具。

    换句话说,Scapy是一个强大的交互式包操作程序。它能够伪造或解码大量协议的数据包,在网络上发送它们,捕获它们,匹配请求和响应,等等。Scapy可以轻松地处理大多数经典任务,如扫描、跟踪、探测、单元测试、攻击或网络发现。它可以代替hping、arpsoof、arp-sk、arping、p0f甚至Nmap、tcpdump和tshark的某些部分。

    scapy还可以很好地执行许多其他工具无法处理的其他特定任务,例如发送无效帧、注入自己的802.11帧、结合技术(VLAN跳跃+ARP缓存中毒、WEP加密通道上的VoIP解码等)。

    这个想法很简单。scapy主要做两件事:发送数据包和接收答案。定义一组数据包,发送它们,接收答案,将请求与答案匹配,并返回数据包对(请求、应答)列表和不匹配数据包列表。与nmap或hping等工具相比,这有一个很大的优势,即答案不会减少到(打开/关闭/过滤),而是整个包。

    除此之外,还可以构建更多的高级功能,例如,跟踪路由并仅给出请求的起始TTL和应答的源IP。一种能发出整个网络信号并给出应答机列表的设备。一个执行portscan并返回一个 Latex 报告的函数。

    参考文档:介绍 — Scapy 2.4.4. 文档

    功能

    安装scapy

    pip3 install scapy 

    scapy基本使用

    发送和接收数据包

    send

    - 在第3层发送数据包(Scapy创建第2层标头),不接收任何数据包。

    • loop 参数默认为0,如果它的值不是0,那么数据包将一直循环发送,直到按CTRL-C为止。
    • count 可用于设置要发送的数据包的确切数量。
    • inter 可用于设置每个数据包之间的秒数。

    >>> send(IP(dst='8.8.8.8')/TCP(dport=53, flags='S')) . Sent 1 packets. >>> >>> send(IP(dst='8.8.8.8')/TCP(dport=53, flags='S'), count=10) .......... Sent 10 packets. >>> >>> send(IP(dst='8.8.8.8')/TCP(dport=53, flags='S'), loop=1) ......................... [... snipped ...] Sent 1503 packets.

    sendp

    • 与send()相同,但在第2层发送数据包(必须提供第2层标头),不接收任何数据包。
    • 使用iface到设置界面上发送数据包。(如果未设置,将使用conf.iface的值)

    >>> sendp(Ether()/IP(dst="1.2.3.4",ttl=(1,4)), iface="eth0") .... Sent 4 packets. >>> sendp("I’m travelling on Ethernet", iface="eth0", loop=1, inter=0.2) >>> sendp(rdpcap("/tmp/pcapfile")) # tcpreplay ........... Sent 11 packets.

    sr

    • 发送数据包并接收响应。
    • sr()返回两个列表,第一个列表包含响应的,第二个列表包含未响应的。

    >>> sr(IP(dst="60.205.177.168")/TCP(dport=[21,22,23]))

    Begin emission: Finished sending 3 packets. ...**...............................^C Received 36 packets, got 2 answers, remaining 1 packets (, ) >>> ans,unans=_ >>> unans.summary() IP / TCP 172.17.51.80:ftp_data > 60.205.177.168:telnet S >>> ans[0] (>, >) >>> ans[0][0] >

    sr1

    • 发送所有数据包并仅记录第一个响应。

    >>> p=sr1(IP(dst="www.baidu.com")/ICMP()/"asdqwe")

    Begin emission: Finished sending 1 packets. .* Received 2 packets, got 1 answers, remaining 0 packets

    srloop

    • 循环发送,接收响应并显示响应。
    • 该函数返回几个数据包和响应,以及未响应的。

    >>> packet = IP(dst='60.205.177.168')/ICMP()

    >>> srloop(packet)

    RECV 1: IP / ICMP 60.205.177.168 > 172.17.51.80 echo-reply 0 RECV 1: IP / ICMP 60.205.177.168 > 172.17.51.80 echo-reply 0 RECV 1: IP / ICMP 60.205.177.168 > 172.17.51.80 echo-reply 0 RECV 1: IP / ICMP 60.205.177.168 > 172.17.51.80 echo-reply 0 ^C Sent 4 packets, received 4 packets. 100.0% hits. (, )

    使用Scapy创建数据包

    • Scapy数据包的创建与网络中的分层方法一致。
    • 数据包的基本构建块是一层,而整个数据包则是通过将各个层堆叠在一起而构建的。
    • scapy通过在TCP / IP的不同层上为每个协议定义数据包头,然后按顺序堆叠这些层,来构造数据包。

    在一行中创建数据包

    >>> packet = Ether()/IP(dst='8.8.8.8')/TCP(dport=53,flags='S')

    分别创建每个图层并使用'/'运算符将它们堆叠

    >>> l2 = Ether()

    >>> l3 = IP(dst='8.8.8.8/30')

    >>> l4 = TCP(dport=53, flags = 'S')

    >>> packet = l2/l3/l4

    Scapy IP表示法

    Scapy接受普通的IP表示法,CIDR表示法,主机名。

    >>> packet = IP(dst = '8.8.8.8')

    >>> packet = IP(dst = 'scanme.nmap.org')

    >>> packet = IP(dst = '8.8.8.8/30')

    >>> [a for a in packet] [, , , ] >>> packet = IP(dst = 'egadz.metasploit.com/30')

    创建一组数据包

    我们可以使用Scapy创建一组数据包

    >>> pkts = IP(ttl=[1,3,5,(7,10)])/TCP()
    >>> [pkt for pkt in pkts]
    [>,
     >,
     >,
     >,
     >,
     >,
     >]
     >>> packet=IP(dst="192.168.*.1-10")/TCP(dport=(0,100))
     >>> [a for a in packet]
    [>,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
     >,
    ...

    检查数据包

    获取数据包的详细说明以及数据类型

    >>> packet = IP()/TCP()
    >>> ls(packet)
    version    : BitField             = 4               (4)
    ihl        : BitField             = None            (None)
    tos        : XByteField           = 0               (0)
    len        : ShortField           = None            (None)
    id         : ShortField           = 1               (1)
    flags      : FlagsField           = 0               (0)
    frag       : BitField             = 0               (0)
    ttl        : ByteField            = 64              (64)
    proto      : ByteEnumField        = 6               (0)
    chksum     : XShortField          = None            (None)
    src        : Emph                 = '127.0.0.1'     (None)
    dst        : Emph                 = '127.0.0.1'     ('127.0.0.1')
    options    : PacketListField      = []              ([])
    [-- snipped --]

    show

    显示详细的包头

    >>> packet.show()
    ###[ IP ]### 
      version= 4
      ihl= None
      tos= 0x0
      len= None
      id= 1
      flags= 
      frag= 0
      ttl= 64
      proto= tcp
      chksum= None
      src= 127.0.0.1
      dst= 127.0.0.1
      \options\
    ###[ TCP ]### 
         sport= ftp_data
         dport= http
         seq= 0
         ack= 0
         dataofs= None
         reserved= 0
         flags= S
         window= 8192
         chksum= None
         urgptr= 0
         options= []

    show2

    与show()类似,但可以组装数据包并计算校验和和IHL(报头长度,最小值是5)。

    >>> packet.show2()
    ###[ IP ]### 
      version= 4
      ihl= 5
      tos= 0x0
      len= 40
      id= 1
      flags= 
      frag= 0
      ttl= 64
      proto= tcp
      chksum= 0x7ccd
      src= 127.0.0.1
      dst= 127.0.0.1
      \options\
    ###[ TCP ]### 
         sport= ftp_data
         dport= http
         seq= 0
         ack= 0
         dataofs= 5
         reserved= 0
         flags= S
         window= 8192
         chksum= 0x917c
         urgptr= 0
         options= []

    summary

    显示数据包的简短的摘要

    >>> packet.summary()
    'IP / TCP 127.0.0.1:ftp_data > 127.0.0.1:http S'

    与数据包内部的字段进行交互

    >>> Ether(dst="d8:55:a3:fe:80:78")/IP(dst="8.8.8.8")
    >
    >>> packet=_
    >>> packet.dst
    'd8:55:a3:fe:80:78'
    >>> packet[IP].dst
    '8.8.8.8'

    检查数据包中是否存在层

    haslayer方法

    >>> if packet.haslayer(IP):
    ...:     print (packet[IP].dst)
    ...: 
    8.8.8.8

    使用in构造

    >>> pkt = IP()/TCP()/DNS()
    >>> DNS in pkt
    True

    Scapy的sprintf

    • sprintf()方法是Scapy的强大功能之一,在编写自定义工具时非常方便。
    • sprintf 用数据包中的值填充格式字符串,就像C语言库中的sprintf一样,不同的是这里用数据包中的字段值填充格式字符串。
    >>> packet.sprintf("Ethernet source is %Ether.src% and IP proto is %IP.proto%")
    'Ethernet source is 00:16:3e:0c:d1:ad and IP proto is tcp'
    >>> a.sprintf("%dst% %IP.dst% vlan=%Dot1Q.vlan%")
    '00:00:d4:ae:3f:71 192.168.0.1 vlan=42'
    >>>
    >>>a.sprintf(" %TCP.flags% | %5s,TCP.flags% | %#05xr,TCP.flags%")
    ' RA | RA    | 0x014'

    数据包处理程序

    我们可以使用lambda函数编写处理TCP数据包的数据包处理程序,但该功能仅适用于TCP数据包。

    >>>  f=lambda x:x.sprintf("%IP.dst%:%TCP.dport%")
    >>> f(IP(dst="8.8.8.8")/TCP())
    '8.8.8.8:http'
    >>> f(IP(dst="8.8.8.8")/UDP())
    '8.8.8.8:??'

    还可以使用sprintf()中的条件子字符串来实现处理其它层的目的。条件子字符串仅在数据包中存在某个层时才触发,否则将被忽略。还可以!用于检查是否缺少图层。条件子字符串格式: {[!]层:子字符串}

    >>> f=lambda x: x.sprintf("=> {IP:ip=%IP.dst% {UDP:dport=%UDP.dport%}\
    ...: ... {TCP:%TCP.dport%/%TCP.flags%}{ICMP:type=%r,ICMP.type%}}\
    ...: ... {!IP:not an IP packet}")
    >>> f(IP()/TCP())
    '=> ip=127.0.0.1 http/S'
    >>> f(IP()/UDP())
    '=> ip=127.0.0.1 dport=domain'
    >>> f(IP()/ICMP())
    '=> ip=127.0.0.1 type=8'
    >>> f(Ether()/ARP())
    '=> not an IP packet'

    导入与导出数据

    PCAP格式

    从PCAP文件导入数据包。

    pkts = rdpcap("temp.cap")
    pkts = sniff(offline="temp.cap")

    将数据包导出到pcap文件。

    wrpcap("temp.cap",pkts)

    十六进制转储格式

    • Scapy允许以各种十六进制格式导出数据包。
    • 使用hexdump()函数使用hexdump格式显示一个或多个数据包:

    十六进制字符串

    还可以使用str()函数将整个数据包转换为十六进制字符串

    Base64

    • Scapy可以使用export_object()函数导出数据包的base64编码数据。
    >>> export_object(s)
    b'eNprYEouTk4sqNTLSaxMLSrWyzHici3JSC3iKmTQDCpk1EiOT85PSU0u5krNAzG4Cpki7BkYGA7PCD20+PC+Qw0VDGJ2PIcnHlrLweDKwKDBwMjA4MB2qDvu0BpB4wAOIGAQYQhggIIAJgWGQwt4GRgKmSPYgPycxJLMPMNClrZC1qBCNnfHGxoeDcsdkv2AoKSQPUkPALURLMU='
    >>> new_pkt = import_object

    嗅探

    Sniff()

    • sniff()函数可帮助我们捕获所有流量:
    • 包括count,filter,iface,lfilter,prn,timeout选项。
    >>> sniff(count=4, iface='eth0')
    

    可以添加过滤以捕获需要的数据包,使用标准的tcpdump / libpcap语法:

    >>> pkts = sniff(count=1,filter="tcp and host 60.205.177.168 and port 80")
    >>> pkts.summary()
    Ether / IP / TCP 172.17.51.80:54578 > 60.205.177.168:http S
    • 可以做类似tcpdump的简单流量分析器
    >>>  pkts = sniff(count=5,filter="host 60.205.177.168",prn=lambda x:x.summary())
    Ether / IP / TCP 172.17.51.80:54624 > 60.205.177.168:http S
    Ether / IP / TCP 60.205.177.168:54624 > 172.17.51.80:http S
    Ether / IP / TCP 172.17.51.80:http > 60.205.177.168:54624 SA
    Ether / IP / TCP 60.205.177.168:http > 172.17.51.80:54624 SA
    Ether / IP / TCP 172.17.51.80:54624 > 60.205.177.168:http A
    • 也可以从pcap文件中嗅探数据包。
    pkts = sniff(offline='test.pcap')
    >>> pkts.nsummary()
    0000 Ether / IP / TCP 172.16.16.128:1606 > 74.125.95.104:http S
    0001 Ether / IP / TCP 74.125.95.104:http > 172.16.16.128:1606 SA
    0002 Ether / IP / TCP 172.16.16.128:1606 > 74.125.95.104:http A
    0003 Ether / IP / TCP 172.16.16.128:1606 > 74.125.95.104:http PA / Raw
    0004 Ether / IP / TCP 74.125.95.104:http > 172.16.16.128:1606 A / Padding
    >>> sniff(offline='test.pcap', lfilter = lambda s: s[TCP].flags == 18, prn = lambda x: x[IP].dst)
    192.168.1.1
    

    样例

    # encoding: utf-8
    from scapy.all import *
    import threading
    import sys
    
    
    def dealwith():
        print '开始抓包'
        # 下面的iface是电脑网卡的名称 count是捕获报文的数目
        dpkt = sniff(iface="Qualcomm Atheros AR956x Wireless Network Adapter", count=1000)  # 抓包 # prn=lambda x: x.show(),
        # pkts = sniff(prn=lambda x : x.sprintf("{IP:%IP.src% -> %IP.dst%\n}{Raw:%Raw.load%\n}"))
        print ( '抓包成功')
    
        wrpcap("c://demo.pcap", dpkt)
        print ('所抓的包已经保存')
    
        pcks = rdpcap('c://demo.pcap')
        print ('开始解析pcap包')
    
        # 输出重定向  讲在控制台的输出重定向到 txt文本文件中
        output = sys.stdout
        outputfile = open('c://capture.txt', 'w')
        sys.stdout = outputfile
    
        zArp = 0
        zIcmp = 0
        ipNum = set()
    
        for p in pcks:
            status1 = p.payload.name  # 可能是ARP的报文
            status2 = p.payload.payload.name  # 可能是TCP报文 也可能是ICMP的报文
    
            # p.show() 输出报文, 在符合的情况下
            if status1 == 'IP':
                ipNum.add(p.payload.src)  # 将ip报文的源地址,和目的地址存在set集合里面(set去重)
                ipNum.add(p.payload.dst)
                p.show()
                print ('')
            else:
                if status1 == 'ARP':
                    p.show()
                    print ('')
                    zArp += 1
    
                if status2 == 'ICMP':
                    p.show()
                    print ('')
                    zIcmp += 1
    
        print ('IP:' + str(len(ipNum)) + ' ARP:' + str(zArp) + ' ICMP:' + str(zIcmp) )# 报文数量的输出
    
        outputfile.close()
        sys.stdout = output  # 恢复到控制台输出
    
        print ( '输出结束')
        print( dpkt)
    
    dealwith() # 运行报文捕获函数
    
    
    

     

  • 相关阅读:
    54亿美元!谷歌收购Mandiant,推动网络安全行业创新
    批量插入优化
    python自动化测试—Python自动化框架及工具
    AxGlyph矢量绘图软件 | 绘图软件
    设计模式:建造者模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)
    Pikachu靶场之SSRF服务器端请求伪造
    Python程序设计实例 | 条形码图片识别
    .NET混合开发解决方案11 WebView2加载的网页中JS调用C#方法
    Linux SSH免密登录
    android studio提示HAXM device is not found或者could not start avd
  • 原文地址:https://blog.csdn.net/qq_32378713/article/details/126823592