Scapy 解析 pcap 文件从HTTP流量中提取图片

Scapy 解析 pcap 文件从HTTP流量中提取图片

      • 前言
      • 一、网络环境示例
      • 二、嗅探流量示例
      • 三、pcap 文件处理
      • 最后
      • 参考

​ 作者:高玉涵

​ 时间:2023.9.17 10:25

​ 环境:Linux kali 5.15.0-kali3-amd64,Python 3.11.4,scapy 2.5.0

只有在拿到一些数据之后事情才会变得有趣起来。

前言

​ 通常我在网络嗅探与数据包分析中,使用 Wireshark 就可以很方便地浏览 pcap 文件的内容。但当捕获得流量很大或数据包特征不太明显,再或者数据包特征已确定,要从中进一步分析(提取)流量。以往采用人工方式可以说是种恶梦。幸运的是 Philippe Biondi 为 Python 开发的数据包处理库 Scapy 以精巧和令人惊叹,一两行代码就能解决上述问题(功能远远不止如此)。这里我会演示如何借助 Scapy 的 pcap 数据处理能力,从嗅探到的 HTTP 流量中提取图片。

​ 建议你在 Linux 上使用 Scapy,因为它最初就是按照兼容 Linux 来设计的。最新版本的 Scapy 虽然支持 Windows,但是我假设你使用的和我一样的环境。

一、网络环境示例

在这里插入图片描述

图 1-1 网络架构

二、嗅探流量示例

​ 为了感受一下 Scapy 的用法,我先给出用 Wireshark 嗅探的流量,图 2-1 是原始流量样式,图 2-2 为指定一个数据包过滤器(这里是 HTTP)表示要进一步分析的流量。

在这里插入图片描述

图 2-1 原始流量样式

在这里插入图片描述

图 2-2 HTTP 流量样式

三、pcap 文件处理

​ 我会试着从 HTTP 流量中提取出图片文件,为此编写分析 pcap 文件的 recapper.py 程序,定位数据流中出现的所有图片,并把它们保存到磁盘上。在接下来的代码中,我将用到 namedtuple(命名元组),它是 Python 的一种数据结构,可以通过名称来访问某个字段。标准的元组(tuple)是用来存储一串不可变的值的,跟列表(list)差不多,只是没法修改里面的数据。使用标准元组时,要用数字索引来访问其内部的字段:

point = (1.1, 2.5)
print(point[0], point[1])

​ 要命名元组跟标准元组基本相同,唯一的区别是可以通过属性名来访问它的字段。它能让你写出可读性更高的代码,却又比字典(dictionary)

消耗的内存更少。创建命名元组需要两个参数:元组本身的名字,以及由逗号分隔的若干字段名。举个例子,假设你想定义一个名为 Point 的数据结构,它有两个属性:x 和 y。你可以这样定义:

Point = namedtuple('Point', ['x', 'y'])

​ 接着你可以创建一个,比方说叫 p 的 Point 命名元组,p = Point(35, 65),然后使用 p.x 和 p.y 来访问它的 x 和 y 属性,就像是在访问一个类的属性一样。这比使用一堆数字索引来访问标准元组要好懂得多。在接下来的示例代码中,我们将创建一个名为 Response 的命名元组:

Response = namedtuple('Response', ['header', 'payload'])

​ 现在,无须用数字索引,直接用 Response.header 和 Response.payload 就能访问 Response 的成员数据,大大改善了代码的可读性。

​ 接下来是 recapper.py 文件,代码如下:

from scapy.all import TCP, rdpcap
import collections
import os
import re
import sys
import zlibOUTDIR = '/home/kali/dev/python/scapy/pic'
PCAPS = '/home/kali/dev/python/scapy'Response = collections.namedtuple('Response', ['header', 'payload'])def get_header(payload):passdef extract_content(Response, content_name='image'):passclass Recapper:def __init__(self, fname):passdef get_responses(self):passdef write(self, content_name):passif __name__ == '__main__':pfile = os.path.join(PCAPS, 'face.pcap')recapper = Recapper(pfile)recapper.get_responses()recapper.write('image')

​ 这是整个脚本的主要框架,之后我会往里面填充辅助函数。首先,导入所需的库,然后指定保存图片的目录和 pcap 文件的路径。接着,定义一个名为 Response 的命名元组,它有两个属性:数据包的头(header)和载荷(payload)。我编写两个辅助函数,分别负责获取数据包头和提取数据包内容。这两个函数将用在 Recapper 类里,而这个类负责重构在数据包流中出现过的图片。除了 __init__ 函数外,Recapper 类还有两个函数:get_responses,负责从 pcap 文件中读取响应数据;write,负责把在响应数据中找到的图片写到输出目录里。

