2.5 Bro脚本入门

基于Bro 2.5.3 文档,按照原文结构整理,非原文翻译,建议阅读英文文档后阅读,如有错误请不吝指出。

时间修改内容
2018.02.24完成全文
  
  

一、理解Bro脚本语言

Bro主要提供两项能力:
  • 解析流量并生成event,整个过程不对使用者开放,也不对生成的event进行分析。
  • 分析event并产生输出,Bro自实现了一套脚本语言,并内置了常用功能,使用者可以根据需要实现分析脚本。注意,Bro脚本语言是基于event驱动的。

Bro脚本主要由三部分组成,导入和导出在parse-time执行,业务逻辑在run-time执行:
  • 导入:@load+相对路径,结尾无分号。Bro搜索路径+相对路径=导入文件绝对路径。相对路径为目录,则导入该路径下的__load__.bro文件,该文件多包含一组@load指令,类似python包的__init__.py文件;相对路径为文件,可忽略.bro后缀。导入将递归执行,重复导入将被忽略。
  • 导出:export{scope name: type = value;},export代码块结束无分号,声明及初始化语句后有。用于声明或重定义公用变量,可供当前和其他脚本使用。需要重定义的一般为常量,常量允许在parse-time重定义,run-time不允许修改,与变量不同。
  • 业务逻辑:event/hook/function等可执行代码块,实现业务功能。

二、了解event queue和event handler

关于event queue(事件队列):
  • event queue是Bro底层(event生成器)和Bro脚本共用的有序队列(先进先出)。
  • event生成器根据解析的流量生成event推入event queue,相当于生产者。这部分功能没有Bro脚本参与,不对使用者开放,基本没有其他有效输出。
  • Bro脚本从event queue取出event并触发对应event handler完成既定处理,相当于消费者。

关于event handler(事件处理器):
  • Bro脚本声明并初始化handler,handler被event触发后接收event携带的信息,并基于这些信息进行决策,也可能修改它们。
  • Bro在內建函数脚本和*.bif.bro脚本中使用event关键字声明event handler,涵盖全部內建event类型,event将触发对应名称的event handler。
  • handler包含用户对event的自定义处理,因此,哪些需要初始化、如何初始化一般由用户根据业务场景自定义。相同类型的handler可以被初始化多次并指定优先级,Bro将在parse-time合并handler,优先级高低与数值大小正相关,默认为0,接受负数。
  • Logging Framework可以配置生成自定义event,用户需要自行声明并初始化log_x event handler,以便完成处理。

三、了解connection record

  • record提供了一个key-value模式的数据类型框架,使用户可以通过type和record关键字自定义数据类型。文档中的record类型多泛指这类自定义数据类型,connection就是其中一种。
  • 一条connection数据对应一个连接,连接的生命周期内可能触发多个event handler,connection数据也在这个过程中被不断修改和完善。connection类型就是用于跟踪记录一个连接在其生命周期内的状态变化。

四、数据类型和数据结构

4.1 了解scope(作用域):

  • Bro脚本有两种等价的参数声明方式:
    • SCOPE name: TYPE;
    • SCOPE name = EXPRESSION;
  • SCOPE支持三种关键字:global(全局变量)、const(全局常量)和local(局部变量)。
  • global一般定义在parse-time,作用于整个命名空间,只能在run-time修改。
  • const一般定义在parse-time,作用于整个命名空间,有&redef属性可以在parse-time修改。
  • local只能定义在run-time,作用于当前代码块,只能在run-time的当前代码块修改。注意:event和hook可能由多代码块组成。
  • 当前命名空间由当前文件+已导入且module name相同的其他文件组成,所有已导入且module name不同的文件的export区域将根据module name合并后成为当前命名空间的子空间,并通过module name引用。
  • Bro启动时将处理脚本中除可执行类型(event/hook/function)以外内容,称为parse-time;然后等待事件触发相应可执行类型,完成处理,即所谓的事件驱动,该阶段称为run-time。
  • 相同作用域内,global、const和local均不允许重名。

4.2 数据类型

4.2.1 基本数据类型—数字类型—int类型

