Click here to Skip to main content
15,895,779 members
Articles / Desktop Programming / MFC

An Adventure: How to Implement a Firewall-Hook Driver?

Rate me:
Please Sign up or sign in to vote.
4.83/5 (65 votes)
28 Oct 20049 min read 672.5K   11K   194  
Firewall-Hook driver is a completely unknown method to develop simple packet filtering applications. With this article, I want to tell you how this driver works and what you need to do to use it in your applications.
#include "stdafx.h"

#include "TDriver.h"


//Constructor. Inicializacion de variables.
TDriver::TDriver(void)
{
	driverHandle = NULL;
	   
	removable = TRUE;

	driverName = NULL;
	driverPath = NULL;
	driverDosName = NULL;

	initialized = FALSE;
	loaded = FALSE;
	started = FALSE;
}


//Destructor. Libera recursos.
TDriver::~TDriver(void)
{
	if(driverHandle != NULL)
	{
		CloseHandle(driverHandle); 
		driverHandle = NULL; 
	}
   
    UnloadDriver();
}

//Si removable = TRUE, no se descarga el driver en la "destruccion" del objeto
void TDriver::SetRemovable(BOOL value)
{
	removable = value;
}


// Esta inicializado el driver?
BOOL TDriver::IsInitialized(void)
{
	return initialized;
}

// Esta cargado el driver?
BOOL TDriver::IsLoaded(void)
{
	return loaded;
}

// Esta en estado Start el driver?
BOOL TDriver::IsStarted(void)
{
	return started;
}


// Inicia las variables del driver
DWORD TDriver::InitDriver(LPCTSTR path)
{
	// Si ya esta cargado primero lo descargo
	if(initialized)
	{
		if(UnloadDriver() != DRV_SUCCESS)
			return DRV_ERROR_ALREADY_INITIALIZED;
	}

	//  Reservo memoria para almacenar el path del driver
	driverPath = (LPTSTR)malloc(strlen(path) + 1);

	if(driverPath == NULL)
		return DRV_ERROR_MEMORY;

	strcpy(driverPath, path);


	// Voy a extraer el nombre del driver

	//Primero busco el caracter '\'
	LPTSTR sPos1 = strrchr(driverPath, (int)'\\');

	// Si no existe, me loco al principio
	if (sPos1 == NULL)
		sPos1 = driverPath;


	// Ahora busco la extension .sys
	LPTSTR sPos2 = strrchr(sPos1, (int)'.');

	// Si no tiene extension .sys salgo sin hacer nada mas
	if (sPos2 == NULL || sPos1 > sPos2)
	{
		free(driverPath);
		driverPath = NULL;

		return DRV_ERROR_INVALID_PATH_OR_FILE;
	}
	
	// Me quedo con el nombre del driver
	driverName = (LPTSTR) malloc (sPos2 - sPos1);
	
	if(driverName == NULL)
	{
		free(driverPath);
		driverPath = NULL;

		return DRV_ERROR_MEMORY;
	}

	// Copio los caracteres
	memcpy(driverName, sPos1 + 1, sPos2 - sPos1 - 1);
	
	driverName[sPos2 - sPos1 - 1] = 0;

	//driverDosName = \\.\driverName 
	driverDosName = (LPTSTR) malloc (strlen(driverName) + 5);

	if(driverDosName == NULL)
	{
		free(driverPath);
		driverPath = NULL;

		free(driverName);
		driverName = NULL;

		return DRV_ERROR_MEMORY;
	}

	sprintf(driverDosName, "\\\\.\\%s", driverName);

		
	initialized = TRUE;

	return DRV_SUCCESS;
}


