编写lua脚本扩展ethereal的功能
一直用ethereal做一些网络截包的工作,感叹于ethereal强大的同时,也为其无法如analsys捕获工具的netpdl语言一般扩展而感到不便,自己写解析器又觉得麻烦,现在ethereal由了0.99.0版本(一个跟前一个版本跳跃很大的版本号),支持lua扩展,真是福音。
当然做法就是采用lua扩展,把一些基本功能变成lua可以调用的库,然后在启动的时候把lua脚本作为参数传递进去。用lua脚本可以做三种方式的事情:
tap:一种从每一帧中提取数据的方式,可以用显示过滤器定义,下面是一个例子:
一个简单的tap,可以用于tethereal,用于统计通过10.0.0.1得http和dns包:
-- this is going to be our counter
http_packets = 0
-- this is going to be our tap
tap_http = nil
-- first we declare the tap called "http tap" with the filter it is going to use
tap_http = new_tap("mytap","ip.addr == 10.0.1.3 && http")
-- then we define a function to (re)initialize our counter
-- this one is going to be called every time the capture restarts (2)
function tap_http.init()
http_packets = 0
end
-- this function will get called at the end(3) of the capture to print the summary
function tap_http.draw()
print("http packets:",http_packets)
end
-- this function is going to be called once each time the filter of the tap matches
function tap_http.packet()
http_packets = http_packets + 1
-- We return true to have ethereal printing a report every few seconds
-- if we returned false ethereal would call the draw function never
return true
end
GUI方式
-- text_window_tap.lua
-- an example of a tap that registers a menu
-- and prints to a text window
instances = 0 -- number of instances of the tap created so far
function mytap_menu()
instances = instances + 1
local td = {}
-- the tap data, passed to 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.
td.win = TextWindow.new("My Tap " .. instances) -- the window we'll use
td.text = "" -- the text of the tap
td.instance = instances -- the instance number of this tap
-- this tap will be local to the menu_function that called it
-- it's called mytap
-- has no filter (filter = nil)
-- and we pass to it the tap data so that it gets passed to the tap's functions
local tap = new_tap("mytap"..instances,nil, td)
-- make sure the tap doesn't hang arround after the window was closed
td.win:at_close(remove_tap,tap)
-- this function will be called for every packet
function tap.packet(pinfo,tvb,tapdata)
local text = "packet " .. pinfo.number
tapdata.text = tapdata.text .. "/n" .. text
-- print("packet " .. pinfo.number, tapdata.instance)
end
-- this function will be called once every few seconds to redraw the window
function tap.draw(tapdata)
tapdata.win:set(tapdata.text)
-- print("draw", tapdata.instance)
end
-- this function will be called before every run of the tap
function tap.init(tapdata)
tapdata.text = ""
-- print("init", 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 (defaults to false) tells to re-run the capture once the function is run
register_menu("Lua Tap Test",mytap_menu,true)
-- print("registered")
dissectors:解析器
解析器用与分析包的数据,类似C解析器,可以注册lua写的解析器来某个协议,ethereal会将一个数据缓冲区和一个包信息传递给lua处理函数
-- trivial protocol example
-- declare our protocol
trivial_proto = Protocol("trivial","TRIVIAL","Trivial Protocol")
-- create a function to dissect it
function trivial_proto.dissector(buffer,pinfo,tree)
pinfo.cols.protocol = "TRIVIAL"
local subtree = tree:add_item(trivial_proto,buffer(),"Trivial Protocol Data"):add_subtree()
subtree:add_item(buffer(0,2),"The first two bytes: " .. buffer(0,2):uint())
end
-- load the udp.port table
udp_table = DissectorTable.get("udp.port")
-- register our protocol to handle udp port 7777
udp_table:add(7777,trivial_proto)
postdissectors 后解析器:
一个后解析器是在其他解析器已经被调用后才调用的解析器,由于所有的域已经被解析,所以这种类型的解析器可以存取所有域并且能加域到解析树中:
-- trivial postdissector example
-- declare some Fields to be read
ip_src_f = Field("ip.src")
ip_dst_f = Field("ip.dst")
tcp_src_f = Field("tcp.srcport")
tcp_dst_f = Field("tcp.dstport")
-- declare our (pseudo) protocol
trivial_proto = Protocol("trivial","TRIVIAL","Trivial Postdissector")
-- create the fields for our "protocol"
src_F = ProtoField.string("trivial.src","Source");
dst_F = ProtoField.string("trivial.dst","Destination");
conv_F = ProtoField.string("trivial.conv","Conversation","A Conversation");
-- add the field to the protocol
trivial_proto.fields = ProtoFieldArray.new(src_F, dst_F, conv_F)
-- create a function to "postdissect" each frame
function trivial_proto.dissector(buffer,pinfo,tree)
-- obtain the current values the protocol fields
local tcp_src = tcp_src_f()
local tcp_dst = tcp_dst_f()
local ip_src = ip_src_f()
local ip_dst = ip_dst_f()
if tcp_src then
local subtree = tree:add_item(trivial_proto,"Trivial Protocol Data"):add_subtree()
local src = ip_src .. ":" .. tcp_src
local dst = ip_dst .. ":" .. tcp_dst
local conv = src .. "->" .. dst
subtree:add_item(src_F,src)
subtree:add_item(dst_F,dst)
subtree:add_item(conv_F,conv)
end
end
-- register our protocol as a postdissector
register_postdissector(trivial_proto)
看来,以后常跟截包打交道的同仁们有了自由发挥的余地了,记得一定要学lua,这是最火的脚本语言之一