Skip to main content
  1. Posts/

如何使用Scapy从raw bytes中提取自定义Header?

·190 words·1 min· 0 · 0 ·
Ryan
Author
Ryan

How to extract custom header from raw bytes using Scapy #

自定义协议 #

有些时候,需要构造一些自定义的协议以完成某些功能。但是这些协议不属于Scapy中已知协议的一部分,因此Scapy无法自动的将数据流中的raw bytes解析为已知的协议。因此,需要经过一些手动的步骤来完成解析。

构造自定义协议 #

Scapy中已经内置了一些常见的协议,可以非常方便的构造给予这些协议的包。

常用协议 #

例如,构造一个Ether+IP的包

import scapy.all as scapy

pkt = scapy.Ether(src="ff:ff:ff:ff:ff:ff",dst="ff:ff:ff:ff:ff:ff")/scapy.IP(src="127.0.0.1",dst="233.233.233.233")
pkt.show()
print(scapy.raw(pkt))

输出如下

###[ Ethernet ]### 
  dst       = ff:ff:ff:ff:ff:ff
  src       = ff:ff:ff:ff:ff:ff
  type      = IPv4
###[ IP ]### 
     version   = 4
     ihl       = None
     tos       = 0x0
     len       = None
     id        = 1
     flags     = 
     frag      = 0
     ttl       = 64
     proto     = ip
     chksum    = None
     src       = 127.0.0.1
     dst       = 233.233.233.233
     \options   \

b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x08\x00E\x00\x00\x14\x00\x01\x00\x00@\x00(\x15\x7f\x00\x00\x01\xe9\xe9\xe9\xe9'

可以看到,直接指定layer的名字即可构造一层。其中layer的内容有默认值,也可以如上所示在构造的时候指定某些字段的值。

构造协议 #

在Scapy中,如果要构造一个layer,只需要一个新的类继承Packet即可,并制定新的layer有哪些字段。

class MyLayer(scapy.Packet):
    name = "my layer"
    fields_desc=[
        scapy.BitField("first",0,size=4),
        scapy.XBitField("second",0,size=4)
    ]

可以看到,自定义的header中,有两个字段。一个是长4bit的字段,第二个也是长4bit的字段,不过用16进制的方式显示。

构造一个有自定义协议的包

pkt = scapy.Ether(src="ff:ff:ff:ff:ff:ff",dst="ff:ff:ff:ff:ff:ff")/MyLayer(first=6,second=0xf)
pkt.show()
print(scapy.raw(pkt))
###[ Ethernet ]### 
  dst       = ff:ff:ff:ff:ff:ff
  src       = ff:ff:ff:ff:ff:ff
  type      = 0x9000
###[ my layer ]### 
     first     = 6
     second    = 0xf

b'\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x90\x00o'

解析自定义协议 #

在Scapy中,使用sniff可以自动嗅探网卡,并将得到的数据包的数据给解析为对应的字段。不过本例中不使用该方式,而是将一个包转化为raw bytes,再将raw bytes转化为包。

pkt = scapy.Ether(src="ff:ff:ff:ff:ff:ff",dst="ff:ff:ff:ff:ff:ff")/MyLayer(first=6,second=0xf)
raw_bytes = scapy.raw(pkt)

new_pkt = scapy.Ether(raw_bytes)
new_pkt.show()

输出如下

###[ Ethernet ]### 
  dst       = ff:ff:ff:ff:ff:ff
  src       = ff:ff:ff:ff:ff:ff
  type      = 0x9000
###[ Raw ]### 
     load      = 'o'

可以看到,此时只能将raw bytes解析为Ethernet,无法继续解析。此时除了人Ethernet层以外的字节被当作了payload,但payload具体是什么还不知道,因此需要告诉scapy下一层是什么。

new_pkt.decode_payload_as(MyLayer)
new_pkt.show()

输出

###[ Ethernet ]### 
  dst       = ff:ff:ff:ff:ff:ff
  src       = ff:ff:ff:ff:ff:ff
  type      = 0x9000
###[ my layer ]### 
     first     = 6
     second    = 0xf

可以看到scapy正确的解析了MyLayer层,并得到了和构造包的时候相同的字段值。