Click here to Skip to main content
15,892,737 members
Articles / Desktop Programming / Windows Forms

NDIS MONITOR .NET 32-bit v1.00

Rate me:
Please Sign up or sign in to vote.
4.81/5 (36 votes)
27 Apr 20078 min read 176.3K   9.9K   90  
NDIS Monitor allows to catch and log the exchange of packet data between NDIS miniport drivers and network protocol modules that occurs in kernel space.
//
// NdisHook.cpp - by Vito Plantamura - (c) 2003 - All rights reserved.
//

#include "NdisHook.h"
#include "resource.h"
#include "..\Include\InstallDriver.h"

//
// DEFINITIONS.
//

enum EHookStatus
{
	EHS_NOT_INITIALIZED = 0,
	EHS_INITIALIZING,
	EHS_FUNCTIONING,
	EHS_STOPPING
};

#define MACRO_STORAGE_BUFFER_SIZE			( 1 * 1024 * 1024 )

//
// MODULE PRIVATE STORAGE.
//

static HINSTANCE				g_hiModule = NULL;

static LONG						g_lHooked;
static HANDLE					g_hKernelEvent;
static HANDLE					g_hDevice;
static PINITIALIZE_HOOK_OUTPUT	g_phoOutData;
static CRITICAL_SECTION			g_csSerialCs;
static BYTE*					g_pbStorage;
static CRITICAL_SECTION			g_csWaitPacketCs;
static HANDLE					g_hExitFromThread;
static HANDLE					g_hThreadExited;

static HOOK_STATS				g_hsPrevStats;

//
// HELPERS.
//

class CApiSerializationClass
{
public:
	CApiSerializationClass ()
	{
		::EnterCriticalSection( & g_csSerialCs );
	}
	virtual ~ CApiSerializationClass ()
	{
		::LeaveCriticalSection( & g_csSerialCs );
	}
};

#define SERIAL_ACCESS			CApiSerializationClass			__api_serialization_scope_instance__;

class CWaitForPacketAccessClass
{
public:
	CWaitForPacketAccessClass ()
	{
		::EnterCriticalSection( & g_csWaitPacketCs );
	}
	virtual ~ CWaitForPacketAccessClass ()
	{
		::LeaveCriticalSection( & g_csWaitPacketCs );
	}
};

#define WAITPACKET_ACCESS		CWaitForPacketAccessClass		__wait_for_packet_scope_access_instance__;

//
// MODULE ENTRY POINT.
//

BOOL WINAPI DllMain(
  HINSTANCE hinstDLL,  // handle to the DLL module
  DWORD fdwReason,     // reason for calling function
  LPVOID lpvReserved   // reserved
)
{
	// initializes or uninitializes global resources

	if ( fdwReason == DLL_PROCESS_ATTACH )
	{
		// helper.
		g_hiModule = hinstDLL;

		// state.
		g_lHooked = EHS_NOT_INITIALIZED;
		g_hKernelEvent = ::CreateEvent( NULL, FALSE, FALSE, NULL );
		g_hDevice = NULL;
		g_phoOutData = NULL;
		::InitializeCriticalSection( & g_csSerialCs );
		g_pbStorage = (PBYTE) ::malloc( MACRO_STORAGE_BUFFER_SIZE );
		::InitializeCriticalSection( & g_csWaitPacketCs );
		g_hExitFromThread = ::CreateEvent( NULL, TRUE, FALSE, NULL );
		g_hThreadExited = ::CreateEvent( NULL, TRUE, FALSE, NULL );
	}
	else if ( fdwReason == DLL_PROCESS_DETACH )
	{
		// stop.
		Stop ();

		// free.
		::CloseHandle( g_hKernelEvent );
		if ( g_hDevice )
			::CloseHandle( g_hDevice );
		if ( g_phoOutData )
			::free( g_phoOutData );
		::DeleteCriticalSection( & g_csSerialCs );
		if ( g_pbStorage )
			::free( g_pbStorage );
		::DeleteCriticalSection( & g_csWaitPacketCs );
		::CloseHandle( g_hExitFromThread );
		::CloseHandle( g_hThreadExited );

		// close.
		g_hiModule = NULL;
	}

	return TRUE;
}

//
// MODULE EXPORTS.
//

NH_EXPORT BOOLEAN NH_API Start(
	IN DWORD				dwBacklog,
	IN DWORD				dwLtLoopbackIp,
	IN DWORD				dwLtNatIp,
	OUT PNT_PROTOCOL_LIST*	pplProtocolListPtr
)
{
	SERIAL_ACCESS

	* pplProtocolListPtr = NULL;

	// check previous state.

	LONG			prev = ::InterlockedCompareExchange( & g_lHooked, EHS_INITIALIZING, EHS_NOT_INITIALIZED );
	if ( prev != EHS_NOT_INITIALIZED )
		return FALSE;

	// check out the event creation.

	if ( g_hKernelEvent == NULL )
	{
		g_lHooked = EHS_NOT_INITIALIZED;
		return FALSE;
	}

	// load the device driver.

	BOOL		bDriverFileCreatRes = ::CreateDriverFileFromAppResources( g_hiModule, MAKEINTRESOURCE( IDR_VPC_SYSFILE ), "SYSFILE", "vpcknt.sys" );
	if ( bDriverFileCreatRes == FALSE )
	{
		g_lHooked = EHS_NOT_INITIALIZED;
		return FALSE;
	}

	HANDLE		hDevice = NULL;
	BOOL		bInstallDriverRes = ::StartVersionedDeviceDriver( & hDevice, "vpcknt.sys", "vpcknt", IOCTL_VPCKNT_GET_VERSION, _VPCKnt_DRIVER_VERSION_, NULL );
	if ( bInstallDriverRes == FALSE || hDevice == NULL )
	{
		g_lHooked = EHS_NOT_INITIALIZED;
		return FALSE;
	}

	// save the handle to the device object / delete the sys file.

	g_hDevice = hDevice;

	::DeleteDriverFile( "vpcknt.sys" );

	// send the initial IOCTL.

	DWORD		dwBytesReturned = 0;

	PINITIALIZE_HOOK_INPUT		pihiHookInput = (PINITIALIZE_HOOK_INPUT) ::malloc( INITIALIZE_HOOK_INPUT_SIZE );
	PINITIALIZE_HOOK_OUTPUT		pihoHookOutput = (PINITIALIZE_HOOK_OUTPUT) ::malloc( INITIALIZE_HOOK_OUTPUT_MAXSIZE );

	if ( pihiHookInput == NULL )
	{
		::CloseHandle( g_hDevice );
		g_hDevice = NULL;

		g_lHooked = EHS_NOT_INITIALIZED;

		return FALSE;
	}
	else if ( pihoHookOutput == NULL )
	{
		::free( pihiHookInput );

		::CloseHandle( g_hDevice );
		g_hDevice = NULL;

		g_lHooked = EHS_NOT_INITIALIZED;

		return FALSE;
	}

	::memset( pihiHookInput, 0, INITIALIZE_HOOK_INPUT_SIZE );
	::memset( pihoHookOutput, 0, INITIALIZE_HOOK_OUTPUT_MAXSIZE );

	pihiHookInput->hNotificationEvent = g_hKernelEvent;
	pihiHookInput->dwPacketsBuffMaxItems = dwBacklog;
	pihiHookInput->dwLtLoopbackAdapterIpAddress = dwLtLoopbackIp;
	pihiHookInput->dwLtTrickNatIpAddress = dwLtNatIp;

	BOOL		bIoctlRes = ::DeviceIoControl( hDevice, IOCTL_VPCKNT_INITIALIZE_HOOK,
		pihiHookInput, INITIALIZE_HOOK_INPUT_SIZE,
		pihoHookOutput, INITIALIZE_HOOK_OUTPUT_MAXSIZE,
		& dwBytesReturned, NULL );

	::free( pihiHookInput );

	if ( bIoctlRes && pihoHookOutput->bNdisHookSucceeded )
	{
		g_phoOutData = pihoHookOutput;

		* pplProtocolListPtr = (PNT_PROTOCOL_LIST) ( (BYTE*) pihoHookOutput + sizeof( INITIALIZE_HOOK_OUTPUT ) );
	}
	else
	{
		//
		// # # # RETURN FAILURE. # # #
		//

		::free( pihoHookOutput );

		::CloseHandle( g_hDevice );
		g_hDevice = NULL;

		g_lHooked = EHS_NOT_INITIALIZED;

		return FALSE;
	}

	// initialize several variables.

	::memset( & g_hsPrevStats, 0, sizeof( g_hsPrevStats ) );

	// return success.

	g_lHooked = EHS_FUNCTIONING;
	return TRUE;
}

NH_EXPORT VOID NH_API Stop(
)
{
	SERIAL_ACCESS

	// check previous state.

	LONG			prev = ::InterlockedCompareExchange( & g_lHooked, EHS_STOPPING, EHS_FUNCTIONING );
	if ( prev != EHS_FUNCTIONING )
		return;

	// stop thread.

	::ResetEvent( g_hThreadExited );
	::SetEvent( g_hExitFromThread );
	::WaitForSingleObject( g_hThreadExited, 1000 );

	// stop.

	if ( g_hDevice )
	{
		::CloseHandle( g_hDevice ); // this will stop the driver.
		g_hDevice = NULL;
	}

	if ( g_phoOutData )
	{
		::free( g_phoOutData );
		g_phoOutData = NULL;
	}

	::ResetEvent( g_hKernelEvent );

	::ResetEvent( g_hExitFromThread );
	::ResetEvent( g_hThreadExited );

	// unload the driver.

	::UnloadDeviceDriver( "vpcknt" );

	// return.

	g_lHooked = EHS_NOT_INITIALIZED;
	return;
}

