在这篇文章中,我们将首先介绍Lua解剖器的基本调试。之后,我们将扩展解剖器以显示操作码的名称,而不仅仅是数字。
调试
当我在谈论调试时,我并没有真正考虑通常的调试方法——使用符号调试器来逐步执行代码。你不会在这里这样做。我正在考虑在代码中查找和修复错误的过程。调试用Lua编写的解剖器通常有三种方法。
第一个是检查在脚本启动期间是否收到任何错误消息。当您启动Wireshark或使用Ctrl + Shift + L重新加载脚本时,会发生这种情况。脚本中的语法错误将以这种方式捕获。以下是缺少end语句时的错误消息:

运行时错误通常显示在解剖器的子树中。例如,如果使用错误的名称调用函数是错误的,则错误消息将如下所示。

最后,Wireshark内置了一个Lua控制台,您可以打印错误消息。它位于Tools→Lua菜单中。 Wireshark有一个名为
print()的函数,可用于日志记录。类似下面的代码:
print("buffer length: " .. length)
打印到控制台时最终会看起来像这样:

..用于字符串连接。在早于3.0的Wireshark版本中,有不同的函数用于日志。它们是:critical(“message”),warn(“message”),message(“message”),info(“message”)和debug(“message”)。它们都打印到控制台,不同之处在于感知的严重程度。以下代码:
critical("buffer length: " .. length)
warn("buffer length: " .. length)
message("buffer length: " .. length)
info("buffer length: " .. length)
debug("buffer length: " .. length)
打印到控制台时最终会看起来像这样:

您可以通过在文件开头要求它来访问debug library
local d = require('debug')
然后通过调用它的函数开始使用它。例如:
print(d.traceback())
这就是我们可用于调试的内容。在处理Lua解剖器时,不要指望任何带有内置调试器的花哨IDE。如果你想,你可以尝试让ZeroBrane Studio工作,但我还没有想出如何轻松地做到这一点,所以我将自己调试printf。
扩展MongoDB协议解析器
在上一篇文章中,我们制作了一个解剖器,最终在数据包详细信息窗格中显示如下:

这里的操作码只是一个数字。如果我们也显示操作码名称会更好。根据MongoDB有线协议,操作码具有以下名称:

要将操作码作为整数抓取,我们可以使用
local opcode = buffer(12,4):le_int()
le_int()从缓冲区范围获取一个小端int。变量操作码现在包含一个表示十进制操作码的int。然后我们可以创建一个函数,在给定操作码编号的情况下返回操作码名称:
function get_opcode_name(opcode)
local opcode_name = "Unknown"
if opcode == 1 then opcode_name = "OP_REPLY"
elseif opcode == 2001 then opcode_name = "OP_UPDATE"
elseif opcode == 2002 then opcode_name = "OP_INSERT"
elseif opcode == 2003 then opcode_name = "RESERVED"
elseif opcode == 2004 then opcode_name = "OP_QUERY"
elseif opcode == 2005 then opcode_name = "OP_GET_MORE"
elseif opcode == 2006 then opcode_name = "OP_DELETE"
elseif opcode == 2007 then opcode_name = "OP_KILL_CURSORS"
elseif opcode == 2010 then opcode_name = "OP_COMMAND"
elseif opcode == 2011 then opcode_name = "OP_COMMANDREPLY" end
return opcode_name
end
最后,我们必须使用以下代码替换子树的旧添加:
local opcode_name = get_opcode_name(opcode)
subtree:add_le(opcode, buffer(12,4)):append_text(" (" .. opcode_name .. ")")
我们将括号中的操作码名称附加到仅显示操作码编号的原始语句中。 Wireshark中的数据包详细信息窗格最终将如下所示:

最终代码为:
mongodb_protocol = Proto("MongoDB", "MongoDB Protocol")
message_length = ProtoField.int32("mongodb.message_length", "messageLength", base.DEC)
request_id = ProtoField.int32("mongodb.requestid" , "requestID" , base.DEC)
response_to = ProtoField.int32("mongodb.responseto" , "responseTo" , base.DEC)
opcode = ProtoField.int32("mongodb.opcode" , "opCode" , base.DEC)
mongodb_protocol.fields = { message_length, request_id, response_to, opcode }
function mongodb_protocol.dissector(buffer, pinfo, tree)
length = buffer:len()
if length == 0 then return end
pinfo.cols.protocol = mongodb_protocol.name
local subtree = tree:add(mongodb_protocol, buffer(), "MongoDB Protocol Data")
subtree:add_le(message_length, buffer(0,4))
subtree:add_le(request_id, buffer(4,4))
subtree:add_le(response_to, buffer(8,4))
local opcode = buffer(12,4):le_uint()
local opcode_name = get_opcode_name(opcode)
subtree:add_le(opcode, buffer(12,4)):append_text(" (" .. opcode_name .. ")")
end
function get_opcode_name(opcode)
local opcode_name = "Unknown"
if opcode == 1 then opcode_name = "OP_REPLY"
elseif opcode == 2001 then opcode_name = "OP_UPDATE"
elseif opcode == 2002 then opcode_name = "OP_INSERT"
elseif opcode == 2003 then opcode_name = "RESERVED"
elseif opcode == 2004 then opcode_name = "OP_QUERY"
elseif opcode == 2005 then opcode_name = "OP_GET_MORE"
elseif opcode == 2006 then opcode_name = "OP_DELETE"
elseif opcode == 2007 then opcode_name = "OP_KILL_CURSORS"
elseif opcode == 2010 then opcode_name = "OP_COMMAND"
elseif opcode == 2011 then opcode_name = "OP_COMMANDREPLY" end
return opcode_name
end
local tcp_port = DissectorTable.get("tcp.port")
tcp_port:add(59274, mongodb_protocol)
现在,整个消息头已经解析完毕。下一部分将介绍如何解码特定消息。