python使用原始套接字 解析原始ip頭數據


使用底層套接字解碼底層流量,是這次做的重點工作。

 

首先來捕獲第一個包

# coding:utf-8import socket

# 監聽的主機IP
host = "192.168.1.100"

socket_protocol = socket.IPPROTO_ICMP

sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0))
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

raw_buffer = sniffer.recvfrom(65535)
print raw_buffer

下面一行一行解釋上面代碼的意思。

1. 導入socket包

2. 需要監聽的本機ip地址

3. 給socket_protocol變量賦值icmp變量

4. 為sniffer變量創建一個soket對象,該對象為ipv4 原始套接字並指定其協議為icmp

5. 綁定到指定地址和端口進行監聽

6. 為sniffer套接字設置選項參數,使其攜帶ip頭

7. 將監聽端口的套接字收到的原始數據賦值給raw_buffer

8. 打印raw_buffer的值

這個時候,我們使用root權限運行這個腳本,並且開啟另外一個terminal對任意一個地址發送icmp包,我們監聽的接口的recvfrom 會收到回監聽回包到指定地址。recvfrom與recv不同的是 recvfrom會同時接收回包地址。(string, address)的格式

 

這個時候我們可以看到打印出來的值,是一堆完全看不懂的東西,因為是沒有解碼的狀態,下面我們將對ip頭進行解碼。

使用python的struct和ctypes兩個庫實現這一點。

 

# coding:utf-8import socket
import struct
from ctypes import *

# 監聽的主機IPhost = "192.168.1.100"

# IP頭定義
class IP(Structure):
    _fields_ = [
        ("ihl",             c_ubyte, 4),
        ("version",         c_ubyte, 4),
        ("tos",             c_ubyte),
        ("len",             c_ushort),
        ("id",              c_ushort),
        ("offset",          c_ushort),
        ("ttl",             c_ubyte),
        ("protocol_num",    c_ubyte),
        ("sum",             c_ushort),
        ("src",             c_uint),
        ("dst",             c_uint),
    ]

    def __new__(self, socket_buffer=None):
        return self.from_buffer_copy(socket_buffer)

    def __init__(self, socket_buffer=None):
        self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"}

        # readable ip address
        self.src_address = socket.inet_ntoa(struct.pack("<I", self.src))
        self.dst_address = socket.inet_ntoa(struct.pack("<I", self.dst))

        # type of protocol
        try:
            self.protocol = self.protocol_map[self.protocol_num]
        except:
            self.protocol = str(self.protocol_num)

socket_protocol = socket.IPPROTO_ICMP

sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0))
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

try:
    while True:
        raw_buffer = sniffer.recvfrom(65535)[0]

        ip_header = IP(raw_buffer[:20])

        print "Protocol: %s %s -> %s " % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)

except KeyboardInterrupt:
    pass

1. 導入各模塊

2. 監聽的本機ip地址

3. 使用ctypes 構造一個解析ip頭的結構體(structure) IP

4. 使用from_buffer_copy方法在__new__方法將收到的數據生成一個IP class的實例

5. __init__方法初始化一部分數據保存到對應的實例屬性值中。

6. 特別說明下面代碼, 使用了python struct庫的pack方法 用指定的格式化參數將src 和dst的long型數值轉換為字符串,然后使用socket.inet_ntoa方法將字符串的一串數字轉換為對應的ip格式。最后賦值給對應的src或者dst變量

# readable ip address
self.src_address = socket.inet_ntoa(struct.pack("<I", self.src))
self.dst_address = socket.inet_ntoa(struct.pack("<I", self.dst))

7. 一個接收icmp包的服務器,沒什么說的。

8. 無限循環監聽指定端口,將recvfrom收到的數據的第一部分 也就是不要ip地址的部分傳遞給raw_buffer

9. ip頭raw_buffer的前20個字節傳遞給結構體進行解碼。

10. 然后打印。

 

可以看到大致思路就是,將原型socket數據拿過來,然后通過模擬c語言的結構體,使用python的庫對這個格式的包進行一一對應的解碼,將解碼之后的數據打印出來。

到此為止可以看到,在ip層已經可以解析出數據包從哪兒去哪兒的信息。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM