//
// 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;
}