How find correct original entry in SSDT Shadow table?

Forum for discussion about kernel-mode development.

How find correct original entry in SSDT Shadow table?

Postby fl4shc0d3r » Mon Apr 10, 2017 7:37 pm

I have a code that restores SSDT and Shadow table in x32 Windows and is able to find correct address of original entry in shadow table from Win Server until Win8, already when i tested with Win8.1 part of original address is wrong, so causing a BSOD on system in this line:

Code: Select all
pTable->ServiceTable[i] = pOrigAddress;


Debug:

Image

Driver.c

Code: Select all
#include "stdafx.h"
#include "Driver.h"
#include <ntstrsafe.h>

#ifdef __cplusplus
extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING  RegistryPath);
#endif

#pragma comment(lib, "ntstrsafe.lib")

void MyDriverUnload(IN PDRIVER_OBJECT DriverObject);

#define echo(x) x
#define label(x) echo(x)__LINE__
#define RTL_CONSTANT_STRINGW(s) { sizeof( s ) - sizeof( (s)[0] ), sizeof( s ),(PWSTR)(s) }

#define STATIC_UNICODE_STRING(name, str) static const WCHAR label(__)[] = L##str; static const UNICODE_STRING name = RTL_CONSTANT_STRINGW(label(__))

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define IMAGE_DOS_SIGNATURE 0x5A4D
#define IMAGE_NT_SIGNATURE                  0x00004550

