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.lua") to the end of /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