//
// thanks to 90210 for the code
// http://www.rootkit.com/newsread.php?newsid=176
// u saved some of my time
//

#include "stdafx.h"

#define RVATOVA(base,offset) ((PVOID)((DWORD)(base)+(DWORD)(offset)))
#define ibaseDD *(PDWORD)&ibase
#define STATUS_INFO_LENGTH_MISMATCH      ((NTSTATUS)0xC0000004L)
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)

typedef struct {
	WORD    offset:12;
	WORD    type:4;
} IMAGE_FIXUP_ENTRY, *PIMAGE_FIXUP_ENTRY;


typedef LONG NTSTATUS;

NTSTATUS (WINAPI *pNtQuerySystemInformation)(    
		DWORD    SystemInformationClass,
		PVOID    SystemInformation,
		ULONG    SystemInformationLength,
		PULONG    ReturnLength
		);


typedef struct _SYSTEM_MODULE_INFORMATION {//Information Class 11
	ULONG    Reserved[2];
	PVOID    Base;
	ULONG    Size;
	ULONG    Flags;
	USHORT    Index;
	USHORT    Unknown;
	USHORT    LoadCount;
	USHORT    ModuleNameOffset;
	CHAR    ImageName[256];
}SYSTEM_MODULE_INFORMATION,*PSYSTEM_MODULE_INFORMATION;

typedef struct {
	DWORD    dwNumberOfModules;
	SYSTEM_MODULE_INFORMATION    smi;
} MODULES, *PMODULES;

#define    SystemModuleInformation    11

DWORD GetHeaders(PBYTE ibase,
				 PIMAGE_FILE_HEADER *pfh,
				 PIMAGE_OPTIONAL_HEADER *poh,
				 PIMAGE_SECTION_HEADER *psh)

{
	PIMAGE_DOS_HEADER mzhead=(PIMAGE_DOS_HEADER)ibase;

	if    ((mzhead->e_magic!=IMAGE_DOS_SIGNATURE) ||        
		(ibaseDD[mzhead->e_lfanew]!=IMAGE_NT_SIGNATURE))
		return FALSE;

	*pfh=(PIMAGE_FILE_HEADER)&ibase[mzhead->e_lfanew];
	if (((PIMAGE_NT_HEADERS)*pfh)->Signature!=IMAGE_NT_SIGNATURE) 
		return FALSE;
	*pfh=(PIMAGE_FILE_HEADER)((PBYTE)*pfh+sizeof(IMAGE_NT_SIGNATURE));

	*poh=(PIMAGE_OPTIONAL_HEADER)((PBYTE)*pfh+sizeof(IMAGE_FILE_HEADER));
	if ((*poh)->Magic!=IMAGE_NT_OPTIONAL_HDR32_MAGIC)
		return FALSE;

	*psh=(PIMAGE_SECTION_HEADER)((PBYTE)*poh+sizeof(IMAGE_OPTIONAL_HEADER));
	return TRUE;
}


DWORD FindKiServiceTable(HMODULE hModule,DWORD dwKSDT)
{
	PIMAGE_FILE_HEADER    pfh;
	PIMAGE_OPTIONAL_HEADER    poh;
	PIMAGE_SECTION_HEADER    psh;
	PIMAGE_BASE_RELOCATION    pbr;
	PIMAGE_FIXUP_ENTRY    pfe;    

	DWORD    dwFixups=0,i,dwPointerRva,dwPointsToRva,dwKiServiceTable;
	BOOL    bFirstChunk;

	GetHeaders((PBYTE)hModule,&pfh,&poh,&psh);

	// loop thru relocs to speed up the search
	if ((poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) &&
		(!((pfh->Characteristics)&IMAGE_FILE_RELOCS_STRIPPED))) {

			pbr=(PIMAGE_BASE_RELOCATION)RVATOVA(poh->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress,hModule);

			bFirstChunk=TRUE;
			// 1st IMAGE_BASE_RELOCATION.VirtualAddress of ntoskrnl is 0
			while (bFirstChunk || pbr->VirtualAddress) {
				bFirstChunk=FALSE;

				pfe=(PIMAGE_FIXUP_ENTRY)((DWORD)pbr+sizeof(IMAGE_BASE_RELOCATION));

				for (i=0;i<(pbr->SizeOfBlock-sizeof(IMAGE_BASE_RELOCATION))>>1;i++,pfe++) {
					if (pfe->type==IMAGE_REL_BASED_HIGHLOW) {
						dwFixups++;
						dwPointerRva=pbr->VirtualAddress+pfe->offset;
						// DONT_RESOLVE_DLL_REFERENCES flag means relocs aren't fixed
						dwPointsToRva=*(PDWORD)((DWORD)hModule+dwPointerRva)-(DWORD)poh->ImageBase;

						// does this reloc point to KeServiceDescriptorTable.Base?
						if (dwPointsToRva==dwKSDT) {
							// check for mov [mem32],imm32. we are trying to find 
							// "mov ds:_KeServiceDescriptorTable.Base, offset _KiServiceTable"
							// from the KiInitSystem.
							if (*(PWORD)((DWORD)hModule+dwPointerRva-2)==0x05c7) {
								// should check for a reloc presence on KiServiceTable here
								// but forget it
								dwKiServiceTable=*(PDWORD)((DWORD)hModule+dwPointerRva+4)-poh->ImageBase;
								return dwKiServiceTable;
							}
						}

					} else
						if (pfe->type!=IMAGE_REL_BASED_ABSOLUTE)
						{
							// should never get here
						}
							
				}
				*(PDWORD)&pbr+=pbr->SizeOfBlock;
			}
		}    

		if (!dwFixups) 
		{
			// should never happen - nt, 2k, xp kernels have relocation data
		}
		return 0;
}

