Click here to Skip to main content
15,885,096 members
Articles / Desktop Programming / MFC

API hooking revealed

Rate me:
Please Sign up or sign in to vote.
4.94/5 (322 votes)
2 Dec 2002CPOL38 min read 2.8M   64.1K   1.3K  
The article demonstrates how to build a user mode Win32 API spying system
//---------------------------------------------------------------------------
//
// NtProcessMonitor.h
//
// SUBSYSTEM: 
//				API Hooking system
// MODULE:    
//				Implements a thread that uses an NT device driver
//              for monitoring process creation
//
// DESCRIPTION:
//
// AUTHOR:		Ivo Ivanov (ivopi@hotmail.com)
//                                                                         
//---------------------------------------------------------------------------

#include "..\Common\Common.h"
#include "NtProcessMonitor.h"
#include "..\Common\SysUtils.h"
#include <process.h>
#include <winioctl.h>
#include "NtDriverController.h"

//---------------------------------------------------------------------------
//
// File scope consts and typedefs
//
//---------------------------------------------------------------------------

#define FILE_DEVICE_UNKNOWN             0x00000022
#define IOCTL_UNKNOWN_BASE              FILE_DEVICE_UNKNOWN

#define IOCTL_PROCVIEW_GET_PROCINFO     CTL_CODE(IOCTL_UNKNOWN_BASE, 0x0800, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS)


//---------------------------------------------------------------------------
//
// Thread function prototype
//
//---------------------------------------------------------------------------
typedef unsigned (__stdcall *PTHREAD_START) (void *);
//---------------------------------------------------------------------------
//
// class CNtProcessMonitor
//
//---------------------------------------------------------------------------

CNtProcessMonitor::CNtProcessMonitor():
	m_hShutdownEvent(NULL),
	m_hProcessEvent(NULL),
	m_bThreadActive(FALSE),
	m_dwThreadId(0),
	m_hDriver(INVALID_HANDLE_VALUE),
	m_pDriverCtl(NULL)
{
	if (!IsWindows9x())
		m_pDriverCtl = new CNtDriverController();
}

CNtProcessMonitor::~CNtProcessMonitor()
{
	delete m_pDriverCtl;
}

//
// Accessor method
//
BOOL CNtProcessMonitor::Get_ThreadActive()
{
	CLockMgr<CCSWrapper> lockMgr(m_CritSec, TRUE);	
	return m_bThreadActive;
}

//
// Accessor method
//
void CNtProcessMonitor::Set_ThreadActive(BOOL val)
{
	CLockMgr<CCSWrapper> lockMgr(m_CritSec, TRUE);	
	m_bThreadActive = val;
}


//
// Accessor method
//
HANDLE CNtProcessMonitor::Get_ShutdownEvent() const
{
	return m_hShutdownEvent;
}

//
// Accessor method
//
HANDLE CNtProcessMonitor::Get_ProcessEvent() const
{
	return m_hProcessEvent;
}


//
// Activate / Stop the thread which gets the notification from the 
// device driver
//
void CNtProcessMonitor::SetActive(BOOL bVal)
{
	if (m_pDriverCtl)
	{
		if (bVal)
		{
			if (!Get_ThreadActive())
			{
				// Terminal Services W2K/XP: The name can have a "Global\" 
				// prefix to explicitly create the object in the global 
				// or session name space.
				char szDriverName[MAX_PATH];
				if ( IsWindowsNT4() )
					strcpy(szDriverName, "\\\\.\\NTProcDrv");
				else
					strcpy(szDriverName, "\\\\.\\Global\\NTProcDrv");				

				// Try opening the device driver
				m_hDriver = CreateFile(
					szDriverName,
					GENERIC_READ | GENERIC_WRITE, 
					FILE_SHARE_READ | FILE_SHARE_WRITE,
					0,                     // Default security
					OPEN_EXISTING,
					FILE_FLAG_OVERLAPPED,  // Perform asynchronous I/O
					0);                    // No template
        
				if(INVALID_HANDLE_VALUE == m_hDriver)
					return;
    				
				m_hShutdownEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
				// Attach to KM-created event handle
				m_hProcessEvent = ::OpenEvent(
					SYNCHRONIZE, FALSE, "NTProcDrvProcessEvent");

				_beginthreadex(
					(void *)NULL,
					(unsigned)0,
					(PTHREAD_START)CNtProcessMonitor::ThreadFunc,
					(PVOID)this,
					(unsigned)0,
					(unsigned *)&m_dwThreadId
					);
			} // if
		} 
		else
		{
			if (Get_ThreadActive())
			{
				::SetEvent(m_hShutdownEvent);
				// Give some time to the injector thread to clean up itself
				while (Get_ThreadActive())
				{
				}
				::CloseHandle(m_hShutdownEvent);
				::CloseHandle(m_hProcessEvent);
				m_dwThreadId = 0;
				if (NULL != m_hDriver)
					::CloseHandle(m_hDriver);
			} // if
		} // else
	} // if
}