int类型相关总结请参考下表:
int
简要说明:有符号整形
显式声明及初始化:scope name: int; name = return_int_expression;
scope name: int = return_int_expression;  # 相比隐式声明,可读性和可维护性更好
隐式声明及初始化:scope name = return_int_expression;
数据格式:
算数运算:
比较运算:
逻辑运算:不可自动转换为bool类型,因此不可参与逻辑运算
添加成员:容器类型操作,不具备
删除成员:容器类型操作,不具备
成员数量:容器类型操作,不具备
成员是否存在:容器类型操作,不具备
索引成员:容器类型操作,不具备
分片:容器类型操作,不具备
遍历操作:容器类型操作,不具备
內建函数:

4.2.2 基本数据类型—数字类型—count类型

count类型相关总结与int类型基本一致,请参考下表:
count
简要说明:无符号整形
显式声明及初始化:scope name: count; name = return_count_expression;
scope name: count = return_count_expression;  # 相比隐式声明,可读性和可维护性更好
隐式声明及初始化:scope name = return_count_expression;
数据格式:
算数运算:
比较运算:
逻辑运算:不可自动转换为bool类型,因此不可参与逻辑运算
添加成员:容器类型操作,不具备
删除成员:容器类型操作,不具备
成员数量:容器类型操作,不具备
成员是否存在:容器类型操作,不具备
索引成员:容器类型操作,不具备
分片:容器类型操作,不具备
遍历操作:容器类型操作,不具备
內建函数:

4.2.3 基本数据类型—数字类型—double类型

double类型相关总结与int类型基本一致,请参考下表:
double
简要说明:
显式声明及初始化:scope name: double; name = return_double_expression;
scope name: double = return_double_expression;  # 相比隐式声明,可读性和可维护性更好
隐式声明及初始化:scope name = return_double_expression;
数据格式:
算数运算:
比较运算:
逻辑运算:不可自动转换为bool类型,因此不可参与逻辑运算
添加成员:容器类型操作,不具备
删除成员:容器类型操作,不具备
成员数量:容器类型操作,不具备
成员是否存在:容器类型操作,不具备
索引成员:容器类型操作,不具备
分片:容器类型操作,不具备
遍历操作:容器类型操作,不具备
內建函数:

4.2.4 基本数据类型—string类型

string类型相关总结请参考下表:
string
简要说明:字符串类型,与python字符串类型非常类似
支持负索引;具备容器部分特征
注意使用双引号描述字符串
显式声明及初始化:scope name: string; name = return_str_expression;
scope name: string = return_str_expression;  # 相比隐式声明,可读性和可维护性更好
隐式声明及初始化:scope name = return_str_expression;
数据格式:"hello world"
算数运算:支持加法运算,用于字符串连接,其他不支持
比较运算:支持,判断相等时,注意 str1 == str2 和 my_pattern == str
逻辑运算:不可自动转换为bool类型,因此不可参与逻辑运算
添加成员:容器类型操作,不具备
删除成员:容器类型操作,不具备
成员数量:支持,使用||操作符
成员是否存在:支持,使用in或!in,注意 str1 in str2 和 my_pattern in str
索引成员:支持,使用从0开始递增的数字索引,支持负索引
分片:支持,使用[from : to]操作符,from和to为索引,支持负索引、索引越界、索引缺失
遍历操作:支持,使用for (...in...)
內建函数:

4.2.5 基本数据类型—bool类型

bool类型相关总结请参考下表:
bool
简要说明:布尔类型,使用T表示真,使用F表示假
显式声明及初始化:scope name: bool; name = return_bool_expression;
scope name: bool = return_bool_expression;  # 相比隐式声明,可读性和可维护性更好
隐式声明及初始化:scope name = return_bool_expression;
数据格式:T或F
算数运算:不支持
比较运算:不支持
逻辑运算:支持,使用&&和||操作符
添加成员:容器类型操作,不具备
删除成员:容器类型操作,不具备
成员数量:容器类型操作,不具备
成员是否存在:容器类型操作,不具备
索引成员:容器类型操作,不具备
分片:容器类型操作,不具备
遍历操作:容器类型操作,不具备
內建函数:

4.2.6 基本数据类型—网络数据类型—addr类型

