一、问题:
用Wireshark抓包查看HTTP POST消息,Content-Type为application/x-www-form-urlencoded(对应HTML为<form method="POST">那种提交后的网络包)时不太方便,因为抓包只显示了原始的param1=value1¶m2=value2这样的数据,没有把参数提出出来,并对参数进行url decode,如下图所示:
二、问题解决过程及心得
Google上没有找到解决方法,最近发现wireshark可以用lua编写解析器,我就试着自己写了一个。过程中走了一些弯路总结一下:
1. 不能用postdissectors,因为HTTP请求有时候是会分几个TCP包的,这种情况下postdissectors只能得到每个包的数据,不能得到完整的application/x-www-form-urlencoded体部内容。
2. 不用自己重新写一个dissector去监听http的tcp端口,这样,你不仅还要调用原来http的dissector,还要自己判断tcp分包了没(reassembled)。
刚开始的几种方法都不太理想,最后,看了一下wireshark源码中http解析包的实现,发现只要写一个dissector注册到media_type的dissector table里就可以了。具体的分析请参考《如何在wireshark里用lua脚本编写dissector解析HTTP BODY (after TCP reassembled)》(http://blog.csdn.net/jasonhwang/archive/2010/04/25/5526383.aspx)。
三、如何搞定!!
好,上面的东西大家看不懂没关系只要照下面步骤来一下就搞定了:
1. 在wireshark安装目录(如C:/Program Files/Wireshark)下编写一个文本文件my_form_urlencoded.lua,文件内容如下:
-- Decode param=value from "application/x-www-form-urlencoded" type http body -- Author: Huang Qiangxiong ([email protected]) -- change log: -- 2010-04-20 -- Just can play. -- 2010-04-24 -- Add option "Turn on/off debug tree item" to preference window. -- Add option "add_orig_item" to preference window. ------------------------------------------------------------------------------------------------ do local form_urlencoded_proto = Proto("my_form_urlencoded", "MIME Encapsulation [HQX's plugins]: application/x-www-form-urlencoded") --setup options that could be found in preferences->MY_FORM_URLENCODED local prefs = form_urlencoded_proto.prefs prefs.debug_flag = Pref.bool("Turn on debug (a [DEBUG Tree proto: my_form_urlencoded] item will appear in Package Details tree)", false, "If you turn of debug, (a [DEBUG Tree proto: my_form_urlencoded] item will appear in Package Details tree)") prefs.add_orig_item = Pref.bool("Show orignal wireshark's data-text-lines dissection item in Package Details tree", false, "Show orignal wireshark's data-text-lines dissection item in Package Details tree") -----------DEBUG Function ------------------------------------------------ --local debug_flag = true local dmap = {} function d(tree, msg) if prefs.debug_flag and tree then local dt = dmap[tree] if dt == nil then dt = tree:add("[DEBUG Tree for " .. form_urlencoded_proto.name .. "]") dmap[tree] = dt end dt:add("[DEBUG] " .. msg) end end --------------------------------------------------------------------------------- ---- url decode (from www.lua.org guide) function unescape (s) s = string.gsub(s, "+", " ") s = string.gsub(s, "%%(%x%x)", function (h) return string.char(tonumber(h, 16)) end) return s end ---- save old dissector local media_type_table = DissectorTable.get("media_type") local old_dissector = media_type_table:get_dissector("application/x-www-form-urlencoded") ---- my dissector function form_urlencoded_proto.dissector(tvb, pinfo, tree) d(tree, "pinfo.curr_proto=" .. pinfo.curr_proto) d(tree, "tvb:offset()=" .. tvb:offset()) d(tree, "tvb:len()=" .. tvb:len()) if prefs.add_orig_item then old_dissector:call(tvb, pinfo, tree) end -- begin build my tree local tvb_range = tvb() local content = tvb_range:string() -- add proto item to tree local subtree = tree:add(form_urlencoded_proto, tvb_range) -- add raw data to tree subtree:add(tvb_range, "[Raw Data] (" .. tvb_range:len() .. " bytes)"):add(tvb_range, content) -- add param value pair to tree local pairs_tree = subtree:add(tvb_range, "[Decoded Data]") local si = 1 local ei = 0 local count = 0 while ei do si = ei + 1 ei = string.find(content, "&", si) local xlen = (ei and (ei - si)) or (content:len() - si + 1) if xlen > 0 then pairs_tree:add(tvb(si-1, xlen), unescape(content:sub(si, si+xlen-1))) count = count + 1 end end pairs_tree:append_text(" (" .. count .. ")") end -- register this dissector media_type_table:add("application/x-www-form-urlencoded", form_urlencoded_proto) end
然后修改wireshark安装目录下的init.lua文件:
(1)把disable_lua = true; do return end;这行注释掉:在前面加“--”
(2)然后在init.lua文件最后面加一句:dofile("my_form_urlencoded.lua")
OK大功告成,重新打开抓包界面变为如下图:
可以看到,原来的Line-based Text...被替换成了行MIME Encapsulation ...。看该节点下[Raw Data]为原始数据,[Decoded Data]下为解开后的一个个参数(经过url decode)。
如果要显示原来的Line-based Text...行也可以,到preferences窗口里找到my_form_urlencoded协议,然后选上"Show orignal wireshark's data-text-lines dissection item in Package Details tree"选项即可。
实际上这种方法还可以解析其它媒体类型的HTTP Body,如何编写解析HTTP Body媒体数据的方法请参考《如何在wireshark里用lua脚本编写dissector解析HTTP BODY (after TCP reassembled)》(http://blog.csdn.net/jasonhwang/archive/2010/04/25/5526383.aspx)。
注:wireshark版本为1.2.7
2015年 更新:
-- Decode param=value from "application/x-www-form-urlencoded" type http body -- Author: Huang Qiangxiong ([email protected]) -- change log: -- 2010-08-23 -- Place spaces before and after '=' between name and value ( convert '=' to ' = '). -- 2010-04-20 -- Just can play. -- 2010-04-24 -- Add option "Turn on/off debug tree item" to preference window. -- Add option "add_orig_item" to preference window. ------------------------------------------------------------------------------------------------ do local form_urlencoded_proto = Proto("my_form_urlencoded", "MIME Encapsulation [HQX's plugins]: application/x-www-form-urlencoded") --setup options that could be found in preferences->MY_FORM_URLENCODED local prefs = form_urlencoded_proto.prefs prefs.debug_flag = Pref.bool("Turn on debug (a [DEBUG Tree proto: my_form_urlencoded] item will appear in Package Details tree)", false, "If you turn of debug, (a [DEBUG Tree proto: my_form_urlencoded] item will appear in Package Details tree)") prefs.add_orig_item = Pref.bool("Show orignal wireshark's data-text-lines dissection item in Package Details tree", false, "Show orignal wireshark's data-text-lines dissection item in Package Details tree") -----------DEBUG Function ------------------------------------------------ --local debug_flag = true local dmap = {} function d(tree, msg) if prefs.debug_flag and tree then local dt = dmap[tree] if dt == nil then dt = tree:add("[DEBUG Tree for " .. form_urlencoded_proto.name .. "]") dmap[tree] = dt end dt:add("[DEBUG] " .. msg) end end --------------------------------------------------------------------------------- ---- url decode (from www.lua.org guide) function unescape (s) s = string.gsub(s, "+", " ") s = string.gsub(s, "%%(%x%x)", function (h) return string.char(tonumber(h, 16)) end) return s end ---- save old dissector local media_type_table = DissectorTable.get("media_type") local old_dissector = media_type_table:get_dissector("application/x-www-form-urlencoded") ---- my dissector function form_urlencoded_proto.dissector(tvb, pinfo, tree) d(tree, "pinfo.curr_proto=" .. pinfo.curr_proto) d(tree, "tvb:offset()=" .. tvb:offset()) d(tree, "tvb:len()=" .. tvb:len()) if prefs.add_orig_item then old_dissector:call(tvb, pinfo, tree) end -- begin build my tree local tvb_range = tvb() local content = tvb_range:string() -- add proto item to tree local subtree = tree:add(form_urlencoded_proto, tvb_range) -- add raw data to tree subtree:add(tvb_range, "[Raw Data] (" .. tvb_range:len() .. " bytes)"):add(tvb_range, content) -- add param value pair to tree local pairs_tree = subtree:add(tvb_range, "[Decoded Data]") local si = 1 local ei = 0 local count = 0 while ei do si = ei + 1 ei = string.find(content, "&", si) local xlen = (ei and (ei - si)) or (content:len() - si + 1) if xlen > 0 then -- pairs_tree:add(tvb(si-1, xlen), unescape(content:sub(si, si+xlen-1))) pairs_tree:add(tvb(si-1, xlen), unescape(content:sub(si, si+xlen-1):gsub("=", " = ", 1))) count = count + 1 end end pairs_tree:append_text(" (" .. count .. ")") end -- register this dissector media_type_table:add("application/x-www-form-urlencoded", form_urlencoded_proto) end