Wireshark lua dissector 对TCP消息包合并分析
应用程序发送的数据报都是流式的,IP不保证同一个一个应用数据包会被抓包后在同一个IP数据包中,因此对于使用自制dissector的时候需要考虑这种情况。
Lua Dissector相关资料可以见:http://wiki.wireshark.org/Lua/Dissectors
Lua脚本书写wireshark dissector非常方便,使用Lua合并tcp数据报进行分析的样例如下,其实就是多了一个条件分支,所谓难者不会,会者不难:
1 local slicer = Proto("slicer","Slicer") 2 function slicer.dissector(tvb, pinfo, tree) 3 local offset = pinfo.desegment_offset or 0 4 5 local len = get_len() -- for tests i used a constant, but can be taken from tvb 6 7 while true do 8 local nxtpdu = offset + len 9 10 if nxtpdu > tvb:len() then 11 pinfo.desegment_len = nxtpdu - tvb:len() 12 pinfo.desegment_offset = offset 13 return 14 end 15 16 tree:add(slicer, tvb(offset, len)) 17 18 offset = nxtpdu 19 20 if nxtpdu == tvb:len() then 21 return 22 end 23 end 24 end 25 local tcp_table = DissectorTable.get("tcp.port") 26 tcp_table:add(2506, slicer)
对于Lua Dissector脚本使用方法如下:
tshark
tshark -X lua_script:
slicer.lua -i lo0 -f "tcp port 2506" -O aa -V
Wireshark
On OSX
Copy slicer.lua
to ~/.wireshark
Add dofile(USER_DIR.."slicer
to the end of .lua
")/Applications/Wireshark.app/Contents/Resources/share/wireshark/init.lua
在wireshark的C语言版本中,有针对tcp合并报的相关函数,packet-tcp.c 具体见下:
/* 2152 * Loop for dissecting PDUs within a TCP stream; assumes that a PDU 2153 * consists of a fixed-length chunk of data that contains enough information 2154 * to determine the length of the PDU, followed by rest of the PDU. 2155 * 2156 * The first three arguments are the arguments passed to the dissector 2157 * that calls this routine. 2158 * 2159 * "proto_desegment" is the dissector's flag controlling whether it should 2160 * desegment PDUs that cross TCP segment boundaries. 2161 * 2162 * "fixed_len" is the length of the fixed-length part of the PDU. 2163 * 2164 * "get_pdu_len()" is a routine called to get the length of the PDU from 2165 * the fixed-length part of the PDU; it's passed "pinfo", "tvb" and "offset". 2166 * 2167 * "dissect_pdu()" is the routine to dissect a PDU. 2168 */ 2169 void 2170 tcp_dissect_pdus(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, 2171 gboolean proto_desegment, guint fixed_len, 2172 guint (*get_pdu_len)(packet_info *, tvbuff_t *, int), 2173 dissector_t dissect_pdu) 2174 { 2175 volatile int offset = 0; 2176 int offset_before; 2177 guint length_remaining; 2178 guint plen; 2179 guint length; 2180 tvbuff_t *next_tvb; 2181 proto_item *item=NULL; 2182 void *pd_save; 2183 2184 while (tvb_reported_length_remaining(tvb, offset) != 0) { 2185 /* 2186 * We use "tvb_ensure_length_remaining()" to make sure there actually 2187 * *is* data remaining. The protocol we're handling could conceivably 2188 * consists of a sequence of fixed-length PDUs, and therefore the 2189 * "get_pdu_len" routine might not actually fetch anything from 2190 * the tvbuff, and thus might not cause an exception to be thrown if 2191 * we've run past the end of the tvbuff. 2192 * 2193 * This means we're guaranteed that "length_remaining" is positive. 2194 */ 2195 length_remaining = tvb_ensure_length_remaining(tvb, offset); 2196 2197 /* 2198 * Can we do reassembly? 2199 */ 2200 if (proto_desegment && pinfo->can_desegment) { 2201 /* 2202 * Yes - is the fixed-length part of the PDU split across segment 2203 * boundaries? 2204 */ 2205 if (length_remaining < fixed_len) { 2206 /* 2207 * Yes. Tell the TCP dissector where the data for this message 2208 * starts in the data it handed us and that we need "some more 2209 * data." Don't tell it exactly how many bytes we need because 2210 * if/when we ask for even more (after the header) that will 2211 * break reassembly. 2212 */ 2213 pinfo->desegment_offset = offset; 2214 pinfo->desegment_len = DESEGMENT_ONE_MORE_SEGMENT; 2215 return; 2216 } 2217 } 2218 2219 /* 2220 * Get the length of the PDU. 2221 */ 2222 plen = (*get_pdu_len)(pinfo, tvb, offset); 2223 if (plen < fixed_len) { 2224 /* 2225 * Either: 2226 * 2227 * 1) the length value extracted from the fixed-length portion 2228 * doesn't include the fixed-length portion's length, and 2229 * was so large that, when the fixed-length portion's 2230 * length was added to it, the total length overflowed; 2231 * 2232 * 2) the length value extracted from the fixed-length portion 2233 * includes the fixed-length portion's length, and the value 2234 * was less than the fixed-length portion's length, i.e. it 2235 * was bogus. 2236 * 2237 * Report this as a bounds error. 2238 */ 2239 show_reported_bounds_error(tvb, pinfo, tree); 2240 return; 2241 } 2242 2243 /* 2244 * Do not display the the PDU length if it crosses the boundary of the 2245 * packet and no more packets are available. 2246 * 2247 * XXX - we don't necessarily know whether more packets are 2248 * available; we might be doing a one-pass read through the 2249 * capture in TShark, or we might be doing a live capture in 2250 * Wireshark. 2251 */ 2252 #if 0 2253 if (length_remaining >= plen || there are more packets) 2254 { 2255 #endif 2256 /* 2257 * Display the PDU length as a field 2258 */ 2259 item=proto_tree_add_uint(pinfo->tcp_tree, hf_tcp_pdu_size, 2260 tvb, offset, plen, plen); 2261 PROTO_ITEM_SET_GENERATED(item); 2262 #if 0 2263 } else { 2264 item = proto_tree_add_text(pinfo->tcp_tree, tvb, offset, -1, 2265 "PDU Size: %u cut short at %u",plen,length_remaining); 2266 PROTO_ITEM_SET_GENERATED(item); 2267 } 2268 #endif 2269 2270 2271 /* give a hint to TCP where the next PDU starts 2272 * so that it can attempt to find it in case it starts 2273 * somewhere in the middle of a segment. 2274 */ 2275 if(!pinfo->fd->flags.visited && tcp_analyze_seq) { 2276 guint remaining_bytes; 2277 remaining_bytes=tvb_reported_length_remaining(tvb, offset); 2278 if(plen>remaining_bytes) { 2279 pinfo->want_pdu_tracking=2; 2280 pinfo->bytes_until_next_pdu=plen-remaining_bytes; 2281 } 2282 } 2283 2284 /* 2285 * Can we do reassembly? 2286 */ 2287 if (proto_desegment && pinfo->can_desegment) { 2288 /* 2289 * Yes - is the PDU split across segment boundaries? 2290 */ 2291 if (length_remaining < plen) { 2292 /* 2293 * Yes. Tell the TCP dissector where the data for this message 2294 * starts in the data it handed us, and how many more bytes we 2295 * need, and return. 2296 */ 2297 pinfo->desegment_offset = offset; 2298 pinfo->desegment_len = plen - length_remaining; 2299 return; 2300 } 2301 } 2302 2303 /* 2304 * Construct a tvbuff containing the amount of the payload we have 2305 * available. Make its reported length the amount of data in the PDU. 2306 * 2307 * XXX - if reassembly isn't enabled. the subdissector will throw a 2308 * BoundsError exception, rather than a ReportedBoundsError exception. 2309 * We really want a tvbuff where the length is "length", the reported 2310 * length is "plen", and the "if the snapshot length were infinite" 2311 * length is the minimum of the reported length of the tvbuff handed 2312 * to us and "plen", with a new type of exception thrown if the offset 2313 * is within the reported length but beyond that third length, with 2314 * that exception getting the "Unreassembled Packet" error. 2315 */ 2316 length = length_remaining; 2317 if (length > plen) 2318 length = plen; 2319 next_tvb = tvb_new_subset(tvb, offset, length, plen); 2320 2321 /* 2322 * Dissect the PDU. 2323 * 2324 * Catch the ReportedBoundsError exception; if this particular message 2325 * happens to get a ReportedBoundsError exception, that doesn't mean 2326 * that we should stop dissecting PDUs within this frame or chunk of 2327 * reassembled data. 2328 * 2329 * If it gets a BoundsError, we can stop, as there's nothing more to 2330 * see, so we just re-throw it. 2331 */ 2332 pd_save = pinfo->private_data; 2333 TRY { 2334 (*dissect_pdu)(next_tvb, pinfo, tree); 2335 } 2336 CATCH(BoundsError) { 2337 RETHROW; 2338 } 2339 CATCH(ReportedBoundsError) { 2340 /* Restore the private_data structure in case one of the 2341 * called dissectors modified it (and, due to the exception, 2342 * was unable to restore it). 2343 */ 2344 pinfo->private_data = pd_save; 2345 show_reported_bounds_error(tvb, pinfo, tree); 2346 } 2347 ENDTRY; 2348 2349 /* 2350 * Step to the next PDU. 2351 * Make sure we don't overflow. 2352 */ 2353 offset_before = offset; 2354 offset += plen; 2355 if (offset <= offset_before) 2356 break; 2357 } 2358 } 2359