Skip to content

Instantly share code, notes, and snippets.

@monyxie
Last active June 26, 2019 03:37
Show Gist options
  • Save monyxie/417c4175ba185a532f36677e5afce73d to your computer and use it in GitHub Desktop.
Save monyxie/417c4175ba185a532f36677e5afce73d to your computer and use it in GitHub Desktop.
MySQL Query Log Plugin for Wireshark
-- wireshark-mysql-query-log.lua
-- this script adds a menu entry in Wireshark for MySQL query logging
-- put this file under wiresharks' plugin directory,
-- typically "C:\Program Files\Wireshark\plugins" on Windows
-- and restart wireshark.
-- then choose "Tools > MySQL Query Log" in the menu.
instances = 0 -- number of instances of the tap created so far
-- calling tostring() on random FieldInfo's can cause an error, so this func handles it
local function getstring(finfo)
local ok, val = pcall(tostring, finfo)
if not ok then val = "(unknown)" end
return val
end
-- check if string starts with something
local function starts_with(str, start)
return str:sub(1, #start) == start
end
function mytap_menu()
instances = instances + 1
local td = {}
-- the tap data, locally accessible by every function of the tap
-- beware not to use a global for taps with multiple instances or you might
-- find it been written by more instances of the tap, not what we want.
-- each tap will have its own private instance of td.
td.win = TextWindow.new("MySQL Query Log " .. instances) -- the window we'll use
td.text = "" -- the text of the tap
td.instance = instances -- the instance number of this tap
td.last_port = 0
-- this tap will be local to the menu_function that called it
local tap = Listener.new(nil, "mysql.command == 3 || mysql.command == 22 || mysql.command == 23", true)
-- local tap = Listener.new(nil, "mysql.command == 23", true)
-- callback to remove the tap when the text window closes
function remove_tap()
if tap and tap.remove then
tap:remove()
end
end
-- make sure the tap doesn't hang around after the window was closed
td.win:set_atclose(remove_tap)
-- this function will be called for every packet
function tap.packet(pinfo,tvb,tapdata)
--local text = "packet " .. pinfo.number
--td.text = td.text .. "\n" .. text
-- debug("packet " .. pinfo.number, tapdata.instance)
local fields = { all_field_infos() }
local mysql_command = ""
local mysql_query = ""
local mysql_params = ""
for ix, finfo in pairs(fields) do
if finfo.name == "mysql.command" then
mysql_command = finfo.value
elseif finfo.name == "mysql.query" then
mysql_query = finfo.value
elseif starts_with(finfo.name, "mysql.exec.field.") then
if not (mysql_params == "") then
mysql_params = mysql_params .. ", "
end
mysql_params = mysql_params .. "'" .. getstring(finfo) .. "'"
end
end
local append_text = ""
if mysql_command == 3 then -- Plain query
append_text = "(QUERY) " .. mysql_query
elseif mysql_command == 22 then -- Prepare statement
append_text = "(PREPARE) " .. mysql_query
elseif mysql_command == 23 then -- Execute statement
append_text = "(EXECUTE) " .. mysql_params
end
if not (append_text == "") then
if td.last_port > 0 and not (td.last_port == pinfo.src_port) then
td.text = td.text .. "\n"
end
td.last_port = pinfo.src_port
td.text = td.text .. "[" .. pinfo.rel_ts .. "]"
td.text = td.text .. " <" .. getstring(pinfo.src) .. ":" .. pinfo.src_port .. " - " .. getstring(pinfo.dst) .. ":" .. pinfo.dst_port .. "> "
td.text = td.text .. append_text .. "\n"
end
end
-- this function will be called once every few seconds to redraw the window
function tap.draw()
td.win:append(td.text)
td.text = ""
-- debug("draw", tapdata.instance)
end
end
-- last we register the menu
-- the first arg is the menu name
-- the 2nd arg is the function to be called
-- the third argument is the menu to hold this new menu
register_menu("MySQL Query Log", mytap_menu, MENU_TOOLS_UNSORTED)
-- debug("registered")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment