在Bro中完成第一个协议分析器—RIP协议

前言

熟悉代码结构最好的方式,就是尝试自己去写一个模块加入到整个系统中,在调试代码的过程中,熟悉代码。

实现流程

使用Binpac quick start脚本生成自己的Analyzer目录结构

下载Binpac Quick Start脚本

git clone https://github.com/grigorescu/binpac_quickstart.git 下载完成之后,进入该目录,目录结构如下图所示:

Guoze@node-0:~/00_Workbench$ cd binpac_quickstart/
Guoze@node-0:~/00_Workbench/binpac_quickstart$ ls -al
total 32
drwxr-xr-x  4 Guoze senfv-PG0 4096 May 22 19:30 .
drwxr-xr-x 11 Guoze senfv-PG0 4096 May 23 12:15 ..
drwxr-xr-x  8 Guoze senfv-PG0 4096 May 22 19:30 .git
-rw-r--r--  1 Guoze senfv-PG0   21 May 22 19:30 __init__.py
-rw-r--r--  1 Guoze senfv-PG0  128 May 22 19:30 README.md
-rwxr-xr-x  1 Guoze senfv-PG0 7151 May 22 19:30 start.py
drwxr-xr-x  2 Guoze senfv-PG0 4096 May 22 19:30 templates

使用脚本生产分析器目录结构

start.py的python就是我们可以用来生成bro中Analyzer的python脚本,使用方式:

Guoze@node-0:~/00_Workbench/binpac_quickstart$ ./start.py 
Usage:
    start.py NAME DESCRIPTION PATH_TO_BRO_SRC (--tcp|--udp) [--buffered] [--plugin]

Example: ./start.py RIP "Routing Internet Protocl" ../bro --udp 协议名称:RIP; 协议介绍:Routing Internet Protocl Bro源代码的路径:../bro 底层的网络协议:–udp

我们书写的第一个脚本就是实现RIP协议的解析,RIP协议的详细内容请参考。 在终端输入:./start.py RIP "Routing Internet Protocl" ../bro --udp 执行结束之后分别在bro源代码下两个位置生产文件:

  1. bro/scripts/base/protocols/rip/
Guoze@node-0:~/00_Workbench/bro/scripts/base/protocols/rip$ ls -l
total 12
-rw-r--r-- 1 Guoze senfv-PG0  245 May 23 12:49 dpd.sig
-rw-r--r-- 1 Guoze senfv-PG0   66 May 23 12:49 __load__.bro
-rw-r--r-- 1 Guoze senfv-PG0 1327 May 23 12:49 main.bro

load.bro: This allows all the contents of the directory to be loaded via @load base/protocols/sip. dpd.sig: This file contains a signature that can be used to attach the analyzer to connections if their content matches. main.bro: Contains the base script-layer functionality for processing events emitted from the analyzer.

  1. src/analyzer/protocol/sip/
Guoze@node-0:~/00_Workbench/bro/src/analyzer/protocol/rip$ ls -l
total 32
-rw-r--r-- 1 Guoze senfv-PG0  301 May 23 12:50 CMakeLists.txt
-rw-r--r-- 1 Guoze senfv-PG0  472 May 23 12:50 events.bif
-rw-r--r-- 1 Guoze senfv-PG0  480 May 23 12:50 Plugin.cc
-rw-r--r-- 1 Guoze senfv-PG0  738 May 23 15:27 rip-analyzer.pac
-rw-r--r-- 1 Guoze senfv-PG0  721 May 23 12:50 RIP.cc
-rw-r--r-- 1 Guoze senfv-PG0  715 May 23 12:50 RIP.h
-rw-r--r-- 1 Guoze senfv-PG0 1005 May 23 12:50 rip.pac
-rw-r--r-- 1 Guoze senfv-PG0  939 May 23 15:03 rip-protocol.pac

