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