Bro脚本语法2-数据类型

本文详细介绍了Bro脚本中的数据类型,包括bool、int、count、double、time、interval、string、pattern、port、addr、subnet、enum、table、set、vector、record、function、event、hook、file、opaque和any。讲解了每种数据类型的用法、特点以及如何进行操作和比较。此外,还提到了函数和事件的声明及调用,以及hook的执行机制。
摘要由CSDN通过智能技术生成

Bro脚本语法2-数据类型

@(教程)[Bro]

内建类型

NameDescription
bool布尔类型
count, int, double数字类型
time, interval时间类型
string字符串
pattern正则表达式
port, addr, subnet网络类型
enum枚举(用户自定义类型)
table, set, vector, record容器类型
function, event, hook可执行类型
file文件类型 (only for writing)
opaque透明类型 (for some built-in functions)
anyAny 类型 (for functions or containers)

bool

使用 T 和 F 表示
|T| 的值是1, |F| 的值是0

int

64位有符号整数
可以用16进制表示,如 -0xFF,+0xabc123

count

64位无符号整形

double

双精度浮点数

time

绝对时间类型,
可以通过 double_to_time , current_time ,network_time 来给time类型分配值

interval

相对时间类型

string

既可以保存文本,也可以保存二进制数据
跨行注意使用换行连接符 (\ )

local orig = "0123456789";
local second_char = orig[1];
local first_two_char = orig[:2];
local cop_orig = orig[:]

pattern

用于文本搜索的正则表达式,通过括号内的斜杠创建 (/)
正则表达式规则参照flex 正则表达式
pattern支持两种匹配,精确匹配(exact)和嵌入匹配(embedded)
精确匹配使用 “==” 来关联匹配的字符
例:

    /foo|bar/ =="foo";     将返回true
    /foo|bar/ == "foobar"  返回false

embedded 匹配使用 “in” 来进行匹配(!in 也支持)

    /foo|bar/ in "foobar" ; 返回true
    /^oob/ in "foobar";     返回false

port

表示传输层的端口号类型
无符号整型跟上 /tcp./udp,/icmp./unknow中的一个
不同协议的端口号比较操作是
unknown < tcp < udp < icmp
比如 65535/tcp 就小于 0/udp
可以通过内建函数 get_port_transport_proto 来获得传输层协议类型
或者通过内建函数 port_to_count 获得数字类型的端口号


addr

表示IP地址的数据类型

local a: addr = 192.168.1.100
local s:subnet = 192.168.0.0/16
if(a/16 == s)
    print "true";  

检查一个地址是否属于某个 subnet ,可以通过in ,!in 来判断

local a: addr = 192.168.1.100;
local s: subnet = 192.168.0.0/16;
if(a in s)
    print "true";

可以通过内建函数 is_v4_addr, is_v6_addr 来检查一个地址是ipv4还是ipv6

另外 hostname 也是可以使用的,但是主机名可能包含多个IP地址,所以这种情况下这个变量其实是 “set[addr]”

local a = www.google.com;

subnet

表示一段网络的数据类型
格式是addr + /

192.168.0.0/16
[fe90::]/64

enum

允许用户定义一个关联的集合
例子:

type color: enum {Red,White,Blue,};

类型名称color 和 枚举的值(Red.Blue…)都有全局的作用域
只支持”==” 和 “!=” 及“=” 操作


table

类似于map的概念,一个集合映射到另一个集合
定义table的语法:

table [ type^+] of type

索引:type^+ 可以指定一个或者多种类型,通过逗号分割,索引不能是pattern,table,set, vector, file,opaque,any
例:

global a: table[count] of string

或者可以更复杂一定

global a:table[count] of table[addr,port] of string;

table a 索引到 另一个table 再索引到一个string

初始化table

global t: table[count] of sting = {
    [11] = "eleven",
    [5] = "five",
};
# 通过另一个table初始化
global t2 = table{
    [192.168.0.2,22/tcp] = "ssh",
    [192.168.0.3,80/tcp] = "http"
};

table 结构也可以被type命名为一个类型,这可以用在当一个复杂的索引类型双关

type MyRec:record {
    a:count &optional;
    b:count;
}

type MyTable: table[MyRec] of strubg;

global t3 = MyTable{[[$b=5]] = "b5",[[$b=7]] = "b7" };