CMakeLists.txt: Informs the CMake build system how to compile the analyzer. Plugin.cc: Analyzers in Bro are a type of plugin. This file does what’s necessary to register the new analyzer plugin with Bro. RIP.h: Defines the API for the new analyzer which derives from one of Bro’s already-existing analyzer classes. RIP.cc: mplementation of the analyzer. It’s mostly just responsible for handing off data to the protocol parser that’s been generated by BinPAC. events.bif: Defines events that the analyzer will generate. rip.pac: The main entry point for the BinPAC definition of the protocol that you want to parse. rip-protocol.pac: Where the message format is defined.
rip-analyzer.pac: Defines a connection, flow, and other processing functions for the analyzer.

在这些文件当中,我们主要需要完成:

  • events.bif
  • rip.pac
  • rip-protocol.pac
  • rip-analyzer.pac

代码实现

rip-protocol.pac文件

这个文件实现的是对RIP协议的组成部分进行定义,以type的方式定义协议数据传输的各个部分。其中RIP_PDU函数是将需要处理的部分传递到分析仪去。

1   # Generated by binpac_quickstart
  2 # ## TODO: Add your protocol structures in here.                                                                                                                                      
  3 # ## some examples:                                                                                                                                                                   
  4                                                                                                                                                                                       
  5 # Types are your basic building blocks.                                                                                                                                               
  6 # There are some builtins, or you can define your own.                                                                                                                                
  7 # Here's a definition for a regular expression:                                                                                                                                       
  8 # type RIP_WHITESPACE = RE/[ \t]*/;                                                                                                                                                   
  9                                                                                                                                                                                       
 10 # A record is a collection of types.                                                                                                                                                  
 11 # Here's one with the built-in types                                                                                                                                                  
 12                                                                                                                                                                                       
 13 enum Rip_Command {                                                                                                                                                                    
 14     RIP_REQUEST     = 1,                                                                                                                                                              
 15     RIP_RESPONSE    = 2,                                                                                                                                                              
 16 }                                                                                                                                                                                     
 17                                                                                                                                                                                       
 18 enum Rip_Version {                                                                                                                                                                    
 19     RIP_V1   = 1,                                                                                                                                                                     
 20     RIP_V2   = 2,                                                                                                                                                                     
 21 }                                                                                                                                                                                     
 22                                                                                                                                                                                       
 23 type Rip_Message = record {                                                                                                                                                           
 24     command : uint8;                                                                                                                                                                  
 25     version : uint8;                                                                                                                                                                  
 26     pad     : padding[2];                                                                                                                                                             
 27     entry   : Rip_Entry[] &until($input.length()) == 0;                                                                                                                               
 28 };                                                                                                                                                                                    
 29                                                                                                                                                                                       
 30 type Rip_Entry = record {
 31     af      : uint16;                                                                                                                                                                 
 32     rt      : uint16;                                                                                                                                                                 
 33     ip      : uint32;                                                                                                                                                                 
 34     mask    : uint32;                                                                                                                                                                 
 35     gateway : uint32;                                                                                                                                                                 
 36     metric  : uint32;                                                                                                                                                                 
 37 };                                                                                                                                                                                    
 38                                                                                                                                                                                       
 39 type RIP_PDU(is_orig: bool) = record {                                                                                                                                                
 40     command : uint8;                                                                                                                                                                  
 41     version : uint8;                                                                                                                                                                  
 42     pad     : padding[2];                                                                                                                                                             
 43 } &byteorder=bigendian;                                                                                                                                                               

rip_analyzer.pac文件实现

该文件主要完成对命令做出对RIP_PDU函数传递过来的数据进行处理解析, 如果有RIP数据包出现那么就进入这个处理流程。