// Inicia las variables del driver
DWORD TDriver::InitDriver(LPCTSTR name, LPCTSTR path, LPCTSTR dosName)
{	
	// Si ya esta cargado primero lo descargo
	if(initialized)
	{
		if(UnloadDriver() != DRV_SUCCESS)
			return DRV_ERROR_ALREADY_INITIALIZED;
	}

	LPTSTR dirBuffer;

	// Compruebo si el usuario introdujo un path
	if (path != NULL) 
	{
		// Lo copio en un buffer auxiliar para su posterior procesamiento
		DWORD len = (DWORD)(strlen(name) + strlen(path) + 1);
		dirBuffer = (LPTSTR) malloc (len);

		if(dirBuffer == NULL)
			return DRV_ERROR_MEMORY;

		strcpy(dirBuffer, path);

	}

	else 
	{
		// Si no tenemos path, supongo el directorio actual
		LPTSTR pathBuffer;
        DWORD len = GetCurrentDirectory(0, NULL);
      
		pathBuffer = (LPTSTR) malloc (len);

		if(pathBuffer == NULL)
			return DRV_ERROR_MEMORY;

		        
        if (GetCurrentDirectory(len, pathBuffer) != 0) 
		{
			len = (DWORD)(strlen(pathBuffer) + strlen(name) + 6);
			dirBuffer = (LPTSTR) malloc (len);

			if(dirBuffer == NULL)
			{
				free(pathBuffer);

				return DRV_ERROR_MEMORY;
			}

			// Path = directorio\driverName.sys
			sprintf(dirBuffer, "%s\\%s.sys", pathBuffer, name);

			// Compruebo si existe el fichero en cuestion
			if(GetFileAttributes(dirBuffer) == 0xFFFFFFFF)
			{
				free(pathBuffer);
				free(dirBuffer);

				// Si no existe, busco en system32\Drivers
				LPCTSTR sysDriver = "\\system32\\Drivers\\";
				LPTSTR sysPath;
	    	    
				DWORD len = GetWindowsDirectory(NULL, 0);
     			sysPath = (LPTSTR) malloc (len + strlen(sysDriver));

				if(sysPath == NULL)
					return DRV_ERROR_MEMORY;

				if (GetWindowsDirectory(sysPath, len) == 0) 
				{
					free(sysPath);
					
					return DRV_ERROR_UNKNOWN;
				}
	
				// Completo el path y compruebo si existe el fichero de nuevo
				strcat(sysPath, sysDriver);
				len = (DWORD)(strlen(sysPath) + strlen(name) + 5);

				dirBuffer = (LPTSTR) malloc (len);

				if(dirBuffer == NULL)
					return DRV_ERROR_MEMORY;

				sprintf(dirBuffer, "%s%s.sys", sysPath, name);

				free(sysPath);

				// Si el fichero no existe, salgo
				if(GetFileAttributes(dirBuffer) == 0xFFFFFFFF)
				{
					free(dirBuffer);

					return DRV_ERROR_INVALID_PATH_OR_FILE;
				}
			}

			free(pathBuffer);

        }

		else
		{
			free(pathBuffer);

			return DRV_ERROR_UNKNOWN;
		}
	}
	
	// Escribo las variables del driver
	driverPath = dirBuffer;

	driverName = (LPTSTR)malloc(strlen(name) + 1);

	if(driverName == NULL)
	{
		free(driverPath);
		driverPath = NULL;
		
		return DRV_ERROR_MEMORY;
	}

	strcpy(driverName, name);
	
	LPCTSTR auxBuffer;
	if(dosName != NULL)
        auxBuffer = dosName;
	
	else
		auxBuffer = name;

	//dosName=\\.\driverName
	if(auxBuffer[0] != '\\' && auxBuffer[1] != '\\')
	{
		driverDosName = (LPTSTR) malloc (strlen(auxBuffer) + 5);

		if(driverDosName == NULL)
		{
			free(driverPath);
			driverPath = NULL;

			free(driverName);
			driverName = NULL;

			return DRV_ERROR_MEMORY;
		}

		sprintf(driverDosName, "\\\\.\\%s", auxBuffer);
	}

	else
	{
		driverDosName = (LPTSTR) malloc (strlen(auxBuffer));

		if(driverDosName == NULL)
		{
			free(driverPath);
			driverPath = NULL;

			free(driverName);
			driverName = NULL;

			return DRV_ERROR_MEMORY;
		}

		strcpy(driverDosName, auxBuffer);
	}

	// Pongo el estado en inicializado
	initialized = TRUE;

	return DRV_SUCCESS;
}


// Funcion para cargar el driver.
DWORD TDriver::LoadDriver(LPCTSTR name, LPCTSTR path, LPCTSTR dosName, BOOL start)
{
	// Primero es necesario inicializarlo
	DWORD retCode = InitDriver(name, path, dosName);

	// Despues lo cargo
	if(retCode == DRV_SUCCESS)
		retCode = LoadDriver(start);

	return retCode;
}

// Funcion para cargar el driver
DWORD TDriver::LoadDriver(LPCTSTR path, BOOL start)
{
	// Primero lo inicializo
	DWORD retCode = InitDriver(path);

	// Despues lo cargo
	if(retCode == DRV_SUCCESS)
		retCode = LoadDriver(start);

	return retCode;
}


