为tshrak增加保存http图片、音乐功能
欢迎转载,转载请注明出处!原文地址
Follow me on GitHub ^_^
本文最新版发布在了看雪论坛,请移步到看雪论坛:https://bbs.pediy.com/thread-224502.htm
github: https://github.com/KevinsBobo/wireshark-modify
tshark 是 wireshark 的命令行版,所以修改起来更加容易 ^_^
其实直接在wireshark的底层解析函数里做保存图片更容易,而且GUI版本也可以用,但是出于想了解wireshark解析库的调用方法,所以就在tshark中搞了
wireshark版本:2.2.0 平台:vs2013
2017.7.31更新:新增保存mp3音乐功能,并重构代码
tshark 捕获、解析逻辑
tshark.c
main()
解析命令行参数
初始化环境
-->
caption()
初始化信息
while(...)
--> capchild\capture_sync.c -> syn_pipe_input_cp(...) // 回调的方式
从管道(文件)中读取数据头部信息
如果读到的是一个数据包的头部则开始处理
--> capture_input_new_packets(capture_session ...)
根据传入的capture_session获得一个 capture_file 类型的数据指针
如需解码:
获得一个 epan_dissect_t 类型的空间
并根据 capture_file 结构体初始化数据
// edt 中将包含一个具有链表结构的数据包
--> process_packet(capture_file *cf, epan_dissect_t *edt ...)
解析数据
--> epan_dissect_run_with_taps(...)
获得 edt 中的 tvbuff_t 类型的数据指针 // 具有链表结构的数据包
--> print_packet(...)
打印数据包
--> print_columns()
以列的形式打印数据
通过 cf 获得数据包的列结构数据
// 编号、时间 IP TCP/UDP 端口 类型 数据类型
// 我是在这里通过字符串比较判断是否为 HTTP 图片 jpg/png/gif
如果通过参数设置需要打印数据包16进制数据包
则继续打印16进制数据
这里打印的16进制数据是数据包的原格式
后面我借鉴了这里面对 tvbuff_t 类型数据的分解的代码
从而得到图片数据
调试、分析过程
以下为主要过程,在分析过程中还下了很多条件断点和内存断点来寻找关键代码
抓包时调试、分析
tshark src tcp 80
-
单步找到
caption() capture_input_new_packets() print_packet() print_columns()
等函数 -
通过vs2013下
fprintf()
API断点并通过栈回溯分析函数调用情况(因为tshark是通过fprintf
输出数据的) -
通过API断点发现了
print_columns()
的输出HTTP
状态的位置
分析数据包时调试
tshark -r out.pcap -Y http -x
-
在
printf_columns()
中发现了输出16进制数据的print.c -> print_hex_data()
函数 -
通过条件断点发现这里输出的数据是抓取到的整个数据包
-
于是找到了
print_hex_data()
中使用tvbuff_t
的代码
保存图片新增代码
注:在tshark原代码中新增的代码都以
/* 保存图片新增 start */ ... /* 保存图片新增 stop */
的形式标注
调试新增代码都以/* 调试新增 start */ ... /* 调试新增 stop */
的形式标注
为方便管理,新增代码的实现和声明放在了单独的addsave-....c
和addsave-....h
文件中,位于wireshark源码目录的addsave
目录中
所有函数和全局变量均以addsave-...
开头
addsave-file.h
这里的函数是文件相关的,一个实创建目录,一个是初始化文件,没什么说的
#define ADDSAVE_MAXPATHLEN 255
void addsave_init_floder(char* szFloder);
int addsave_init_file(char* szFile);
addsave-save.h
这是保存图片的主要代码
/* 照抄 tsark.h 包含的头文件 start */
... // 省略
/* 照抄 tsark.h 包含的头文件 stop */
/* 新增的头文件 start */
#include <epan/tvbuff-int.h>
#include <addsave/addsave-file.h>
/* 新增的头文件 stop */
// 图片根目录
#define ADDSAVE_FILE_FLODER_NAME "..\\addsave_save\\"
// 图片名前缀
#define ADDSAVE_FILE_NAME "save_file"
// 全局变量 标记数据包是否为图片
extern int addsave_g_is_savefile;
// 全局变量 标记数据包来源IP
extern char addsave_g_src_ip[ ADDSAVE_MAXPATHLEN ];
// 图片类型
#define ADDSAVE_PIC_JPG 1
#define ADDSAVE_PIC_PNG 2
#define ADDSAVE_PIC_GIF 3
#define ADDSAVE_FILE_AUDIO 4
// 主要功能函数
void addsave_save_pic(epan_dissect_t * edt);
addsave-save.c
实现代码
#include <addsave/addsave-save.h>
int addsave_g_is_savefile = 0;
char addsave_g_src_ip[ ADDSAVE_MAXPATHLEN ];
void addsave_save_pic(epan_dissect_t * edt)
{
if(edt == NULL)
{
return ;
}
char szFilePath[ MAXPATHLEN ] = { 0 };
char* pszFileType = NULL;
tvbuff_t * tvb = NULL;
u_char* pData = NULL;
unsigned long long *pVerify = NULL;
FILE* fpPic = NULL;
time_t save_time = 0;
time(&save_time);
static int nTime = 0;
const guchar *cp = NULL;
guint length = 0;
// gboolean multiple_sources;
GSList *src_le = NULL;
struct data_source *src = NULL;
// 将 tvb 指针移动到合适的位置
for( tvb = edt->tvb ; tvb != NULL; tvb = tvb->next)
{
// 可以确定 jgp/gif 图片肯定在最后一个数据包
// PNG 图片在倒数第六个数据包
// 所以直接将 jpg/gif 的指针移到最后一个数据包的位置
if(((addsave_g_is_savefile == ADDSAVE_PIC_JPG
|| addsave_g_is_savefile == ADDSAVE_PIC_GIF
|| addsave_g_is_savefile == ADDSAVE_FILE_AUDIO)
&& tvb->next != NULL))
{
continue;
}
if(tvb->real_data == NULL)
{
return ;
}
// jpg 数据首地址在最后一个数据包地址的前两个字节的位置,png 和 gif 则正常
pData = (unsigned char*)(tvb->real_data);
pVerify = (unsigned long long*)tvb->real_data;
// 再次判断,匹配则跳出循环,按照逻辑只有png才会多次判断
if((*(unsigned short*)(pData - 2) == 0xD8FF && *pVerify == 0x4649464A1000E0FF) // jpg
|| (*(unsigned long long*)(pData) == 0x0A1A0A0D474E5089) // png
|| (((*(unsigned long long*)(pData)) & 0x0000FFFFFFFFFFFF) == 0x0000613938464947) // gif
|| addsave_g_is_savefile == ADDSAVE_FILE_AUDIO // audio 类型直接跳出
)
{
break;
}
}
/* 参考自 print.c -> print_hex_data 函数 */
// 获取数据长度 和 http 数据包首部指针
for(src_le = edt->pi.data_src; src_le != NULL;
src_le = src_le->next)
{
if(src_le->next != NULL)
{
continue;
}
src = (struct data_source *)src_le->data;
tvb = get_data_source_tvb(src);
length = tvb_captured_length(tvb);
if(length == 0)
return ;
// 获取http数据首部指针
cp = tvb_get_ptr(tvb , 0 , length);
if(cp == NULL)
{
return ;
}
break;
}
if(addsave_g_is_savefile == ADDSAVE_PIC_JPG)
{
// 偏移指针
pData -= 2;
pszFileType = "jpg";
}
else if(addsave_g_is_savefile == ADDSAVE_PIC_PNG)
{
pszFileType = "png";
}
else if(addsave_g_is_savefile == ADDSAVE_PIC_GIF)
{
pszFileType = "gif";
}
else if(addsave_g_is_savefile == ADDSAVE_FILE_AUDIO)
{
pszFileType = "mp3";
}
if(pszFileType == NULL)
{
return ;
}
sprintf_s(szFilePath , MAXPATHLEN , "%s%s\\%s\\" ,
ADDSAVE_FILE_FLODER_NAME , addsave_g_src_ip, pszFileType);
// 创建文件夹
addsave_init_floder(szFilePath);
sprintf_s(szFilePath ,
MAXPATHLEN ,
"%s%s\\%s\\%s_%lld.%s" ,
ADDSAVE_FILE_FLODER_NAME ,
addsave_g_src_ip,
pszFileType ,
ADDSAVE_FILE_NAME,
save_time + nTime++,
pszFileType);
// 创建文件
if(addsave_init_file(szFilePath))
{
return ;
}
// 打开文件
fopen_s(&fpPic , szFilePath , "wb");
if(fpPic == NULL)
{
return ;
}
// 获取文件长度
u_int pic_length = length - (pData - cp);
// 写文件
fwrite(pData , pic_length , 1 , fpPic);
// 关闭文件
fclose(fpPic);
}
tshrk.c新增代码
头文件
将
addsave-....c
文件加入到tshark项目工程中并包含新增代码的头文件
/* 保存图片新增 start */
#include <addsave/addsave-save.h>
#include <addsave/addsave-file.h>
/* 保存图片新增 stop */
print_columns()函数中
这里主要判别是否为图片,并设置标志位
static gboolean
print_columns(capture_file *cf)
{
...
switch (col_item->col_fmt) {
...
case COL_UNRES_NET_SRC:
column_len = col_len = strlen(col_item->col_data);
if (column_len < 12)
column_len = 12;
line_bufp = get_line_buf(buf_offset + column_len);
put_spaces_string(line_bufp + buf_offset, col_item->col_data, col_len, column_len);
/* 保存图片新增 start */
strcpy_s(addsave_g_src_ip , ADDSAVE_MAXPATHLEN , col_item->col_data);
/* 保存图片新增 stop */
break;
...
default:
column_len = strlen(col_item->col_data);
line_bufp = get_line_buf(buf_offset + column_len);
put_string(line_bufp + buf_offset, col_item->col_data, column_len);
/* 保存图片新增 start */
if(!strcmp(col_item->col_data , "HTTP/1.1 200 OK (JPEG JFIF image)"))
{
addsave_g_is_savefile = ADDSAVE_PIC_JPG;
}
else if(!strcmp(col_item->col_data , "HTTP/1.1 200 OK (PNG)"))
{
addsave_g_is_savefile = ADDSAVE_PIC_PNG;
}
else if(!strcmp(col_item->col_data , "HTTP/1.1 200 OK (GIF89a)")
|| !strcmp(col_item->col_data , "HTTP/1.1 200 OK (GIF89a) (image/jpeg)"))
{
addsave_g_is_savefile = ADDSAVE_PIC_GIF;
}
else if(!strcmp(col_item->col_data , "HTTP/1.1 206 Partial Content (audio/mpeg)")
|| !strcmp(col_item->col_data , "HTTP/1.0 206 Partial Content (audio/mpeg)"))
{
addsave_g_is_savefile = ADDSAVE_FILE_AUDIO;
}
else
{
// 保险措施
addsave_g_is_savefile = 0;
}
/* 保存图片新增 stop */
process_packet()函数中
是在获取
edt
中tvb
指针数据后调用保存图片的函数
static gboolean
process_packet(capture_file *cf, epan_dissect_t *edt, gint64 offset, struct wtap_pkthdr *whdr,
const guchar *pd, guint tap_flags)
{
...
if (passed) {
frame_data_set_after_dissect(&fdata, &cum_bytes);
/* Process this packet. */
if (print_packet_info) {
/* We're printing packet information; print the information for
this packet. */
print_packet(cf, edt);
/* 保存图片新增 start */
if(addsave_g_is_savefile)
{
addsave_save_pic(edt);
addsave_g_is_savefile = 0;
}
/* 保存图片新增 stop */
...
/* 保存图片新增 start */
// 保险措施,标志位置 0
addsave_g_is_savefile = 0;
/* 保存图片新增 start */
return passed;
}
使用方法
编译
-
将
addsave
文件夹拷贝到wirshark
源码目录中 -
替换
tshark.c
文件 -
打开
wirshark
vs2013解决方案 -
将
addsave
中的addsave-save.c
和addsave-file.c
添加到tshark
项目Soure Files
中 -
重新编译
tshark
使用
-
捕获网卡信息流时保存
tshark src port 80
-
从
.pcap
文件中保存文件tshark -r out.pcap -Y http
-
保存的文件在执行目录的上级目录中的
addsave_save
目录中按照IP和文件类型分文件夹保存
效果展示
从已经捕获的数据包中抓取图片
tshark -r out.pcap -Y http
在捕获数据包同时保存图片
tshark src port 80