0

I'm trying to intercept and locate tcp packets in memory through debugging of windows 10. Suppose I set a break point at TcpReceive, the call stack looks like this

kd> kc
# Call Site
tcpip!TcpReceive
tcpip!TcpNlClientReceiveDatagrams
tcpip!IppProcessDeliverlist
tcpip!IppReceiveHeaderBatch
tcpip!IppReceivePackets
tcpip!FlpReceiveNonPreValidatedNetBufferListChain
tcpip!FlReceiveNetBufferListChainCalloutRoutine
nt!KeExpandKernelStackAndCalloutInternal
nt!KeExpandKernelStackAndCalloutEx
tcpip!NetioExpandKernelStackAndCallout
tcpip!FlReceiveNetBufferListChain
NDIS!ndisMIndicateNetBufferlistsToOpen
NDIS!ndisMTopReceiveNetBufferLists

Here are the problems:

  1. Through static analysis of code in IDA. tcpip!IppProcessDeliverlist doesn't even call TcpNlClientReceiveDatagrams. The code of tcpip.sys(file directly extracted from machine) doesn't even have tcpip!IppReceivePackets. What might be the problem?
  2. There are multiple protocols that will use tcpip!IppReceiveHeaderBatch. But I only want break point of tcpip!IppReceiveHeaderBatch to be triggered when tcpip!TcpReceive is triggered. My approach is
bp tcpip!TcpReceive
bp tcpip!IppReceiveHeaderBatch ".echo header batch;r;k;g" //display function arguments

But the output of IppReceiveHeaderBatch just above TcpReceive won't necessarily be the actual upstream of TcpReceive. How do I know which output is the real upstream?

  1. Windows use NET_BUFFER and NET_BUFFER_LIST to store packet data. I can use !ndiskd.nb and !ndiskd.nbl to walk through them and I also have the offset and address of the above structures. But these 2 structures serve for general purpose and doesn't have the section in header like IP,port number. Memory scan of Mdl and physical address doesn't help. Can someone point out a direction?
typedef struct _NET_BUFFER {
      NET_BUFFER *Next;
      MDL        *CurrentMdl;
      ULONG      CurrentMdlOffset;
...
PHYSICAL_ADDRESS DataPhysicalAddress;
...
}
2
  • What you might be seeing is that kc is walking the frame pointers, and it's likely that the function being called does not use a frame pointer. This results in a partial view of the callstack. However, if you go to your Disassembly Options in IDA and enable display of the "Stack Pointer". This will show you the distance to the top of the frame for any given instruction address. Essentially if you add this value to @rsp, that should shift your stack to the actual return address. You can then use that address to get the next frame size and repeat until you get to the actual top of the stack. Commented Dec 5, 2023 at 15:34
  • Solution to point 2 might be slow, but you can use 2 conditional breakpoints. Within the conditions of those breakpoints, set a temporary register once the correct path is taken and clear the temporary register with another breakpoint on exit. Then for the address you're trying to monitor, only perform your desired actions when that temporary register is actually set. Commented Dec 5, 2023 at 15:38

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.