// Funcion para cargar el driver
DWORD TDriver::LoadDriver(BOOL start)
{
	// Si ya esta cargado no hago nada
	if(loaded)
		return DRV_SUCCESS;

	if(!initialized)
		return DRV_ERROR_NO_INITIALIZED;

	// Abro el Service Manager para crear el nuevo driver
	SC_HANDLE SCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	DWORD retCode = DRV_SUCCESS;
	
	if (SCManager == NULL) 
		return DRV_ERROR_SCM;

    

    // Creo el driver ("Servicio")
    SC_HANDLE  SCService = CreateService(SCManager,			  // SCManager database
									     driverName,            // nombre del servicio
							    		 driverName,            // nombre a mostrar
										 SERVICE_ALL_ACCESS,    // acceso total
										 SERVICE_KERNEL_DRIVER, // driver del kernel
										 SERVICE_DEMAND_START,  // comienzo bajo demanda
										 SERVICE_ERROR_NORMAL,  // control de errores normal
										 driverPath,	          // path del driver
										 NULL,                  // no pertenece a un grupo
										 NULL,                  // sin tag
										 NULL,                  // sin dependencias
										 NULL,                  // cuenta local del sistema
										 NULL                   // sin password
										 );
    
	// Si no puedo crearlo, miro si es porque ya fue cargado por otro proceso.
	if (SCService == NULL) 
	{
		SCService = OpenService(SCManager, driverName, SERVICE_ALL_ACCESS);
		
		if (SCService == NULL) 
			retCode = DRV_ERROR_SERVICE;
	}

    CloseServiceHandle(SCService);
	SCService=NULL;

	CloseServiceHandle(SCManager);
	SCManager = NULL;

	// Si todo fue bien, actualizo el estado e inicio si fuera necesario.
	if(retCode == DRV_SUCCESS)
	{
		loaded = TRUE;

		if(start)
			retCode = StartDriver();
	}

	return retCode;
}


// Funcion para descargar el driver.
DWORD TDriver::UnloadDriver(BOOL forceClearData)
{
	DWORD retCode = DRV_SUCCESS;

	// Si esta en estado started, primero lo paro
	if (started)
	{
		if ((retCode = StopDriver()) == DRV_SUCCESS) 
		{
			// Solo lo extraigo del service manager si es removable
			if(removable)
			{
				// Abro el servicio y elimino el driver
				SC_HANDLE SCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
				
				if (SCManager == NULL) 
					return DRV_ERROR_SCM;

				SC_HANDLE SCService = OpenService(SCManager, driverName, SERVICE_ALL_ACCESS);
				
				if (SCService != NULL)
				{
					if(!DeleteService(SCService))
						retCode = DRV_ERROR_REMOVING;
					else
						retCode = DRV_SUCCESS;
				}

				else
					retCode = DRV_ERROR_SERVICE;

				CloseServiceHandle(SCService);
				SCService = NULL;

				CloseServiceHandle(SCManager);
				SCManager = NULL;

				// Si todo fue bien, actualizo el estado
				if(retCode == DRV_SUCCESS)
					loaded = FALSE;
			}
		}
	}

	// Si el driver esta inicializado...
	if(initialized) 
	{
		// Si hubo algun problema pero esta a TRUE forceClear borro las variables de igual forma
		if(retCode != DRV_SUCCESS && forceClearData == FALSE)
			return retCode;
		
		// Actualizo el estado
		initialized = FALSE;
				
		// Libero memoria
		if(driverPath != NULL)
		{
			free(driverPath);
			driverPath = NULL;
		}


		if(driverDosName != NULL)
		{
			free(driverDosName);
			driverDosName = NULL;
		}

		if(driverName != NULL)
		{
			free(driverName);
			driverName = NULL;
		}

	}

	return retCode;
}



// Funcion para poner en Start al driver
DWORD TDriver::StartDriver(void)
{
	// Si ya esta comenzado, no hago nada
	if(started)
		return DRV_SUCCESS;

	// Abro el servicio y lo pongo en estado Start
	SC_HANDLE SCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	DWORD retCode;
	
	if (SCManager == NULL) 
		return DRV_ERROR_SCM;

    SC_HANDLE SCService = OpenService(SCManager,
		                              driverName,
				                      SERVICE_ALL_ACCESS);
    
	if (SCService == NULL) 
        return DRV_ERROR_SERVICE;

    
    if (!StartService( SCService, 0, NULL)) 
	{
		// Si el driver fue puesto en Start antes de mi, no lo elimino al destruir el objeto
		// debido a que fue creado por otro aplicacion que posiblemente lo este usando
        if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) 
		{
			removable = FALSE;

			retCode = DRV_SUCCESS;
		}

		else
			retCode = DRV_ERROR_STARTING;
    }

	else
		retCode = DRV_SUCCESS;

  
    CloseServiceHandle(SCService);
	SCService = NULL;

	CloseServiceHandle(SCManager);
	SCManager = NULL;

	// Actualizo el estado y abro el dispositivo para comunicacion Usuario -> Driver
	if(retCode == DRV_SUCCESS)
	{
		started = TRUE;

		retCode = OpenDevice();
	}

    return retCode;
}



