luawireshark-dissectorole-automation

Lua wireshark dissector extract timestamp in OLE automation format


ref: OLE Automation date in lua

I'm writing a simple UDP dissector in LUA to decode packets that contain a timestamp that was encoded as an OLE automation date. The encoding results in an 8-byte data pattern:

C2 F5 4F 12 FD 6B E5 40     

which decodes as 43871.90848539352 = '2/10/2020 9:48:13 PM'. In C# .NET this is via the DateTime.ToOADate() method (which works fine).

Here is my simplified dissector, which 'decodes' an 8-byte message that consists only of the timestamp:

local logger_proto = Proto("logger", "LOGGER")
local logger_hdr={
    atime = ProtoField.absolute_time("test.atime","test atime",base.UTC),
    rtime = ProtoField.relative_time("test.rtime","test rtime",base.UTC),
}
logger_proto.fields=logger_hdr

function logger_proto.init() end

function logger_proto.dissector(tvbuf, pktinfo, root)
    root:add(logger_hdr.atime,tvbuf:range(0,8))
    root:add(logger_hdr.atime,tvbuf:range(8,8))
end

In the test packet I send the timestamp twice, the second time is with the bytes in reverse order to test for endian issues (the 'grasping at straws' approach). The packet data is:

C2 F5 4F 12 FD 6B E5 40 40 E5 6B FD 12 4F F5 C2 

the two retrieved timestamps are

Aug 25, 2073 03:14:26.-4360608 UTC
Jul  2, 2004 14:06:53.307230146 UTC

the 'correct' timestamp should be

Feb 10, 2020 21:48:13 UTC

It looks like ProtoField.absolute_time with base.UTC is not quite what I am looking for. So my questions are

  1. Is there a ProtoField option to extract this timestamp correctly, in a format that excludes fractional seconds?
  2. If not, how would I extract the date/time bytewise and make calculations to format the correct date by hand?

Solution

  • I had to learn a bit about OLE dates but found relevant information at DateTime.ToOADate Method. I also used information from the OLE Automation date in lua question as well as from Convert us-timestamp to absolute_time to come up with a possible solution for you. I doubt it's an optimal one, but it seems to work based on the data provided. Hopefully it works for you but if not, it should get you closer to a solution.

    A long-term solution would be if Wireshark could supply native support for OLE dates, and for that to happen, I'd recommend filing a Wireshark enhancement bug report requesting just such a thing.

    A possible solution (with some extra stuff for illustrative purposes):

    local p_logger = Proto("logger", "LOGGER")
    local logger_hdr = {
        -- Only the oletime_abs field is useful; the rest just help illustrate things
        -- and helped me during debugging.  Remove/rename as you see fit.
    
        oletime = ProtoField.double("logger.oletime", "OLE time"),
    
        -- Note: .int64 doesn't work so just using .int32
        oletime_days = ProtoField.int32("logger.oletime.days", "Days"),
        oletime_partial = ProtoField.double("logger.oletime.partial", "Partial"),
        oletime_abs = ProtoField.absolute_time("logger.oleabstime", "OLE Abs time", base.UTC)
    }
    p_logger.fields = logger_hdr
    
    function p_logger.dissector(tvbuf, pinfo, tree)
        local logger_tree = tree:add(p_logger, tvbuf(0,-1))
        local OLE_DIFF_TO_UNIX = 25569
        local SECS_PER_DAY = 86400
        local oletime = tvbuf:range(0, 8):le_float() - OLE_DIFF_TO_UNIX
        local oletime_days = math.floor(oletime)
        local oletime_partial = (oletime - oletime_days) * SECS_PER_DAY
        local secs = oletime_days * SECS_PER_DAY + math.floor(oletime_partial)
        local nstime = NSTime.new(secs, 0)
    
        pinfo.cols.protocol:set("LOGGER")
        logger_tree:add(logger_hdr.oletime, tvbuf:range(0, 8), oletime)
        logger_tree:add(logger_hdr.oletime_days, tvbuf:range(0, 8), oletime_days)
        logger_tree:add(logger_hdr.oletime_partial, tvbuf:range(0, 8), oletime_partial)
        logger_tree:add(logger_hdr.oletime_abs, tvbuf:range(0, 8), nstime)
    end
    
    -- Register dissector (this particular registration is just for my testing)
    local udp_table = DissectorTable.get("udp.port")
    udp_table:add(33333, p_logger)
    

    I'm sure this can be cleaned up/optimized, but I leave that as an exercise to the reader. Tested with the following hand-crafted packet:

    0000   00 0e b6 00 00 02 00 0e b6 00 00 01 08 00 45 00   ..............E.
    0010   00 37 00 00 40 00 40 11 b5 ea c0 00 02 65 c0 00   .7..@.@......e..
    0020   02 66 82 35 82 35 00 23 e8 54 c2 f5 4f 12 fd 6b   .f.5.5.#.T..O..k
    0030   e5 40 40 e5 6b fd 12 4f f5 c2 00 00 00 00 00 00   .@@.k..O........
    0040   00 00 00 00 00                                    .....
    

    ... and you should get the following decode of the "Logger" OLE date:

    LOGGER
        OLE time: 18302.9084853935
        Days: 18302
        Partial: 78493.1380001362
        OLE Abs time: Feb 10, 2020 21:48:13.000000000 UTC
    

    You can use text2pcap to convert it to a pcap file for testing. For example: text2pcap -a logger.txt logger.pcap ... where logger.txt is a text file containing the raw packet bytes pasted above.