addr类型相关总结请参考下表:
addr
简要说明:表示网络层地址,支持IPv4和IPv6,Bro自定义数据类型之一
显式声明及初始化:scope name: addr; name = return_addr_expression;
scope name: addr = return_addr_expression;  # 相比隐式声明,可读性和可维护性更好
隐式声明及初始化:scope name = return_addr_expression;
数据格式:IPv4:10.136.17.35
IPv6:
域名:www.baidu.com
# 域名将进行自动转换,因域名可能对应多个IP,转换为set[addr]
# Bro运行过程中,域名-IP映射关系不会更新,建议用于域名稳定的场景
# 因域名自动转换为set[addr],使用显式声明会导致声明和初始化数据类型不一致的报错
算数运算:不支持
比较运算:支持
逻辑运算:不可自动转换为bool类型,因此不可参与逻辑运算
添加成员:容器类型操作,不具备
删除成员:容器类型操作,不具备
成员数量:容器类型操作,不具备
成员是否存在:容器类型操作,不具备
索引成员:容器类型操作,不具备
分片:容器类型操作,不具备
遍历操作:容器类型操作,不具备
內建函数:

4.2.7 基本数据类型—网络数据类型—port类型

port类型相关总结请参考下表:
port
简要说明:表示传输层端口,支持UDP/TCP/ICMP/UNKNOWN协议,Bro自定义数据类型之一
显式声明及初始化:scope name: port; name = return_port_expression;
scope name: port = return_port_expression;  # 相比隐式声明,可读性和可维护性更好
隐式声明及初始化:scope name = return_port_expression;
数据格式:端口号/协议
UDP:514/udp
TCP:80/tcp
ICMP:/icmp,没有端口概念,Bro将ICMP类型和编码设置为来源和目的端口
UNKNOWN:/unknown
算数运算:不支持
比较运算:支持,默认unknown < tcp < udp < icmp
逻辑运算:不可自动转换为bool类型,因此不可参与逻辑运算
添加成员:容器类型操作,不具备
删除成员:容器类型操作,不具备
成员数量:容器类型操作,不具备
成员是否存在:容器类型操作,不具备
索引成员:容器类型操作,不具备
分片:容器类型操作,不具备
遍历操作:容器类型操作,不具备
內建函数:

4.2.8 基本数据类型—网络数据类型—subnet类型

subnet类型相关总结请参考下表:
subnet
简要说明:表示子网,使用CIDR记法,支持容器类型部分特征,Bro自定义数据类型之一
显式声明及初始化:scope name: subnet; name = return_subnet_expression;
scope name: subnet = return_subnet_expression;  # 相比隐式声明,可读性和可维护性更好
隐式声明及初始化:scope name = return_subnet_expression;
数据格式:10.136.17.35/24
算数运算:不支持
比较运算:仅支持==和!=
逻辑运算:不可自动转换为bool类型,因此不可参与逻辑运算
添加成员:容器类型操作,不具备
删除成员:容器类型操作,不具备
成员数量:支持,使用||操作符
成员是否存在:支持,使用in或!in,用于判断IP是否属于该子网
索引成员:容器类型操作,不具备
分片:容器类型操作,不具备
遍历操作:容器类型操作,不具备
內建函数:

4.2.9 基本数据类型—时间类型—time类型

time类型相关总结请参考下表:
time
简要说明:表示时间戳,Bro自定义数据类型之一
显式声明及初始化:scope name: time; name = return_time_expression;
scope name: time = return_time_expression;  # 相比隐式声明,可读性和可维护性更好
隐式声明及初始化:scope name = return_time_expression;
数据格式:无法直接初始化,需要借助current_time()或network_time()函数
算数运算:支持,注意time类型相减返回interval类型,注意time类型和interval类型的加减法
比较运算:支持
逻辑运算:不可自动转换为bool类型,因此不可参与逻辑运算
添加成员:容器类型操作,不具备
删除成员:容器类型操作,不具备
成员数量:容器类型操作,不具备
成员是否存在:容器类型操作,不具备
索引成员:容器类型操作,不具备
分片:容器类型操作,不具备
遍历操作:容器类型操作,不具备
內建函数:current_time()  # 返回操作系统当前时间
network_time()  # 返回最后一个已处理数据包内记录的时间,未曾处理数据包则返回当前时间
strftime("%Y-%m-%d %H:%M:%S", current_time())  # 将time转换为可读字符串,注意时区