​ 现在我开始写 get_header函数:

def get_header(payload):try:header_raw = payload[:payload.index(b'\r\n\r\n')+2]# 切片前包后不包,+2 是为了将分隔数据行的'\r\n'也含进去except ValueError:sys.stdout.write('-')sys.stdout.flush()return Noneheader = dict(re.findall(r'(?P<name>.*?):(?P<value>.*?)\r\n', header_raw.decode()))if 'Content-Type' not in header:return Nonereturn header

get_header函数会读取原始 HTTP 流量,并把 HTTP 头数所单独切出来。我提取 HTTP 头的方式是,从数据包开头一路往下找两个连续的 “\r\n”,把这整段数据切出来。图 3-1 HTTP 报文结构。图 3-2 真实示例。
在这里插入图片描述

图 3-1 HTTP 报文结构

在这里插入图片描述

图 3-2 真实示例

​ 如果拿到的数据里不存在两个连续的 ”\r\n“,就会产生一个 ValueError 异常,这时我只会在屏幕上输出一个横杠(-),然后返回。如果没有发生异常,我就创建一个名为 header 的字典,把 HTTP 头里的每一行以冒号分割,冒号左边的是字段名,右边的是字段值,按这样的方式存进 header 字典里。如果 HTTP 头里面没有名为 Content-Type 的字段(图 3-3 Content-Type 字段),就返回 None,表示数据包里没有我感兴趣的内容。现在写一个函数,从响应数据里提取内容:

def extract_content(Response, content_name='image'):content, content_type = None, Noneif content_name in Response.header['Content-Type']:content_type = Response.header['Content-Type'].split('/')[1]content = Response.payload[Response.payload.index(b'\r\n\r\n')+4:]if 'Content-Encoding' in Response.header:if Response.header['Content-Encoding'] == 'gzip':content = zlib.decompress(Response.payload, zlib.MAX_WBITS | 32)elif Response.header['Content-Encoding'] == 'deflate':content = zlib.decompress(Response.payload)return content, content_type

extract_content函数会接受一段 HTTP 响应数据(Response),以及我想提取的数据类型的名字作为参数。这段响应数据是一个命名元组,里面有两部分:header 和 payload。

​ 如果检测到响应数据被 gzip 或 deflate 之类的工具压缩过,就调用 zlib 库来解压这段数据。任何一个含有图片的响应包,其数据头的 Content-Type 属性里都会有 image 字样(图 3-3)。遇到这种数据头,我就创建一个 content_type 变量,将数据头里指定的实际数据类型保存下来。我还创建另一个变量 content 来保存数据内容,也就是 payload 中 HTTP 头之后的全部数据。最后,将 content 和 content_type 打包成一个元组返回。

在这里插入图片描述

图 3-3 Content-Type 字段

​ 写完这两个辅助函数后,就可以开始编写 Recapper 类了:

class Recapper:def __init__(self, fname):pcap = rdpcap(fname)self.sessions = pcap.sessions()self.responses = list()

​ 首先,初始化这个对象,把要读取的 pcap 文件路径传给它。接着我用到 Scapy 的一个美妙功能,自动切分每个 TCP 会话,并保存到一个字典里,字典里面的每个会话都是一段完整的 TCP 数据流。最后,创建一个名为 responses 的空列表,之后我会把在 pcap 文件中读到的响应填进去。

get_responses函数会遍历整个 pcap 文件,将找到的每个单独的 Response 都填入刚才的 responses 列表:

def get_responses(self):for session in self.sessions:payload = b''for packet in self.sessions[session]:try:if packet[TCP].dport == 80 or packet[TCP].sport == 80:payload += bytes(packet[TCP].payload)except IndexError:sys.stdout.write('x')sys.stdout.flush()if payload:header = get_header(payload)if header is None:continueself.responses.append(Response(header=header, payload=payload))

get_responses函数先遍历整个 sessions 字典中的每个会话,以及每个会话中的每个数据包。然后过滤这些数据,只处理发往 80 端口或者从 80 端口接收的数据。接着,把从所有流量中读取到的数据载荷,拼接成一个单独的名为 payload 的缓冲区。这个操作相当于在 Wireshark 中右键单击一个数据包,选择”Follow TCP Stream“选项(图 3-4)。如果没能成功地拼接 payload 缓冲区(最有可能的情况是,数据包里没有出现 TCP 数据),就在屏幕上打印一个 ”x" 然后继续。

在这里插入图片描述

图 3-4 Follow TCP Steram

​ 重组 HTTP 数据后,如果 payload 缓冲区里有数据,就把它交给解析 HTTP 头的函数 get_header,它能帮我逐个检查 HTTP 头的内容。然后,把构造出的 Response 对象附加到 responses 列表里。