//
// Retrieves data from the driver after received notification 
//
void CNtProcessMonitor::RetrieveProcessInfo(
	CALLBACK_INFO& callbackInfo,
	CALLBACK_INFO& callbackTemp
	)
{
	OVERLAPPED ov          = { 0 };
	BOOL       bReturnCode = FALSE;
	DWORD      dwBytesReturned;

    // Create an event handle for async notification from the driver
	ov.hEvent = ::CreateEvent(
		NULL,  // Default security
		TRUE,  // Manual reset
		FALSE, // non-signaled state
		NULL
		); 

	// Get the process info
	bReturnCode = ::DeviceIoControl(
		m_hDriver,
		IOCTL_PROCVIEW_GET_PROCINFO,
		0, 
		0,
		&callbackInfo, sizeof(callbackInfo),
		&dwBytesReturned,
		&ov
		);

	// Wait here for the event handle to be set, indicating
	// that the IOCTL processing is completed.
	bReturnCode = ::GetOverlappedResult(
		m_hDriver, 
		&ov,
		&dwBytesReturned, 
		TRUE
		);

	::CloseHandle(ov.hEvent);

	// Pevent getting duplicated events
	if((callbackTemp.ParentId  != callbackInfo.ParentId) ||
	   (callbackTemp.ProcessId != callbackInfo.ProcessId) ||
	   (callbackTemp.bCreate    != callbackInfo.bCreate))
	{
		if(callbackInfo.bCreate)
			// Process creation notification
			OnCreateProcess((DWORD)callbackInfo.ProcessId);
		else
			// Process termination notification
			OnTerminateProcess((DWORD)callbackInfo.ProcessId);
	} // if

	// Store the data for next time
	callbackTemp = callbackInfo;
}

//
// The thread function 
//
unsigned __stdcall CNtProcessMonitor::ThreadFunc(void* pvParam)
{
	CNtProcessMonitor* me = (CNtProcessMonitor*)pvParam;
	CALLBACK_INFO callbackInfo, callbackTemp;
	DWORD dwResult; 
	HANDLE handles[2] = 
	{
		me->Get_ShutdownEvent(),
		me->Get_ProcessEvent()
	};
	me->Set_ThreadActive(TRUE);

	while (TRUE)
	{
		dwResult = ::WaitForMultipleObjects(
			sizeof(handles)/sizeof(handles[0]), // number of handles in array
			&handles[0],                        // object-handle array
			FALSE,                              // wait option
			INFINITE                            // time-out interval
			);
		//	
		// the system shuts down
		//
		if (handles[dwResult - WAIT_OBJECT_0] == me->Get_ShutdownEvent())
			break;
		//
		// A new process has been just created / or terminated
		//
		else
			me->RetrieveProcessInfo(
				callbackInfo, 
				callbackTemp
				);
	} // while
	me->Set_ThreadActive( FALSE );
	_endthreadex(0);
	return 0;
}

//----------------------------End of the file -------------------------------

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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
I specialize in OS Internals and Reverse Engineering.
Before joining my current employer I used to run a security blog for malware analysis: http://vinsula.com/security-blog

Comments and Discussions