lua入门及wireshark自定义协议lua解码

介绍

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

Lua 是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组于 1993 年开发的,该小组成员有:Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo。

其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

Lua 特性

  • 轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。

  • 可扩展: Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。

  • 其它特性:

    • 支持面向过程(procedure-oriented)编程和函数式编程(functional programming);
    • 自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;
    • 语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;
    • 通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。

lua环境搭建

Window 系统上安装 Lua

window下你可以使用一个叫"SciTE"的IDE环境来执行lua程序,下载地址为:

  • Github 下载地址:https://github.com/rjpcomputing/luaforwindows/releases

    img

  • Google Code下载地址 : https://code.google.com/p/luaforwindows/downloads/list

双击安装后即可在该环境下编写 Lua 程序并运行。

你也可以使用 Lua 官方推荐的方法使用 LuaDist:http://luadist.org/

Lua 基本语法

第一个 Lua 程序

交互式编程

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 的保留关键词。保留关键字不能作为常量或变量或其他用户自定义标示符:

andbreakdoelse
elseifendfalsefor
functionifinlocal
nilnotorrepeat
returnthentrueuntil
whilegoto

一般约定,以下划线开头连接一串大写字母的名字(比如 _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
  • optional_function_scope: 该参数是可选的制定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字 local
  • function_name: 指定函数名称。
  • argument1, argument2, argument3…, argumentn: 函数参数,多个参数以逗号隔开,函数也可以不带参数。
  • function_body: 函数体,函数中需要执行的代码语句块。
  • result_params_comma_separated: 函数返回值,Lua语言函数可以返回多个值,每个值以逗号隔开。

实例

以下实例定义了函数 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

wireshark与lua

wireshark的lua API —— Proto

表示一个新的Protocol,在Wireshark中Protocol对象有很多用处,解析器是其中主要的一个。主要接口有:

接口说明
proto:__call (name,desc)创建Proto对象。name和desc分别是对象的名称和描述,前者可用于过滤器等
proto.nameget名称
proto.fieldsget/set字段
proto.prefsget配置项
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 = "PProtocol"
local proto_desc = "Private Protocol (PCP)"
local proto_obj = Proto(proto_name, proto_desc)
local proto_port = 8088

--register this dissector add Packet Details
DissectorTable.get("udp.port"):add(proto_port, proto_obj)
ProtoField

表示协议字段,一般用于解析字段后往解析树上添加节点。根据字段类型不同,其接口可以分为两大类。

这些接口都会返回一个新的字段对象。方括号内是可选字段,花括号内是可替换的类型字段。

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.DECbase.HEXbase.OCTbase.DEC_HEXbase.HEX_DECbase.UNIT_STRINGbase.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", "PProtocol 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

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))
Pinfo

报文信息(packet information)。主要接口有:

接口说明
pinfo.len pinfo.caplenget报文长度
pinfo.abs_tsget报文捕获时间
pinfo.numberget报文编号
pinfo.src pinfo.dstget/set报文的源地址、目的地址
pinfo.columns pinfo.colsget报文列表列(界面)
-- show protocol name
pinfo.cols.protocol = "PProtocol"
-- show in info
pinfo.cols.info:set("Private Protocol(HELLO)") 
TreeItem

表示报文解析树中的一个树节点。主要接口有:

接口说明
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), "PCP")	
--conv
subtree:add_le(fields.conv, tvb(offset, 4))
offset = offset + 4
--cmd
subtree:add_le(fields.cmd, tvb(offset, 1))
offset = offset + 1
--wnd
subtree:add_le(fields.wnd, tvb(offset, 2))
offset = offset + 2
--sn
subtree:add_le(fields.sn, tvb(offset, 4))
offset = offset + 4

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fg6xLCrJ-1644300257539)(C:\Users\210744417\AppData\Roaming\Typora\typora-user-images\image-20211201101225673.png)]

wireshark文档分享:

wireshark官方文档:https://www.wireshark.org/docs/wsdg_html_chunked/lua_module_Proto.html

对于TCP分包的合并分析

应用程序发送的数据报都是流式的,当一次发送的数据量很大时,数据包会进行截断分包发送,因此对于使用自制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(8088, Agreement)
  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OrangeLBlue

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值