一份菜单

如何在C程序中使用libwireshark解码网络数据包

Wireshark是一个开源网络数据包分析器。

它可以捕获,分解和解码各种协议。这可以帮助Linux系统管理员对网络问题进行故障排除。

除了使用wirehshark作为调试网络数据包的独立应用程序之外,您还可以使用wireshark库为自定义应用程序编写自己的扩展程序或插件。

本教程说明了如何使用Wireshark库编写自定义代码来使用C示例程序调试网络数据包。

该代码解释了两个部分。首先,捕获网络数据包。其次,使用libwireshark解码数据包。

作为前提条件,您的系统应同时安装libpcap库和wireshark库。

要捕获数据包,请参阅 如何使用带有C示例代码的Libpcap执行数据包嗅探.

您还可以在C程序中使用以下api打开现有的pcap文件:

pd = pcap_open_offline(pcap_path, errbuf);

Wireshark代码使用其自己的解剖引擎(扩展模块库)来解剖网络数据包。

以下代码显示了正确初始化它的必要步骤。

下面提到的功能来自wireshark开源代码,它将初始化数据包解析引擎,所需的数据结构,变量,GUID映射,内存分配子系统,注册所有协议解析器句柄,主机名查找,这些是解析过程所必需的。

static void initialize_epan(void)
{
  int i;
  e_prefs *prefs;
  char *gpf_path, *pf_path;
  int gpf_open_errno, gpf_read_errno;
  int pf_open_errno, pf_read_errno;

  //set timestamp type
  timestamp_set_type(TS_RELATIVE);

  // This function is called when the program starts, to save whatever credential information
  // we'll need later, and to do other specialized platform-dependent initialization
  init_process_policies();
  
  epan_init(register_all_protocols, register_all_protocol_handoffs,
    NULL, NULL, failure_message, open_failure_message,
    read_failure_message, NULL);
  
  
  // Register all non-dissector modules' preferences.
  prefs_register_modules();

  // Read the preferences file, fill in "prefs", and return a pointer to it, 
  // preference file has information about protocol preferences (e.g. default port)
  prefs = read_prefs(&gpf_open_errno, &gpf_read_errno, &gpf_path,
    &pf_open_errno, &pf_read_errno, &pf_path);
  
  if (gpf_path != NULL) {
    if (gpf_open_errno != 0)
      fprintf(stderr, "Can't open global preferences file \"%s\": %s.\n", pf_path, strerror(gpf_open_errno) );
    
    if (gpf_read_errno != 0)
      fprintf(stderr, "I/O error reading global preferences file " "\"%s\": %s.\n", pf_path, strerror(gpf_read_errno) );
  }

  if (pf_path != NULL) {
    if (pf_open_errno != 0)
      fprintf(stderr, "Can't open your preferences file \"%s\": %s.\n",pf_path, strerror(pf_open_errno));
    
    if (pf_read_errno != 0)
      fprintf(stderr, "I/O error reading your preferences file " "\"%s\": %s.\n", pf_path, strerror(pf_read_errno));
    
    g_free(pf_path);
    pf_path = NULL;

  }

  cleanup_dissection();

  // Initialize the dissection engine
  init_dissection();

  /* Set the given nstime_t to (0,maxint) to mark it as "unset"
   * That way we can find the first frame even when a timestamp
   * is zero */

  nstime_set_unset(&first_ts);
  nstime_set_unset(&prev_cap_ts);
}

以下是上述epan_init函数中使用的一些帮助器函数,这些函数将帮助调试epan_init()函数执行期间遇到的任何错误情况。

如果在读取任何配置文件期间发生任何读取错误,将执行以下功能。

static void
read_failure_message(const char *filename, int err)
{
  fprintf(stderr, "An error occurred while reading from the file \"%s\": %s.",
    filename, strerror(err) );
}

将执行以下功能以打印错误消息。

