9.3. How to handle transformed data
Some protocols do clever things with data. They might possibly
encrypt the data, or compress data, or part of it. If you know
how these steps are taken it is possible to reverse them within the
dissector.
As encryption can be tricky, let's consider the case of compression.
These techniques can also work for other transformations of data,
where some step is required before the data can be examined.
What basically needs to happen here, is to identify the data that
needs conversion, take that data and transform it into a new stream,
and then call a dissector on it.
Often this needs to be done "on-the-fly" based on clues in the packet.
Sometimes this needs to be used in conjunction with other techniques,
such as packet reassembly. The following shows a technique to
achieve this effect.
Example 9.13. Decompressing data packets for dissection.
guint8 flags = tvb_get_guint8(tvb, offset);
offset ++;
if (flags & FLAG_COMPRESSED) { /* the remainder of the packet is compressed */
guint16 orig_size = tvb_get_ntohs(tvb, offset);
guchar *decompressed_buffer = (guchar*)g_malloc(orig_size);
offset += 2;
decompress_packet(tvb_get_ptr(tvb, offset, -1),
tvb_length_remaining(tvb, offset),
decompressed_buffer, orig_size);
/* Now re-setup the tvb buffer to have the new data */
next_tvb = tvb_new_real_data(decompressed_buffer, orig_size, orig_size);
tvb_set_child_real_data_tvbuff(tvb, next_tvb);
add_new_data_source(pinfo, next_tvb, "Decompressed Data");
} else {
next_tvb = tvb_new_subset(tvb, offset, -1, -1);
}
offset = 0;
/* process next_tvb from here on */
The first steps here are to recognise the compression. In this case
a flag byte alerts us to the fact the remainder of the packet is compressed.
Next we retrieve the original size of the packet, which in this case
is conveniently within the protocol. If it's not, it may be part of the
compression routine to work it out for you, in which case the logic would
be different.
So armed with the size, a buffer is allocated to receive the uncompressed
data using g_malloc, and the packet is decompressed into it.
The tvb_get_ptr function is useful to get a pointer to the raw data of
the packet from the offset onwards. In this case the
decompression routine also needs to know the length, which is
given by the tvb_length_remaining function.
Next we build a new tvb buffer from this data, using the tvb_new_real_data
call. This data is a child of our original data, so we acknowledge that
in the next call to tvb_set_child_real_data_tvbuff.
Finally we add this data as a new data source, so that
the detailed display can show the decompressed bytes as well as the original.
One procedural step is to add a handler to free the data when it's no longer needed.
In this case as g_malloc was used to allocate the memory, g_free is the appropriate
function.
After this has been set up the remainder of the dissector can dissect the
buffer next_tvb, as it's a new buffer the offset needs to be 0 as we start
again from the beginning of this buffer. To make the rest of the dissector
work regardless of whether compression was involved or not, in the case that
compression was not signaled, we use the tvb_new_subset to deliver us
a new buffer based on the old one but starting at the current offset, and
extending to the end. This makes dissecting the packet from this point on
exactly the same regardless of compression.