文章归档

置顶文章

Web安全

Web安全基础

PHP相关

Writeups

靶机系列

HackTheBox

VulnHub

代码审计

PHP代码审计

流量分析

机器学习

基础学习

Python

Python编程

Java

Java编程

算法

Leetcode

随笔

经验

技术

 2019-09-23   3k

Bro语法学习之基础知识

最近一个项目使用到了Bro和Argus这两个开源的流量分析工具,前者现在改名为Zeek,后者在2015年停止了维护。借此机会简单的介绍一下Bro脚本的基本语法,后面有时间的话会再写一本关于通过编写脚本自定义日志文件的文章。

http://try.zeek.org/

Hello World

Zeek是基于事件(event)驱动的,也就说,你可以通过事件触发来控制任何要执行的动作。这里以bro_init()bro_done()这两个事件举例:

1
2
3
4
5
6
7
8
9
event bro_init()
{
print "Hello, World";
}

event bro_done()
{
print "Goodbye, World";
}

bro_init()bro_done()都是基本事件,当Zeek开始运行时会执行bro_init()事件,当Zeek停止运行时会执行bro_done()事件。

这里还有更多的Zeek基本事件。

加载脚本

像其他的编程语言一样,Zeek也可以通过@load关键字来加载其他脚本中的代码。即:

1
@load misc/dump-events

misc/dump-events这个脚本将Zeek产生的事件以可读的形式展现出来,通常用于调试以及理解事件及其参数。部分输出如下图所示:

函数

首先来看一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
# Function implementation
function emphasize(s: string, p: string &default = '*'): string
{
return p + s + p;
}

envent bro_init()
{
# Function calls
print emphasize("yes");
print emphasize("no", "_");
}

输出:

1
2
*yes*
_no_

有其他编程经验的同学看这个例子应该比较好理解。这里简单说一下emphasize()函数,它接收两个String类型的参数,其中第二个参数有一个默认值*(用&default关键字修饰),即可选参数,并规定了返回值类型是String

另外一点,Zeek可以直接使用+来拼接字符串。

Another side note on the relation between functions and events: Events are a kind of function as well, and both can only be declared at the global level; one cannot nest them.

变量

关于变量的作用域,Zeek提供了两种关键字来修饰变量:localglobal。具体示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
global x = 'Hello';

event bro_init()
{
print x;

const y = "Guten Tag";
# Changing value of 'y' is not allowed.
#y = "Nope";

local z = "What does that mean?";
print z;
}

event bro_done()
{
x = "Bye";
print x;
}

输出:

1
2
3
Hello
What does that mean?
Bye

可以看到,用local修饰的变量的作用域严格限制在函数体内,global类似于全局变量的效果,如果在函数内部定义了一个与全局变量相同的局部变量,那么局部变量会覆盖全局变量。

另外,用const修饰的变量相当于常数,是不可更改的。

原始数据类型

对于变量的数据类型,Zeek和C、Java等语言相似,即一个变量保存的数据类型是固定的,并且它可以从你的赋值来推断该变量的数据类型,不用显示地声明,也就说local x = 0local x: count = 0是等价的。

这里列举了Zeek支持的所有数据类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
event bro_init()
{
local x: string = "two";
local y: int = 10000000000000000000000000000000000000000000000000;
print "y is a large int:", y;
print "x is a short string:", x;

#pattern matching
print /one|two|three/ == "two";
print /one|two|three/ == "ones";
print /one|two|three/ in ones;
print /[123].*/ == "2 two";
print /[123].*/ == "4 four";
}

输出:

1
2
3
4
5
6
7
y is a large int:, -1
x is a short string:, two
T
F
T
T
F

上面例子的第二部分给我们演示了一种模式匹配(看起来有点像正则表达式),两个/符号中是待匹配的式子,不同元素用|符号隔开。==是精确匹配,in是模糊匹配,[123].*匹配任意以123其中某一个数字开头的字符串。

操作符

数学操作符

Name Syntax Example Usage
a + b print 2 + 2; # 4
a - b print 2 - 2; # 0
a * b print 4 * 4; # 16
a / b print 15 / 3; # 5
取余 a % b print 18 % 15; # 3
一元加号 +a local a = +1; # Force use of a signed integer
一元减号 -a local a = 5; print -a; # -5
自增 ++a local a = 1; print ++a, a; # 2, 2
自减 --a local a = 2; print --a, a; # 1, 1