​ 最后,遍历整个 responses 列表,如果发现任何含有图片的响应,就用 write 函数将这些图片写到磁盘上:

def write(self, content_name):for i, response in enumerate(self.responses):content, content_type = extract_content(response, content_name)if content and content_type:fname = os.path.join(OUTDIR, f'ex_{i}.{content_type}')print(f'Writeing {fname}')with open(fname, 'wb') as f:f.write(content)

​ 由于我已经提取完所有响应,所以 write 函数只需要遍历这些响应,提取其中的内容,并将内容写到一个文件里就可以了。这个文件会被创建到指定的输出目录里,文件名由 enumerate 函数提供的计数和 content_type 两个值拼接而成,例如 ex_2.jpg 就是一个可能出现的图片文件名。当我运行这个程序时,会创建一个 Recapper 对象,调用它的 get_responses 函数来搜索 pcap 文件中的所有响应,然后将提取出的图片写入磁盘。图 3-5。图 3-6.

在这里插入图片描述

图 3-5 程序执行样式

在这里插入图片描述

图 3-5 提取的图片

最后

​ 当然,你也可以进一步拓展这个程序,让它不仅能从 pcap 文件中提取图片。

参考

  • Scapy
  • HTTP 标头

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/135148.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

线性代数的本质(二)——线性变换与矩阵

文章目录 线性变换与矩阵线性变换与二阶方阵常见的线性变换复合变换与矩阵乘法矩阵的定义列空间与基矩阵的秩逆变换与逆矩阵 线性变换与矩阵 线性变换与二阶方阵 本节从二维平面出发学习线性代数。通常选用平面坐标系 O x y Oxy Oxy &#xff0c;基向量为 i , j \mathbf i,…

什么是无人机全自动飞行系统?概念、构成、作用深度解析

无人机的工业化应用深入催生出新的痛点&#xff0c;无人机应用飞手培养难、成本高、技术参差不齐&#xff0c;以及应急响应和采集作业价值等没有得到充分释放&#xff0c;由此无人机自动飞行系统、无人机自动机场横空出世&#xff0c;因其无人化、自动化、无人机值守的应用特性…

【项目经验】:elementui多选表格默认选中

一.需求 在页面刚打开就默认选中指定项。 二.方法Table Methods toggleRowSelection用于多选表格&#xff0c;切换某一行的选中状态&#xff0c;如果使用了第二个参数&#xff0c;则是设置这一行选中与否&#xff08;selected 为 true 则选中&#xff09;row, selected 详细…

SSLRec:统一的自监督推荐算法库

论文链接&#xff1a; https://arxiv.org/pdf/2308.05697.pdf 论文代码&#xff1a; https://github.com/HKUDS/SSLRec TLDR 我们搭建了 SSLRec&#xff0c;一个统一的自监督推荐算法库。SSLRec 提供了一个标准化、灵活和全面的框架&#xff0c;用于整合不同场景下的推荐算法&a…

Vue2+Vue3

文章目录 Vue快速上手Vue是什么第一个Vue程序插值表达式Vue核心特性&#xff1a;响应式 Vue指令v-htmlv-show 与 v-ifv-else 与 v-else-ifv-onv-bindv-forv-model指令修饰符 计算属性watch侦听器&#xff08;监视器&#xff09;watch——简写watch——完整写法 Vue生命周期 和 …

网页的快捷方式打开自动全屏--Chrome、Firefox 浏览器相关设置

Firefox 的全屏方式与 Chrome 不同&#xff0c;Chrome 自带全屏模式以及APP模式&#xff0c;通过简单的参数即可设置&#xff0c;而Firefox暂时么有这个功能&#xff0c;Firefox 的全屏功能可以通过全屏插件实现。 全屏模式下&#xff0c;按 F11 不会退出全屏&#xff0c;鼠标…

