Appearance
Appearance
下载这个 dissector.lua 文件,查看一个协议 dissector 的 Lua 脚本示例。这个脚本太长,无法嵌入本页面;最好在支持 Lua 语法高亮的文本编辑器中查看,因为脚本中有大量注释用于解释相关内容。
这个脚本有两个目的:
如果你想知道为什么某些函数以某种方式调用,或者为什么与同一函数之前的调用方式不同:原因是它既试图展示同一件事可以用多种方式完成,也试图测试这些不同方式。
这个脚本为 DNS 创建了一个基础的 dissector。就 DNS 协议而言,它既不全面,也不能保证没有错误。这没关系。目标并不是完整而正确地 dissect DNS——Wireshark 已经内置了一个很好的 DNS dissector。我们不需要再写一个。我们还有其他 Lua 示例脚本,但这个脚本的好处是,用于运行它的抓包文件非常容易获得。
如何使用这个脚本:脚本加载后,会创建一个名为 “MyDNS”(在某些地方为 “MYDNS”)的新协议。如果你有一个包含 DNS 数据包的抓包文件,只需在 Packet List 窗格中选择一个数据包,右键单击它,选择 “Decode As ...”,然后在出现的对话框中向下滚动协议列表,找到名为 “MYDNS” 的协议,选择它并点击 “ok” 或 “apply” 按钮。Voila`,现在你就会使用这个脚本中的简易 dissector 来解码 DNS 数据包。另一种方式是下载为这个脚本准备的 dns_port.pcap 抓包文件并打开它——由于其中的 DNS 数据包使用 UDP 端口 65333(而不是默认的 53),并且这个脚本中的 MyDNS 协议已设置为自动解码 UDP 端口 65333,因此它会自动完成解码,而无需执行 “Decode As ...”。
下载这个 fpm.lua 文件,查看一个基于 TCP 的协议 dissector 的 Lua 脚本示例。这个脚本太长,无法嵌入本页面;最好在支持 Lua 语法高亮的文本编辑器中查看,因为脚本中有大量注释用于解释相关内容。
如何使用这个脚本:脚本加载后,会创建一个名为 “FPM” 的新协议。要查看其运行效果,请下载为这个脚本准备的 segmented_fpm.pcap 抓包文件并打开它。
下载这个 pcap_file.lua 文件,查看一个自定义文件格式读取器和写入器的 Lua 脚本示例。
与上面的 dissector 教程脚本一样,这个脚本太长,无法嵌入本页面;最好在支持 Lua 语法高亮的文本编辑器中查看,因为脚本中有大量注释用于解释相关内容。
同样与上面的 dissector 教程脚本一样,这个脚本的目的是提供一个参考教程,同时也作为测试脚本。
这个脚本为旧版 pcap 文件格式创建了一个基础的文件读取器和写入器。它既不全面,也不能保证没有错误,并且无意替代 Wireshark/Tshark 内置的读取 pcap 文件能力。目标并不是这样做。之所以编写它,是因为获取“测试”文件来查看其工作方式非常容易,因为任何 pcap 文件都可以(旧式 pcap 文件,而不是 pcapng)。
如何使用这个脚本:脚本加载后,它实际上会把自己作为新的文件读取器插入到内置 pcap 文件读取器之前,因此打开任何 pcap 文件时,都会由这个新的文件读取器来读取。与任何 Lua 脚本一样,你可以通过以下三种方式之一加载它:
最后一种方法是此文件读取器推荐的方式,这样你就不会在无意中继续使用这个文件读取器。(毕竟,内置的 pcap 文件格式读取器比这个示例读取器好得多)
下载这个 fileshark_pcap.lua 文件和这个 linktype.lua 文件,查看一个用于 pcap 格式 FileShark 脚本的 Lua 脚本示例。这是什么意思?意思是它读取 pcap 文件,并显示文件格式本身的内容,展示文件头、记录头等及其字段。为此,它创建了一个 “pcapfile” 协议 dissector,并带有 pcap 文件格式所包含内容对应的协议字段。这同时实现了一个基于 Lua 的 dissector 和自定义文件格式读取器。
与上面的教程脚本一样,这个脚本太长,无法嵌入本页面;最好在支持 Lua 语法高亮的文本编辑器中查看,因为脚本中有大量注释用于解释相关内容。
在 Wireshark 中,“PcapFile” 协议下有多个可设置的首选项。(Edit->Preferences->Protocols->PcapFile)
如何使用这个脚本:主脚本是 fileshark_pcap.lua,需要加载的就是它——第二个脚本(linktype.lua)由主脚本使用 Lua require 函数调用。
与任何 Lua 脚本一样,你可以通过以下三种方式之一加载它:
脚本加载后,如果要实际以 FileShark 方式读取 pcap 文件,需要告诉 Wireshark/Tshark 使用 “Fileshark Pcap” 格式读取器。有两种方式可以做到:
例如,以下命令以 Fileshark 方式读取名为 “test.pcap” 的文件:tshark -r test.pcap -X lua_script:fileshark_pcap.lua -X 'read_format:Fileshark Pcap'
注意,最后一个 “read_format:Fileshark Pcap” 参数用单引号包裹,因为 “Fileshark Pcap” 中包含空格。
1 -- register http to handle ports 4888-4891 2 do 3 local tcp_port_table = DissectorTable.get("tcp.port") 4 local http_dissector = tcp_port_table:get_dissector(80) 5 for i,port in ipairs{4888,4889,4890,4891} do 6 tcp_port_table:add(port,http_dissector) 7 end 8 end也可以使用整数范围: (dissectortable:add(pattern, dissector) 函数的 “Integer range” 语法是什么?)
local tcp_port_table = DissectorTable.get("tcp.port")local http_dissector = tcp_port_table:get_dissector(80)tcp_port_table:add("4888-4891",http_dissector) 1 -- Create a file named by_ip/''ip_addess''.cap with all ip traffic of each ip host. (tshark only?) 2 -- Dump files are created for both source and destination hosts 3 function createDir (dirname) 4 -- this will print out an error if the directory already exists, but that's fine 5 os.execute("mkdir " .. dirname) 6 end 7 8 local dir = "by_ip" 9 createDir(dir) 10 11 -- create a table to hold the dumper objects/file handles 12 local dumpers = {} 13 14 -- create a listener tap. By default it creates one for "frame", but we're tapping IP layer. 15 -- Valid values can be any protocol with tapping support, but to get something useful in the 16 -- "extractor" argument of the tap's 'packet' function callback (the third argument passed by 17 -- wireshark into it), it has to be one of the following currently: 18 -- "actrace", "ansi_a", "ansi_map", "bacapp", "eth", "h225", "http", "ip", "ldap", 19 -- "smb", "smb2", "tcp", "udp", "wlan", and "frame" 20 local tap = Listener.new("ip") 21 22 23 -- we will be called once for every IP Header. 24 -- If there's more than one IP header in a given packet we'll dump the packet once per every header 25 function tap.packet(pinfo,tvb,ip) 26 --print("packet called") 27 local ip_src, ip_dst = tostring(ip.ip_src), tostring(ip.ip_dst) 28 local src_dmp, dst_dmp 29 30 -- get the dumper file handle for this ip addr 31 src_dmp = dumpers[ip_src] 32 if not src_dmp then 33 -- doesn't exist, make a new one, of the same encapsulation type as current file 34 src_dmp = Dumper.new_for_current( dir .. "/" .. ip_src .. ".pcap" ) 35 dumpers[ip_src] = src_dmp 36 end 37 38 -- dump the current packet as it is (same encap format and content) 39 src_dmp:dump_current() 40 src_dmp:flush() 41 42 -- now do the same for dest addr 43 dst_dmp = dumpers[ip_dst] 44 if not dst_dmp then 45 dst_dmp = Dumper.new_for_current( dir .. "/" .. ip_dst .. ".pcap" ) 46 dumpers[ip_dst] = dst_dmp 47 end 48 49 dst_dmp:dump_current() 50 dst_dmp:flush() 51 52 end 53 54 -- a listener tap's draw function is called every few seconds in the GUI 55 -- and at end of file (once) in tshark 56 function tap.draw() 57 --print("draw called") 58 for ip_addr,dumper in pairs(dumpers) do 59 dumper:flush() 60 end 61 end 62 63 -- a listener tap's reset function is called at the end of a live capture run, 64 -- when a file is opened, or closed. Tshark never appears to call it. 65 function tap.reset() 66 --print("reset called") 67 for ip_addr,dumper in pairs(dumpers) do 68 dumper:close() 69 end 70 dumpers = {} 71 end 1 -- Append "<dst> -> <src>" to the Info column with a post-dissector. 2 -- (Taps are not guaranteed to be run at a point when they can set the 3 -- column text, so they can't be used for this.) 4 5 -- create a new protocol so we can register a post-dissector 6 local myproto = Proto("swapper","Dummy proto to edit info column") 7 8 -- the dissector function callback 9 function myproto.dissector(tvb,pinfo,tree) 10 pinfo.cols.info:append(" " .. tostring(pinfo.dst).." -> "..tostring(pinfo.src)) 11 end 12 -- register our new dummy protocol for post-dissection 13 register_postdissector(myproto) 1 -- This Example will add a menu "Lua Dialog Test" under the Tools menu, 2 -- which when selected will pop a dialog prompting the user for input 3 -- that when accepted will pop a window with a result. 4 5 if gui_enabled() then 6 local splash = TextWindow.new("Hello!"); 7 splash:set("This time wireshark has been enhanced with a useless feature.\n") 8 splash:append("Go to 'Tools->Lua Dialog Test' and check it out!") 9 end 10 local function dialog_menu() 11 local function dialog_func(person,eyes,hair) 12 local win = TextWindow.new("The Person"); 13 win:set(person) 14 win:append(" with " .. eyes .." eyes and") 15 win:append(" " .. hair .. " hair."); 16 end 17 18 new_dialog("Dialog Test",dialog_func,"A Person","Eyes","Hair") 19 end 20 21 -- optional 3rd parameter to register_menu. 22 -- See http://www.wireshark.org/docs/wsug_html_chunked/wsluarm_modules.html 23 -- If omitted, defaults to MENU_STAT_GENERIC. Other options include: 24 -- MENU_STAT_UNSORTED (Statistics), 25 -- MENU_STAT_GENERIC (Statistics, first section), 26 -- MENU_STAT_CONVERSATION (Statistics/Conversation List), 27 -- MENU_STAT_ENDPOINT (Statistics/Endpoint List), 28 -- MENU_STAT_RESPONSE (Statistics/Service Response Time), 29 -- MENU_STAT_TELEPHONY (Telephony), 30 -- MENU_ANALYZE_UNSORTED (Analyze), 31 -- MENU_ANALYZE_CONVERSATION (Analyze/Conversation Filter), 32 -- MENU_TOOLS_UNSORTED (Tools) 33 34 register_menu("Lua Dialog Test",dialog_menu,MENU_TOOLS_UNSORTED) 1 do 2 packets = 0; 3 local function init_listener() 4 local tap = Listener.new("frame","ip.addr == 10.0.0.0/8") 5 function tap.reset() 6 packets = 0; 7 end 8 function tap.packet(pinfo,tvb,ip) 9 packets = packets + 1 10 end 11 function tap.draw() 12 print("Packets to/from 10.0.0./8",packets) 13 end 14 end 15 init_listener() 16 end 1 -- This example iterates through the field tree of the packets, and prints out the tree field information in a text window. 2 -- It shows the current tree for the selected packet, but this does not mean it always shows the full tree, 3 -- because wireshark performs multiple dissection passes of a packet, with the initial pass only being high-level and not 4 -- dissecting fully (for performance reasons). You can see this behavior better by changing line 30 of this example 5 -- to this, so it concatenates output instead of clearing it every time: 6 -- output = output .. "\nTree fields for packet #".. pinfo.number .. ":\n" 7 8 -- this only works in wireshark 9 if not gui_enabled() then return end 10 11 local output = "" -- for the output we'll show in the text window 12 local tw = nil -- the text window 13 -- function to refresh the text window 14 local function updateWindow() 15 if tw then tw:set(output) end 16 end 17 18 -- calling tostring() on random FieldInfo's can cause an error, so this func handles it 19 local function getstring(finfo) 20 local ok, val = pcall(tostring, finfo) 21 if not ok then val = "(unknown)" end 22 return val 23 end 24 25 -- create a new protocol so we can register a post-dissector 26 local myproto = Proto("tree_view","Dummy proto to view tree") 27 28 -- the dissector function callback 29 function myproto.dissector(tvb,pinfo,tree) 30 output = output.. "\nTree fields for packet #".. pinfo.number .. ":\n" 31 -- get a table of all FieldInfo objects 32 local fields = { all_field_infos() } 33 for ix, finfo in ipairs(fields) do 34 output = output .. "\t[" .. ix .. "] " .. finfo.name .. " = " .. getstring(finfo) .. "\n" 35 end 36 updateWindow() 37 end 38 -- register our new dummy protocol for post-dissection 39 register_postdissector(myproto) 40 41 42 -- now we create the menu function for this, which creates a text window to display this stuff 43 local function menu_view_tree() 44 tw = TextWindow.new("Tree View") 45 tw:set_atclose(function() tw = nil end) 46 updateWindow() 47 end 48 49 -- add this to the Tools->Lua submenu 50 register_menu("Lua/Tree View", menu_view_tree, MENU_TOOLS_UNSORTED) 1 -- This script is meant to be used with tshark/wireshark, with command-line 2 -- arguments, using the '-X lua_script[N]:argN' option. 3 -- Each argument identifies a field we will extract into two new 4 -- fields called "extract.string" and "extract.hex" 5 -- Those new fields can then be printed by tshark. 6 -- 7 -- For example, if this script is saved as "extract.lua", then the following: 8 -- tshark -r myfile -X lua_script:extract.lua -X lua_script1:data-text-lines -T fields -e extract.string 9 -- will read the file called "myfile", extract each "data-text-lines" field, and 10 -- print its string value out to the console. 11 -- The following: 12 -- wireshark -r myfile -X lua_script:extract.lua -X lua_script1:data-text-lines 13 -- will do something similar in the GUI, showing the extracted values in the tree. 14 15 local args = { ... } 16 17 -- exit if no arguments were passed in 18 if #args == 0 then 19 return 20 end 21 22 -- verify tshark/wireshark version is new enough - needs to be 1.12+ 23 local major, minor, micro = 0, 0, 0 24 if get_version then 25 major, minor, micro = get_version():match("(%d+)%.(%d+)%.(%d+)") 26 if not major then 27 major, minor, micro = 0, 0, 0 28 end 29 end 30 if (tonumber(major) == 0) or ((tonumber(major) <= 1) and (tonumber(minor) < 12)) then 31 error("Sorry, but your Wireshark/Tshark version is too old for this script!\n".. 32 "This script needs Wireshark/Tshark version 1.12 or higher.\n" ) 33 end 34 35 -- a table to hold field extractors 36 local fields = {} 37 38 -- create field extractor(s) for the passed-in argument(s) 39 for i, arg in ipairs(args) do 40 fields[i] = Field.new(arg) 41 end 42 43 -- our fake protocol 44 local exproto = Proto.new("extract", "Data Extractor") 45 46 -- the new fields that contain the extracted data (one in string form, one in hex) 47 local exfield_string = ProtoField.new("Extracted String Value", "extract.string", ftypes.STRING) 48 local exfield_hex = ProtoField.new("Extracted Hex Value", "extract.hex", ftypes.STRING) 49 50 -- register the new fields into our fake protocol 51 exproto.fields = { exfield_string, exfield_hex } 52 53 function exproto.dissector(tvbuf,pktinfo,root) 54 local tree = nil 55 56 for i, field in ipairs(fields) do 57 -- extract the field into a table of FieldInfos 58 finfos = { field() } 59 60 if #finfos > 0 then 61 -- add our proto if we haven't already 62 if not tree then 63 tree = root:add(exproto) 64 end 65 66 for _, finfo in ipairs(finfos) do 67 -- get a TvbRange of the FieldInfo (fieldinfo.range in WSDG) 68 local ftvbr = finfo.tvb 69 tree:add(exfield_string, ftvbr:string(ENC_UTF_8)) 70 tree:add(exfield_hex,tostring(ftvbr:bytes())) 71 end 72 end 73 end 74 75 end 76 77 -- register it as a postdissector, and force all fields to be generated 78 register_postdissector(exproto, true) 1 -- voip.lua 2 -- Written by: Jason Garland <jgarland@jasongarland.com> 3 4 print("Starting voip.lua script.") 5 6 7 rex = require "rex_pcre" 8 9 --MySQL database connection 10 require "luasql.mysql" 11 env = assert (luasql.mysql()) 12 con = assert (env:connect("voiper","voiper","password")) 13 14 15 do 16 local voiperdir = os.getenv("voiperdir") 17 local capturesdir = os.getenv("voiperdir") .. "/captures" 18 dumpers = {} 19 local frames = {} 20 local rtp = {} 21 last_packet = {} 22 timeout = 300 23 inin_callid = {} 24 closed = {} 25 files = {} 26 files_path = {} 27 --local tcp_src_f = Field.new("tcp.srcport") 28 --local tcp_dst_f = Field.new("tcp.dstport") 29 local udp_src_f = Field.new("udp.srcport") 30 local udp_dst_f = Field.new("udp.dstport") 31 local rtp_ssrc_f = Field.new("rtp.ssrc") 32 local rtp_setup_frame_f = Field.new("rtp.setup-frame") 33 local t38_setup_frame_f = Field.new("t38.setup-frame") 34 local rtcp_setup_frame_f = Field.new("rtcp.setup-frame") 35 local rtcp_ssrc_jitter_f = Field.new("rtcp.ssrc.jitter") 36 local rtcp_ssrc_fraction_f = Field.new("rtcp.ssrc.fraction") 37 local rtcp_ssrc_identifier_f = Field.new("rtcp.ssrc.identifier") 38 local sip_callid_f = Field.new("sip.Call-ID") 39 local sip_cseq_method_f = Field.new("sip.CSeq.method") 40 local sip_status_code_f = Field.new("sip.Status-Code") 41 42 local sip_contact_addr_f = Field.new("sip.contact.addr") 43 local sip_request_line_f = Field.new("sip.Request-Line") 44 local sip_from_addr_f = Field.new("sip.from.addr") 45 46 local sdp_connection_info_address_f = Field.new("sdp.connection_info.address") 47 local raw_sip_line_f = Field.new("raw_sip.line") 48 local function init_listener() 49 local tap = Listener.new("ip", "(rtp or rtcp or t38) or (sip and ((sip.CSeq.method != REGISTER) and (sip.CSeq.method != OPTIONS))) ") 50 -- we will be called once for every IP Header. 51 -- If there's more than one IP header in a given packet we'll dump the packet once per every header 52 function tap.packet(pinfo,tvb,ip) 53 local ip_src, ip_dst = tostring(ip.ip_src), tostring(ip.ip_dst) 54 --local rtp_ssrc, rtp_setup_frame = rtp_ssrc_f(), rtp_setup_frame_f() 55 local rtp_setup_frame,rtp_ssrc = rtp_setup_frame_f(), rtp_ssrc_f() 56 local t38_setup_frame = t38_setup_frame_f() 57 local sip_cseq_method, sip_status_code, sip_callid, sdp_connection_info_address, raw_sip_line = sip_cseq_method_f(), sip_status_code_f(), sip_callid_f(), sdp_connection_info_address_f(), raw_sip_line_f() 58 local sip_contact_addr, sip_request_line, sip_from_addr = sip_contact_addr_f(), sip_request_line_f(), sip_from_addr_f() 59 local rtcp_setup_frame, rtcp_ssrc_jitter, rtcp_ssrc_fraction, rtcp_ssrc_identifier = rtcp_setup_frame_f(), rtcp_ssrc_jitter_f(), rtcp_ssrc_fraction_f(), rtcp_ssrc_identifier_f() 60 local frame = tostring(pinfo.number) 61 local src_dmp, dst_dmp, rtp_dmp, sip_dmp 62 63 if sdp_connection_info_address then 64 --print("Frame: " .. frame .. " = " .. tostring(sip_callid)) 65 frames[frame] = tostring(sip_callid) 66 end 67 68 if rtcp_setup_frame then 69 if not (frames[tostring(rtcp_setup_frame)] == nil) then 70 sip_callid = frames[tostring(rtcp_setup_frame)] 71 end 72 end 73 74 if t38_setup_frame then 75 if not (frames[tostring(t38_setup_frame)] == nil) then 76 sip_callid = frames[tostring(t38_setup_frame)] 77 end 78 end 79 80 --if rtp_setup_frame then 81 if not (frames[tostring(rtp_setup_frame)] == nil) then 82 sip_callid = frames[tostring(rtp_setup_frame)] 83 rtp[tostring(rtp_ssrc)] = sip_callid 84 -- else 85 -- rtp_dmp = dumpers[tostring(rtp_ssrc)] 86 -- if not rtp_dmp then 87 -- rtp_dmp = Dumper.new_for_current( capturesdir .. "/rtp/" .. tostring(rtp_ssrc) .. ".pcap" ) 88 -- dumpers[tostring(rtp_ssrc)] = rtp_dmp 89 -- end 90 -- rtp_dmp:dump_current() 91 -- rtp_dmp:flush() 92 end 93 --end 94 95 if (sip_callid == nil) then 96 if (rtp_ssrc) then 97 sip_callid = rtp[tostring(rtp_ssrc)] 98 --if not (sip_callid == nil) then print("SSRC: " .. tostring(rtp_ssrc) .. " = " .. sip_callid) end 99 end 100 end 101 102 if sip_callid then 103 if not ((closed[tostring(sip_callid)] == true)) then 104 --check_age() 105 if (files[tostring(sip_callid)] == nil) then 106 files_path[tostring(sip_callid)] = os.date("%Y", pinfo.abs_ts) .. "/" .. os.date("%m", pinfo.abs_ts) .. "/" .. os.date("%d", pinfo.abs_ts) .. "/" .. os.date("%H", pinfo.abs_ts) .. "/" 107 os.execute("mkdir -p " .. capturesdir .. "/" .. files_path[tostring(sip_callid)]) 108 files[tostring(sip_callid)] = os.date("%Y%m%d%H%M%S", pinfo.abs_ts) .. "-" .. tostring(sip_callid) .. ".pcap" 109 -- print("Creating: " .. files_path[tostring(sip_callid)] .. files[tostring(sip_callid)]) 110 res = assert (con:execute(string.format([[ 111 INSERT INTO calls 112 (filepath, filename, callid, state) 113 VALUES ('%s', '%s', '%s', '%s') 114 ]], files_path[tostring(sip_callid)], files[tostring(sip_callid)], tostring(sip_callid), "open") 115 )) 116 117 end 118 119 -- print(tostring(sip_callid)) 120 sip_dmp = dumpers[tostring(sip_callid)] 121 if not sip_dmp then 122 print("Opening: " .. files_path[tostring(sip_callid)] .. files[tostring(sip_callid)]) 123 sip_dmp = Dumper.new_for_current( capturesdir .. "/" .. files_path[tostring(sip_callid)] .. files[tostring(sip_callid)] ) 124 dumpers[tostring(sip_callid)] = sip_dmp 125 end 126 sip_dmp:dump_current() 127 sip_dmp:flush() 128 last_packet[tostring(sip_callid)] = os.clock() 129 if (tostring(sip_cseq_method) == "BYE" and tostring(sip_status_code) == "200") then 130 sip_dmp:close() 131 sip_dmp = nil 132 mark_closed(sip_callid) 133 end 134 if (tostring(sip_cseq_method) == "INVITE" and tostring(sip_status_code) == "487") then 135 sip_dmp:close() 136 sip_dmp = nil 137 mark_closed(sip_callid) 138 end 139 else 140 print("Ignoring packet after " .. tostring(sip_callid) .. " was closed.") 141 end 142 end 143 144 145 if raw_sip_line_f() then 146 local line = {raw_sip_line_f()} 147 for i=1,#line do 148 line[i] = tostring(line[i]) 149 local inin_callid_start, inin_callid_stop, inin_callid_header, inin_callid_value = rex.find(line[i], '^(ININCrn|x-inin-crn): ([0-9]*)\\\\r\\\\n$') 150 if inin_callid_value then 151 if not inin_callid[tostring(sip_callid)] then 152 inin_callid[tostring(sip_callid)] = inin_callid_value 153 -- print(inin_callid_header .. ": " .. inin_callid_value) 154 res = assert (con:execute(string.format([[ 155 UPDATE calls 156 SET `inin_callid` = '%s' WHERE `callid` = '%s']], inin_callid[tostring(sip_callid)], tostring(sip_callid)) 157 )) 158 end 159 break 160 end 161 end 162 end 163 end 164 165 166 function check_age() 167 for item in pairs(last_packet) do 168 local age = os.difftime(os.clock(), last_packet[tostring(item)]) 169 if ( age >= timeout ) then 170 print("Timeout: " .. item .. " because age is " .. age .. " seconds.") 171 --return true 172 mark_closed(item) 173 --dumper = dumpers[tostring(item)] 174 --print("Dumper: " .. tostring(dumper)) 175 --dumper:flush() 176 --dumper:close() 177 --dumpers[tostring(item)] = nil 178 179 --dumpers[tostring(item)]:close() 180 --dumpers[tostring(item)] = nil 181 end 182 end 183 end 184 185 function mark_closed(sip_callid) 186 print("Closing: " .. files_path[tostring(sip_callid)] .. files[tostring(sip_callid)]) 187 res = assert (con:execute(string.format([[ 188 UPDATE calls 189 SET `state` = 'closed' WHERE `callid` = '%s' AND `filepath` = '%s' AND `filename` = '%s']], tostring(sip_callid), files_path[tostring(sip_callid)], files[tostring(sip_callid)]) 190 )) 191 closed[tostring(sip_callid)] = true 192 inin_callid[tostring(sip_callid)] = nil 193 last_packet[tostring(sip_callid)] = nil 194 files[tostring(sip_callid)] = nil 195 files_path[tostring(sip_callid)] = nil 196 end 197 198 function tap.draw() 199 -- The show is over. Close the database connection and flush the buffers. 200 for item in pairs(closed) do 201 print("cleaning up: " .. item) 202 dumpers[tostring(item)] = nil 203 end 204 205 for item,dumper in pairs(dumpers) do 206 print("Flushing: " .. files_path[tostring(item)] .. files[tostring(item)]) 207 dumper:flush() 208 mark_closed(item) 209 end 210 211 -- Close the database connection 212 con:close() 213 env:close() 214 end 215 216 function tap.reset() 217 for item,dumper in pairs(dumpers) do 218 mark_closed(item) 219 dumper:close() 220 print("Tap reset") 221 end 222 dumpers = {} 223 end 224 end 225 init_listener() 226 end> “我写了一个 Lua 脚本,用于在没有抓包文件的情况下测试不同的 float 和 double 值。”
> 18865:Wireshark 在 Lua 上下文中的无效 UDS 数据包上崩溃
proto=Proto("test","Test")localinfo={version="git",author="Stefan Tatschner",repository="fo"}set_plugin_info(info)localuds_dissector=Dissector.get("uds")function_get_length(tvb,pinfo,offset)returntvb:len()endfunction_dissect(tvb,pinfo,tree)uds_dissector:call(tvb,pinfo,tree)returntvb:len()endfunctionproto.dissector(tvb,pinfo,tree)dissect_tcp_pdus(tvb,tree,0,_get_length,_dissect,true)endtcp_table=DissectorTable.get("tcp.port")tcp_table:add(1234,proto)你是否曾经希望能够为抓包中的 IP 地址标注额外的元数据?也许你有一个来自 AWS 内部的抓包。能够看到源端点和目标端点的区域与可用区,不是很好吗?现在可以通过这个插件做到。
CSV 文件可以包含任意数量的列,但其中一列必须是 CIDR(除非你修改 cidrmeta.lua 的配置部分)。源 IP 地址和目标 IP 地址都会通过最具体的前缀进行匹配。这意味着你可以将元数据附加到具体主机,也可以大到一个 \8。例如,使用这个 CSV 文件:
CIDR,Region,Availability Zone10.1.0.0/16,us-east-1,use1-az1,10.2.0.0/16,us-east-2,use2-az3,10.0.0.0/8,on-prem,no az加载 Wireshark 后,你将能够执行以下操作:
cidrmeta.lua 的内容-- Wireshark Lua Post-Dissector for CIDR metadata lookup (data-driven)---- Loads a CSV containing rows of:-- CIDR,<any other metadata columns...>---- Performs longest-prefix match for IPv4 src/dst addresses and exposes-- ALL columns dynamically for use as disploy filters, columns, and a tree-- in the packet details pane called "CIDR Metadata".set_plugin_info({version="1.0.0",author="Pete Kazmier",description="Annotate IP packets with metadata from a CSV file",})----------------------------------------- Configuration---------------------------------------localcsv_path=Dir.personal_plugins_path().."/cidrmetadata.csv"localcsv_cidr_column="CIDR"-- exact column name for CIDR prefixes----------------------------------------- Proto---------------------------------------localp_cidr=Proto("cidrmeta","CIDR Metadata")-- Dynamic ProtoFields registries (filled after reading CSV header)localsrc_fields_by_key={}-- key -> ProtoFieldlocaldst_fields_by_key={}-- key -> ProtoFieldlocalmeta_columns={}-- array of {key=<sanitized>, label=<original>, idx=<col_index>}----------------------------------------- Existing IP field extractors---------------------------------------localip_src_f=Field.new("ip.src")localip_dst_f=Field.new("ip.dst")----------------------------------------- Bitwise compatibility (Wireshark Lua may provide 'bit' or 'bit32')---------------------------------------localbitops=_G.bitor_G.bit32ifnotbitopsthenerror("cidrmeta: No bit/bit32 library available in this Lua runtime.")end----------------------------------------- Utilities---------------------------------------localfunctiontrim(s)ifnotsthenreturnsendreturn(s:gsub("^%s+",""):gsub("%s+$",""))end-- Sanitize CSV header names into field-safe tokens:-- - lowercase-- - non-alnum -> underscore-- - collapse multiple underscores-- - ensure doesn't start with a digit (prefix "f_")localfunctionsanitize_fieldname(s)localt=s:lower()t=t:gsub("[%W]+","_")t=t:gsub("^_+",""):gsub("_+$","")t=t:gsub("__+","_")ift:match("^[0-9]")thent="f_"..tendift==""thent="field"endreturntend-- Robust CSV parser for a single line:-- - Supports RFC-4180 style quoting per single line:-- * Commas inside quotes are allowed-- * Double quotes inside quoted fields are escaped as ""-- - Does NOT support multi-line fields (one line in, one record out)-- - Returns an array of raw field strings (no trimming). Trim later if desired.-- - 'sep' defaults to ','; pass a different one if needed.localfunctionparse_csv_line(line,sep)sep=sepor","localout={}localfield={}locali,len=1,#linelocalin_quotes=falsewhilei<=lendolocalch=line:sub(i,i)ifin_quotesthenifch=='"'then-- Check for escaped quote ("")localnextch=line:sub(i+1,i+1)ifnextch=='"'thenfield[#field+1]='"'-- escaped quotei=i+2else-- closing quotein_quotes=falsei=i+1endelsefield[#field+1]=chi=i+1endelseifch=='"'thenin_quotes=truei=i+1elseifch==septhenout[#out+1]=table.concat(field)field={}i=i+1elsefield[#field+1]=chi=i+1endendend-- push last fieldout[#out+1]=table.concat(field)returnoutendlocalfunctionipv4_to_int(ipstr)locala,b,c,d=ipstr:match("^(%d+)%.(%d+)%.(%d+)%.(%d+)$")ifnotathenreturnnilenda,b,c,d=tonumber(a),tonumber(b),tonumber(c),tonumber(d)ifnotaornotbornotcornotdthenreturnnilendifa>255orb>255orc>255ord>255thenreturnnilend-- safe under double precision (<= 2^32)returna*16777216+b*65536+c*256+dend----------------------------------------- Longest-prefix match tables---------------------------------------localipv4_maps={}-- ipv4_maps[masklen][network_int] = metadata_table (key->value)localmasklens_desc={}-- list of mask lengths present, descendinglocalmasks={}-- precomputed bit masks per prefix lengthlocalBITS=32localALL=0xFFFFFFFFmasks[0]=0x00000000masks[BITS]=ALLform=1,BITS-1do-- mask with m leading onesmasks[m]=bitops.band(ALL,bitops.lshift(ALL,BITS-m))endlocalfunctionadd_masklen_sorted(m)fori,vinipairs(masklens_desc)doifm>vthentable.insert(masklens_desc,i,m)returnelseifm==vthenreturn-- already presentendendmasklens_desc[#masklens_desc+1]=mendlocalfunctionadd_prefix(ip_int,m,meta_tbl)localnet=bitops.band(ip_int,masks[m])localbucket=ipv4_maps[m]ifnotbucketthenbucket={}ipv4_maps[m]=bucketadd_masklen_sorted(m)endbucket[net]=meta_tblendlocalfunctiondo_lookup(ip_int)for_,minipairs(masklens_desc)dolocalbucket=ipv4_maps[m]ifbucketthenlocalkey=bitops.band(ip_int,masks[m])localmeta=bucket[key]ifmetathenreturnmetaendendendreturnnilend-- Provides modest improvement in speed for repeated lookupslocalcache={}localfunctionlookup(ip_int)localv=cache[ip_int]ifv~=nilthenreturnvornilendlocalmeta=do_lookup(ip_int)cache[ip_int]=metaorfalsereturnmetaend----------------------------------------- CSV Loader (build fields dynamically + load prefixes)---------------------------------------localfunctionload_csv_and_build_fields()localfh=io.open(csv_path,"r")ifnotfhthenprint("cidrmeta: Could not open CSV: "..tostring(csv_path))return0end-- Find first non-empty non-comment line as headerlocalheader_linewhiletruedolocall=fh:read("*l")ifnotlthenbreakend-- strip UTF-8 BOM if presentl=l:gsub("^\239\187\191","")iftrim(l)~=""andnottrim(l):match("^#")thenheader_line=lbreakendendifnotheader_linethenfh:close()print("cidrmeta: CSV has no header")return0end-- Parse headerlocalraw_headers=parse_csv_line(header_line)-- Build index map and determine the CIDR columnlocalname_to_idx={}foridx,hinipairs(raw_headers)dolocalt=trim(hor"")iftandt~=""thenname_to_idx[t]=idxendendlocalcidr_idx=name_to_idx[csv_cidr_column]ifnotcidr_idxthenfh:close()print("cidrmeta: CSV missing required "..csv_cidr_column.." column")return0end-- Prepare dynamic field definitions for all columns-- Keep column order as in the CSV for display consistency.localfields_list={}foridx,hinipairs(raw_headers)dolocallabel=trim(hor"")~=""andtrim(h)or("Column_"..tostring(idx))localkey=sanitize_fieldname(label)-- Create ProtoFields for src and dst using the keylocalsrc_f=ProtoField.string("cidrmeta.src."..key,"Src "..label)localdst_f=ProtoField.string("cidrmeta.dst."..key,"Dst "..label)src_fields_by_key[key]=src_fdst_fields_by_key[key]=dst_fmeta_columns[#meta_columns+1]={key=key,label=label,idx=idx}fields_list[#fields_list+1]=src_ffields_list[#fields_list+1]=dst_fend-- Register all dynamic fields with the protocolp_cidr.fields=fields_list-- Helper does all checks and returns true if a prefix was addedlocalfunctionprocess_line(line)ifnotlinethenreturnfalseendlocals=trim(line)ifs==""ors:match("^#")thenreturnfalseendlocalcols=parse_csv_line(line)localcidr=cols[cidr_idx]ifnotcidrthenreturnfalseendcidr=trim(cidr)ifcidr==""thenreturnfalseendlocalip_part,mask_str=cidr:match("^([^/]+)/(%d+)$")ifnotip_partornotmask_strthenreturnfalseendlocalmask=tonumber(mask_str)ifnotmaskormask<0ormask>32thenreturnfalseendip_part=trim(ip_part)localip_int=ipv4_to_int(ip_part)ifnotip_intthenreturnfalseend-- Build metadata map: key -> value (non-empty only)localmeta={}for_,colinipairs(meta_columns)dolocalv=cols[col.idx]meta[col.key]=trim(vor"")endadd_prefix(ip_int,mask,meta)returntrueend-- Parse all rowslocalcount=0forlineinfh:lines()doifprocess_line(line)thencount=count+1endendfh:close()print(string.format("cidrmeta: loaded %d prefixes from %s (mask buckets=%d)",count,csv_path,#masklens_desc))returncountend-- Load CSV & create dynamic fields at script loadload_csv_and_build_fields()----------------------------------------- Dissector---------------------------------------functionp_cidr.dissector(buffer,pinfo,tree)localip_src_fi=ip_src_f()localip_dst_fi=ip_dst_f()ifnotip_src_fiandnotip_dst_fithenreturnendlocalsrc_ip_int,dst_ip_intifip_src_fithensrc_ip_int=ip_src_fi.range:uint()endifip_dst_fithendst_ip_int=ip_dst_fi.range:uint()endifnotsrc_ip_intandnotdst_ip_intthenreturnendlocalsrc_meta=src_ip_intandlookup(src_ip_int)ornillocaldst_meta=dst_ip_intandlookup(dst_ip_int)ornilifnotsrc_metaandnotdst_metathenreturnendlocalroot=tree:add(p_cidr,"CIDR Metadata")ifsrc_metathenlocalsrc_tree=root:add(p_cidr,"Source")localadded_any=falsefor_,colinipairs(meta_columns)dolocalv=src_meta[col.key]ifvandv~=""thenlocalpf=src_fields_by_key[col.key]ifpfthensrc_tree:add(pf,v)added_any=trueendendendifnotadded_anythensrc_tree:append_text(" (no metadata)")endendifdst_metathenlocaldst_tree=root:add(p_cidr,"Destination")localadded_any=falsefor_,colinipairs(meta_columns)dolocalv=dst_meta[col.key]ifvandv~=""thenlocalpf=dst_fields_by_key[col.key]ifpfthendst_tree:add(pf,v)added_any=trueendendendifnotadded_anythendst_tree:append_text(" (no metadata)")endendendregister_postdissector(p_cidr)导入自 https://wiki.wireshark.org/Lua/Examples,时间为 2020-08-11 23:16:09 UTC