static void
failure_message(const char * msg_format, va_list ap)
{
  vfprintf(stderr, msg_format, ap);
  fprintf(stderr, "\n");
}

如果epan_init无法执行以下函数’t打开配置文件:

static void
open_failure_message(const char *filename, int err, gboolean for_writing)
{
  fprintf(stderr, "open error. filename = %s, err = %d, for_writing = %d\n",
    filename, err, for_writing);
}

以下帮助程序功能是填充帧数据,当解析保存的pcap文件时,将逐帧检查所有帧,将使用framd_data结构存储捕获的帧数据,然后将解剖算法应用于它。

fill_framedata function will be used to populate the frame information in fram_data structure.
void fill_framedata(frame_data *fdata, uint64_t frame_number,
                     const struct pcap_pkthdr *h, int ll_type)
{
  fdata->pfd = NULL;
  fdata->num = frame_number;
  fdata->pkt_len = h->len;
  fdata->cum_bytes  = 0;
  fdata->cap_len = h->caplen;
  fdata->file_off = 0;
  fdata->lnk_t = ll_type;
  fdata->abs_ts.secs = h->ts.tv_sec;
  fdata->abs_ts.nsecs = h->ts.tv_usec*1000;
  fdata->flags.passed_dfilter = 0;
  fdata->flags.encoding = CHAR_ASCII;
  fdata->flags.visited = 0;
  fdata->flags.marked = 0;
  fdata->flags.ref_time = 0;
  fdata->color_filter = NULL;

  if (nstime_is_unset(&first_ts) )
   first_ts = fdata->abs_ts;

  nstime_delta(&fdata->rel_ts, &fdata->abs_ts, &first_ts);

  if (nstime_is_unset(&prev_cap_ts) )
    prev_cap_ts = fdata->abs_ts;

  nstime_delta(&fdata->del_cap_ts, &fdata->abs_ts, &prev_cap_ts);
               fdata->del_dis_ts = fdata->del_cap_ts;
               prev_cap_ts = fdata->abs_ts;
}

以下clear_data函数将用于释放frame_data结构,以清理frame_data结构实例。

static void clear_fdata(frame_data *fdata)
{
  if (fdata->pfd)
    g_slist_free(fdata->pfd);
}

初始化Wireshark解剖引擎后,其余步骤很容易,请在pcap_loop API中将该函数注册为回调。

static void process_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *bytes)
{
  (void) user;
  
  // declare dissection tree data structure, it will contain all the packet information (all the layers)
  epan_dissect_t *edt;

  //declare the frame_data strcture that will be used in populating frame data
  frame_data fdata;
  
  //pseaudo header 
  union wtap_pseudo_header pseudo_header;
  
  static uint32_t frame_number; /* Incremented each time libpcap gives us a packet */
  
  memset(&pseudo_header, 0, sizeof(pseudo_header) );
  
  frame_number++;
  
  fill_framedata(&fdata, frame_number, h, ll_type);
  
  // get new dissection tree 
  edt = epan_dissect_new(verbose /* create_proto_tree */,
                         verbose /* proto_tree_visible */);
  
  // execute dissection engine  上  frame data
  epan_dissect_run(edt, &pseudo_header,  通过 tes, &fdata,
                   !verbose ? &cinfo : NULL);
  if (verbose)
    proto_tree_print(edt); //print the packet information

  //free the dissection tree   
  epan_dissect_free(edt);

  // free the frame data 
  clear_fdata(&fdata);

}

在wireshark代码中可以使用proto_tree_print函数。跟着 如何使用Libpcap执行数据包嗅探 了解如何使用pcap_loop注册回调

打开一个文件并复制所有这些功能,将以上功能注册为pcap_loop函数的回调。

完成所有步骤之后,编译代码并将wireshark的epan文件夹复制到您的工作目录中,并包括所有必需的文件。

以下是如何编译此程序的示例。