4.2.10 基本数据类型—时间类型—interval类型

interval类型相关总结请参考下表:
interval
简要说明:表示相对时间,Bro自定义数据类型之一
显式声明及初始化:scope name: interval; name = return_interval_expression;
scope name: interval = return_interval_expression;  # 相比隐式声明,可读性和可维护性更好
隐式声明及初始化:scope name = return_interval_expression;
数据格式:数字+0或1个空格+单位
数字支持整数、浮点数、负数
单位支持:微秒(usec)、毫秒(msec)、秒(sec)、分( min)、时(hr)和天(day)
单位单数复数均可,即:print 1hr == 1 hrs;  # return T
算数运算:支持,注意time类型相减返回interval类型,注意time类型和interval类型的加减法
比较运算:支持
逻辑运算:不可自动转换为bool类型,因此不可参与逻辑运算
添加成员:容器类型操作,不具备
删除成员:容器类型操作,不具备
成员数量:容器类型操作,不具备
成员是否存在:容器类型操作,不具备
索引成员:容器类型操作,不具备
分片:容器类型操作,不具备
遍历操作:容器类型操作,不具备
內建函数:

4.2.11 基本数据类型—时间类型—pattern类型

pattern类型相关总结请参考下表:
pattern
简要说明:表示正则表达式,用于对文本快速检索,具备容器类型的部分特征,Bro自定义数据类型之一
显式声明及初始化:scope name: pattern; name = return_pattern_expression;
scope name: pattern = return_pattern_expression;  # 相比隐式声明,可读性和可维护性更好
隐式声明及初始化:scope name = return_pattern_expression;
数据格式:/regular expression/
算数运算:不支持
比较运算:仅支持文本完整匹配,使用==或!=操作符
逻辑运算:不可自动转换为bool类型,因此不可参与逻辑运算
添加成员:容器类型操作,不具备
删除成员:容器类型操作,不具备
成员数量:容器类型操作,不具备
成员是否存在:支持文本嵌入式匹配,使用in或!in,正则表达式必须位于左侧
索引成员:容器类型操作,不具备
分片:容器类型操作,不具备
遍历操作:容器类型操作,不具备
內建函数:split(str, my_pattern)  # 返回数字索引的table,索引由1开始,有序保存分割后的字符串

4.2.12 容器类型—set类型

set类型相关总结请参考下表:
set
简要说明:表示成员唯一的集合,类似于python中的set,但要求成员类型一致
显式声明及初始化:scope name: set[int]; name = set(1, 2, 3);
scope name: set[int] = set(1, 2, 3);  # 可读性和可维护性更好
隐式声明及初始化:scope name = set(1, 2, 3);
数据格式: 
算数运算:不支持
比较运算:不支持
逻辑运算:不可自动转换为bool类型,因此不可参与逻辑运算
添加成员:支持,使用add关键字:local s = set(1, 2); add s[3]; print s;  # return {1, 2, 3}
删除成员:支持,使用delete关键字,用法同add
成员数量:支持,使用||操作符
成员是否存在:支持,使用in或!in
索引成员:不支持
分片:不支持
遍历操作:支持,使用for (...in...)
內建函数:

4.2.13 容器类型—table类型

table类型相关总结请参考下表:
table
简要说明:表示key-value间的映射,key唯一,类似python的dict,但key类型一致且value类型一致
显式声明及初始化:scope name: table[string] of string; name = table(["a"]="b", ["c"]="b");
scope name: table[string] of string =table(["a"]="b", ["c"]="b");  # 可读性和可维护性更好
隐式声明及初始化:scope name = table(["a"]="b", ["c"]="b");
数据格式:支持多个基本数据类型组合作为key,如:local t: table[int, string] of int = table([1, "2"]=3);
后续初始化和赋值时需要注意key需要遵循声明时的格式
算数运算:不支持
比较运算:不支持
逻辑运算:不可自动转换为bool类型,因此不可参与逻辑运算
添加成员:支持,使用赋值语句即可
删除成员:支持,使用delete关键字,用法同set
成员数量:支持,使用||操作符
成员是否存在:支持,使用in或!in,注意,仅针对key进行检测,如:
local t = table([1, "2"] = 3); print [1, "2"] in t;  # return T
索引成员:支持
分片:不支持
遍历操作:支持,使用for (...in...),注意,仅针对key进行遍历,对组合key的遍历只能整体遍历,如:
for ([i, j] in table([1, "2"] = 3)) {
    print i;
}
# return 1
內建函数:

