【Python】pcap抓MySQL网络包

2022/4/24 2:12:46

本文主要是介绍【Python】pcap抓MySQL网络包,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

pcap
# -*- coding:utf-8 -*-
# yum install libpcap-devel python-devel
# pip install pypcap hexdump -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com

import pcap, hexdump, zlib
import re, threading, requests

INFO = """## SRC %s:%s
## DST %s:%s
## TYPE %d
## QUERY:
    %s

"""


class Pkt:
    def __init__(self, pkt, dloff):
        """

        :param pkt:
        :param dloff: IP层数据开始位置,成员函数的offset以此为准
        """
        self.pkt = pkt
        self.dloff = dloff
        self.src = self.__addr__()
        self.dst = self.__addr__(src=False)
        self.ipv, self.ip_header_length = self.__l3_base_info__()
        self.l4type, self.l4_header_length = self.__l4__()

    def __l4__(self):
        """
            4层协议类型 1:ICMP 6:TCP 7:UDP
            TCP Header 长度位置 = dloff + ip_header_length + 12 的高四位
        :return: ICMP/TCP/UDP
        """
        d = {1: "ICMP", 6: "TCP", 7: "UDP"}
        l, null = self.__split_bin__(bin(ord(self.pkt[self.dloff + self.ip_header_length + 12])))
        # print "tcp length", ord(self.pkt[self.dloff + self.ip_header_length + 12])
        # print "tcp length", bin(ord(self.pkt[self.dloff + self.ip_header_length + 12]))
        # print "tcp length", l * 4, null, self.ip_header_length
        return d[ord(self.pkt[self.dloff + 9])], l * 4

    def __port__(self, src=True):
        offset, s = 0 if src else 2, ""
        for h in [hex(ord(self.pkt[self.dloff + self.ip_header_length + offset])), hex(ord(self.pkt[self.dloff + self.ip_header_length + 1 + offset]))]:
            s = s + h[2:]
        return int(s, 16)

    def __split_bin__(self, s):
        """

        :param s: 8位bit,例如'0b1000101'
        :return: 高位(0-15),低位(0-15)
        """
        pass
        if not s.startswith("0b"):
            raise TypeError
        if len(s[2:]) < 8:
            s = (8-len(s[2:])) * "0" + s[2:]
        else:
            s = s[2:]
        return int(s[0:4], 2), int(s[4:], 2)

    def __addr__(self, src=True):
        """
            src addr offset 12
            dst addr offset 16
        :param src: True 源,False 目的
        :return: ip
        """
        if src:
            return '.'.join(str(ord(self.pkt[i])) for i in range(self.dloff + 12, self.dloff + 16))
        else:
            return '.'.join(str(ord(self.pkt[i])) for i in range(self.dloff + 16, self.dloff + 20))

    def __l3_base_info__(self):
        """
            ip协议信息(协议版本和头长度) offset 0
        :return: 协议版本号(4,6),头部长度
        """
        s = bin(ord(self.pkt[self.dloff]))
        v, l = self.__split_bin__(s)
        return v, l * 4

    def __mysql_protocol__(self):
        """
            mysql protocol offset: ip_header_length + l4_header_length - 1
            5层内容,包含:
            Packet Length 3字节
            Packet Number 1字节
            Command Type 1字节 https://dev.mysql.com/doc/internals/en/command-phase.html
                3 -> query
                22 -> Prepare DML 预编译语句
                23 -> Execute DML 预编译的Value
                25 -> Close
            5字节后是实际内容
        :return:
        """
        offset = self.dloff + self.ip_header_length + self.l4_header_length
        # length = self.pkt[offset: offset+3]
        if not self.pkt[offset: offset+3]:
            return None, None
        command = ord(self.pkt[offset+4])
        # print type(self.pkt[self.dloff + self.ip_header_length + self.l4_header_length:])
        # print dir(self.pkt[self.dloff + self.ip_header_length + self.l4_header_length:])
        return command, self.pkt[offset+5:]
        # print zlib.decompress(self.pkt[self.dloff + self.ip_header_length + self.l4_header_length:])
        # print zlib.decompress(self.pkt[self.dloff + self.ip_header_length + self.l4_header_length+6:])
        # return hexdump.hexdump(self.pkt[self.dloff + self.ip_header_length + self.l4_header_length:])

    def format(self):
        src_port, dst_port = self.__port__(), self.__port__(False)
        command, query = self.__mysql_protocol__()
        if not command:
            return None
        global INFO
        # return self.dloff, self.src, self.dst, "IPv%s" % str(self.ipv), self.__l4__()
        return INFO % (self.src, src_port, self.dst, dst_port, command, query)


if __name__ == "__main__":
    # catch_pack("eth0", 1)
    sniffer = pcap.pcap(name="en0", immediate=True, timeout_ms=10)
    sniffer.setfilter("dst port 3306")  # 只抓取TCP包
    # addr = lambda pkt, offset: '.'.join(str(ord(pkt[i])) for i in range(offset, offset + 4))
    for ts, pkt in sniffer:
        # print ts, sniffer.dloff
        # print '%d\tSRC %-16s\tDST %-16s' % (ts, addr(pkt, sniffer.dloff + 12), addr(pkt, sniffer.dloff + 16))
        msg = Pkt(pkt, sniffer.dloff).format()
        if msg:
            print msg

查询请求

payload_length -> Packet length: 3字节
sequence_id -> Packet Number:1字节
payload -> Command:1字节
Packet Data:4字节后的所有内容

包的基础结构
https://dev.mysql.com/doc/internals/en/mysql-packet.html
Packet length: 3字节
Packet Number:1字节
Packet Data:4字节后的所有内容

解压缩 https://dev.mysql.com/doc/internals/en/uncompressed-payload.html

不压缩的情况:
set length of payload before compression to 0

the compressed payload contains the uncompressed payload instead.



这篇关于【Python】pcap抓MySQL网络包的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程