现代计算机程序大部分时候离不开网络,作为开发者,在日常开发网络相关的程序或者排查程序错误时经常会用抓包工具来分析网卡收发的数据,比如著名的tcpdump,Wireshark等。今天我们尝试用100行左右的Python代码在Linux系统上实现一个简单的抓包工具。

本文共分为四节,前三节分别介绍了三个基本概念,包括端序、socket、以太网帧结构,最后一节介绍具体实现,文末有完整代码。

1. 端序

试想一个16进制数0x12345678,大端序在内存中的储存顺序如下,高字节保存在内存的低地址,高字节在前:

内存地址 0 1 2 3

---------------------

字节数据 | 12 | 34 | 56 | 78 |

---------------------

而小端序高字节保存在内存的高地址,低字节在前:

内存地址 0 1 2 3

---------------------

字节数据 | 78 | 56 | 34 | 12 |

---------------------

大端序还是小端序是由硬件决定的,通常CPU和内存中的数据都是小端序,而网络传输都用大端序,因为大端序字节流解析起来更方便,但有些特殊的设备是例外。

Python中socket模块的htons,ntohs,htonl,ntohl函数用来处理网络字节顺序与本地字节顺序之间的转换。函数名中h表示host,n表示net,s表示short int(2字节),l表示long int(4字节),比如htons是将2字节长的整数从本地端序转换为网络端序。比如:

import socket

i = 0x1234 # 本地小端序

net_i = socket.htons(i) # 网络大端序

print(hex(i), hex(net_i))

# 输出 0x1234 0x3412

2. socket编程

socket是操作系统用户与TCP/IP协议族通信的接口,创建一个socket需要3个参数,地址簇,socket类型,协议。比如可以用以下代码创建一个常用的TCP套接字,用来收发TCP数据流:

import socket

# AF_INET: internet协议簇

# SOCK_STREAM: stream类型的socket

# 0: 使用该socket类型的默认协议,即TCP协议

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)

也可以用以下代码创建一个UDP套接字,用来收发UDP数据包:

# SOCK_STREAM: data gram类型的socket

# 0: 使用该socket类型的默认协议,即UDP协议

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)

但是我们今天要用的不是这类套接字,而是更底层的原始套接字raw socket,这类套接字可以操作更底的协议比如IP协议和以太网协议,创建raw socket需要root权限。可以用以下代码创建一个接收以太网数据包的原始套接字:

ETH_P_ALL = 0x3

ETH_P_IP = 0x0800

ETH_P_ARP = 0x0806

ETH_P_RARP = 0x8035

ETH_P_IPV6 = 0x086dd

raw_sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL))

其中ETH_P_ALL表示监听所有协议的以太网数据包,如果改用ETH_P_IP则只会监听IP协议的数据包。(由于Windows系统上没有定义AF_PACKET,所以这段代码无法在Windows系统上运行)

然后可以开始监听:

while True:

packet, packet_info = raw_sock.recvfrom(1500)

print(packet)

recvfrom函数返回2个值,第一个是以太网数据包,第二个是该数据包相关的信息。

3. 以太网帧格式

以太网帧根据不同的标准有多种格式,常见的有Ethernet II格式和Ethernet 802.3格式。

Ethernet II帧格式DMAC:6字节,目标MAC地址

SMAC:6字节,源MAC地址

Type:2字节,上层协议类型

Data:46~1500字节可变长度,载荷数据

FCS:最后4字节,帧校验

Ethernet 802.3帧格式DMAC:6字节,目标MAC地址

SMAC:6字节,源MAC地址

Length:2字节,Data字段包含的字节数

LLC:3字节,逻辑链路控制

SNAP:5字节,Sub-network Access Protocol

Data:38~1492字节可变长度,载荷数据

FCS:最后4字节,帧校验

可以看到Ethernet II和Ethernet802.3格式的前12字节结构相同,由于Ethernet 802.3的头部多了8字节,所以载荷数据长度最大只能为1492字节。另外需要注意,原始套接字读取到的包最后不包含帧校验。

4. 解析以太网头部

在我们了解了以太网帧格式后就可以开始解析接收到的二进制数据了,我们可以用以下代码把MAC地址转换成整数:

BIG_ENDIAN = 'big'

dst_mac = int.from_bytes(packet[:6], BIG_ENDIAN)

src_mac = int.from_bytes(packet[6:12], BIG_ENDIAN)

