在這里,我將解釋如何將代碼分成幾個(gè)模塊。在我的例子中,我將標(biāo)題和有效負(fù)載部分分成單獨(dú)的文件。
劃分代碼
我想為Header提供單獨(dú)的文件,OP_REPLY消息的文件,OP_QUERY消息的文件,幫助函數(shù)的文件和main文件。最終的文件結(jié)構(gòu)如下所示:

我創(chuàng)建一個(gè)名為mongodb的獨(dú)立文件夾。這將包含協(xié)議的所有模塊。主文件仍然直接存在于plugins中。
加載文件
在Lua中,我們通常有兩個(gè)可以加載文件的函數(shù):dofile和require。我將使用require,原因如下
Lua提供了一個(gè)更高級(jí)的函數(shù)來加載和運(yùn)行庫(kù),稱為require。粗略地說,require與dofile完成相同的工作,但有兩個(gè)重要的區(qū)別。首先,require在路徑中搜索文件;第二,require控制是否已運(yùn)行文件以避免重復(fù)工作。由于這些功能,require是Lua中用于加載庫(kù)的首選功能。
除了require我們必須使用package.prepend_path()。package.path是Wireshark查找文件的地方。 prepend_path將為package.path添加一個(gè)新路徑。在我的特定情況下,工作目錄是Wireshark根目錄,這意味著我必須將“plugins / mongodb”添加到package.path。如果您使用的是另一個(gè)操作系統(tǒng),那么您可能有另一條路徑,或者將文件放在另一個(gè)文件夾中(例如用戶插件目錄而不是全局插件目錄)。
要導(dǎo)入模塊,我們必須在主文件的開頭添加以下內(nèi)容:
package.prepend_path("plugins/mongodb")
local header = require("header")
如前所述,prepend_path()行將使Wireshark能夠在plugins / mongodb目錄中查找文件,而require行將導(dǎo)入header.lua中的代碼。不應(yīng)包含文件結(jié)尾。正如我們進(jìn)一步看到的那樣,我在header.lua中“導(dǎo)出”一個(gè)Table(對(duì)象),我們可以在主文件中使用點(diǎn)表示法:local var = header.myFunction()。
創(chuàng)建header.lua
我正在將一些與頭文件相關(guān)的代碼從主文件移到header.lua文件中:
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 m = {}
function m.parse(headerSubtree, buffer, message_length, request_id, response_to, opcode)
headerSubtree:add_le(message_length, buffer(0,4))
headerSubtree:add_le(request_id, buffer(4,4))
headerSubtree:add_le(response_to, buffer(8,4))
local opcode_number = buffer(12,4):le_uint()
local opcode_name = get_opcode_name(opcode_number)
headerSubtree:add_le(opcode, buffer(12,4)):append_text(" (" .. opcode_name .. ")")
return opcode_name
end
return m
我已經(jīng)將mongodb.lua中的get_opcode_name()移動(dòng)到header.lua。我還創(chuàng)建了一個(gè)名為m的Table(對(duì)象),我創(chuàng)建了一個(gè)名為parse()的新函數(shù)。 parse()函數(shù)包含之前在主文件中的頭字段解析邏輯。因?yàn)?code>headerSubtree是一個(gè)引用類型,所以我不必從函數(shù)中返回它:在從parse()返回后它仍然會(huì)被修改。但是,我需要在主文件中使用opcode_name,所以我會(huì)返回它。表m從文件返回,因此可以在主文件中使用。我們不必將get_opcode_name()添加到m,因?yàn)樗鼉H在header.lua中使用。
在取出一些Header內(nèi)容后,主文件看起來像這樣:
package.prepend_path("plugins/mongodb")
local header = require("header")
mongodb_protocol = Proto("MongoDB", "MongoDB Protocol")
-- Header fields
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)
-- Payload fields
flags = ProtoField.int32 ("mongodb.flags" , "flags" , base.DEC)
full_coll_name = ProtoField.string("mongodb.full_coll_name" , "fullCollectionName", base.ASCII)
number_to_skip = ProtoField.int32 ("mongodb.number_to_skip" , "numberToSkip" , base.DEC)
number_to_return= ProtoField.int32 ("mongodb.number_to_return", "numberToReturn" , base.DEC)
query = ProtoField.none ("mongodb.query" , "query" , base.HEX)
response_flags = ProtoField.int32 ("mongodb.response_flags" , "responseFlags" , base.DEC)
cursor_id = ProtoField.int64 ("mongodb.cursor_id" , "cursorId" , base.DEC)
starting_from = ProtoField.int32 ("mongodb.starting_from" , "startingFrom" , base.DEC)
number_returned = ProtoField.int32 ("mongodb.number_returned" , "numberReturned" , base.DEC)
documents = ProtoField.none ("mongodb.documents" , "documents" , base.HEX)
mongodb_protocol.fields = {
message_length, request_id, response_to, opcode, -- Header
flags, full_coll_name, number_to_skip, number_to_return, query, -- OP_QUERY
response_flags, cursor_id, starting_from, number_returned, documents -- OP_REPLY
}
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")
local headerSubtree = subtree:add(mongodb_protocol, buffer(), "Header")
local payloadSubtree = subtree:add(mongodb_protocol, buffer(), "Payload")
-- Header
local opcode_name = header.parse(headerSubtree, buffer, message_length, request_id, response_to, opcode)
-- Payload
if opcode_name == "OP_QUERY" then
local flags_number = buffer(16,4):le_uint()
local flags_description = get_flag_description(flags_number)
payloadSubtree:add_le(flags, buffer(16,4)):append_text(" (" .. flags_description .. ")")
-- Loop over string
local string_length
for i = 20, length - 1, 1 do
if (buffer(i,1):le_uint() == 0) then
string_length = i - 20
break
end
end
payloadSubtree:add_le(full_coll_name, buffer(20,string_length))
payloadSubtree:add_le(number_to_skip, buffer(20+string_length,4))
payloadSubtree:add_le(number_to_return, buffer(24+string_length,4))
payloadSubtree:add_le(query, buffer(28+string_length,length-string_length-28))
elseif opcode_name == "OP_REPLY" then
local response_flags_number = buffer(16,4):le_uint()
local response_flags_description = get_response_flag_description(response_flags_number)
payloadSubtree:add_le(response_flags, buffer(16,4)):append_text(" (" .. response_flags_description .. ")")
payloadSubtree:add_le(cursor_id, buffer(20,8))
payloadSubtree:add_le(starting_from, buffer(28,4))
payloadSubtree:add_le(number_returned, buffer(32,4))
payloadSubtree:add_le(documents, buffer(36,length-36))
end
end
function get_flag_description(flags)
local flags_description = "Unknown"
if flags == 0 then flags_description = "Reserved"
elseif flags == 1 then flags_description = "TailableCursor"
elseif flags == 2 then flags_description = "SlaveOk.Allow"
elseif flags == 3 then flags_description = "OplogReplay"
elseif flags == 4 then flags_description = "NoCursorTimeout"
elseif flags == 5 then flags_description = "AwaitData"
elseif flags == 6 then flags_description = "Exhaust"
elseif flags == 7 then flags_description = "Partial"
elseif 8 <= flags and flags <= 31 then flags_description = "Reserved" end
return flags_description
end
function get_response_flag_description(flags)
local flags_description = "Unknown"
if flags == 0 then flags_description = "CursorNotFound"
elseif flags == 1 then flags_description = "QueryFailure"
elseif flags == 2 then flags_description = "ShardConfigStale"
elseif flags == 3 then flags_description = "AwaitCapable"
elseif 4 <= flags and flags <= 31 then flags_description = "Reserved" end
return flags_description
end
local tcp_port = DissectorTable.get("tcp.port")
tcp_port:add(59274, mongodb_protocol)
注意對(duì)header.parse()的調(diào)用。如上所述,我返回opcode_name,因?yàn)槲倚枰诖a中進(jìn)一步向下。子樹headerSubtree也將被修改(添加到它的字段),因?yàn)樗且粋€(gè)引用類型,因此在parse()中是可變的。
創(chuàng)建OP_QUERY.lua 和 OP_REPLY.lua
如您所見,主文件中仍有很多Header內(nèi)容可以移動(dòng)到Header模塊中。稍后我會(huì)移動(dòng)它,但首先我要對(duì)OP_QUERY和OP_REPLY解析代碼執(zhí)行相同的操作,就像使用Header代碼一樣
我正在制作OP_QUERY.lua并將get_flag_description()和OP_QUERY解析邏輯移動(dòng)過去:
function get_flag_description(flags)
local flags_description = "Unknown"
if flags == 0 then flags_description = "Reserved"
elseif flags == 1 then flags_description = "TailableCursor"
elseif flags == 2 then flags_description = "SlaveOk.Allow"
elseif flags == 3 then flags_description = "OplogReplay"
elseif flags == 4 then flags_description = "NoCursorTimeout"
elseif flags == 5 then flags_description = "AwaitData"
elseif flags == 6 then flags_description = "Exhaust"
elseif flags == 7 then flags_description = "Partial"
elseif 8 <= flags and flags <= 31 then flags_description = "Reserved" end
return flags_description
end
local m = {}
function m.parse(payloadSubtree, buffer, length, flags, full_coll_name, number_to_skip, number_to_return, query)
local flags_number = buffer(16,4):le_uint()
local flags_description = get_flag_description(flags_number)
payloadSubtree:add_le(flags, buffer(16,4)):append_text(" (" .. flags_description .. ")")
-- Loop over string
local string_length
for i = 20, length - 1, 1 do
if (buffer(i,1):le_uint() == 0) then
string_length = i - 20
break
end
end
payloadSubtree:add_le(full_coll_name, buffer(20,string_length))
payloadSubtree:add_le(number_to_skip, buffer(20+string_length,4))
payloadSubtree:add_le(number_to_return, buffer(24+string_length,4))
payloadSubtree:add_le(query, buffer(28+string_length,length-string_length-28))
end
return m
我也在制作OP_REPLY.lua并將get_response_flag_description()和OP_REPLY解析邏輯移動(dòng)過去:
function get_response_flag_description(flags)
local flags_description = "Unknown"
if flags == 0 then flags_description = "CursorNotFound"
elseif flags == 1 then flags_description = "QueryFailure"
elseif flags == 2 then flags_description = "ShardConfigStale"
elseif flags == 3 then flags_description = "AwaitCapable"
elseif 4 <= flags and flags <= 31 then flags_description = "Reserved" end
return flags_description
end
local m = {}
function m.parse(payloadSubtree, buffer, response_flags, cursor_id, starting_from, number_returned, documents)
local response_flags_number = buffer(16,4):le_uint()
local response_flags_description = get_response_flag_description(response_flags_number)
payloadSubtree:add_le(response_flags, buffer(16,4)):append_text(" (" .. response_flags_description .. ")")
payloadSubtree:add_le(cursor_id, buffer(20,8))
payloadSubtree:add_le(starting_from, buffer(28,4))
payloadSubtree:add_le(number_returned, buffer(32,4))
payloadSubtree:add_le(documents, buffer(36,length-36))
end
return m
主文件如下:
package.prepend_path("plugins/mongodb")
local header = require("header")
local op_query = require("OP_QUERY")
local op_reply = require("OP_REPLY")
mongodb_protocol = Proto("MongoDB", "MongoDB Protocol")
-- Header fields
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)
-- Payload fields
flags = ProtoField.int32 ("mongodb.flags" , "flags" , base.DEC)
full_coll_name = ProtoField.string("mongodb.full_coll_name" , "fullCollectionName", base.ASCII)
number_to_skip = ProtoField.int32 ("mongodb.number_to_skip" , "numberToSkip" , base.DEC)
number_to_return= ProtoField.int32 ("mongodb.number_to_return", "numberToReturn" , base.DEC)
query = ProtoField.none ("mongodb.query" , "query" , base.HEX)
response_flags = ProtoField.int32 ("mongodb.response_flags" , "responseFlags" , base.DEC)
cursor_id = ProtoField.int64 ("mongodb.cursor_id" , "cursorId" , base.DEC)
starting_from = ProtoField.int32 ("mongodb.starting_from" , "startingFrom" , base.DEC)
number_returned = ProtoField.int32 ("mongodb.number_returned" , "numberReturned" , base.DEC)
documents = ProtoField.none ("mongodb.documents" , "documents" , base.HEX)
mongodb_protocol.fields = {
message_length, request_id, response_to, opcode, -- Header
flags, full_coll_name, number_to_skip, number_to_return, query, -- OP_QUERY
response_flags, cursor_id, starting_from, number_returned, documents -- OP_REPLY
}
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")
local headerSubtree = subtree:add(mongodb_protocol, buffer(), "Header")
local payloadSubtree = subtree:add(mongodb_protocol, buffer(), "Payload")
-- Header
local opcode_name = header.parse(headerSubtree, buffer, message_length, request_id, response_to, opcode)
-- Payload
if opcode_name == "OP_QUERY" then
op_query.parse(payloadSubtree, buffer, length, flags, full_coll_name, number_to_skip, number_to_return, query)
elseif opcode_name == "OP_REPLY" then
op_reply.parse(payloadSubtree, buffer, response_flags, cursor_id, starting_from, number_returned, documents)
end
end
local tcp_port = DissectorTable.get("tcp.port")
tcp_port:add(59274, mongodb_protocol)
當(dāng)然,我們必須require這兩個(gè)新文件。
移動(dòng)字段創(chuàng)建代碼
主文件mongodb.lua現(xiàn)在看起來更干凈了。仍有Header,OP_QUERY和OP_REPLY相關(guān)邏輯可以移動(dòng)到各自的文件中。移動(dòng)字段創(chuàng)建代碼后,我們也可以擺脫對(duì)三個(gè)parse()方法的笨拙調(diào)用。它們包含的參數(shù)太多,應(yīng)該從模塊中了解到。
讓我們?cè)谀K中移動(dòng)字段創(chuàng)建代碼。這是header.lua:
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 m = {
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)
}
function m.get_fields()
local fields = {
message_length = m.message_length,
request_id = m.request_id,
response_to = m.response_to,
opcode = m.opcode
}
return fields
end
function m.parse(headerSubtree, buffer)
headerSubtree:add_le(m.message_length, buffer(0,4))
headerSubtree:add_le(m.request_id, buffer(4,4))
headerSubtree:add_le(m.response_to, buffer(8,4))
local opcode_number = buffer(12,4):le_uint()
local opcode_name = get_opcode_name(opcode_number)
headerSubtree:add_le(m.opcode, buffer(12,4)):append_text(" (" .. opcode_name .. ")")
return opcode_name
end
return m
這些字段作為m表的成員存在。 get_fields()函數(shù)用于在模塊外部訪問它們。另請(qǐng)注意,parse()函數(shù)通過模塊本身訪問字段,而不是將它們作為參數(shù)傳遞。
我還將OP_QUERY和OP_REPLY的字段移動(dòng)到各自的模塊中。主文件現(xiàn)在看起來像這樣:
package.prepend_path("plugins/mongodb")
local helpers = require("helpers")
local header = require("header")
local op_query = require("OP_QUERY")
local op_reply = require("OP_REPLY")
mongodb_protocol = Proto("MongoDB", "MongoDB Protocol")
local header_fields = header.get_fields()
local op_query_fields = op_query.get_fields()
local op_reply_fields = op_reply.get_fields()
helpers.merge_tables( header_fields, mongodb_protocol.fields)
helpers.merge_tables(op_query_fields, mongodb_protocol.fields)
helpers.merge_tables(op_reply_fields, mongodb_protocol.fields)
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")
local headerSubtree = subtree:add(mongodb_protocol, buffer(), "Header")
local payloadSubtree = subtree:add(mongodb_protocol, buffer(), "Payload")
local opcode_name = header.parse(headerSubtree, buffer)
if opcode_name == "OP_QUERY" then op_query.parse(payloadSubtree, buffer, length)
elseif opcode_name == "OP_REPLY" then op_reply.parse(payloadSubtree, buffer) end
end
local tcp_port = DissectorTable.get("tcp.port")
tcp_port:add(59274, mongodb_protocol)
您可以看到所有字段初始化代碼都消失了。我們?nèi)匀恍枰趍ongodb_protocol.fields中插入字段,這就是我們用get_fields()獲取它們的原因。我使用一個(gè)名為merge_tables()的輔助函數(shù)將三個(gè)表合并在一起。我把這個(gè)函數(shù)放在一個(gè)名為helpers.lua的模塊中。它看起來像這樣:
local m = {}
-- Made by Doug Currie (https://stackoverflow.com/users/33252/doug-currie)
-- on Stack Overflow. https://stackoverflow.com/questions/1283388/lua-merge-tables
function m.merge_tables(from, to)
for k,v in pairs(from) do to[k] = v end
end
return m
如您所見,我在Stack Overflow上找到了代碼。
最后,您可以看到對(duì)parse()的調(diào)用已縮短為:
local opcode_name = header.parse(headerSubtree, buffer)
if opcode_name == "OP_QUERY" then op_query.parse(payloadSubtree, buffer, length)
elseif opcode_name == "OP_REPLY" then op_reply.parse(payloadSubtree, buffer) end
我們不必再傳遞所有字段變量,因?yàn)樗鼈円呀?jīng)被放入模塊本身。
完整實(shí)現(xiàn)
因此,您可以將代碼分成幾個(gè)文件。
- 使用package.prepend_path()將目錄添加到包路徑。
- 使用require()從另一個(gè)文件中讀取代碼。
- 在模塊文件中創(chuàng)建一個(gè)名為m的表(對(duì)象)或任何你想要的。
- 向表中添加方法和變量。
- 返回您創(chuàng)建的表并在主文件中使用它。