4.2.14 容器类型—vector类型

vector类型相关总结请参考下表:
vector
简要说明:表示有序数组,类似python的list,但要求成员类型一致
显式声明及初始化:scope name: vector of int; name = vector(1, 1, 2);
scope name: vector of int = vector(1, 1, 2);  # 可读性和可维护性更好
隐式声明及初始化:scope name =  vector(1, 1, 2);
数据格式:索引由0开始递增,连续不间断
算数运算:不支持
比较运算:不支持
逻辑运算:不可自动转换为bool类型,因此不可参与逻辑运算
添加成员:支持,使用赋值语句即可,注意索引连续
删除成员:不支持
成员数量:支持,使用||操作符
成员是否存在:支持,使用in或!in,注意,仅针对索引进行检测
索引成员:支持
分片:不支持
遍历操作:支持,使用for (...in...),注意,仅针对索引进行遍历
內建函数:

4.2.15 自定义类型—type和record关键字

type关键字可以用于定义数据类型别名,提高代码可读性和可维护性,如:
type string_array: table[count] of string;
type和record关键字结合使用可以用于自定义数据类型,相关总结请参考下表:
自定义数据类型
简要说明:使用type和record关键字自定义数据类型,类似于C语言的结构体
可以任意定义数据类型中每个成员key和value的数据类型,但使用时必须按定义初始化或赋值
类型自定义type Service: record {
    name: string;
    ports: set[port];
};
自定义数据类型时允许嵌套包含其他自定义的数据类型
类型定义需在Bro解析阶段完成,即类型定义必须位于可执行代码(event/hook/function)外部
显式声明及初始化:scope name: Service; name = Service($name="dns", $ports=set(53/tcp, 53/udp));
scope name: Service = Service($name="dns", $ports=set(53/tcp, 53/udp));
后者可读性和可维护性更好
初始化时在key前添加$符号
隐式声明及初始化:scope name = Service($name="dns", $ports=set(53/tcp, 53/udp));
数据格式:
算数运算:不支持
比较运算:不支持
逻辑运算:不可自动转换为bool类型,因此不可参与逻辑运算
添加成员:容器类型操作,不具备
删除成员:容器类型操作,不具备
成员数量:支持,使用||操作符
成员是否存在:容器类型操作,不具备
索引成员:访问自定义类型数据的成员使用$符号
分片:容器类型操作,不具备
遍历操作:容器类型操作,不具备
內建函数:

五、如何自定义日志

  • 日志生成主要涉及Log Stream、Filter和Writer三部分,具体含义如下:
    • Log Stream:定义字段种类、字段类型、日志类型、日志名等日志处理过程中的基本规则,以便用户根据这些要求提供源数据。
    • Filter:对源进行过滤、修改、重定向等处理,默认Filter不进行任何处理。
    • Writer:决定日志输出方式和格式,默认writer将日志输出为tab分隔的文本文件,也支持ES等输出方式。
  • 使用Logging Framework的步骤如下:
    • Bro解析阶段:注册Log Stream ID、自定义数据类型Info描述日志内容
    • Bro运行初始化阶段:创建Log Stream、修改Log Stream的Filter
    • Bro运行阶段:写入Log Steam。
  • Logging Framework可以配置生成自定义的event,用户需要自行声明并初始化log_x event handler,并在其t中进行后置处理,避免了解析文本日志再分析的冗余操作,提高分析实时性。

六、如何触发通知

使用Notice Framework的步骤如下:
  • Bro解析阶段:注册Notice Type。
  • Bro运行阶段:根据Notice::Info生成notice数据,通过NOTICE函数传入Notice Framework。
  • Notice Framework:通过hook修改notice数据并触发相应通知行为(可以在Bro解析阶段定义Notice Policy shortcut简化该步骤)。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值