获取table 的元素可以通过索引值加”[ ]”的方式

print t[11];

元素成员可以通过”in” 和 “!in” 来检测

if (13 in t)
    ...
if ([192.168.0.2,22/tcp] in t2)
    ...

也可以通过索引来重新赋值

t[13] = "thirteen";

删除table 中的元素可以通过 “delete “关键字

delete t[13];

如果不存在索引为13的元素,则什么也不会发生

通过取绝对值的操作,可以获得元素的个数

|t|

这个操作可以用于for 语句中


set

和类型 table 相似,但是这个集合没有映射关系
声明语法:

set [type^+]

通过逗号分割多种类型,有效的类型不包含 pattern,table,set,vector,file,opaque,any
set 可以通过大括号列出元素值来初始化

global s: set[port] = {21/tcp,23/tcp};
global s2: set[port,string] = {[21/tcp,"ftp"],[23/tcp,"telnet"]};

也可以直接通过另一个set来创建

global s3 = set(21/tcp,23/tcp,80/tcp,442/tcp);

set 结构也可以被type命名为一个类型,这可以用在当一个复杂的索引类型双关

type MyRec: record {
    a: count &optional;
    b: count;
};

type MySet : set[MyRec];

global s4 = MySet([$b=1],[$b=2]);

元素成员可以通过 “in” 和 “!in” 来判断

if (21/tcp in s)
    ...
if ([21/tcp,"ftp"] !in s2)
    ...

通过关键字 add来添加set成员

add s[22/tcp];

如果成员已经存在那么什么也不会发生
通过关键字 delete 来删除一个set成员

delete s[21/tcp];

如果成员不存在,什么也不会发生;

通过取绝对值的操作,可以获得元素的个数

|s|

这个操作可以用于for 语句中


vector

同样的类似于table,但始终是通过count类型来进行索引的且始终是从零开始索引的,声明语句如下

global v: vector of string;

可以通过vector来行径构造

local v = vector("one","two","three");

vector 结构也可以被type命名为一个类型,这可以用在当一个复杂的索引类型双关

type MyRec : record {
    a:count &optional;
    b:count;
};

type MyVec :vector of MyRec;

global v2 = myVec([$b=1],[$b=2],[$b=3]);

通过索引来和”[ ]”来获取元素

print v[2];

通过索引来直接赋值

v[3] = "four";

通过取绝对值的操作,可以获得元素的个数

|v|

如果vectors的数据是整数(int 或 count),则支持直接进行“++” , “–”才操作;
如果是数字型(int ,count ,double),则可以支持“+”,“-”,”*”,”/”,“%”的操作
但是这些操作需要有相同的元素个数
如果是布尔型(bool),则可以直接进行“&&”,“||”的操作


reocrd

record类型是数据的集合,所有的数据都具有名称和值两个域,他们不需要具有相同的数据类型,类型也没有限制(有点像结构体),例:

type MyRecordType:record {
    c:count;
    s:string &optional;
};

records 类型可以通过三种方式进行初始化或者赋值,所有没有 &optinal 或者 &default属性的元素必须被明确赋值,例:

local r:MyRecordType = record($c = 7);

因为record类型一般通过type定义,所以可以使用更可读的方式进行声明和赋值

local r = MyRecordType($c = 42);

第三种方式像这样:

local r:MyRecordType = [$c = 13,$s="thirteen"];

获取元素域通过 “$” 符号来操作,如下

local r: MyRecordType;
r$c = 12;

测试具有 “&optional “属性的域是否被赋值,可以通过?$ 操作符返回一个bool类型来进行判断(T:有,F:没有)

if (r ?$ s)
    ...

function

在bro 中,function 类型通过如下方法声明:

function(argument *): type

参数(argument )可以为空或者是通过逗号来分割,返回值(type) 是可选的,例子:

global greeting :function(name: string):string;

这里 greeting 是函数的标识,函数体还未定义,函数的函数体甚至可以在不同的时间有不同的函数体,定义一个包含函数体的函数:

function greeting(name: string):string
    {
    return "Heloow," + name;
    }

通过这种方式定义函数,就不需要像上面那样对函数进行声明,如果要声明的话,那函数类型返回值,参数列表必须完全一致