typedef struct _IMAGE_DOS_HEADER {      // DOS .EXE header
   short   e_magic;                     // Magic number
   short   e_cblp;                      // Bytes on last page of file
   short   e_cp;                        // Pages in file
   short   e_crlc;                      // Relocations
   short   e_cparhdr;                   // Size of header in paragraphs
   short   e_minalloc;                  // Minimum extra paragraphs needed
   short   e_maxalloc;                  // Maximum extra paragraphs needed
   short   e_ss;                        // Initial (relative) SS value
   short   e_sp;                        // Initial SP value
   short   e_csum;                      // Checksum
   short   e_ip;                        // Initial IP value
   short   e_cs;                        // Initial (relative) CS value
   short   e_lfarlc;                    // File address of relocation table
   short   e_ovno;                      // Overlay number
   short   e_res[4];                    // Reserved words
   short   e_oemid;                     // OEM identifier (for e_oeminfo)
   short   e_oeminfo;                   // OEM information; e_oemid specific
   short   e_res2[10];                  // Reserved words
   LONG   e_lfanew;                    // File address of new exe header
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

typedef struct _IMAGE_FILE_HEADER {
   UINT16  Machine;
   UINT16  NumberOfSections;
   UINT32   TimeDateStamp;
   UINT32   PointerToSymbolTable;
   UINT32   NumberOfSymbols;
   UINT16  SizeOfOptionalHeader;
   UINT16  Characteristics;
} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;

typedef struct _IMAGE_DATA_DIRECTORY {
   UINT32   VirtualAddress;
   UINT32   Size;
} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;


#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES    16
typedef struct _IMAGE_OPTIONAL_HEADER {
   /*
   * Standard fields.
   */

   UINT16  Magic;
   UINT8   MajorLinkerVersion;
   UINT8   MinorLinkerVersion;
   UINT32   SizeOfCode;
   UINT32   SizeOfInitializedData;
   UINT32   SizeOfUninitializedData;
   UINT32   AddressOfEntryPoint;
   UINT32   BaseOfCode;
   UINT32   BaseOfData;

   /*
   * NT additional fields.
   */

   UINT32   ImageBase;
   UINT32   SectionAlignment;
   UINT32   FileAlignment;
   UINT16  MajorOperatingSystemVersion;
   UINT16  MinorOperatingSystemVersion;
   UINT16  MajorImageVersion;
   UINT16  MinorImageVersion;
   UINT16  MajorSubsystemVersion;
   UINT16  MinorSubsystemVersion;
   UINT32   Reserved1;
   UINT32   SizeOfImage;
   UINT32   SizeOfHeaders;
   UINT32   CheckSum;
   UINT16  Subsystem;
   UINT16  DllCharacteristics;
   UINT32   SizeOfStackReserve;
   UINT32   SizeOfStackCommit;
   UINT32   SizeOfHeapReserve;
   UINT32   SizeOfHeapCommit;
   UINT32   LoaderFlags;
   UINT32   NumberOfRvaAndSizes;
   IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER;

typedef struct _IMAGE_NT_HEADERS {
   UINT32 Signature;
   IMAGE_FILE_HEADER FileHeader;
   IMAGE_OPTIONAL_HEADER OptionalHeader;
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;

#define IMAGE_SIZEOF_SHORT_NAME              8

typedef struct _IMAGE_SECTION_HEADER {
   UINT8   Name[IMAGE_SIZEOF_SHORT_NAME];
   union {
      UINT32   PhysicalAddress;
      UINT32   VirtualSize;
   } Misc;
   UINT32   VirtualAddress;
   UINT32   SizeOfRawData;
   UINT32   PointerToRawData;
   UINT32   PointerToRelocations;
   UINT32   PointerToLinenumbers;
   UINT16  NumberOfRelocations;
   UINT16  NumberOfLinenumbers;
   UINT32   Characteristics;
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;

extern "C" __declspec(dllimport) NTSTATUS NTAPI KeAddSystemServiceTable(ULONG, ULONG, ULONG, ULONG, ULONG);
extern "C" __declspec(dllimport) NTSTATUS NTAPI NtClose(HANDLE Handle);
extern "C" __declspec(dllimport) NTSTATUS NTAPI KeAttachProcess(PEPROCESS);
extern "C" __declspec(dllimport) NTSTATUS NTAPI KeDetachProcess();

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 UniqueProcessId;
   HANDLE InheritedFromUniqueProcessId;
   ULONG HandleCount;
   ULONG SessionId;
   ULONG PageDirectoryBase;
   VM_COUNTERS VirtualMemoryCounters;
   SIZE_T PrivatePageCount;
   IO_COUNTERS IoCounters;
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;

////////////////////////////////////////////////////////////////////////////////////////////////////////////

PSYSTEM_SERVICE_TABLE KeServiceDescriptorTableShadow;

unsigned int getAddressOfShadowTable()
{
   unsigned int i;
   unsigned char *p;
   unsigned int dwordatbyte;

   p = (unsigned char*) KeAddSystemServiceTable;

   for(i = 0; i < 4096; i++, p++)
   {
      __try
      {
         dwordatbyte = *(unsigned int*)p;
      }
      __except(EXCEPTION_EXECUTE_HANDLER)
      {
         return 0;
      }

      if(MmIsAddressValid((PVOID)dwordatbyte))
      {
         if(memcmp((PVOID)dwordatbyte, &KeServiceDescriptorTable, 16) == 0)
         {
            if((PVOID)dwordatbyte == &KeServiceDescriptorTable)
            {
               continue;
            }

            return dwordatbyte;
         }
      }
   }

   return 0;
}

ULONG getShadowTable()
{
   KeServiceDescriptorTableShadow = (PSYSTEM_SERVICE_TABLE) getAddressOfShadowTable();

   if(KeServiceDescriptorTableShadow == NULL)
   { 
      return FALSE;
   }
   else
   {
      return TRUE;
   }
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////

PSERVICE_DESCRIPTOR_TABLE __stdcall GetServiceDescriptorShadowTableAddress() {
char * check = (char *)KeAddSystemServiceTable;
PSERVICE_DESCRIPTOR_TABLE rc = NULL; int i;
for (i = 0; i < 1024; i++) {
rc = *(PPSERVICE_DESCRIPTOR_TABLE)check;
if (!MmIsAddressValid(rc) || ((PVOID)rc == (PVOID)&KeServiceDescriptorTable)
|| (memcmp(rc, &KeServiceDescriptorTable, sizeof(SYSTEM_SERVICE_TABLE)))) {
check++; rc = NULL;
}
if (rc)
break;
}
return rc;
}

PSYSTEM_MODULE_INFORMATION GetSystemModuleInformation() {
   PSYSTEM_MODULE_INFORMATION pSMInfo = NULL;
   NTSTATUS Status = STATUS_NO_MEMORY;
   ULONG SMInfoLen = 1000;
   do {
      pSMInfo = (PSYSTEM_MODULE_INFORMATION)ExAllocatePoolWithTag(PagedPool, SMInfoLen, 0);
      if (!pSMInfo)
         break;
      Status = /*ZwQuerySystemInformation*/NtQuerySystemInformation(SystemModuleInformation, pSMInfo, SMInfoLen, &SMInfoLen);
      if (!NT_SUCCESS(Status)) {
         ExFreePoolWithTag(pSMInfo, 0);
         pSMInfo = NULL;
      }
   } while (Status == STATUS_INFO_LENGTH_MISMATCH);
   return pSMInfo;
}

PSYSTEM_HANDLE_INFORMATION_EX GetSystemHandleInformation() {
   PSYSTEM_HANDLE_INFORMATION_EX pSHInfo = NULL;
   NTSTATUS Status = STATUS_NO_MEMORY;
   ULONG SMInfoLen = 0x1000;
   do {
      pSHInfo = (PSYSTEM_HANDLE_INFORMATION_EX)ExAllocatePoolWithTag(PagedPool, SMInfoLen, 0);
      if (!pSHInfo)
         break;
      Status = /*ZwQuerySystemInformation*/NtQuerySystemInformation(SystemHandleInformation, pSHInfo, SMInfoLen, &SMInfoLen);
      if (!NT_SUCCESS(Status)) {
         ExFreePoolWithTag(pSHInfo, 0);
         pSHInfo = NULL;
      }
   } while (Status == STATUS_INFO_LENGTH_MISMATCH);
   return pSHInfo;
}

PVOID GetKernelBaseAddressByAddress(PSYSTEM_MODULE_INFORMATION pSMInfo, PVOID pAddress, PVOID* pKernelAddr) {
   if (pSMInfo) {
      int i;
      for (i = 0; i < pSMInfo->Count; i++) {
         if (pAddress >= pSMInfo->Module[i].Base&&pAddress <= (((char *)pSMInfo->Module[i].Base) + pSMInfo->Module[i].Size)) {
            if (pKernelAddr)
               *pKernelAddr = (PVOID)pSMInfo->Module[i].Base;
            return &pSMInfo->Module[i].ImageName[pSMInfo->Module[i].PathLength];
         }
      }
   }
   return NULL;
}

ULONG RVAToRaw(PVOID lpBase, ULONG VirtualAddress, PULONG pImageBase) {
   PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)lpBase;
   if (pDosHeader->e_magic != IMAGE_DOS_SIGNATURE)
      return 0;
   PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((char *)lpBase + pDosHeader->e_lfanew);
   if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
      return 0;
   if (pImageBase)
      *pImageBase = pNtHeader->OptionalHeader.ImageBase;
   int i;
   for (i = 0; i < pNtHeader->FileHeader.NumberOfSections; i++) {
      IMAGE_SECTION_HEADER *pSectionHeader = (PIMAGE_SECTION_HEADER)((char *)&pNtHeader->OptionalHeader + pNtHeader->FileHeader.SizeOfOptionalHeader + (i * sizeof(IMAGE_SECTION_HEADER)));
      if (VirtualAddress >= pSectionHeader->VirtualAddress&&VirtualAddress <= (pSectionHeader->VirtualAddress + pSectionHeader->SizeOfRawData))
         return VirtualAddress - pSectionHeader->VirtualAddress + pSectionHeader->PointerToRawData;
   }
   return 0;
}

VOID RestoreHooks(PSYSTEM_MODULE_INFORMATION pSMInfo, PSYSTEM_SERVICE_TABLE pTable) {
   CHAR sKernelPath[1024] = { 0 }; PVOID pKernelAddres = NULL;
   PCHAR pKrnlMod = (PCHAR)GetKernelBaseAddressByAddress(pSMInfo, pTable->ServiceTable, &pKernelAddres);
   if (pKrnlMod&&pKernelAddres) {
      ULONG uImageBase = 0;
      ULONG uSSDTRaw = RVAToRaw(pKernelAddres, (ULONG)pTable->ServiceTable - (ULONG)pKernelAddres, &uImageBase);
      if (uSSDTRaw != 0 && uImageBase != 0) {
         ANSI_STRING aFileName; UNICODE_STRING uFileName;
         RtlStringCbPrintfA(sKernelPath, 1024, "\\SystemRoot\\System32\\%s", pKrnlMod);

         RtlInitAnsiString(&aFileName, sKernelPath);
         if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&uFileName, &aFileName, TRUE))) {
            DbgPrint("%wZ\n", &uFileName);
            OBJECT_ATTRIBUTES ObjAttr; HANDLE hFile; IO_STATUS_BLOCK ioStatus;
            InitializeObjectAttributes(&ObjAttr, &uFileName, OBJ_CASE_INSENSITIVE, NULL, NULL);
            if (NT_SUCCESS(ZwOpenFile(&hFile, FILE_READ_DATA, &ObjAttr, &ioStatus, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SYNCHRONOUS_IO_NONALERT))) {
               PULONG lpArraySSDT = (PULONG)ExAllocatePool(PagedPool, pTable->ServiceLimit * sizeof(ULONG));
               if (lpArraySSDT != NULL) {
                  FILE_POSITION_INFORMATION FilePos = { { { uSSDTRaw,0 } } };
                  if (NT_SUCCESS(ZwSetInformationFile(hFile, &ioStatus, &FilePos, sizeof(FILE_POSITION_INFORMATION), FilePositionInformation))) {
                     if (NT_SUCCESS(ZwReadFile(hFile, NULL, NULL, NULL, &ioStatus, lpArraySSDT, pTable->ServiceLimit * sizeof(ULONG), NULL, NULL))) {
                        __asm {
                           cli /*dissable interrupt*/
                              mov eax, cr0 /*mov CR0 register into EAX*/
                              and eax, 0xfffeffff /*disable WP bit*/
                              mov cr0, eax /*update register*/
                              sti /*enable interrupt*/
                        };
                        int i = 0;
                        for (i = 0; i < pTable->ServiceLimit; i++) {
                           NTPROC pOrigAddress = (NTPROC)(lpArraySSDT[i] - uImageBase + (char *)pKernelAddres);
                           if (pTable->ServiceTable[i] != pOrigAddress) {
                              LPCSTR sHooker = (LPCSTR)GetKernelBaseAddressByAddress(pSMInfo, pTable->ServiceTable[i], NULL);
                              if (!sHooker)sHooker = "Unknown";
                              if(strcmp(sHooker, "win32k.sys") == 0) continue;
                              DbgPrint("Func %d Hooked by %s! Current: %p - Original: %p", i, sHooker, pTable->ServiceTable[i], pOrigAddress);
                              pTable->ServiceTable[i] = pOrigAddress;
                              DbgPrint("Func %d Unhook %s", i, (pTable->ServiceTable[i] == pOrigAddress ? "OK" : "FAIL"));
                           }
                        }
                        __asm {
                           cli /*dissable interrupt*/
                              mov eax, cr0 /*mov CR0 register into EAX*/
                              or eax, 0x00010000 /*enable WP bit*/
                              mov cr0, eax /*update register*/
                              sti /*enable interrupt*/
                        };
                     }
                     else
                        DbgPrint("Error: Can't Read Kernel File");
                  }
                  else
                     DbgPrint("Error: Can't Change Kernel File Position");
                  ExFreePool(lpArraySSDT);
               }
               else
                  DbgPrint("Error: Can't Alloc Memory!\n");
               NtClose(hFile);
            }
            else
               DbgPrint("Error: Can't Open Kernel File");
            RtlFreeUnicodeString(&uFileName);
         }
         else
            DbgPrint("Error: Can't Convert to Unicode");
      }
      else
         DbgPrint("Error: Can't Get SSDT RAW Address");
   }
   else
      DbgPrint("Error: Can't Get Kernel Address!\n");
}

NTSTATUS GetCsrssPid(HANDLE* pUniqueProcessId)
{
   NTSTATUS status;
   ULONG cb = 0x10000;
   do
   {
      status = STATUS_INSUFFICIENT_RESOURCES;

      if (PVOID buf = ExAllocatePool(PagedPool, cb))
      {
         if (0 <= (status = NtQuerySystemInformation(SystemProcessInformation, buf, cb, &cb)))
         {
            status = STATUS_NOT_FOUND;

            union {
               PSYSTEM_PROCESS_INFORMATION pspi;
               PVOID pv;
               PUCHAR pb;
            };

            pv = buf;

            ULONG NextEntryOffset = 0;
            do
            {
               pb += NextEntryOffset;

               STATIC_UNICODE_STRING(csrss, "csrss.exe");

               if (RtlEqualUnicodeString(&csrss, &pspi->ImageName, TRUE))
               {
                  *pUniqueProcessId = pspi->UniqueProcessId;
                  DbgPrint("csrss.exe PID: %d\n", *pUniqueProcessId);
                  status = STATUS_SUCCESS;
                  break;
               }

            } while (NextEntryOffset = pspi->NextEntryOffset);
         }
         ExFreePool(buf);
      }

   } while (status == STATUS_INFO_LENGTH_MISMATCH);

   return status;
}

VOID FindHooks() {
   HANDLE hCsrssPid = (HANDLE)0;
   NTSTATUS ret = STATUS_SUCCESS;
   DbgPrint("Start Finding Hooks in SSDT!\n");
   PSYSTEM_MODULE_INFORMATION pSMInfo = GetSystemModuleInformation();
   if (pSMInfo) {
      //ntoskrnl
      DbgPrint("Finding Hooks in NTOSKrnl!\n");
      RestoreHooks(pSMInfo, &KeServiceDescriptorTable);
      DbgPrint("Find Hooks in NTOSKrnl Complete!\n");
      //win32k
      if (getShadowTable()) {
         PSERVICE_DESCRIPTOR_TABLE pShadow = /*GetServiceDescriptorShadowTableAddress()*/(PSERVICE_DESCRIPTOR_TABLE)KeServiceDescriptorTableShadow;
         if (pShadow) {
            PEPROCESS EProcess;
            ret = GetCsrssPid(&hCsrssPid);
            DbgPrint("Retorno de GetCsrssPid: 0x%x\n", ret);
            //we need to do this to have access to win32k memory...
            if (hCsrssPid&&NT_SUCCESS(PsLookupProcessByProcessId(hCsrssPid, &EProcess))) {
               KeAttachProcess(EProcess);
               DbgPrint("Finding Hooks in Win32k!\n");
               RestoreHooks(pSMInfo, &pShadow->win32k);
               DbgPrint("Find Hooks in Win32k Complete!\n");
               KeDetachProcess();
               ObDereferenceObject(EProcess);
            }
            else
               DbgPrint("Error: Can't get CSRSS Process Id!\n");
         }
         else
            DbgPrint("Error: Can't get Win32k Address!\n");
         ExFreePoolWithTag(pSMInfo, 0);
      }
   }
   else
      DbgPrint("Error: GetSystemModuleInformation Fail!\n");
   DbgPrint("End Finding Hooks in SSDT!\n");
}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING  RegistryPath)
{
   DbgPrint("DriverEntry()!\n");

   DriverObject->DriverUnload = MyDriverUnload;

   FindHooks();

   return STATUS_SUCCESS;
}

