需要进行抓包分析,由于不支持自定义协议,因此需要做wireshark的protobuf插件开发。
wireshark 插件开发有两种,
一种是用lua开发,挂在wireshark上。
另一种是通过c插件编译出dll文件,将dll文件挂在wireshark上。
软件版本:
vs2008
python 2.7
protobuf 2.6.1
wireshark 1.8.6
主要参考帖子:
wireshark protobuf 插件
protobuf-wireshark编译小结
Wireshark 插件开发整体解决方案
其中前两个是最主要的 ,第三个是前期环境配置的主要问题。
1、环境搭建。
1.1 安装vs2008(不需要拷贝vsc**.*等环境配置文件,用vs2008 commond promot运行nmake命令相当于配好了环境)
1.2 安装最新版本的cgwin,并配置:
1.3 下载wireshark源码。
http://www.Wireshark.org/download/src/all-versions/
我用的版本是wireshark 1.8.6
1.4 编译config.nmake文件
(1)WIRESHARK_LIBS: 设置编译wireshark所需要的库所在的目录。默认即可(因此不需要Wireshark 插件开发整体解决方案中的第5部.)
(2)PROGRAM_FILES:设置本机程序安装目录,默认即可。
(3)MSVC_VARIANT我使用的是VS2008,所以在这里把值为MSVC2008的那一行前面的#去掉,其余MSVC_VARIANT项保持不变。
(4)CYGWIN_PATH将其设置为Cygwin的bin目录,例如C:\Cygwin\bin.
(5)PYTHON及其后的PATH将其修改为本机python.exe和其安装目录的位置,例如:C:\Python27\Python.exe
(6)MSVCR_DLL如果VS不是安装在C盘,请在这里相应的地方用绝对路径表示,而不要去修改前面的PROGRAM_FILES。如果是在c盘,
则不能使用绝对路径。若是安装在c盘,此项不需要修改。
(7)MAKENSIS 如果你没有安装NSIS安装程序制作工具,用#注释掉此行
(8)HHC_DIR如果没有安装HTMLHelpWorkshop(chm帮助文件制作工具),注释掉此行
(9)注释掉HHC_EXE
1.5
在vs2008中cmd中cd到wireshark 源码的目录。
执行nmake -f makefile.nmake verify_tools,检查工具。全部成功如下图。
执行nmake -f makefile.nmake setup
执行nmake -f makefile.nmake distclean
执行nmake -f makefile.nmake all。
protobuf插件开发主要就是前两个链接。
1. 首先要先搭建好wireshark编译环境,参见前面参考。
2. 下载protobuf-wireshark代码,下载protobuf-wireshark-runtime-0.1.tar.gz文件点击打开链接
3. 解压protobuf-wireshark-runtime-0.1.tar.gz文件后,修改 wireshark.conf配置文件。设置wireshark的源代码和安装目录,本人配置如下
wireshark_src_dir : /cygdrive/h/wireshark-1.8.6
wireshark_install_dir : /cygdrive/c/Program Files/Wireshark
wireshark_version : 1.8.6
4. 启动cygwin终端,并切换到protobuf-wireshark-runtion-0.1的目录下面,本人地址为;/cygdrive/h/a/protobuf-wireshark-runtime-0.1
5. 执行$ ./make_wireshark_plugin.py wireshark.conf
注意:编译是通不过的,因为该工程是针对linux的,而我们要的是windows的版本。
执行后,在wireshark\plusins目录下会创建protobuf目录,并且生成了moduleinfo.h、Makefile.am、packet-protobuf.c三个文件
同时在protobuf-wireshark-runtime-0.1源代码目录下也会生成2个c++文件wireshark-glue-protobuf.h和wireshark-glue-protobuf.cc,把这2个文件拷贝到plugins\protobuf目录下面。
6. 切换到plugins\protobuf目录,并从其他插件目录拷贝 Makefile.common、moduleinfo.nmake、Makefile.nmake、plugin.rc.in 4个文件,对5和6中的文件做修改。
wireshark的所有源代码都是基于c语言的,但是protobuf插件多了c++文件。
修改6中的三个文件内容。
makefile.common 文件内容:
# Makefile.common for protobuf plugin
# Contains the stuff from Makefile.am and Makefile.nmake that is
# a) common to both files and
# b) portable between both files
#
# $Id: Makefile.common 27491 2009-02-21 16:33:48Z jake $
#
# Wireshark - Network traffic analyzer
# By Gerald Combs
# Copyright 1998 Gerald Combs
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# the name of the plugin
PLUGIN_NAME = protobuf
# the dissector sources (without any helpers)
DISSECTOR_SRC = \
packet-protobuf.c
DISSECTOR_SRCC = \
wireshark-glue-protobuf.cc
# corresponding headers
DISSECTOR_INCLUDES = \
wireshark-glue-protobuf.h\
moduleinfo.h\
# Dissector helpers. They're included in the source files in this
# directory, but they're not dissectors themselves, i.e. they're not
# used to generate "plugin.c".
DISSECTOR_SUPPORT_SRC =
#
# $Id: moduleinfo.nmake 20157 2006-12-19 22:23:22Z jake $
#
# The name
PACKAGE=protobuf
# The version
MODULE_VERSION_MAJOR=0
MODULE_VERSION_MINOR=0
MODULE_VERSION_MICRO=1
MODULE_VERSION_EXTRA=0
#
# The RC_VERSION should be comma-separated, not dot-separated,
# as per Graham Bloice's message in
#
# http://www.ethereal.com/lists/ethereal-dev/200303/msg00283.html
#
# "The RC_VERSION variable in config.nmake should be comma separated.
# This allows the resources to be built correctly and the version
# number to be correctly displayed in the explorer properties dialog
# for the executables, and XP's tooltip, rather than 0.0.0.0."
#
MODULE_VERSION=$(MODULE_VERSION_MAJOR).$(MODULE_VERSION_MINOR).$(MODULE_VERSION_MICRO).$(MODULE_VERSION_EXTRA)
RC_MODULE_VERSION=$(MODULE_VERSION_MAJOR),$(MODULE_VERSION_MINOR),$(MODULE_VERSION_MICRO),$(MODULE_VERSION_EXTRA)
# Makefile.nmake
# nmake file for Wireshark plugin
#
# $Id: Makefile.nmake 42971 2012-06-01 14:08:12Z wmeier $
#
PROTOBUF_DIR=F:\protobuf-2.6.1\src
PROTOBUF_LIB=F:\protobuf-2.6.1\vsprojects\Release\libprotobuf.lib
include ..\..\config.nmake
include moduleinfo.nmake
PLUGIN_NAME=protobuf
DISSECTOR_SRC=packet-protobuf.c
DISSECTOR_SRCC=wireshark-glue-protobuf.cc
DISSECTOR_SUPPORT_SRC=
DISSECTOR_INCLUDES=wireshark-glue-protobuf.h moduleinfo.h
CFLAGS=$(WARNINGS_ARE_ERRORS) $(STANDARD_CFLAGS) /I../.. $(GLIB_CFLAGS) /I$(PROTOBUF_DIR)
.c.obj::
$(CC) $(CFLAGS) -Fd.\ -c $<
.cc.obj::
$(CC) $(CFLAGS) -Fd.\ -c $<
LDFLAGS = $(PLUGIN_LDFLAGS)
!IFDEF ENABLE_LIBWIRESHARK
LINK_PLUGIN_WITH=..\..\epan\libwireshark.lib ..\..\wsutil\libwsutil.lib $(PROTOBUF_LIB)
CFLAGS=/D_NEED_VAR_IMPORT_ $(CFLAGS)
DISSECTOR_OBJECTS = $(DISSECTOR_SRC:.c=.obj)
DISSECTOR_OBJECTSS = $(DISSECTOR_SRCC:.cc=.obj)
DISSECTOR_SUPPORT_OBJECTS = $(DISSECTOR_SUPPORT_SRC:.c=.obj)
OBJECTS = $(DISSECTOR_OBJECTS) $(DISSECTOR_SUPPORT_OBJECTS) $(DISSECTOR_OBJECTSS)
RESOURCE=$(PLUGIN_NAME).res
all: $(PLUGIN_NAME).dll
$(PLUGIN_NAME).rc : moduleinfo.nmake
sed -e s/@PLUGIN_NAME@/$(PLUGIN_NAME)/ -e s/@RC_MODULE_VERSION@/$(RC_MODULE_VERSION)/ -e s/@RC_VERSION@/$(RC_VERSION)/ -e s/@MODULE_VERSION@/$(MODULE_VERSION)/ -e s/@PACKAGE@/$(PACKAGE)/ -e s/@VERSION@/$(VERSION)/ -e s/@MSVC_VARIANT@/$(MSVC_VARIANT)/ < plugin.rc.in > $@
$(PLUGIN_NAME).dll $(PLUGIN_NAME).exp $(PLUGIN_NAME).lib : $(OBJECTS) $(LINK_PLUGIN_WITH) $(RESOURCE)
link -dll /out:$(PLUGIN_NAME).dll $(LDFLAGS) $(OBJECTS) $(LINK_PLUGIN_WITH) $(GLIB_LIBS) $(RESOURCE)
#
# Build plugin.c, which contains the plugin version[] string, a
# function plugin_register() that calls the register routines for all
# protocols, and a function plugin_reg_handoff() that calls the handoff
# registration routines for all protocols.
#
# We do this by scanning sources. If that turns out to be too slow,
# maybe we could just require every .o file to have an register routine
# of a given name (packet-aarp.o -> proto_register_aarp, etc.).
#
# Formatting conventions: The name of the proto_register_* routines an
# proto_reg_handoff_* routines must start in column zero, or must be
# preceded only by "void " starting in column zero, and must not be
# inside #if.
#
# DISSECTOR_SRC is assumed to have all the files that need to be scanned.
#
# For some unknown reason, having a big "for" loop in the Makefile
# to scan all the files doesn't work with some "make"s; they seem to
# pass only the first few names in the list to the shell, for some
# reason.
#
# Therefore, we have a script to generate the plugin.c file.
# The shell script runs slowly, as multiple greps and seds are run
# for each input file; this is especially slow on Windows. Therefore,
# if Python is present (as indicated by PYTHON being defined), we run
# a faster Python script to do that work instead.
#
# The first argument is the directory in which the source files live.
# The second argument is "plugin", to indicate that we should build
# a plugin.c file for a plugin.
# All subsequent arguments are the files to scan.
#
!ENDIF
clean:
rm -f $(OBJECTS) $(RESOURCE) *.pdb *.sbr $(PLUGIN_NAME).dll $(PLUGIN_NAME).dll.manifest $(PLUGIN_NAME).lib $(PLUGIN_NAME).exp $(PLUGIN_NAME).rc
distclean: clean
maintainer-clean: distclean
checkapi:
# TODO: Fix api's :)
# $(PERL) ../../tools/checkAPIs.pl -g abort -g termoutput -build $(DISSECTOR_SRC) $(DISSECTOR_INCLUDES)
8修改packet-protobuf.c,wireshark-glue-protobuf.cc
packet-protobuf.c内容:
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include
#include
#include
#include
#include
#include
#include
#include
/* TCP数据包的固定头大小*/
//#define FRAME_HEADER_LEN 4
/*这个值在本程序中没有使用。本身他的作用是用来合并TCP包的时候,传入的包头固定大小,
由于自定义协议中前面有13字节固定长度,后面有两个字节固定长度,本程序中没有合并TCP包。
*/
#define FRAME_HEADER_LEN 11
void proto_register_protobuf();
void proto_reg_handoff_protobuf();
static void dissect_protobuf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
static void dissect_protobuf_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree);
/* Define version if we are not building ethereal statically */
#ifndef ENABLE_STATIC
G_MODULE_EXPORT const gchar version[] = "0.0";
#endif
static int proto_protobuf = -1;
static dissector_handle_t protobuf_handle;
// Protocol field variables - START
static int hf_protobuf = -1;
// Protocol field variables - END
int wireshark_pb_process_protobuf(void *tree_root, int srcport, int dstport, int item_id, void *tvb, void *buf, int buf_size);
void wireshark_pb_process_protobuf_register_proto( int proto, const char* dirname );
void wireshark_pb_process_protobuf_register_ports();
void wireshark_pb_process_protobuf_register_subtree( int proto, const char* name,
int *handle, int ** tree_handle );
void wireshark_pb_process_protobuf_register_field( int proto, int type,
const char* name, const char * fullName,
int *handle );
/* Register plugin - START */
#ifndef ENABLE_STATIC
G_MODULE_EXPORT void
plugin_register(void) {
/* register the new protocol, protocol fields, and subtrees */
if (proto_protobuf == -1) { /* execute protocol initialization only once */
proto_register_protobuf();
}
}
G_MODULE_EXPORT void
plugin_reg_handoff(void){
proto_reg_handoff_protobuf();
}
#endif
void proto_register_protobuf(void) {
module_t *protobuf_module;
char* dirname;
if (proto_protobuf == -1) {
proto_protobuf = proto_register_protocol (
"protobuf ",/* name */
"protobuf",/* short name */
"protobuf"/* abbrev */
);
}
protobuf_module= prefs_register_protocol(proto_protobuf, proto_reg_handoff_protobuf);
/*char**/ dirname = get_persconffile_path("protobuf", FALSE, FALSE);
if( test_for_directory( dirname ) != EISDIR )
{
/* Although dir isn't a directory it may still use memory */
g_free( dirname );
dirname = get_datafile_path("protobuf");
if( test_for_directory( dirname ) != EISDIR )
{
g_free( dirname );
dirname = NULL;
}
}
wireshark_pb_process_protobuf_register_proto( proto_protobuf, dirname );
}
void proto_reg_handoff_protobuf (void) {
static int Initialized=FALSE;
unsigned int i = 0;
if (!Initialized) {
protobuf_handle = create_dissector_handle(dissect_protobuf, proto_protobuf);
wireshark_pb_process_protobuf_register_ports();
}
}
/* Register plugin - END */
/* 解析一个TCP数据包*/
static void dissect_protobuf_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
/*
先进行数据包判断,如果数据长度小于7,则是继续向后偏移,直到取得大于7的长度,进入下一步。
或者数据长度==0,或者偏移量达到最大偏移,则退出。
*/
proto_item * ti = NULL;
proto_item * pi =NULL;
tvbuff_t * next_tvb=NULL;
gint maxOffset = 0;
gint msg_id =0;
gint msglen=0;
gint offect=0;
if (tree) { /* we are being asked for details */
/* Always make sure that offset is LESS than maxOffset */
gint data_len = tvb_get_letohs(tvb,2);
if ( data_len==0)
return ;
msglen= tvb_length(tvb);
offect=data_len+6;
while(data_len <7)
{
if(data_len==0 ||offect>=msglen)
return ;
tvb= tvb_new_subset_remaining(tvb,6+data_len);
data_len = tvb_get_letohs(tvb,2);
offect+=data_len+6;
}
/* TODO: implement your dissecting code */
if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
col_set_str(pinfo->cinfo, COL_PROTOCOL, "protobuf-tcp");
}
/* Clear out stuff in the info column */
if(check_col(pinfo->cinfo,COL_INFO)){
col_clear(pinfo->cinfo,COL_INFO);
}
/*
ti 取得是数据长度,(eb 91 ** ** eb 91 之后的长度)。
pi 取得是数据类型。从字节流开始偏移 9个字节。
*/
//proto_item *pi=NULL;
//tvbuff_t * next_tvb = tvb_new_subset_remaining (tvb,4);
next_tvb = tvb_new_subset_remaining(tvb,13);
maxOffset = tvb_length(next_tvb);
msg_id = tvb_get_ntohs(tvb, 9);
//ti = proto_tree_add_item(tree,proto_protobuf,tvb,0,4,ENC_NA);
ti = proto_tree_add_item(tree,proto_protobuf,tvb,2,2,ENC_NA);
pi = proto_tree_add_item(tree,proto_protobuf,tvb,9,2,ENC_NA);
proto_item_append_text(ti, ", Length %d",data_len);
proto_item_append_text(pi, ", Message type:%d",msg_id);
wireshark_pb_process_protobuf((void *) tree, pinfo->srcport, pinfo->destport, hf_protobuf, msg_id,
(void *)next_tvb, (void *)tvb_get_ptr(next_tvb,0,data_len-9), data_len-9);
}
}
/* TCP数据包的总长度,包含包头长度 */
static guint get_protobuf_message_len(packet_info *pinfo, tvbuff_t *tvb, int offset)
{
/* TODO: change this to your needs */
//return (guint)tvb_get_ntohl(tvb, offset)+4; /* e.g. length is at offset 4 */
/*
这个函数没有使用,但继续4的原因是,字节长度是包含最后两个校验位的长度,因此要减2,再加上报文头
的长度,即+6,因此还是+4.
*/
return (guint)tvb_get_letohs(tvb,2)+4; /* e.g. length is at offset 10 */
}
/* Generate the main dissector function - START */
static void dissect_protobuf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
if( pinfo->tcp_tree == NULL) //进入解析UDP数据包
{
dissect_protobuf_udp(tvb,pinfo,tree);
}else
{
//进入解析TCP数据包。
//没有经过并包处理,直接进行解析TCP数据。
dissect_protobuf_message(tvb, pinfo,tree);
//tcp_dissect_pdus(tvb, pinfo, tree, TRUE, FRAME_HEADER_LEN,
// get_protobuf_message_len, dissect_protobuf_message);
}
}
static void dissect_protobuf_udp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) {
if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
col_set_str(pinfo->cinfo, COL_PROTOCOL, "protobuf-udp");
}
/* Clear out stuff in the info column */
if(check_col(pinfo->cinfo,COL_INFO)){
col_clear(pinfo->cinfo,COL_INFO);
}
if (tree) { /* we are being asked for details */
/* Always make sure that offset is LESS than maxOffset */
gint maxOffset = tvb_length(tvb);
wireshark_pb_process_protobuf((void *) tree, pinfo->srcport, pinfo->destport, hf_protobuf,
(void *)tvb, (void *)tvb_get_ptr(tvb,0,maxOffset), maxOffset);
}
} //dissect_protobuf_udp
/* Generate the main dissector function - END */
/** Called from PB to add msg_str to tree_root */
int wireshark_pb_add_protobuf(void* tree_root, void* tvb, int item_id, char* msg_str) {
proto_tree_add_none_format ((proto_tree *) tree_root, item_id, (tvbuff_t*) tvb, 0, -1, msg_str);
return 0;
}
void wireshark_pb_process_protobuf_register_subtree( int proto, const char* name,
int *handle, int ** p_tree_handle )
{
hf_register_info message_info =
{ handle,
{ (char*)name,
(char*)name,
FT_NONE,
BASE_NONE,
NULL, 0,
"",
HFILL
}
};
hf_register_info *hf_info = malloc(sizeof( hf_register_info ) );
*hf_info = message_info;
proto_register_field_array( proto, hf_info, 1 );
proto_register_subtree_array( p_tree_handle, 1 );
}
void wireshark_pb_process_protobuf_register_field( int proto, int type,
const char* name, const char * fullName,
int *handle )
{
int base = ( ( type == FT_UINT32 ) || ( type == FT_UINT64 ) ) ? BASE_HEX :
( ( type == FT_INT32 ) || ( type == FT_INT64 ) ) ? BASE_DEC :
BASE_NONE;
hf_register_info message_info =
{ handle,
{ (char*)fullName,
(char*)name,
type,
base,
NULL, 0,
"",
HFILL
}
};
hf_register_info *hf_info = malloc(sizeof( hf_register_info ) );
*hf_info = message_info;
proto_register_field_array( proto, hf_info, 1 );
}
void wireshark_pb_process_protobuf_dissector_add( const char* name, int port )
{
dissector_add( name, port, protobuf_handle );
}
#include "wireshark-glue-protobuf.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace google;
using namespace protobuf;
using namespace internal;
extern "C" {
static HandleMap* handleMap = NULL;
static MessageConfigList messageConfigList;
static google::protobuf::compiler::Importer* importer = NULL;
static MaptoMesConfig maptoMesconfig;
//static logger mylog;
static void * _tvb = NULL;
static const char ARRAY_FORMAT[] = "%s[%d]";
/*
添加Utf8ToString,将utf8数据转为string。希望解决中文显示问题,暂未成功。
*/
std::string Utf8ToString(const std::string &str)
{
int nwLen = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
wchar_t * pwBuf = new wchar_t[nwLen + 1];
memset(pwBuf, 0, nwLen * 2 + 2);
MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), pwBuf, nwLen);
int nLen = WideCharToMultiByte(CP_ACP, 0, pwBuf, -1, NULL, NULL, NULL, NULL);
char * pBuf = new char[nLen + 1];
memset(pBuf, 0, nLen + 1);
WideCharToMultiByte(CP_ACP, 0, pwBuf, nwLen, pBuf, nLen, NULL, NULL);
std::string retStr = pBuf;
delete []pBuf;
delete []pwBuf;
pBuf = NULL;
pwBuf = NULL;
return retStr;
}
/**
* @param buf The message contents
* @param buf_size The length of message contents in buf
* @param tree_root The WireShark tree to which this message is to be added.
* @param item_id Internal wireshark id to refer to this FT_NONE datatype.
*/
/*
当某一个port满足要求时,
修改了以前的做法,从第一个MessageConfigList对象开始反序列化,这会导致利用错误的protobuf_MessageConfig解析数据问题。
修改:增加MaptoMesConfig对象,这是一个map,key为数据类型,value 为protobuf_MessageConfig。通过字节流的数据类型,去
MaptoMesConfig中取相应的protobuf_MessageConfig进行解析。for循环应该是可以去掉的。
保留了for循环的考虑是尽量少减小代码修改。
*/
int wireshark_pb_process_protobuf(proto_tree* tree_root, int srcport, int dstport, int item_id, int msg_id ,tvbuff_t* tvb, void* buf, int buf_size)
{
for( MessageConfigList::iterator it = messageConfigList.begin(); it != messageConfigList.end(); it++ )
{
if( (*it)->matchesSrcPort( srcport ) || (*it)->matchesDestPort( dstport ) )
{
//mylog.debug("DEBUG",msg_id);
MaptoMesConfig::iterator index=maptoMesconfig.find(msg_id);
if(index!=maptoMesconfig.end())
*it=maptoMesconfig[msg_id];
return (*it)->dissect( tree_root, item_id, tvb, buf, buf_size );
}
}
DBG_ERR("Failed to find match");
wireshark_pb_add_protobuf(tree_root,tvb,item_id,"Failed to find match");
return -1;
}
void wireshark_pb_process_protobuf_register_subtree_( int proto,
const std::string& name,
const std::string& full_name )
{
Handles *handles = new Handles();
wireshark_pb_process_protobuf_register_subtree( proto,
name.c_str(),
&(handles->handle), &(handles->tree_handle) );
handleMap->insert( StringHandlePair( full_name, handles ) );
}
int wireshark_pb_process_protobuf_get_type( FieldDescriptor::CppType type )
{
struct ftmap_t
{
FieldDescriptor::CppType cpp_type;
ftenum fttype;
int length;
};
static ftmap_t ftmap[] = {
{ FieldDescriptor::CPPTYPE_UINT32, FT_UINT32 },
{ FieldDescriptor::CPPTYPE_INT32, FT_INT32 },
{ FieldDescriptor::CPPTYPE_UINT64, FT_UINT64 },
{ FieldDescriptor::CPPTYPE_INT64, FT_INT64 },
{ FieldDescriptor::CPPTYPE_DOUBLE, FT_DOUBLE },
{ FieldDescriptor::CPPTYPE_FLOAT, FT_FLOAT },
{ FieldDescriptor::CPPTYPE_BOOL, FT_BOOLEAN },
{ FieldDescriptor::CPPTYPE_ENUM, FT_INT32 },
{ FieldDescriptor::CPPTYPE_STRING, FT_STRING } };
for( int i =0; i < sizeof(ftmap)/sizeof(ftmap_t); i++ )
{
if( ftmap[i].cpp_type == type )
{
return ftmap[i].fttype;
}
}
DBG_ERR( "Couldnt find type for cpp type " << type );
return FT_NONE;
}
void wireshark_pb_process_protobuf_register_ports()
{
for( MessageConfigList::iterator it = messageConfigList.begin(); it != messageConfigList.end(); it++ )
{
(*it)->register_ports();
}
}
void wireshark_pb_process_protobuf_register_proto( int proto, const char* dirname )
{
if( dirname == NULL )
{
DBG_ERR( "couldnt get dirname ");
return;
}
DIR* dir = opendir( dirname );
if( dir == NULL )
{
DBG_ERR( "couldnt open " << dirname );
return;
}
dirent* dp;
std::string configFileExtension( ".conf" );
std::string protoFileExtension( ".proto" );
while( ( dp = readdir( dir ) ) != NULL )
{
std::string filename( dp->d_name );
size_t i = filename.rfind( configFileExtension );
if( ( i != std::string::npos ) && ( i == filename.length() - configFileExtension.length() ) )
{
protobuf_MessageConfig* messageConfig = new protobuf_MessageConfig( filename, dirname );
if( messageConfig->isValid() )
{
messageConfigList.push_back( messageConfig );
messageConfig->register_proto( proto );
//mylog.debug("add to maptomes",messageConfig->message_selfid);
maptoMesconfig[messageConfig->message_selfid]=messageConfig;
//mylog.debug("add to maptomes, size ",maptoMesconfig.size());
//mylog.debug(" messageConfigList size ", messageConfigList.size());
}
}
//const FileDescriptor* fileDesc = importer->Import( filename );
//
//for( int i = 0; i < fileDesc->message_type_count(); i++ )
//{
// const Descriptor* desc = fileDesc->message_type( i );
// std::string configFileName = desc->name() + ".config";
//
// // check if desc->name() | .config exists TBD
// messageConfigList.push_back( new protobuf_MessageConfig( configFileName, desc ) );
//}
}
closedir( dir );
}
protobuf_Dissector::protobuf_Dissector()
: _leaf( NULL ),
_len( -1 ),
_message( NULL ),
_offset( 0 ),
_prefix_len( -1 ),
_reflection( NULL )
{
}
protobuf_Dissector::protobuf_Dissector( proto_tree* tree, int offset,
const Message* message,
const FieldDescriptor* field,
int index )
: _leaf( NULL ),
_len( -1 ),
_message( message ),
_offset( offset ),
_prefix_len( 0 ),
_reflection( _message->GetReflection() )
{
bool is_root = ( field == NULL );
const std::string& label = ( is_root ) ? _message->GetDescriptor()->name() : field->name();
const std::string& fullname = _message->GetDescriptor()->full_name();
int data_size = _message->ByteSize();
int total_size = data_size;
// if not root field then prefix_len needs to be computed
if( !is_root )
{
_prefix_len = WireFormat::TagSize( _message->GetDescriptor()->index(),
FieldDescriptor::TYPE_MESSAGE );
_prefix_len+= io::CodedOutputStream::VarintSize32( data_size );
total_size+= _prefix_len;
}
_len = total_size;
// construct subtree here itself rather than in dissect method otherwise
// all repeated fields and messages are pushed to end of current tree
// regardless of their current position
HandleMap::iterator it = handleMap->find( fullname );
if( it == handleMap->end() )
{
DBG_ERR( "couldnt find handle for " << fullname );
return;
}
Handles *handles = it->second;
// add a subtree for current message
proto_item* item;
if( index == -1 )
{
item = proto_tree_add_none_format( tree, handles->handle, _tvb,
_offset, _len,
label.c_str() );
}
else
{
item = proto_tree_add_none_format( tree, handles->handle, _tvb,
_offset, _len, ARRAY_FORMAT,
label.c_str(), index );
}
_leaf = proto_item_add_subtree( item, *(handles->tree_handle) );
}
void protobuf_Dissector::dissect( DissectorList& dissectorList )
{
//DBG( "Dissecting " << _message->GetDescriptor()->name()
// << " of length " << _len
// << " prefix len " << _prefix_len );
if( !_leaf )
{
DBG_ERR( "Couldnt find leaf node for current message" );
return;
}
_offset+= _prefix_len;
// now loop through all the field in current message
FieldDescriptorList fields ;
_reflection->ListFields( *_message, &fields );
for( FieldDescriptorList::iterator it = fields.begin(); it!=fields.end(); it++ )
{
const FieldDescriptor* field = *it;
bool is_message = ( field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ) ;
if( field->is_repeated() )
{
if( field->options().packed() )
{
int data_size = WireFormat::FieldDataOnlyByteSize( field, *_message );
if (data_size > 0)
{
_offset += WireFormat::TagSize( field->number(), FieldDescriptor::TYPE_STRING );
_offset += io::CodedOutputStream::VarintSize32( data_size );
}
}
int size = _reflection->FieldSize( *_message, field );
for( int i = 0; i < size; i++ )
{
if( is_message )
{
const Message& subMessage = _reflection->GetRepeatedMessage( *_message,
field, i );
protobuf_Dissector dissector( _leaf, _offset,
&subMessage, field, i );
dissectorList.push_back( dissector );
_offset+= dissector.messageLength();
}
else
{
dissectRepeatedField( field, i );
}
}
}
else if( is_message )
{
const Message& subMessage = _reflection->GetMessage( *_message, field );
protobuf_Dissector dissector( _leaf, _offset,
&subMessage, field );
dissectorList.push_back( dissector );
_offset+= dissector.messageLength();
}
else // plain simple field. process it immediately
{
dissectField( field );
}
}
}
void protobuf_Dissector::dissectField( const FieldDescriptor* field )
{
int len = WireFormat::FieldByteSize( field, *_message );
//DBG( "Dissecting field " << field->name() << " " << len );
HandleMap::iterator it = handleMap->find( field->full_name() );
if( it == handleMap->end() )
{
DBG_ERR( "Couldnt find handle for " << field->full_name() );
return;
}
Handles *handles = it->second;
const EnumValueDescriptor* enumDesc = NULL;
std::string Fromutf8tostring="";
switch( field->cpp_type() )
{
case FieldDescriptor::CPPTYPE_UINT32:
proto_tree_add_uint( _leaf, handles->handle, _tvb, _offset, len,
_reflection->GetUInt32( *_message, field ) );
break;
case FieldDescriptor::CPPTYPE_INT32:
proto_tree_add_int( _leaf, handles->handle, _tvb, _offset, len,
_reflection->GetInt32( *_message, field ) );
break;
case FieldDescriptor::CPPTYPE_FLOAT:
proto_tree_add_float( _leaf, handles->handle, _tvb, _offset, len,
_reflection->GetFloat( *_message, field ) );
break;
case FieldDescriptor::CPPTYPE_UINT64:
proto_tree_add_uint64( _leaf, handles->handle, _tvb, _offset, len,
_reflection->GetUInt64( *_message, field ) );
break;
case FieldDescriptor::CPPTYPE_INT64:
proto_tree_add_int64( _leaf, handles->handle, _tvb, _offset, len,
_reflection->GetInt64( *_message, field ) );
break;
case FieldDescriptor::CPPTYPE_DOUBLE:
proto_tree_add_double( _leaf, handles->handle, _tvb, _offset, len,
_reflection->GetDouble( *_message, field ) );
break;
case FieldDescriptor::CPPTYPE_BOOL:
proto_tree_add_boolean( _leaf, handles->handle, _tvb, _offset, len,
_reflection->GetBool( *_message, field ) );
break;
case FieldDescriptor::CPPTYPE_ENUM:
enumDesc = _reflection->GetEnum( *_message, field );
proto_tree_add_int_format_value( _leaf, handles->handle, _tvb, _offset, len, //mawu
enumDesc->number(), "%d ( %s )", enumDesc->number(),
enumDesc->name().c_str() );
//Fromutf8tostring= Utf8ToString(enumDesc->name());
//mylog.debug("Fromutf8tostring",Fromutf8tostring);
// proto_tree_add_int_format_value( _leaf, handles->handle, _tvb, _offset, len,
// enumDesc->number(), "%d ( %s )", enumDesc->number(),
// Fromutf8tostring.c_str());
// Fromutf8tostring="";
break;
case FieldDescriptor::CPPTYPE_STRING:
//proto_tree_add_string( _leaf, handles->handle, _tvb, _offset, len, //mawu
// _reflection->GetString( *_message, field ).c_str() );
//std::string ConvertString= Utf8ToString(_reflection->GetString( *_message, field ));
//mylog.debug("befor convert ",_reflection->GetString( *_message, field ));
//mylog.debug("befor convert size",_reflection->GetString( *_message, field ).size() );
//mylog.debug("len is ",len);
Fromutf8tostring = Utf8ToString(_reflection->GetString( *_message, field ));
//mylog.debug("Fromutf8tostring1",Fromutf8tostring);
//mylog.debug("Fromutf8tostring1.size",Fromutf8tostring.size());
//mylog.debug("1temp.size:",temp.size());
proto_tree_add_string( _leaf, handles->handle, _tvb, _offset, len,
Fromutf8tostring.c_str());
Fromutf8tostring="";
break;
default:
proto_tree_add_item( _leaf, handles->handle, _tvb, _offset, len, true );
};
_offset+=len;
}
void protobuf_Dissector::dissectRepeatedField( const FieldDescriptor* field, int index )
{
int len = 0;
string scratch;
if( !field->options().packed() )
{
len+= WireFormat::TagSize( field->number(), field->type() );
}
//DBG( "Dissecting field " << field->name() << " " << len );
HandleMap::iterator it = handleMap->find( field->full_name() );
if( it == handleMap->end() )
{
DBG_ERR( "Couldnt find handle for " << field->full_name() );
return;
}
Handles *handles = it->second;
const EnumValueDescriptor* enumDesc = NULL;
std::string Fromutf8tostring="";
/*
下面所有用了Fromutf8tostring,都是将原utf-8转为string。本来是想解决中文显示问题,没有成功。
*/
switch( field->cpp_type() )
{
case FieldDescriptor::CPPTYPE_UINT32:
if( field->type() == FieldDescriptor::TYPE_FIXED32 )
{
len+= WireFormatLite::kFixed32Size;
}
else
{
len+= WireFormatLite::UInt32Size( _reflection->GetRepeatedUInt32( *_message, field, index ) );
}
proto_tree_add_uint( _leaf, handles->handle, _tvb, _offset, len,
_reflection->GetRepeatedUInt32( *_message, field, index ) );
break;
case FieldDescriptor::CPPTYPE_INT32:
if( field->type() == FieldDescriptor::TYPE_SFIXED32 )
{
len+= WireFormatLite::kSFixed32Size;
}
else if( field->type() == FieldDescriptor::TYPE_SINT32 )
{
len+= WireFormatLite::SInt32Size( _reflection->GetRepeatedInt32( *_message, field, index ) );
}
else
{
len+= WireFormatLite::Int32Size( _reflection->GetRepeatedInt32( *_message, field, index ) );
}
proto_tree_add_int( _leaf, handles->handle, _tvb, _offset, len,
_reflection->GetRepeatedInt32( *_message, field, index ) );
break;
case FieldDescriptor::CPPTYPE_FLOAT:
len+= WireFormatLite::kFloatSize;
proto_tree_add_float( _leaf, handles->handle, _tvb, _offset, len,
_reflection->GetRepeatedFloat( *_message, field, index ) );
break;
case FieldDescriptor::CPPTYPE_UINT64:
if( field->type() == FieldDescriptor::TYPE_FIXED64 )
{
len+= WireFormatLite::kFixed64Size;
}
else
{
len+= WireFormatLite::UInt64Size( _reflection->GetRepeatedUInt64( *_message, field, index ) );
}
proto_tree_add_uint64( _leaf, handles->handle, _tvb, _offset, len,
_reflection->GetRepeatedUInt64( *_message, field, index ) );
break;
case FieldDescriptor::CPPTYPE_INT64:
if( field->type() == FieldDescriptor::TYPE_SFIXED64 )
{
len+= WireFormatLite::kSFixed64Size;
}
else if( field->type() == FieldDescriptor::TYPE_SINT64 )
{
len+= WireFormatLite::SInt64Size( _reflection->GetRepeatedInt64( *_message, field, index ) );
}
else
{
len+= WireFormatLite::Int64Size( _reflection->GetRepeatedInt64( *_message, field, index ) );
}
proto_tree_add_int64( _leaf, handles->handle, _tvb, _offset, len,
_reflection->GetRepeatedInt64( *_message, field, index ) );
break;
case FieldDescriptor::CPPTYPE_DOUBLE:
len+= WireFormatLite::kDoubleSize;
proto_tree_add_double( _leaf, handles->handle, _tvb, _offset, len,
_reflection->GetRepeatedDouble( *_message, field, index ) );
break;
case FieldDescriptor::CPPTYPE_BOOL:
len+= WireFormatLite::kBoolSize;
proto_tree_add_boolean( _leaf, handles->handle, _tvb, _offset, len,
_reflection->GetRepeatedBool( *_message, field, index ) );
break;
case FieldDescriptor::CPPTYPE_ENUM:
enumDesc = _reflection->GetRepeatedEnum( *_message, field, index );
len+= WireFormatLite::EnumSize( enumDesc->number() );
proto_tree_add_int_format_value( _leaf, handles->handle, _tvb, _offset, len, ///mawu
enumDesc->number(), "%d ( %s )", enumDesc->number(),
enumDesc->name().c_str() );
// Fromutf8tostring= Utf8ToString(enumDesc->name());
// mylog.debug("Fromutf8tostring",Fromutf8tostring);
// proto_tree_add_int_format_value( _leaf, handles->handle, _tvb, _offset, len,
// enumDesc->number(), "%d ( %s )", enumDesc->number(),
//Fromutf8tostring.c_str() );
// Fromutf8tostring="";
break;
case FieldDescriptor::CPPTYPE_STRING:
len+= WireFormatLite::StringSize( _reflection->GetRepeatedStringReference( *_message, field, index, &scratch ) );
//proto_tree_add_string( _leaf, handles->handle, _tvb, _offset, len,
// _reflection->GetRepeatedString( *_message, field, index ).c_str() ); ///mawu
Fromutf8tostring=Utf8ToString (_reflection->GetRepeatedString( *_message, field, index ));
//mylog.debug("Fromutf8tostring2",Fromutf8tostring);
proto_tree_add_string( _leaf, handles->handle, _tvb, _offset, len,
Fromutf8tostring.c_str());
Fromutf8tostring="";
break;
default:
proto_tree_add_item( _leaf, handles->handle, _tvb, _offset, len, true );
};
_offset+=len;
}
class ErrorCollector
: public protobuf::compiler::MultiFileErrorCollector
{
// Implementation of MultiFileErrorCollector interface.
void AddError(const string & filename, int line, int column, const string & message)
{
DBG( "Error in file " << filename << " at line/col:" << line << "/" << column
<< message );
}
};
protobuf_MessageConfig::protobuf_MessageConfig()
: _desc( NULL )
{
}
protobuf_MessageConfig::protobuf_MessageConfig( const std::string& filename, const std::string& dirname )
: _desc( NULL )
{
parseConfigFile( filename, dirname );
}
int protobuf_MessageConfig::dissect(proto_tree* tree_root, int item_id, tvbuff_t* tvb, void* buf, int buf_size)
{
DynamicMessageFactory factory;
Message* msg = factory.GetPrototype( _desc )->New();
if (!msg->ParseFromArray((char *) buf, buf_size)) {
wireshark_pb_add_protobuf(tree_root,tvb,item_id,"Failed parse message");
DBG_ERR( "Failed to parse message." );
/*
for (int i=0; i < buf_size; i++) {
printf("%2x ", ((char *)buf)[i]);
}
*/
/*
printf("buf size=%d\n", buf_size);
printf("%s\n\n\n", buf);
*/
return -1;
}
// store tvb for subsequent calls
_tvb = tvb;
int offset = 0;
DissectorList dissectorList;
// we process the message tree breadth wise
dissectorList.push_back( protobuf_Dissector( tree_root, offset, msg, NULL ) );
while ( !dissectorList.empty() )
{
protobuf_Dissector dissector = dissectorList.front();
dissectorList.pop_front();
// this can add further entries to message dissector list
dissector.dissect( dissectorList );
} // while( !dissectorList.empty() )
return 0;
}
void Tokenize(const std::string& str,
std::vector& tokens,
const std::string& delimiters = " ")
{
// Skip delimiters at beginning.
std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
// Find first "non-delimiter".
std::string::size_type pos = str.find_first_of(delimiters, lastPos);
// mylog.debug("tokens content ",str);
while (std::string::npos != pos || std::string::npos != lastPos)
{
// Found a token, add it to the vector.
tokens.push_back(str.substr(lastPos, pos - lastPos));
// Skip delimiters. Note the "not_of"
lastPos = str.find_first_not_of(delimiters, pos);
// Find next "non-delimiter"
pos = str.find_first_of(delimiters, lastPos);
}
}
int stringtoint(string s1)
{
int sum=0;
for (int i=0 ;i< s1.size();++i)
{
sum=sum*10+s1[i]-'0';
}
return sum;
}
bool protobuf_MessageConfig::isValid()
{
return ( _desc != NULL );
}
bool protobuf_MessageConfig::matchesDestPort( int port )
{
return matchesPort( _dstPorts, port );
}
bool protobuf_MessageConfig::matchesSrcPort( int port )
{
return matchesPort( _srcPorts, port );
}
bool protobuf_MessageConfig::matchesPort( PortList& portList, int port )
{
for( PortList::iterator it = portList.begin(); it != portList.end(); it++ )
{
if( *it == port ) return true;
}
return false;
}
void protobuf_MessageConfig::parseConfigFile( const std::string& filename, const std::string& dirname )
{
std::ifstream infile;
infile.open( ( dirname + "/" + filename ).c_str() );
if( infile.fail() )
{
DBG_ERR( "Failed to open " << filename << "in dir " << dirname );
return;
}
std::string line;
std::string message_name;
while( !infile.eof() )
{
std::getline( infile, line );
// skip lines beginning with #
if( line[0] == '#' ) continue;
std::vector tokens;
Tokenize( line, tokens, " =" );
//DBG( "Found " << tokens.size() << " tokens" );
// skip lines which have less that 2 tokens
if( tokens.size() < 2 ) continue;
if( tokens[0] == "name" )
{
message_name = tokens[1];
//mylog.debug("message_name",message_name);
continue;
}
if( tokens[0] == "proto_file" )
{
if( importer == NULL )
{
protobuf::compiler::DiskSourceTree *sourceTree = new protobuf::compiler::DiskSourceTree();
static ErrorCollector* errorCollector = new ErrorCollector();
sourceTree->MapPath( "", dirname );
importer = new protobuf::compiler::Importer( sourceTree, errorCollector );
}
DBG( "filename to import " << tokens[1] );
const FileDescriptor* fileDesc = importer->Import( tokens[1] );
// mylog.debug("proto_file", tokens[1] );
if( fileDesc == NULL )
{
DBG_ERR("Unable to parse " << tokens[1] );
continue;
}
DBG( "searching for message " << message_name );
//for( int i = 0; i < fileDesc->message_type_count(); i++ )
//{
// DBG(" message " << fileDesc->message_type( i )->name() );
//}
_desc = fileDesc->FindMessageTypeByName( message_name );
continue;
}
if(tokens[0]=="message_id")
{
//mylog.debug("message_id", tokens[1] );
message_selfid=stringtoint(tokens[1]);
//mylog.debug("message_id_ind", message_selfid);
}
bool src_port = false;
bool dst_port = false;
bool both_ports = false;
src_port = ( tokens[0] == "src_port" );
dst_port = ( tokens[0] == "dst_port" );
both_ports = ( tokens[0] == "port" );
for( int i = 1; i < tokens.size(); i++ )
{
int port = atoi( tokens[i].c_str() );
// mylog.debug("port_id ", port);
if( src_port || both_ports )
{
_srcPorts.push_back( port );
}
if( dst_port || both_ports )
{
_dstPorts.push_back( port );
}
}
}
infile.close();
if( _desc == NULL )
{
DBG_ERR( "Couldnt get descriptor from " << filename );
_dstPorts.clear();
_srcPorts.clear();
}
}
void protobuf_MessageConfig::register_ports()
{
register_ports( _srcPorts );
register_ports( _dstPorts );
}
void protobuf_MessageConfig::register_ports( PortList& portList )
{
for( PortList::iterator it = portList.begin(); it != portList.end(); it++ )
{
wireshark_pb_process_protobuf_dissector_add( "tcp.port", *it ); // 注册TCP端口,本人添加
wireshark_pb_process_protobuf_dissector_add( "udp.port", *it ); // 注册UDP端口
}
}
void protobuf_MessageConfig::register_proto( int proto )
{
if( handleMap == NULL )
{
handleMap = new HandleMap();
}
DescriptorList messageDescriptorList;
FieldDescriptorList fieldDescriptorList;
// we process the message definition depth first
messageDescriptorList.push_back( _desc );
while( !messageDescriptorList.empty() )
{
const Descriptor* messageDescriptor = messageDescriptorList.back();
messageDescriptorList.pop_back();
DBG( "Register message ( " << messageDescriptor->name() << " )" );
wireshark_pb_process_protobuf_register_subtree_( proto,
messageDescriptor->name(),
messageDescriptor->full_name() );
for( int i = 0; i < messageDescriptor->field_count(); i++ )
{
const FieldDescriptor* fieldDescriptor = messageDescriptor->field( i );
if( fieldDescriptor->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE )
{
messageDescriptorList.push_back( fieldDescriptor->message_type() );
}
else
{
fieldDescriptorList.push_back( fieldDescriptor );
}
}
}
// process all field descriptors at very end
while( !fieldDescriptorList.empty() )
{
const FieldDescriptor* fieldDescriptor = fieldDescriptorList.back();
fieldDescriptorList.pop_back();
Handles *handles = new Handles();
DBG( "Register field ( " << fieldDescriptor->name() << " : " << fieldDescriptor->full_name() << " )" );
wireshark_pb_process_protobuf_register_field( proto,
wireshark_pb_process_protobuf_get_type( fieldDescriptor->cpp_type() ),
fieldDescriptor->name().c_str(),
fieldDescriptor->name().c_str(),
&(handles->handle) );
handleMap->insert( StringHandlePair( fieldDescriptor->full_name(), handles ) );
}
}
}
9.修改plugins目录下的Makefile.nmake,增加protobuf工程的编译。重新编译wireshark。
提示:需要下载dirent-1.13.zip,解压后把dirent.h放到VC\Include目录下面,这是一个模拟linux dir相关接口的源代码。
10.把 plugins\protobuf\protobuf.dll 拷贝到wireshark安装目录下plugins\版本号\ 目录下。
11. 在wireshark 安装目录下创建protobuf目录,用于放置protobuf的配置文件和消息定义文件。
12.启动你的wireshark,可以开始抓包分析google protobuf消息了