1、附上源码
import socket
import os
import struct
from ctypes import *
# 本地监听
host = "127.0.0.1"
#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_ulong),
("dst", c_ulong)
]
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"}
# human readable IP addresses
self.src_address = socket.inet_ntoa(struct.pack("<L",self.src))
self.dst_address = socket.inet_ntoa(struct.pack("<L",self.dst))
# human readable protocol
try:
self.protocol = self.protocol_map[self.protocol_num]
except:
self.protocol = str(self.protocol_num)
# Windows下嗅探所有数据包,Linux下嗅探ICMP数据包
if os.name == "nt":
socket_protocol = socket.IPPROTO_IP
else:
socket_protocol = socket.IPPROTO_ICMP
sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
sniffer.bind((host, 0))
# we want the IP headers included in the capture
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
# Windows下要打开混杂模式
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)
try:
while True:
# 读取数据包
raw_buffer = sniffer.recvfrom(65565)[0]
# 读取前20字节
ip_header = IP(raw_buffer[0:20])
#输出协议和双方通信的IP地址
print "Protocol: %s %s -> %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)
except KeyboardInterrupt:
if os.name == "nt":
sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)
2、源码解读
ctype库
python的底层大部分都是C/C++实现,python赋予了黑客类似于c语言一样的底层操作能力,且不失动态语言的便捷;
python访问C/C++的方式很多(ctypes,pybind11,cffi,swig),但ctypes是最方便的;
ctype库提供了和C语言兼容的数据类型和函数来加载dll文件,因此在调用时不需对源文件做任何的修改。
用ctype库加载dll文件
import platform
from ctypes import *
if platform.system() == 'Windows':
libc = cdll.LoadLibrary('msvcrt.dll')
elif platform.system() == 'Linux':
libc = cdll.LoadLibrary('libc.so.6')
libc.printf('%s\n', 'here!')
libc.printf('%S\n', u'there!')
struct模块
可以按照指定格式将Python数据和字节流进行转换;可以处理c语言中的结构体。
struct
的pack
函数把任意数据类型变成bytes:
pack(fmt,v1,v2…) | 按照给定的格式(fmt),把数据转换成字符串(字节流),并将该字符串返回. |
struct.pack("<L",self.src)
<表示字节顺序是little-endian,即低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
L表示4字节无符号长整型
fmt格式——对齐方式
fmt格式——格式符
IP结构
Socket模块中一些IP转换函数
socket.ntohl(x) // 类似于C语言的ntohl(x)
把32位正整数从网络序转换成主机字节序。
socket.ntohs(x) // 类似于C语言的ntohs(x)
把16位正整数从网络序转换成主机字节序。
socket.htonl(x) // 类似于C语言的htonl(x)
把32位正整数从主机字节序转换成网络序。
socket.htons(x) // 类似于C语言的htons(x)
把16位正整数从主机字节序转换成网络序。
socket.inet_aton(ip_string) // 依赖于inet_aton的C实现
转换IPV4地址字符串(192.168.10.8)成为32位打包的二进制格式(长度为4个字节的二进制字符串),它不支持IPV6。inet_pton()支持IPV4/IPV6地址格式。
socket.inet_ntoa(packed_ip)
转换32位打包的IPV4地址为IP地址的标准点号分隔字符串表示。
socket.inet_pton(address_family,ip_string)
转换IP地址字符串为打包二进制格式。地址家族为AF_INET和AF_INET6,它们分别表示IPV4和IPV6。
socket.inet_ntop(address_family,packed_ip)
转换一个打包IP地址为标准字符串表达式,例如:“5aef:2b::8”或“127.0.0.1”。
以上来自于https://blog.csdn.net/fan_hai_ping/article/details/8435140