g++ t.cpp -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I. -I../include/ -lpthread -L/home/santosh/proj -lpcap -L/home/santosh/proj  -lwireshark

当您执行此代码时,它将打印所有图层。在以下代码段中,输出将被截断。

# ./a.out  http.pcap
proto = frame, start = 0, len = 265
  frame.time: "Nov 11, 2014 11:30:43.000000000 IST"
  frame.time_epoch: 1397196043.000000000
  frame.time_delta: 0.000000000
  frame.time_delta_displayed: 0.000000000

proto = eth, start = 0, len = 14
  eth.dst: b1:f1:e1:a1:31:c0
  eth.addr: b1:f1:e1:a1:31:c0
  eth.lg: 0
  eth.src: b1:b1:21:d1:f1:11
  eth.type: 2048

proto = ip, start = 14, len = 20
  ip.version: 4
  ip.hdr_len: 20
  ip.dsfield.dscp: 0
  ip.dsfield.ecn: 0
  ip.len: 251
  ip.id: 20596
  ip.flags.mf: 0

proto = expert, start = 0, len = 0
  expert.message: Bad checksum
  expert.severity: 8388608
  expert.group: 16777216
  ip.src: 10.34.77.109
  ip.addr: 10.34.77.109
  ip.src_host: 10.34.77.109
  ip.host: 10.34.77.109

proto = tcp, start = 34, len = 20
  tcp.srcport: 61945
  tcp.port: 8080
  tcp.stream: 0
  tcp.len: 211
  tcp.seq: 1

Text label: SEQ/ACK analysis
  tcp.analysis.bytes_in_flight: 211

proto = http, start = 54, len = 211
Text label: CONNECT www.google.com:443 HTTP/1.1\r\n

proto = expert, start = 0, len = 0
  expert.message: CONNECT www.google.com:443 HTTP/1.1\r\n
  expert.severity: 2097152
  expert.group: 33554432
  http.request.method: CONNECT
  http.request.uri: www.google.com:443
  http.request.version: HTTP/1.1
  http.host: www.google.com
  
Text label: Proxy-Connection: keep-alive\r\n
  http.user_agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.154 Safari/537.36
Text label: \r\n
  http.request.full_uri: http://www.google.comwww.google.com:443
  http.request: 1

如果您喜欢这篇文章,您可能还会喜欢..

  1. 50个Linux Sysadmin教程
  2. 50个最常用的Linux命令(包括示例)
  3. 排名前25位的最佳Linux性能监视和调试工具
  4. 妈妈,我找到了! 15个实用的Linux Find命令示例
  5. Linux 101 Hacks第二版电子书 Linux 101黑客手册

Bash 101 Hacks书 Sed和Awk 101黑客手册 Nagios Core 3书 Vim 101黑客手册

{ 8 评论 … 加一 }

  • 匿名 2014年12月11日,上午2:07

    很棒的文章,但是…您能否提供完整的t.cpp源代码? ðŸ™,

  • 丹尼斯·格兹米斯(Deniz Gezmis) 2014年12月11日,上午3:13

    伟大的写作。谢谢你

    -德尼兹

  • 鲍勃 2014年12月11日,上午10:51

    做得好。谢谢!!!

  • 贾加迪什 2014年12月12日,上午8:43

    感谢分享。它帮助了我。

  • 贾拉尔·哈吉霍拉玛利 2014年12月13日,上午6:43

    你好
    非常感谢您的精彩文章

  • 黄昏科西嘉 2014年12月23日,上午1:57

    保持最新>

    由于C99,在这种情况下也可以使用限制>

    const char * msg_format

  • 大卫 2015年6月3日,晚上9:14

    这是一个很大的份额!请提供完整的源代码,以便我们可以使用它吗?预先谢谢〜

  • 内森 2016年2月4日,下午1:51

    我在挖掘tshark代码以使用解剖器….this is great.
    感谢分享…..

发表评论