1   # Generated by binpac_quickstart                                                                                                                                                      
  1             
  2 refine flow RIP_Flow += {                                                                                                                                                             
  3     function proc_rip_message(msg: RIP_PDU): bool                                                                                                                                     
  4         %{  
  5         // Check for RIP commands                                                                                                                                                     
  6         if ( ${msg.command} == RIP_REQUEST) {                                                                                                                                         
  7             BifEvent::generate_rip_request(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(),
  8             ${msg.command},                                                                                                                                                           
  9             ${msg.version});                                                                                                                                                          
 10             return true;                                                                                                                                                              
 11         }   
 12         if ( ${msg.command} == RIP_RESPONSE) {                                                                                                                                        
 13             BifEvent::generate_rip_response(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(),                                                                       
 14             ${msg.command},                                                                                                                                                           
 15             ${msg.version});                                                                                                                                                          
 16             return true;                                                                                                                                                              
 17         }   
 18         %}  
 19 };          
 20             
 21 refine typeattr RIP_PDU += &let {                                                                                                                                                     
 22     proc: bool = $context.flow.proc_rip_message(this);                                                                                                                                
 23 };          
~

events.bif文件实现

1   # Generated by binpac_quickstart                                                                                                                                                      
  1  
  2 # In this file, you'll define the events that your analyzer will                                                                                                                      
  3 # generate. A sample event is included.                                                                                                                                               
  4  
  5 # ## TODO: Edit the sample event, and add more events.                                                                                                                                
  6  
  7 ## Generated for RIP connections                                                                                                                                                      
  8 ##                                                                                                                                                                                    
  9 ## See `Google <http://lmgtfy.com/?q=RIP>`__ for more information about RIP                                                                                                           
 10 ##                                                                                                                                                                                    
 11 ## c: The connection                                                                                                                                                                  
 12 ##                                                                                                                                                                                    
 13 event rip_request%(c: connection, command: count, version: count%);                                                                                                                   
 14 Event rip_response%(c: connection, command: count, version: count%);

测试

配置bro的工作环境

首先需要配置好bro的可执行文件的路径问题: Bro可执行文件的路径在:bro/build/src/bro

Guoze@node-0:~/00_Workbench/bro$ ./build/src/bro -h
bro version 2.5-598
usage: ./build/src/bro [options] [file ...]
    <file>                         | policy file, or read stdin
    -a|--parse-only                | exit immediately after parsing scripts
    -b|--bare-mode                 | don't load scripts from the base/ directory
    -d|--debug-policy              | activate policy file debugging
    -e|--exec <bro code>           | augment loaded policies by given code
    -f|--filter <filter>           | tcpdump filter

Bro已经提供了可执行脚本给你,让你可以方便的配置环境变量,所以我们可以直接执行配置脚本就好了。 配置环境变量的shell脚本为:

Guoze@node-0:~/00_Workbench/bro/build$ cat bro-path-dev.sh 
export BROPATH=`/users/Guoze/00_Workbench/bro/build/bro-path-dev`
export BRO_PLUGIN_PATH="/users/Guoze/00_Workbench/bro/build/src":
export PATH="/users/Guoze/00_Workbench/bro/build/src":$PATH

执行脚本完成配置

Guoze@node-0:~/00_Workbench/bro$ source ./build/bro-path-dev.sh

检测脚本执行情况,判断是否完成配置,在终端输入:bro -h, 如果执行结果和执行./build/src/bro -h的结果一致,那么就表示已经配置成功了。

Guoze@node-0:~/00_Workbench/bro$ bro -h
bro version 2.5-598
usage: bro [options] [file ...]
    <file>                         | policy file, or read stdin
    -a|--parse-only                | exit immediately after parsing scripts
    -b|--bare-mode                 | don't load scripts from the base/ directory
    -d|--debug-policy              | activate policy file debugging
    -e|--exec <bro code>           | augment loaded policies by given code
    -f|--filter <filter>           | tcpdump filter
    -g|--dump-config               | dump current config into .state dir
    -h|--help|-?                   | command line help

测试代码

下载RIP的网络数据抓包

直接下载一个RIPv2的数据包就可以用于测试了 $ wget http://packetlife.net/captures/RIPv2.cap 判断数据包是否是包含的RIP数据,能否符合我们的要求?