从第12、13字节用来区分接收到的是Ethernet II帧还是Ethernet 802.3帧,如果该数字小于等于1500(0x05DC)可以判断接收到的是Ethernet 802.3帧,如果该数字大于等于1536(0x0600)则接收到的是Ethernet II帧。其他字段按照帧格式依次解析即可。

完整代码如下:

import socket

BIG_ENDIAN = 'big'

LITTLE_ENDIAN = 'little'

ETH_P_ALL = 0x3

ETH_P_IP = 0x0800

ETH_P_ARP = 0x0806

ETH_P_RARP = 0x8035

ETH_P_IPV6 = 0x086dd

ETH_TYPE_MAP = {

ETH_P_IP: 'IP',

ETH_P_ARP: 'ARP',

ETH_P_RARP: 'RARP',

ETH_P_IPV6: 'IPv6'

}

class MACAddress:

def __init__(self, addr: int):

self.addr = addr

def __str__(self):

return ':'.join('{:02x}'.format(a) for a in self.addr.to_bytes(6, BIG_ENDIAN))

def __repr__(self):

return self.__str__()

class EthernetHeader:

def __init__(self, dst_mac, src_mac):

self.dst_mac = dst_mac

self.src_mac = src_mac

def describe(self):

return {

'src_mac': MACAddress(self.src_mac),

'dst_mac': MACAddress(self.dst_mac)

}

class EthernetIIHeader(EthernetHeader):

def __init__(self, dst_mac, src_mac):

super().__init__(dst_mac, src_mac)

self.eth_type = 0

def describe(self):

dct = super().describe()

dct['eth_type'] = self._describe_eth_type(self.eth_type)

return dct

@staticmethod

def _describe_eth_type(eth_type):

if eth_type in ETH_TYPE_MAP:

return ETH_TYPE_MAP[eth_type]

return 'Unknown protocol{}'.format(eth_type)

class Ethernet802_3Header(EthernetHeader):

def __init__(self, dst_mac, src_mac):

super().__init__(dst_mac, src_mac)

self.length = 0

self.llc = 0

self.snap = 0

def describe(self):

dct = super().describe()

dct['length'] = self.length

dct['llc'] = self.llc

dct['snap'] = self.snap

return dct

def unpack(packet):

dst_mac = int.from_bytes(packet[:6], BIG_ENDIAN)

src_mac = int.from_bytes(packet[6:12], BIG_ENDIAN)

type_or_length = int.from_bytes(packet[12:14], BIG_ENDIAN)

if type_or_length < 1500:

hdr = Ethernet802_3Header(dst_mac, src_mac)

hdr.length = type_or_length

hdr.llc = int.from_bytes(packet[14:17], BIG_ENDIAN)

hdr.snap = int.from_bytes(packet[17:22], BIG_ENDIAN)

return hdr, packet[22:]

elif type_or_length >= 1536:

hdr = EthernetIIHeader(dst_mac, src_mac)

hdr.eth_type = type_or_length

return hdr, packet[14:]

else:

raise ValueError(type_or_length)

def main():

raw_sock = socket.socket(socket.PF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL))

while True:

try:

packet, packet_info = raw_sock.recvfrom(1500)

eth_header, payload = unpack(packet)

print(eth_header.describe(), payload)

except KeyboardInterrupt:

break

except ValueError as e:

print('unpack failed', e)

if __name__ == '__main__':

main()

最后,用root权限运行抓包程序,可以看到屏幕上输出了解析后的Ethernet头部和未解析的载荷数据,如果有兴趣的话可以按照协议标准继续解析载荷数据中的上层协议。

{'src_mac': 50:fa:84:aa:aa:aa, 'dst_mac': ac:d1:b8:aa:aa:aa, 'eth_type': 'IP'} b'E\x00\x00T\x0c\xfe@\x004\x01\x93*\xb4e1\x0c\xc0\xa8\x00g\x00\x00\xb6\x83-\x8e\x00\x07\xa6\xb4c^\x00\x00\x00\x00G\x01\x0c\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567'

...