NH_EXPORT BOOLEAN NH_API Listen(
	IN OALIST_ITEM*			piOaList,
	IN LONG					lOaListItems
)
{
	SERIAL_ACCESS

	// check out the state.

	if ( g_lHooked != EHS_FUNCTIONING )
		return FALSE;

	// send the ioctl.

	if ( piOaList == NULL )
	{
		if ( lOaListItems )
			return FALSE;
	}

	DWORD		dwBytesReturned = 0;
	BOOL		bIoctlRes = ::DeviceIoControl( g_hDevice, IOCTL_VPCKNT_SUBMIT_OALIST,
		piOaList, lOaListItems * sizeof( OALIST_ITEM ),
		NULL, 0,
		& dwBytesReturned, NULL );

	// return.

	return bIoctlRes;
}

NH_EXPORT BOOLEAN NH_API WaitForPacket(
	OUT HOOK_STATS*			psStats,
	OUT PNEXT_PACKET*		pppPacketPtr
)
{
	WAITPACKET_ACCESS

	// init params.

	if ( psStats )
		::memset( psStats, 0, sizeof( HOOK_STATS ) );

	if ( pppPacketPtr == NULL )
		return FALSE;
	else
		* pppPacketPtr = NULL;

	// wfp main loop.

	while( TRUE )
	{
		// check state and ptrs.

		if ( g_pbStorage == NULL || g_hExitFromThread == NULL || g_hThreadExited == NULL )
			return FALSE;

		if ( g_lHooked != EHS_FUNCTIONING )
			return FALSE;

		// wait on the event.

		HANDLE		vhHandles[] = { g_hKernelEvent, g_hExitFromThread };

		DWORD		dwWaitRes = g_hsPrevStats.bArePacketsRemaining ? WAIT_OBJECT_0 :
			::WaitForMultipleObjects( 2, vhHandles, FALSE, INFINITE );

		if ( dwWaitRes == WAIT_OBJECT_0 ) // a packet is ready.
		{
			// read the packet.

			DWORD		dwBytesReturned = 0;
			BOOL		bIoctlRes = ::DeviceIoControl( g_hDevice, IOCTL_VPCKNT_GET_NEXT_PACKET,
				NULL, 0,
				g_pbStorage, MACRO_STORAGE_BUFFER_SIZE,
				& dwBytesReturned, NULL );

			if ( bIoctlRes != FALSE && dwBytesReturned != 0 )
			{
				// allocate the packet memory.

				BYTE*			pb = (BYTE*) ::malloc( dwBytesReturned );
				if ( pb == NULL )
					return FALSE;
				else
					::memcpy( pb, g_pbStorage, dwBytesReturned );

				// return.

				g_hsPrevStats = * (HOOK_STATS*) pb;

				if ( psStats )
					* psStats = * (HOOK_STATS*) pb;

				* pppPacketPtr = (PNEXT_PACKET) ( pb + sizeof( HOOK_STATS ) );

				return TRUE;
			}
			else
			{
				// we should reset the hook stats for consistency.

				::memset( & g_hsPrevStats, 0, sizeof( g_hsPrevStats ) );
			}
		}
		else // exit from thread.
		{
			::SetEvent( g_hThreadExited );
			return FALSE;
		}
	}
}

NH_EXPORT VOID NH_API FreePacket(
	IN PNEXT_PACKET			ppPacket
)
{
	BYTE*			pb = (BYTE*) ppPacket;
	pb -= sizeof( HOOK_STATS );
	::free( pb );
	return;
}

NH_EXPORT VOID NH_API GetHookStatsFromPacket(
	OUT HOOK_STATS*			psStats,
	IN PNEXT_PACKET			ppPacket
)
{
	// parameters validation.

	if ( psStats == NULL )
		return;
	else
		::memset( psStats, 0, sizeof( HOOK_STATS ) );

	if ( ppPacket == NULL )
		return;

	// return the structure.

	BYTE*			pb = (BYTE*) ppPacket;
	pb -= sizeof( HOOK_STATS );
	* psStats = * (HOOK_STATS*) pb;

	// return.

	return;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Italy Italy
Vito is a former videogame programmer. Now, Vito is the founder and CEO of VPC Technologies, a company that specializes in online services. VPC Technologies also provides consulting, developing and training services to several italian companies and government agencies in the field of kernel, component, enterprise and tridimensional software, for the Microsoft Windows platform.

Vito has attended as a speaker several italian conferences and events on development and security, such as the Microsoft Security Roadshow 2006.

Vito is the man behind GoToTerminal, a secure, reliable and innovative web technology to control remote Microsoft Windows, Telnet and VNC servers over the internet. He is also the author of BugChecker, an independent research project to create the only clone of SoftICE to date, NDIS Monitor, MapGen and Image Downloader.

For more information, you can visit Vito Plantamura's technical website at www.VitoPlantamura.com.

Comments and Discussions