赋值操作符

Name Synatx Example Usage
=赋值 a=b local a = 7;
+赋值 a += b local a = 7; a += 2; # 9
-赋值 a -= b local a = 7; a -= 2; # 5

关系操作符

Name Syntax Example Usage
Equality a == b print 2 == 2; # T
不等 a != b print 2 != 2; # F
小于 a < b print 2 < 3; # T
小于等于 a <= b print 2 <= 2; # T
大于 a > b print 2 > 3; # F
大于等于 a >= b print 2 >= 2; # T

其他操作符

Name Syntax Example Usage
存在于 a in b print "z" in "test"; # F
不存在于 a !in b print "z" !in "test"; # T
大小/长度 |a| print |"test"|; # 4
绝对值 |a| print |-5|; # 5
索引取值 a[i] print "test"[2]; # s
切片 a[i:j], a[i:], a[:j] print "testing"[2:4]; # st

If语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
event bro_init()
{
local x = "3";

for (c in "12345")
{
if (c == x)
{
print "Found it!";
print fmt("And by 'it', I mean %s.", x);
}
else
{
print "I'm looking for", x, "not", c;
}
}
}

输出:

1
2
3
4
5
6
7
I'm looking for, 3, not, 1
I'm looking for, 3, not, 2
Found it.
And by 'it', I mean 3.
I'm looking for, 3, not, 4
I'm looking for, 3, not, 5

Zeek的if语句和其他语言类似,有if...else...语句块,也有if...else if...else...语句块。

这里用到了fmt()函数用于格式化输出,语法与Python类似。它也可以使用,连接字符串和变量之间。

foreach语句

见if语句章节。

这里需要注意的是,除了vector类型外,其他的所有collection类型的数据类型都不能保证循环的顺序。 If the order is important the collection should be a vector.

While语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
event bro_init()
{
local i = 0;

while (i < 5)
print ++i;

while (i % 2 != 0)
{
local finish_up = F;

if (finish_up == F)
print "nope";
++i;
next;

if (finish_up)
break;
}
print i;
}

这里的breaknext类似与其他语言中的breakcontinue

另外,上面这段代码是官方给的示例,Zeek语法的缩进反正我最先看是看的是一愣一愣的,后来才发现Zeek是既支持缩进,也支持{},这两种都认。

练习一

  1. 编写一个程序,从您选择的任意字符串中删除所有的字母“e”(可以不就地处理)。
  2. 编写一个程序,打印从1到100,如果是3的倍数,打印“Fizz”;如果是5的倍数,打印“Buzz”。如果既是3的倍数也是5的倍数打印FizzBuzz
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
event bro_init()
{
local result = "";

for (c in "testing")
{
if (c != "e")
{
result += c;
}
}
print result;
}

# 使用递归
function fizzbuzz(i: count)
{
local s = "";

if (i % 3 == 0)
s += "Fizz";
if (i % 5 == 0)
s += "Buzz";
if (s == "")
print i;
else
print s;

if (i < 100)
fizzbuzz(i + 1);
}

event bro_done()
{
fizzbuzz(1);
}

Switch表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
event bro_init()
{
local x = 4;

switch (x)
{
case 0:
print "case 0";
break;
case 1, 2, 3:
print "case 1, 2, 3";
break;
case 4:
print "case 4 and ...";
fallthrouth;
case 5:
# This block may execute if x is 4 or 5.
print "case 5";
default:
print "default case";
break;
}
}

fallthrouth关键字会让程序忽略限制条件而接着执行下一个case。

练习:

使用Switch语句编写一个程序,计算任意字符串中的元音(a,e,i,o,u)的数量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
event bro_init()
{
local result = 0;
local input = "The Zeek Network Security Monitor";
for (c in input)
{
switch (c)
{
case "a", "e", "i", "o", "u":
++result;
break;
}
}
print result;
}

事件

Event算是Zeek里面比较核心的概念,可以算是一种特殊形式的函数,但与函数又不同的是:

  • 事件并不想函数一样会立刻执行,它们可能会在以后的时间进行调度和执行,因此它们的效果在调用之后可能无法直接实现;
  • 它们没有返回值,因为它们没有被直接调用而是被安排在以后执行;
  • 可以为同一事件定义多个主体,每个主体都被视为“事件处理程序”。 当需要执行一个事件时,该事件的所有处理程序主体均按优先级顺序执行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
