# -*- 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.