Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua 是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组于 1993 年开发的,该小组成员有:Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo。
其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
可扩展: Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。
其它特性:
window下你可以使用一个叫"SciTE"的IDE环境来执行lua程序,下载地址为:
Github 下载地址:https://github.com/rjpcomputing/luaforwindows/releases
Google Code下载地址 : https://code.google.com/p/luaforwindows/downloads/list
双击安装后即可在该环境下编写 Lua 程序并运行。
你也可以使用 Lua 官方推荐的方法使用 LuaDist:http://luadist.org/
Lua 提供了交互式编程模式。我们可以在命令行中输入程序并立即查看效果。
Lua 交互式编程模式可以通过命令 lua -i 或 lua 来启用:
$ lua -i
$ Lua 5.3.0 Copyright (C) 1994-2015 Lua.org, PUC-Rio
>
在命令行中,输入以下命令:
> print("Hello World!")
接着我们按下回车键,输出结果如下:
\> print("Hello World!")
Hello World!
\>
我们可以将 Lua 程序代码保存到一个以 lua 结尾的文件,并执行,该模式称为脚本式编程,如我们将如下代码存储在名为 hello.lua 的脚本文件中:
print("Hello World!")
print("I'm orange")
使用 lua 名执行以上脚本,输出结果为:
$ lua hello.lua
Hello World!
I'm orange
我们也可以将代码修改为如下形式来执行脚本(在开头添加:#!/usr/local/bin/lua
):
#!/usr/local/bin/lua
print("Hello World!")
print("I'm orange")
以上代码中,我们指定了 Lua 的解释器/usr/local/bin directory
。加上 # 号标记解释器会忽略它。接下来我们为脚本添加可执行权限,并执行:
.\hello.lua
Hello World!
I'm orange
两个减号是单行注释:
--
--[[
多行注释
多行注释
--]]
Lua 标示符用于定义一个变量,函数获取其他用户定义的项。标示符以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头后加上 0 个或多个字母,下划线,数字(0 到 9)。
最好不要使用下划线加大写字母的标示符,因为Lua的保留字也是这样的。
Lua 不允许使用特殊字符如 @, $, 和 % 来定义标示符。 Lua 是一个区分大小写的编程语言。因此在 Lua 中 Runoob 与 runoob 是两个不同的标示符。
以下列出了 Lua 的保留关键词。保留关键字不能作为常量或变量或其他用户自定义标示符:
and | break | do | else |
---|---|---|---|
elseif | end | false | for |
function | if | in | local |
nil | not | or | repeat |
return | then | true | until |
while | goto |
一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。
在默认情况下,变量总是认为是全局的。
全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是:nil。
\> print(b)
nil
\> b=10
\> print(b)
10
\>
如果你想删除一个全局变量,只需要将变量赋值为nil。
b = nil
print(b) --> nil
这样变量b就好像从没被使用过一样。换句话说, 当且仅当一个变量不等于nil时,这个变量即存在。
常用算数运算符:设A的值为10,B的值为21:
操作符 | 描述 | 实例 |
---|---|---|
+ | 加法 | A + B 输出结果 31 |
- | 减法 | A - B 输出结果 -11 |
* | 乘法 | A * B 输出结果 210 |
/ | 除法 | B / A 输出结果 2.1 |
% | 取余 | B % A 输出结果 1 |
^ | 乘幂 | A^2 输出结果 100 |
- | 负号 | -A 输出结果 -10 |
关系运算符,设A的值为10,B的值为21:
操作符 | 描述 | 实例 |
---|---|---|
== | 等于,检测两个值是否相等,相等返回 true,否则返回 false | (A == B) 为 false。 |
~= | 不等于,检测两个值是否相等,不相等返回 true,否则返回 false | (A ~= B) 为 true。 |
> | 大于,如果左边的值大于右边的值,返回 true,否则返回 false | (A > B) 为 false。 |
< | 小于,如果左边的值大于右边的值,返回 false,否则返回 true | (A < B) 为 true。 |
>= | 大于等于,如果左边的值大于等于右边的值,返回 true,否则返回 false | (A >= B) 返回 false。 |
<= | 小于等于, 如果左边的值小于等于右边的值,返回 true,否则返回 false | (A <= B) 返回 true。 |
逻辑运算符,设A 的值为 true,B 的值为 false
操作符 | 描述 | 实例 |
---|---|---|
and | 逻辑与操作符。 若 A 为 false,则返回 A,否则返回 B。 | (A and B) 为 false。 |
or | 逻辑或操作符。 若 A 为 true,则返回 A,否则返回 B。 | (A or B) 为 true。 |
not | 逻辑非操作符。与逻辑运算结果相反,如果条件为 true,逻辑非为 false。 | not(A and B) 为 true。 |
其他运算符:
操作符 | 描述 | 实例 |
---|---|---|
… | 连接两个字符串 | a…b ,其中 a 为 "Hello " , b 为 “World”, 输出结果为 “Hello World”。 |
# | 一元运算符,返回字符串或表的长度。 | #“Hello” 返回 5 |
Lua 编程语言函数定义格式如下:
optional_function_scope function function_name( argument1, argument2,......argumentn)
function_body
return result_params_comma_separated
end
local
。以下实例定义了函数 max(),参数为 num1, num2,用于比较两值的大小,并返回最大值:
--[[ 函数返回两个值的最大值 --]]
function max(num1, num2)
if (num1 > num2) then
result = num1;
else
result = num2;
end
return result;
end
-- 调用函数
print("两值比较最大值为 ",max(10,4))
print("两值比较最大值为 ",max(5,6))
以上代码执行结果为:
两值比较最大值为 10
两值比较最大值为 6
表示一个新的Protocol,在Wireshark中Protocol对象有很多用处,解析器是其中主要的一个。主要接口有:
接口 | 说明 |
---|---|
proto:__call (name,desc) | 创建Proto对象。name和desc分别是对象的名称和描述,前者可用于过滤器等 |
proto.name | get名称 |
proto.fields | get/set字段 |
proto.prefs | get配置项 |
proto.init | 初始化,无参数 |
proto.dissector | 解析函数,3个参数tvb,pinfo,tree,分别是报文内容,报文信息和解析树结构 |
proto:register_heuristic (listname, func) | 为Proto注册一个启发式解析器,被调用时,参数func将被传入与dissector方法相同的3个参数 |
Proto举例:
-- create a new protocol
local proto_name = "PNASKCP"
local proto_desc = "Private NAS Protocol (KCP)"
local proto_obj = Proto(proto_name, proto_desc)
local proto_port = 5067
--register this dissector add Packet Details
DissectorTable.get("udp.port"):add(proto_port, proto_obj)
表示协议字段,一般用于解析字段后往解析树上添加节点。根据字段类型不同,其接口可以分为两大类。
这些接口都会返回一个新的字段对象。方括号内是可选字段,花括号内是可替换的类型字段。
ProtoField.{type}(abbr, [name], [base], [valuestring], [mask], [desc])
·type包括:uint8, uint16, uint24, uint32, uint64, framenum, float, double, string, stringz, bytes, bool, ipv4, ipv6, ether,oid, guid
abbr
字段的缩写名称(过滤器中使用的字符串)。
name (optional)
字段的实际名称(出现在树中的字符串)。
base (optional)
base.DEC
,base.HEX
或base.OCT
,base.DEC_HEX
,base.HEX_DEC
,base.UNIT_STRING
或base.RANGE_STRING
。
valuestring (optional)
包含与值对应的文本的表,或包含与值 ({min, max, “string”}) 对应的范围字符串值表的表(如果基数为 )base.RANGE_STRING
,或包含单位名称的表如果 base 是base.UNIT_STRING
.
mask (optional)
此字段的整数掩码。
desc (optional)
字段说明。
举例:
fields.tlv_value_int16 = ProtoField.uint16(proto_name .. ".tlv_value_int", "PNAS TLV VALUE", base.HEX)
fields.uri_ipv4 = ProtoField.ipv4(proto_name .. ".uri_ipv4", "IP ADDRESS")
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2Zg7qC6t-1644300257537)(C:\Users\210744417\AppData\Roaming\Typora\typora-user-images\image-20211201094858126.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rx3VEfm7-1644300257538)(C:\Users\210744417\AppData\Roaming\Typora\typora-user-images\image-20211201094827857.png)]
Tvb(Testy Virtual Buffer)表示报文缓存,也就是实际的报文数据,可以通过下面介绍的TvbRange从报文数据中解出信息。主要接口有:
接口 | 说明 |
---|---|
tvb:__tostring() | 将报文数据转化为字符串,可用于调试 |
tvb:reported_len() | get tvb的(not captured)长度 |
tvb:len() | get tvb的(captured)长度 |
tvb:reported_length_remaining() | 获取当前tvb的剩余长度,如果偏移值大于报文长度,则返回-1 |
tvb:offset() | 返回原始偏移 |
-- tvb(offset, 4)表示从offset开始之后的4个字节
subtree:add_le(fields.peer_ipaddr, tvb(offset, 4))
报文信息(packet information)。主要接口有:
接口 | 说明 |
---|---|
pinfo.len pinfo.caplen | get报文长度 |
pinfo.abs_ts | get报文捕获时间 |
pinfo.number | get报文编号 |
pinfo.src pinfo.dst | get/set报文的源地址、目的地址 |
pinfo.columns pinfo.cols | get报文列表列(界面) |
-- show protocol name
pinfo.cols.protocol = "PNASKCP"
-- show in info
pinfo.cols.info:set("Private NAS Protocol (CALL_REQUEST)")
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-teVeGuBU-1644300257539)(C:\Users\210744417\AppData\Roaming\Typora\typora-user-images\image-20211201100432245.png)]
表示报文解析树中的一个树节点。主要接口有:
接口 | 说明 |
---|---|
treeitem:add([protofield], [tvbrange], [value], [label]) | 向当前树节点添加一个子节点 |
treeitem:set_text(text) | 设置当前树节点的文本 |
treeitem:prepend_text(text) | 在当前树节点文本的前面加上text |
treeitem:append_text(text) | 在当前树节点文本的后面加上text |
还有注意一下网络字节序的问题,如果是网络字节序需要用add_le
添加节点~
添加节点举例
-- kcp header
local subtree = tree:add(proto_obj, tvb(offset, lmmh_len), "KCP")
--conv
subtree:add_le(fields.conv, tvb(offset, 4))
offset = offset + 4
--cmd
subtree:add_le(fields.cmd, tvb(offset, 1))
offset = offset + 1
--frg
subtree:add_le(fields.frg, tvb(offset, 1))
offset = offset + 1
--wnd
subtree:add_le(fields.wnd, tvb(offset, 2))
offset = offset + 2
--ts
subtree:add_le(fields.ts, tvb(offset, 4))
offset = offset + 4
--sn
subtree:add_le(fields.sn, tvb(offset, 4))
offset = offset + 4
--una
subtree:add_le(fields.una, tvb(offset, 4))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fg6xLCrJ-1644300257539)(C:\Users\210744417\AppData\Roaming\Typora\typora-user-images\image-20211201101225673.png)]
wireshark官方文档:https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Proto.html
应用程序发送的数据报都是流式的,当一次发送的数据量很大时,数据包会进行截断分包发送,因此对于使用自制dissector的时候需要考虑这种情况,对分包的TCP进行合并解析。
Lua Dissector相关资料可以见:http://wiki.wireshark.org/Lua/Dissectors
使用Lua合并tcp数据报进行分析的样例如下
local Agreement = Proto("Agreement", "Agreement")
function Agreement.dissector(tvb, pinfo, tree)
--设置指针起始位置
local offset = pinfo.desegment_offset or 0
local massagelen = get_len()
while true do
local nxtpdu = offset + massagelen
if nxtpdu > tvb:len() then
--如果协议数据长度大于该包的长度,说明被分包了,记录被截断包的数据当前的位置指针
pinfo.desegment_len = nxtpdu - tvb:len()
pinfo.desegment_offset = offset
return
end
tree:add(Agreement, tvb(offset, massagelen))
offset = nxtpdu
if nxtpdu == tvb:len() then
return
end
end
end
local tcp_table = DissectorTable.get("tcp.port")
tcp_table:add(5060, Agreement)