This module introduces two functions. The first one is used to get the memory address and the size of the buffer used to receive a network packet. While the second one is used to return a list of memory addresses and sizes of buffers used to send a network packet.

Source

"""
This module introduces two functions. The first one is used to get the memory address and the size of the buffer
used to receive a network packet. While the second one is used to return a list of memory addresses and
sizes of buffers used to send a network packet.

"""

import reven2


def get_memory_address_and_size_of_received_network_packet(ctx):
    """
    This function returns a pair of memory address, size of the received packet
    'ctx' must be a context resulting from searching "RxPacketAssemble" symbol in the trace

    Information
    ===========

    @param ctx: the context used to retrieve the list of memory address and size of sent packet buffer

    @return a list of tuples of L{address.LogicalAddress} and C{int}
    """

    # To get the memory address of received packets, we need to dereference multiple times some pointers in memory.
    # The first one is rcx, as argument. It points to a huge structure.
    # at rcx+0x308 is a pointer to a structure, which contains the size at +8
    # at rcx+0x328 is a pointer index that points the right structure to get for the buffer
    # at rcx+0x328+8 * index is a pointer to the network buffer.

    # at rcx+0x308, then deref +0xc is a byte that is tested at 1 and 2. If 0, then the call to RxPacketAssemble is
    # the last one of a serie and doesn't contain any buffer to fetch.

    # Get a pointer to the huge structure
    pHugeStruct = ctx.read(reven2.arch.x64.rcx, reven2.types.Pointer(reven2.types.USize))

    # Deref to get a pointer to the structure that contains the size
    pSizeStruct = ctx.read(pHugeStruct + 0x308, reven2.types.Pointer(reven2.types.USize))

    u8Flag = ctx.read(pSizeStruct + 0xC, reven2.types.U8)

    # Is last packet part?
    if (u8Flag & 0x3) == 0:
        return None, None

    u32Size = ctx.read(pSizeStruct + 0x8, reven2.types.U32)

    # Next get the index in the structure
    pu32IndexRaw = ctx.read(pHugeStruct + 0x328, reven2.types.Pointer(reven2.types.USize))

    # The index is a dword (eax is used)
    u32IndexRaw = ctx.read(pu32IndexRaw, reven2.types.U32)

    # Now, the system perform an operation on this index.
    u32Index = (u32IndexRaw + u32IndexRaw * 2) * 2

    # Now get a pointer to the buffer
    pArray = ctx.read(pHugeStruct + 0x328, reven2.types.Pointer(reven2.types.USize)) + 8
    pBuffer = ctx.read(pArray + 8 * u32Index + 0x20, reven2.types.Pointer(reven2.types.USize))

    return pBuffer, u32Size


def get_memory_addresses_and_sizes_of_sent_network_packet(ctx):
    """
    This Function returns a list of memory address, size of a sent packet

    'ctx' must be a context resulting from searching "E1000SendNetBufferLists" symbol in the trace

    Information
    ===========

    @param ctx: the context used to retrieve the memory address and size of received packet buffer

    @return a tuple of L{address.LogicalAddress} and C{int}
    """

    # The NET_BUFFER_LIST has a pointer to the first NET_BUFFER at +8
    # The NET_BUFFER has multiple important thing.
    #     The first entry (+0) is a pointer to the next NET_BUFFER
    #     The second entry (+8) is a pointer to an MDL
    #     The third entry (+0x10) is a pointer to the offset inside the MDL, for the data.

    pNetBufferList = ctx.read(reven2.arch.x64.rdx, reven2.types.Pointer(reven2.types.USize))
    pNetBuffer = ctx.read(pNetBufferList + 0x8, reven2.types.Pointer(reven2.types.USize))

    # The first part of the buffer need an offset in the MDL. Seems like the other one don't.
    pMdl = ctx.read(pNetBuffer + 0x8, reven2.types.Pointer(reven2.types.USize))
    pMdlOffset = ctx.read(pNetBuffer + 0x10, reven2.types.U32)

    packet_memory_addresses = []
    packet_memory_addresses.append(_get_network_packet_address_from_mdl(ctx, pMdl, pMdlOffset=pMdlOffset))

    # Next, check other mdl if they exist, and get the buffer for each one of them
    pNextMdl = ctx.read(pMdl, reven2.types.Pointer(reven2.types.USize))

    while pNextMdl.offset != 0:
        packet_memory_addresses.append(_get_network_packet_address_from_mdl(ctx, pNextMdl))

        pNextMdl = ctx.read(pNextMdl, reven2.types.Pointer(reven2.types.USize))
    return packet_memory_addresses


def get_all_send_recv_packet_context(reven_server):
    """
    This function return a list of all contexts used to send or receive network packets.

    To get these contexts, this function searches the symbol `E1000SendNetBufferLists` to get contexts of
    sent network packets. and searches the symbol `RxPacketAssemble` to get contexts of received network packets.

    This function requires that the trace has the PDB of `e1g6032e.sys` binary otherwise no context will be found.

    'reven_server' is the L{reven2.RevenServer} instance on which to perform the search

    Information
    ===========

    @param reven_server: L{reven2.RevenServer} instance on which to search packets

    @return a tuple of send packet list and received packet list
    """
    # Get generators of search results
    send_queries = [
        reven_server.trace.search.symbol(symbol)
        for symbol in reven_server.ossi.symbols(pattern="E1000SendNetBufferLists", binary_hint="e1g6032e.sys")
    ]
    recv_queries = [
        reven_server.trace.search.symbol(symbol)
        for symbol in reven_server.ossi.symbols(pattern="RxPacketAssemble", binary_hint="e1g6032e.sys")
    ]

    if len(send_queries) == 0 and len(recv_queries) == 0:
        print(
            "No network packets exist in this trace, make sure that this trace is a network trace,"
            " and if it is, make sure that the PDB of `e1g6032e.sys` binary is available in the scenario"
        )

    return send_queries, recv_queries


def _get_network_packet_address_from_mdl(ctx, pMdl, pMdlOffset=0):
    """
    Here is the structure MDL
    typedef struct _MDL {
      struct _MDL      *Next;
      CSHORT           Size;
      CSHORT           MdlFlags;
      struct _EPROCESS *Process;
      PVOID            MappedSystemVa;
      PVOID            StartVa;
      ULONG            ByteCount;
      ULONG            ByteOffset;
    } MDL, *PMDL;

    In our case, we get the StartVa, the byte count, and the offset from the NET_BUFFER
    """

    pBufferStartVa = ctx.read(pMdl + 0x18, reven2.types.Pointer(reven2.types.USize))

    u32Size = ctx.read(pMdl + 0x28, reven2.types.U32)

    return pBufferStartVa + pMdlOffset, u32Size - pMdlOffset