定义一个没有参数没有返回值的函数:

function my_func()
    {
    print "my_func";
    }

赋值一个匿名函数

greeting = function(name:string):string {return "Hi, "+ name};

调用函数:

print greeting("Dave");

默认参数的参数放在参数列表的最后:

global foo:function(s: string,t :string &default="abc",u: count &default =0);

如果一个函数之前被声明为使用默认参数,当定义函数体的时候这些默认参数可以不写,在调用的时候任然有效

function foo(s: string,t:string.u:count)
    {
    print s,t,u;
    }

调用该函数可以省略从参数列表中的默认值:

 foo("test");

event

Event 处理器的语法和 function 很接近,不同的两点是event没有返回值且你不能调用他,例:

event my_event(r:bool,s:string)
{
    print "my_event",r,s;   
}

event handler 不能从脚本中被直接调用,当event 被一下三种方法触发将会执行event handler的函数体:
* 来自event engine
当event engine 检测到一个你定义的evnet handler 相关的event时,将这个事件放入event的处理队列,一旦event engine 处理完当前的数据包,将会调用队列里的event handler
* 来自脚本中关键字event
立即触发一个event handler :

    event password_exposed(user,password);

假设 password_exposed 之前已经被声明且定义的event handler 具有相同类型的参数
* 脚本中通过 schedule 来传递
将会延时触发 event handler直到未来的某个时间,例:

    schedule 5 secs {passwod_exposed(user,password)};

不同的event handler 处理体可以用同一个envent 标识,每个evnet handler 实体将会顺序执行,通过关键字 &priority 可以对event handler 进行排序


hook

Hook 是另一种具备 function 和 evnet 特点的 “function”
具有相同标识符的 hook 可以通过&priority 属性来按照优先级执行,调用的时候他们更像是function,因为和envent不同,他们不能通过scheduled 放入event队列而是被立即执行,同时,hook的一个独有特点是hook的handler 函数体可以通过”break ” 关键字让剩余的处理程序的语句中断,(return 是在函数体结束的时候退出)

hook的声明方式

hook (argument *)

argument 可以为空或者是逗号分割的列表,例子

global myhook: hook(s:string)

这里只是定义了hook的标识,没有handler body,我们可以定义多个handler bodies:

hook myhook(s:string) &porority=10
    {
    print "porority 10 myhook handler",s;
    s = "bye";
    }
hook myhook(s:string)
    {
    print "break out of myhook handling ",s;
    }
hook myhook(s: string) &porority=-5
    {
    print "not going to happend",s;
    }

要立即执行 hook 处理,调用方式和function 类似,只不过要通过hook 关键字来调用:

 hook myhook("hi");

或者

if(hook myhook("hi"))
    print "all handlers ran";

输出如下:

priority 10 myhook handler,hi
break out of myhook handling ,bye

注意到参数可以被先执行的hook 修改

hook 执行的返回值是 bool型的(T or F),T 说明所有的hook都被执行了,F指示只有部分hook被执行了,一般F是break 语句执行的结果


file

Bro 支持写文件,但是不支持读文件(读文件参考 Input Framework),通过内建函数open 或者 open_for_append来打开文件,通过close 来关闭文件。声明,打开,写文件,关闭的操作示例:

local f = open("myfile");
print f, "hello world";
close(f);

通过这种方式写入日志是不推荐的,更好的日志支持参考 Logging Framework


opaque

被故意隐藏的数据类型,但是这些数据可以被传递给一些内建的函数,这些数据是内部的/隐藏的资源,通过一些修饰词来对这些不透明的数据类型进行区分,比如 “opaque of md5”,”opaque of sha1”,
内部hash函数的 类型 示例:

local handle = md5_hash_init();
md5_hash_update(handle,"test");
md5_hash_update(handle,"testing");
print md5_hash_finish(handle);

这里展示了opaque类型用于md5的计算,但是这个数据类型的细节我们不清楚,也不需要了解


any

用来绕过强类型,比如一个函数(function)可以通过定义参数类型为any 当这个参数可以为多种类型的时候,这种数据类型只支持赋值操作,
注意用户不被期望使用这种类型,这种类型主要是用于内建函数的


void

一种内部的bro 类型(它不是Bro脚本的关键字),代表没有返回类型的函数(function).

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值