python抓包 windows_教你用100行Python代码写一个抓包工具相关推荐

  1. 100 行 Java 代码实现一个表情包生成器!

    你知道的越多,不知道的就越多,业余的像一棵小草! 你来,我们一起精进!你不来,我和你的竞争对手一起精进! 编辑:业余草 blog.csdn.net/xiaojimanman/article/detai ...

  2. 用100行Nodejs代码写微博爬虫

    文章为原创首发地址: https://hooyes.net/p/nodejs-w... 思路 通过关键字搜索抓取新浪微博的数据,分析得出新浪微博的搜索地址格式如下: http://s.weibo.co ...

  3. 100行的python作品详解_不到 100 行 Python 代码徐峥变葛优

    给照片换脸大家应该都见过,本文我们来介绍一下如何通过 Python 实现换脸. 功能实现 实现换脸功能,我们大致可以分为两种:一种是所有功能都通过自己编码来实现,另一种是借助于第三方 API 来实现, ...

  4. 100行Android代码自定义一个流式布局-FlowLayout

    首先来看一下 手淘HD - 商品详情 - 选择商品属性 页面的UI 商品有很多尺码,而且展现每个尺码所需要的View的大小也不同(主要是宽度),所以在从服务器端拉到数据之前,展现所有尺码所需要的行数和 ...

  5. 手把手教你!100行代码,用Python做一个“消灭病毒”的小游戏

    公众号关注 "菜鸟学Python" 设为 "星标",重磅干货,第一时间送达! 烟花三月下扬州,我想3月能下楼.虽然很多地方都已经开始慢慢的开放了,但是我怀念的胡 ...

  6. 100个必会的python脚本-100行Python代码实现自动抢火车票(附源码)

    前言 又要过年了,今年你不妨自己写一段代码来抢回家的火车票,是不是很Cool.下面话不多说了,来一起看看详细的介绍吧. 先准备好: 12306网站用户名和密码 chrome浏览器及下载chromedr ...

  7. c语言微信挑一挑编程,100行python代码实现微信跳一跳辅助程序

    写在前面 分享一下今天下午用python写的"跳一跳"小游戏的辅助程序.之前是准备用树莓派操控一个"机械手指"来代替人的触摸操作,但该方案还在酝酿中,实现了再分 ...

  8. python做出来的小程序、可以在win10上面运行_超详细,手把手教你用20行Python代码制作飞花令小程序!...

    原标题:超详细,手把手教你用20行Python代码制作飞花令小程序! 来源:早起Python 作者:陈熹 飞花令是古时候人们经常玩一种"行酒令"的游戏,是中国古代酒令之一,属雅令. ...

  9. 100行python代码做一个程序_100行python代码实现微信跳一跳辅助程序

    写在前面 分享一下今天下午用python写的"跳一跳"小游戏的辅助程序.之前是准备用树莓派操控一个"机械手指"来代替人的触摸操作,但该方案还在酝酿中,实现了再分 ...

最新文章

  1. 农发行:BI数据平台建设
  2. mysql优化 mysql.ini优化
  3. java获取panel面板画笔_java - paintComponent()与paint()和JPanel vs Canvas在画笔类型的GUI中 - 堆栈内存溢出...
  4. FD.io VPP环境下运行用户应用程序教程
  5. web前端开发工程师“想都不用想”的几个知识点
  6. PyQt5的信号和槽
  7. 一个...买裤子的全过程
  8. 初识Unity3D(项目结构、ProBuilder第三方插件)
  9. CAD坐标怎么输入?CAD坐标标注教程
  10. Distance from a point to a hyperplane
  11. 数字证书是什么,里面都包含那些内容
  12. Python如何从列表中删除空列表?代码示例
  13. K-periodic Garland
  14. Java,php,运维工程师转型大数据开发怎么样?你属于哪一类?
  15. 成功解决 HP1010在WIN7下打印速度慢问题
  16. 京东开源人脸识别项目faceX-zoo
  17. 数字SOC设计之低功耗设计入门(二)——功耗的分析
  18. 单片机STC89C52RC实现时钟(汇编语言)
  19. 苹果字体对应font-weight大小
  20. 基于直方图的图像全局二值化算法原理、实现--基于谷底最小值的阈值

热门文章

  1. Linux 规定的 4 种文件类型,Linux中的文件类型以及文件属性
  2. 通过navicat工具利用SSH隧道连接MySQL数据库
  3. Python构造函数的继承
  4. 城市三级联动的json文件
  5. mysql如何替换字符串中换行符及回车符
  6. 测一测你是不是险盲?
  7. 蔡康永的一些文字(转)
  8. ndcg 指标和k的关系
  9. JAVA基本数据类型及练习题
  10. 智能安防系统-视频监控系统