BOOL SDT_GetOriginalFunctions(PRESTORE_INFO pRestoreInfo)
{    
	HMODULE    hKernel;
	DWORD    dwKSDT;                // rva of KeServiceDescriptorTable
	DWORD    dwKiServiceTable;    // rva of KiServiceTable
	PMODULES    pModules=(PMODULES)&pModules;
	DWORD    dwNeededSize,rc;
	DWORD    dwKernelBase,dwServices=0;
	PCHAR    pKernelName;
	PDWORD    pService;
	PIMAGE_FILE_HEADER    pfh;
	PIMAGE_OPTIONAL_HEADER    poh;
	PIMAGE_SECTION_HEADER    psh;

	pNtQuerySystemInformation = (NTSTATUS (WINAPI *)(    
		DWORD, PVOID, ULONG, PULONG)) GetProcAddress(
		GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");

	// get system modules - ntoskrnl is always first there
	rc=pNtQuerySystemInformation(SystemModuleInformation,pModules,4,&dwNeededSize);
	if (rc==STATUS_INFO_LENGTH_MISMATCH) {
		pModules = (PMODULES) GlobalAlloc(GPTR,dwNeededSize);
		rc=pNtQuerySystemInformation(SystemModuleInformation,pModules,dwNeededSize,NULL);
	} else {
strange:
		return FALSE;
	}
	if (!NT_SUCCESS(rc)) goto strange;

	// imagebase
	dwKernelBase=(DWORD)pModules->smi.Base;
	// filename - it may be renamed in the boot.ini
	pKernelName=pModules->smi.ModuleNameOffset+pModules->smi.ImageName;

	// map ntoskrnl - hopefully it has relocs
	hKernel=LoadLibraryEx(pKernelName,0,DONT_RESOLVE_DLL_REFERENCES);
	if (!hKernel) {
		return FALSE;        
	}

	GlobalFree(pModules);

	// our own export walker is useless here - we have GetProcAddress :)    
	if (!(dwKSDT=(DWORD)GetProcAddress(hKernel,"KeServiceDescriptorTable"))) {
		return FALSE;
	}

	// get KeServiceDescriptorTable rva
	dwKSDT-=(DWORD)hKernel;    
	// find KiServiceTable
	if (!(dwKiServiceTable=FindKiServiceTable(hKernel,dwKSDT))) {
		return FALSE;
	}

	// let's dump KiServiceTable contents        

	// MAY FAIL!!!
	// should get right ServiceLimit here, but this is trivial in the kernel mode
	GetHeaders((PBYTE) hKernel,&pfh,&poh,&psh);

	//
	// our code
	//

	//
	// get ZwAllocateVirtualMemory
	//

	DWORD nServ = GrabService("ZwAllocateVirtualMemory");

	pService = (PDWORD)((nServ * sizeof (DWORD)) + 
		(DWORD) hKernel + dwKiServiceTable);

	pRestoreInfo->ZwAllocateVirtualMemory = 
		(*pService-poh->ImageBase + dwKernelBase);

	//
	// get ZwCreateThread 
	//

	nServ = GrabService("ZwCreateThread");

	pService = (PDWORD)((nServ * sizeof (DWORD)) + 
		(DWORD) hKernel + dwKiServiceTable);

	pRestoreInfo->ZwCreateThread = 
		(*pService-poh->ImageBase + dwKernelBase);

	//
	// get ZwDebugContinue 
	//

	nServ = GrabService("ZwDebugContinue");

	pService = (PDWORD)((nServ * sizeof (DWORD)) + 
		(DWORD) hKernel + dwKiServiceTable);

	pRestoreInfo->ZwDebugContinue = 
		(*pService-poh->ImageBase + dwKernelBase);

	//
	// get ZwQueryVirtualMemory 
	//

	nServ = GrabService("ZwQueryVirtualMemory");

	pService = (PDWORD)((nServ * sizeof (DWORD)) + 
		(DWORD) hKernel + dwKiServiceTable);

	pRestoreInfo->ZwQueryVirtualMemory = 
		(*pService-poh->ImageBase + dwKernelBase);

	//
	// get ZwReadVirtualMemory 
	//

	nServ = GrabService("ZwReadVirtualMemory");

	pService = (PDWORD)((nServ * sizeof (DWORD)) + 
		(DWORD) hKernel + dwKiServiceTable);

	pRestoreInfo->ZwReadVirtualMemory = 
		(*pService-poh->ImageBase + dwKernelBase);

	//
	// get ZwTerminateProcess 
	//

	nServ = GrabService("ZwTerminateProcess");

	pService = (PDWORD)((nServ * sizeof (DWORD)) + 
		(DWORD) hKernel + dwKiServiceTable);

	pRestoreInfo->ZwTerminateProcess = 
		(*pService-poh->ImageBase + dwKernelBase);

	//
	// get ZwWriteVirtualMemory 
	//

	nServ = GrabService("ZwWriteVirtualMemory");

	pService = (PDWORD)((nServ * sizeof (DWORD)) + 
		(DWORD) hKernel + dwKiServiceTable);

	pRestoreInfo->ZwWriteVirtualMemory = 
		(*pService-poh->ImageBase + dwKernelBase);

	FreeLibrary(hKernel);

}