void MyDriverUnload(IN PDRIVER_OBJECT DriverObject)
{
   DbgPrint("Goodbye from MyDriver!\n");
}



Driver.h

Code: Select all

#pragma once

#ifndef _DRIVER_H_
#define _DRIVER_H_

#include <ntddk.h>
//#include "winternl.h"
#include "ntapi.h"

#ifndef OBJ_KERNEL_HANDLE
#define OBJ_KERNEL_HANDLE 0x00000200
#endif //OBJ_KERNEL_HANDLE

typedef NTSTATUS(NTAPI * NTPROC) (void);
typedef NTPROC * PNTPROC;

typedef struct tag_SYSTEM_SERVICE_TABLE {
   PNTPROC   ServiceTable; // array of entry points to the calls
   int  CounterTable; // array of usage counters
   ULONG ServiceLimit; // number of table entries
   PCHAR ArgumentTable; // array of argument counts
} SYSTEM_SERVICE_TABLE, *PSYSTEM_SERVICE_TABLE, **PPSYSTEM_SERVICE_TABLE;

typedef struct tag_SERVICE_DESCRIPTOR_TABLE {
   SYSTEM_SERVICE_TABLE ntoskrnl; // main native API table
   SYSTEM_SERVICE_TABLE win32k; // win subsystem, in shadow table
   SYSTEM_SERVICE_TABLE sst3;
   SYSTEM_SERVICE_TABLE sst4;
} SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE, **PPSERVICE_DESCRIPTOR_TABLE;