GDB之(任意门)跳到任意位置(十四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

Pytorch-CNN-Mnist

文章目录 model.pymain.py网络设置注意事项及改进运行截图 model.py import torch.nn as nn class CNN_cls(nn.Module):def __init__(self,in_dim28*28):super(CNN_cls,self).__init__()self.conv1 nn.Conv2d(1,32,1,1)self.pool1 nn.MaxPool2d(2,2)self.conv2 nn.Conv2d(3…

Web 第一步:HTTP 协议(基础)

这里是JavaWeb的开头部分&#xff01;那么先解释一下吧&#xff1a; Web&#xff1a;全球广域网&#xff0c;也称为万维网&#xff08;www&#xff09;&#xff0c;能够通过浏览器访问的网站。 JavaWeb&#xff1a;是用Java技术来解决相关 Web 互联网领域的技术栈。 &#xf…

vite和webpack的区别

vite和webpack的区别 1、前言2、Webpack2.1 Webpack简述2.2 Webpack常用插件 3、Vite3.1 Vite简述3.2 Vite插件推荐 4、区别4.1 开发模式不同4.2 打包效率不同4.3 插件生态不同4.4 配置复杂度不同4.5 热更新机制不同 5、总结 1、前言 Webpack和Vite是现代前端开发中非常重要的…

深度解析shell脚本的命令的原理之ls

ls是一个常用的Unix和Linux命令&#xff0c;它的功能是列出目录内容。当运行ls命令时&#xff0c;操作系统会执行一系列步骤&#xff0c;以获取和显示指定目录中的文件和子目录。以下是对这个命令的深度解析&#xff1a; 解析参数和选项&#xff1a;首先&#xff0c;ls命令会解…

Linux:centos9的本地yum仓库配置

其实9和7的配置方法是差不多一样的&#xff0c;只不过你使用7的本地yum仓库里面直接挂载就可以直接把仓库位置指向挂载点 具体可以看我往期文章&#xff0c;但是先看完我下面的描述再去看我链接的文章才能看懂如何配置centos9的yum仓库 Linux&#xff1a;YUM仓库服务_鲍海超-…

Android Media3 ExoPlayer 开启缓存功能

ExoPlayer 开启播放缓存功能&#xff0c;在下次加载已经播放过的网络资源的时候&#xff0c;可以直接从本地缓存加载&#xff0c;实现为用户节省流量和提升加载效率的作用。 方法一&#xff1a;采用 ExoPlayer 缓存策略 第 1 步&#xff1a;实现 Exoplayer 参考 Exoplayer 官…

利用Python将dataframe格式的所有列的数据类型转换为分类数据类型

一、样例理解 import pandas as pd import numpy as np# 创建测试数据 feature_names [col1 , col2, col3, col4, col5, col6] values np.random.randint(20, size(10,6))dataset pd.DataFrame(data values, columns feature_names)print("转换前的数据为\n",d…

NetSuite知识会汇编-管理员篇顾问篇2023

本月初&#xff0c;开学之际&#xff0c;我们发布了《NetSuite知识会汇编-用户篇 2023》&#xff0c;这次发布《NetSuite知识会汇编-管理员篇&顾问篇2023》。本篇挑选了近两年NetSuite知识会中的一些文章&#xff0c;涉及开发、权限、系统管理等较深的内容&#xff0c;共19…

Python 基于PyCharm断点调试

视频版教程 Python3零基础7天入门实战视频教程 PyCharm Debug&#xff08;断点调试&#xff09;可以帮助开发者在代码运行时进行实时的调试和错误排查&#xff0c;提高代码开发效率和代码质量。 准备一段代码 def add(num1, num2):return num1 num2if __name__ __main__:f…

使用stelnet进行安全的远程管理

1. telnet有哪些不足&#xff1f; 2.ssh如何保证数据传输安全&#xff1f; 需求&#xff1a;远程telnet管理设备 用户定义需要在AAA模式下&#xff1a; 开启远程登录的服务&#xff1a;定义vty接口 然后从R2登录&#xff1a;是可以登录的 同理R3登录&#xff1a; 在R1也可以查…

全国职业技能大赛云计算--高职组赛题卷①(容器云)

全国职业技能大赛云计算--高职组赛题卷①&#xff08;容器云&#xff09; 第二场次题目&#xff1a;容器云平台部署与运维任务1 Docker CE及私有仓库安装任务&#xff08;5分&#xff09;任务2 基于容器的web应用系统部署任务&#xff08;15分&#xff09;任务3 基于容器的持续…

驱动开发,IO模型,信号驱动IO实现过程

1.信号驱动IO框架图 分析&#xff1a; 信号驱动IO是一种异步IO方式。linux预留了一个信号SIGIO用于进行信号驱动IO。进程主程序注册一个SIGIO信号的信号处理函数&#xff0c;当硬件数据准备就绪后会发起一个硬件中断&#xff0c;在中断的处理函数中向当前进程发送一个SIGIO信号…

opencv形状目标检测

1.圆形检测 OpenCV图像处理中“找圆技术”的使用-图像处理-双翌视觉OpenCV图像处理中“找圆技术”的使用,图像处理,双翌视觉https://www.shuangyi-tech.com/news_224.htmlopencv 找圆心得&#xff0c;模板匹配比霍夫圆心好用 - 知乎1 相比较霍夫找直线算法&#xff0c; 霍夫找…