global myevent: event(s: string);

global n = 1;

event myevent(s: string) &priority = -10
{
n = n + 2;
}

event myevent(s: string) &priority = 5
{
--n;
}

event myevent(s: string) &priority = 10
{
print "myevent", s, n;
}

event bro_init()
{
print "bro_init()";
event myevent("hi");
event myevent("wait");
schedule 5 sec { myevent("bye") };
}

event bro_done()
{
print "bro_done()";
}

输出:

1
2
3
4
5
6
bro_init()
myevent, hi, 1
myevent, wait, 2
myevent, bye, 3
bro_done()

我把官方的例子修改了一点,感觉这样更能体现Event的特点。

bro_init()中每执行一次my_event都会把该事件的三个主体按照@priority的顺序分别执行一次。其中前两次会以迅速执行my_event,而第三次会5s后或者Zeek执行结束之前执行。

需要注意的是:

  • 如果没有显示指定,@priority参数会默认为0;
  • 如果@priority都相等,则按顺序执行。

Hook

Hook是另外一种形式的函数,与Event类似,但是又有两点不太一样:

  • 它被调用时会立刻执行;
  • 如果执行到主体的末尾或返回语句,则将执行Hook处理程序。 但是,如果Hook的主体以break语句终止,则不会执行其余的Hook处理程序。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
global myhook: hook(s: string)

hook myhook(s: string) @priority = 10
{
print "priority 10 myhook handler", s;
s = "bye";
}

hook myhook(s: string)
{
print "break out of myhook handling", s;
break;
}

hook myhook(s: string) &priority = -5
{
print "not going to happen", s;
}

event bro_init()
{
local ret:bool = hook myhook("hi");
if ( ret )
{
print "all handlers ran";
}
}

1
2
3
priority 10 myhook handler, hi
break out of myhook handling, hi

Composite-types: Set 集合

set:集合类型,其中元素唯一,可以使用adddelete函数对元素进行增删操作,以及in操作符判断是否存在。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
event bro_init()
{
local x: set[string] = {"one", "two", "three"};
add x["four"];
print "four" in x;
delete x["two"];
print "two" != x;
add x["one"];

for (e in x)
{
print e;
}
}

输出:

1
2
3
4
5
6
T
T
four
three
one

Composite-types: Table 映射

Zeek中的table类型类似于dictMap,索引是唯一的,可以使用delete进行删除操作,可以直接赋值操作来新增元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
event bro_init()
{
local x: table[count] of string = { [1] = "one",
[3] = "three",
[5] = "five"};
x[7] = "seven";
print 7 in x;
delete x[3];
print 3 != x;
x[1] = "1"; # changed the value at index 1

for (key in x)
{
print key;
}
}

输出:

1
2
3
4
5
6
7
T
seven
T
1
7
5

需要注意的是,table数据类型定义的方法table [count] of string

Composite-types: Vector 向量

Vector类似于arrarylist,允许有重复的元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
event bro_init()
{
local x: vector of string = {"one", "two", "three"};
print x;
print x[1];
print |x|;
x[|x|] = "one";
print x;

for (i in x)
{
print i;
}
}

输出:

1
2
3
4
5
6
7
8
9
[one, two, three]
two
3
[one, two, three, one]
0
1
2
3

vector of string指明vector中元素类型。

Composite-types: Record 记录

Record记录类型,类似于C语言中的结构体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
type MyRecord: record {
a: string;
b: count;
c: bool &default = T;
d: int &optional;
};

event bro_init()
{
local x = MyRecord($a = "vvvvvv", $b = 6, $c = F, $d = -13);
if (x?$d)
print x$d;
}
print x;

x = MyRecord($a = "abc", $b = 3);
print x;
print x$c;
print x?$d;

输出:

1
2
3
4
5
6
-13
[a=vvvvvv, b=6, c=F, d=-13]
[a=abc, b=3, c=T, d=<uninitialized>]
T
F

$操作符通常类似于.操作符,x?$d用于判断x中是否存在字段d

Zeek特有的数据类型

  • time:时间戳
  • interval:时间相对单位,包括usec, msec, sec, min, hr, 和day
  • port:端口
  • addr:IP地址
  • subnet:子网
Copyright © ca01h 2019-2020 | 本站总访问量