Guoze@node-0:~/00_Workbench$ tcpdump -nr RIPv2.cap
reading from file RIPv2.cap, link-type EN10MB (Ethernet)
23:06:26.942558 IP 10.0.0.1.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:06:30.158769 IP 10.0.0.2.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:06:52.663855 IP 10.0.0.1.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:06:58.416478 IP 10.0.0.2.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:07:19.709681 IP 10.0.0.1.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:07:24.974047 IP 10.0.0.2.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:07:45.389720 IP 10.0.0.1.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:07:53.891896 IP 10.0.0.2.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:08:14.625084 IP 10.0.0.1.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:08:21.933550 IP 10.0.0.2.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:08:41.410659 IP 10.0.0.1.520 > 224.0.0.9.520: RIPv2, Response, length: 84
23:08:47.731064 IP 10.0.0.2.520 > 224.0.0.9.520: RIPv2, Response, length: 84

修改代码,加入测试打印数据

 12 refine flow RIP_Flow += {          
 11     function proc_rip_message(msg: RIP_PDU): bool
 10         %{                         
  9         // Check for RIP commands  
  8         if ( ${msg.command} == RIP_REQUEST) { 
  7             printf("In rip_request\n");
  6             BifEvent::generate_rip_request(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(),
  5             ${msg.command},        
  4             ${msg.version});       
  3             return true;           
  2         }                          
  1         if ( ${msg.command} == RIP_RESPONSE) {
15              printf("In rip_response\n");                                                                                                                                              
  1             BifEvent::generate_rip_response(connection()->bro_analyzer(), connection()->bro_analyzer()->Conn(),
  2             ${msg.command},        
  3             ${msg.version});       
  4             return true;           
  5         }                          
  6         %}

修改代码完成之后,重新编译代码:

Guoze@node-0:~/00_Workbench/bro$ sudo make
make -C build all
make[1]: Entering directory '/users/Guoze/00_Workbench/bro/build'
make[2]: Entering directory '/users/Guoze/00_Workbench/bro/build'
make[3]: Entering directory '/users/Guoze/00_Workbench/bro/build'
make[3]: Leaving directory '/users/Guoze/00_Workbench/bro/build'
........
........
[100%] Built target rst
make[2]: Leaving directory '/users/Guoze/00_Workbench/bro/build'
make[1]: Leaving directory '/users/Guoze/00_Workbench/bro/build'

结尾和我上述结果类似的话,表示编译通过。

使用网络抓包数据进行测试

使用Bro导入这个数据包进行测试

Guoze@node-0:~/00_Workbench$ bro -r RIPv2.cap 
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response
In rip_response

提升理解

在前面的部分,并没有去处理scripts/base/protocols/rip/目录下面的文件,接下来我们需要来理解这几个文件。

Guoze@node-0:~/00_Workbench/bro/scripts/base/protocols/rip$ ls -l
total 12
-rw-r--r-- 1 Guoze senfv-PG0  245 May 23 12:49 dpd.sig
-rw-r--r-- 1 Guoze senfv-PG0   66 May 23 12:49 __load__.bro
-rw-r--r-- 1 Guoze senfv-PG0 1327 May 23 12:49 main.bro

load.bro: This allows all the contents of the directory to be loaded via @load base/protocols/sip. dpd.sig: This file contains a signature that can be used to attach the analyzer to connections if their content matches. main.bro: Contains the base script-layer functionality for processing events emitted from the analyzer.

load.bro理解

这个文件加载了所有的内容到Bro中去。

dpd.sig (digital protocol detection scripts)

当你找出来匹配你定义的特殊的协议的时候,他就会生成和调用对应的分析器去处理,我们可以这里定义它需要处理的接口

main.bro

这个文件包含了所有的基本工作和所有分析器内容,Bro首先云心的内容

<未完待续>

Terry Tang
Terry Tang
Software Development Engineer

My research interests include distributed robotics, mobile computing and programmable matter.

comments powered by Disqus