extern "C" NTOSAPI SYSTEM_SERVICE_TABLE KeServiceDescriptorTable;

typedef struct _SYSTEM_HANDLE_INFORMATION_EX {
   ULONG Count;
   ULONG NumberOfHandles;
   SYSTEM_HANDLE_INFORMATION Handle[1];
   SYSTEM_HANDLE_INFORMATION Information[1];
}SYSTEM_HANDLE_INFORMATION_EX, *PSYSTEM_HANDLE_INFORMATION_EX;

//remove if is defined for you :P
extern "C" NTSTATUS NTAPI PsLookupProcessByProcessId(/*IN*/ PVOID ProcessId,/*OUT*/ PEPROCESS *Process);

#endif //_DRIVER_H_



ntapi.h => https://pastebin.com/BFwWUvmT
fl4shc0d3r
 
Posts: 3
Joined: Fri Jan 20, 2017 3:10 am
Reputation point: 0

Re: How find correct original entry in SSDT Shadow table?

Postby tangptr » Wed Apr 19, 2017 10:46 am

It seems that you have some calculation errors. I didn't strictly examined your code but it seems that the base address of driver that you used when calculating the original address is ntoskrnl.
Coincidentally, I'm writing my own ARK. Therefore, I have some codes about checking shadow ssdt. You may use them for reference.
The driver is compiled via WDK7.1, using win7x86+free configuration. The GUI is written by Visual Basic 6.0.

Something to comment on your code:
Do not use inline assembly as you are writing C code. You may use __readcr0 and __writecr0 to operate CR0. _disable and _enable can be respectively used to replace cli and sti.
To read the official documents for Compiler-Intrinsics Macro, you may visit MSDN.(https://msdn.microsoft.com/en-us/library/26td21ds(v=vs.100).aspx).
Do not clear the WP-Bit in CR0 in order to write read-only memory mandatorily. You may use MDL to bypass the Write-Protection.
It is unnecessary to declare the definitions of PE file structures. Detailed definitions are included in <ntimage.h>.
You do not have the required permissions to view the files attached to this post.
User avatar
tangptr
 
Posts: 11
Joined: Mon Nov 14, 2016 11:14 am
Location: People Republic of China
Reputation point: 0


Return to Kernel-Mode Development

Who is online

Users browsing this forum: No registered users and 6 guests