// Funcion para parar el driver
DWORD TDriver::StopDriver(void)
{
	// Si ya esta parado, no tengo que hacer nada
	if(!started)
		return DRV_SUCCESS;

	// Cambio el estado del proceso
	SC_HANDLE SCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
	DWORD retCode;
	
	if (SCManager == NULL) 
		return DRV_ERROR_SCM;

   
    SERVICE_STATUS  status;

    SC_HANDLE SCService = OpenService(SCManager, driverName, SERVICE_ALL_ACCESS);
    
	if (SCService != NULL)
	{
		// Cierro tambien el handle al driver abierto en funcion start
		CloseHandle(driverHandle); 
		driverHandle = NULL; 

		if(!ControlService(SCService, SERVICE_CONTROL_STOP, &status))
			retCode = DRV_ERROR_STOPPING;

		else
			retCode = DRV_SUCCESS;
	}

	else
		retCode = DRV_ERROR_SERVICE;


    CloseServiceHandle(SCService);
	SCService = NULL;

	CloseServiceHandle(SCManager);
	SCManager = NULL;

	// Actualizo el estado
	if(retCode == DRV_SUCCESS)
		started = FALSE;

    return retCode;
}


// Funcion para abrir el dispositivo
DWORD TDriver::OpenDevice(void)
{
	// Si ya tengo un handle al driver, primero lo cierro
	if (driverHandle != NULL) 
		CloseHandle(driverHandle);

    driverHandle = CreateFile(driverDosName,
							  GENERIC_READ | GENERIC_WRITE,
							  0,
                              NULL,
                              OPEN_EXISTING,
                              FILE_ATTRIBUTE_NORMAL,
                              NULL);


    if(driverHandle == INVALID_HANDLE_VALUE)
		return DRV_ERROR_INVALID_HANDLE;
	
	return DRV_SUCCESS;
}


// Devuelve un handle al driver
HANDLE TDriver::GetDriverHandle(void)
{
	return driverHandle;
}


// Funcion para enviar datos al driver
DWORD TDriver::WriteIo(DWORD code, PVOID buffer, DWORD count)
{
	if(driverHandle == NULL)
		return DRV_ERROR_INVALID_HANDLE;

	DWORD bytesReturned;

	BOOL returnCode = DeviceIoControl(driverHandle,
								      code,
								      buffer,
								      count,
								      NULL,
								      0,
								      &bytesReturned,
								      NULL);

	if(!returnCode)
		return DRV_ERROR_IO;

	return DRV_SUCCESS;
}


// Funcion para leer datos del driver
DWORD TDriver::ReadIo(DWORD code, PVOID buffer, DWORD count)
{
	if(driverHandle == NULL)
		return DRV_ERROR_INVALID_HANDLE;

	DWORD bytesReturned;
	BOOL retCode = DeviceIoControl(driverHandle,
								   code,
								   NULL,
								   0,
								   buffer,
								   count,
								   &bytesReturned,
								   NULL);

	if(!retCode)
		return DRV_ERROR_IO;

	return bytesReturned;
}


// Funcion para realizar operacion de E/S con el driver (leer y/o escribir)
DWORD TDriver::RawIo(DWORD code, PVOID inBuffer, DWORD inCount, PVOID outBuffer, DWORD outCount)
{
	if(driverHandle == NULL)
		return DRV_ERROR_INVALID_HANDLE;

	DWORD bytesReturned;
	BOOL retCode = DeviceIoControl(driverHandle,
								   code,
								   inBuffer,
								   inCount,
								   outBuffer,
								   outCount,
								   &bytesReturned,
								   NULL);

	if(!retCode)
		return DRV_ERROR_IO;

	return bytesReturned;
}

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.


Written By
Chief Technology Officer
Spain Spain
To summarize: learn, learn, learn... and then try to remember something I.... I don't Know what i have to remember...

http://www.olivacorner.com

Comments and Discussions