sábado, 18 de octubre de 2008

A more stable way to locate real KiServiceTable

<br />A more stable way to locate real KiServiceTable By: 90210<br />Tan Chew Keong in his Win2K/XP SDT Restore 0.1 uses a simple way to<br />find changed SDT entries - he just compares SDT from memory with SDT<br />from ntoskrnl.exe file, assuming that KeServiceDescriptorTable.Base is<br />not changed. If it is, Tan\'s code that locates KiServiceTable on disk<br />will fail when KeServiceDescriptorTable.Base points somewhere outside<br />the ntoskrnl.<br />I\'ve found a way how to locate original KiServiceTable in the ntoskrnl<br />file even if KeServiceDescriptorTable.Base has been changed. Method<br />works both in the user mode and in the kmode.<br />This may be useful to bypass SDT-patching hooks, and not only to<br />restore old SDT. For example, KAV uses SDT relocation ;)<br />KeServiceDescriptorTable is initialized by KiInitSystem():<br />KeServiceDescriptorTable[0].Base = &amp;amp;KiServiceTable[0];<br />KeServiceDescriptorTable[0].Count = NULL;<br />KeServiceDescriptorTable[0].Limit = KiServiceLimit;<br />KeServiceDescriptorTable[0].Number = &amp;amp;KiArgumentTable[0]; for<br />(Index = 1; Index &amp;lt; NUMBER_SERVICE_TABLES; Index += 1) {<br />KeServiceDescriptorTable[Index].Limit = 0; }<br />Thus, we can find KiServiceTable by examining all xrefs to<br />KeServiceDescriptorTable in the kernel. We will search for C7 05 ..8<br />bytes.. mov ds:_KeServiceDescriptorTable.Base, offset _KiServiceTable<br />from which we will get _KiServiceTable rva.<br />It\'s easy to find KeServiceDescriptorTable xrefs by scanning the code,<br />but this is dangerous and time-consuming. It\'s better to use<br />ntoskrnl\'s relocation information - it is always present in all nt<br />systems.<br />This \"mov [mem32], imm32\" instruction will have 2 relocs pointing in<br />it, and the second is the one we\'re searching for. So, the usermode<br />code will do these steps:<br />1. Load ntosknrl as a dll.<br />2. Locate KeServiceDescriptorTable - it is exported.<br />3. Enumerate all relocations to find xrefs to the<br />KeServiceDescriptorTable.<br />4. Check these opcodes to be a \"mov [mem32],imm32\".<br />5. Get KiServiceTable - it\'s offset is +6 from the opcode beginning.<br />The example code dumps KiServiceTable data from file and does no<br />comparings with the existing sdt.<br />So, it\'s a must to a SDT-patching rootkit to hook file system and show<br />\"patched\" version of ntoskrnl to all readers. And of course, services<br />hooks code must reside in the ntoskrnl region and have no codepaths<br />outside it. Otherwise it may be tracked in seconds.<br />#include &amp;lt;windows.h&amp;gt;<br />#include &amp;lt;winnt.h&amp;gt;<br />#include &amp;lt;stdio.h&amp;gt;<br />#define RVATOVA(base,offset) ((PVOID)((DWORD)(base)+(DWORD)(offset)))<br />#define ibaseDD *(PDWORD)&amp;amp;ibase<br />#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)<br />#define NT_SUCCESS(Status) ((NTSTATUS)(Status) &amp;gt;= 0)<br />typedef struct { WORD offset:12; WORD type:4;<br />} IMAGE_FIXUP_ENTRY, *PIMAGE_FIXUP_ENTRY;<br />typedef LONG NTSTATUS;<br />#ifdef __cplusplus<br />extern \"C\" {<br />#endif<br />NTSTATUS<br />WINAPI<br />NtQuerySystemInformation( DWORD SystemInformationClass, PVOID<br />SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength<br />);<br />#ifdef __cplusplus<br />}<br />#endif<br />typedef struct _SYSTEM_MODULE_INFORMATION {//Information Class 11 ULONG<br />Reserved[2]; PVOID Base; ULONG Size; ULONG Flags; USHORT Index; USHORT<br />Unknown; USHORT LoadCount; USHORT ModuleNameOffset; CHAR<br />ImageName[256];<br />}SYSTEM_MODULE_INFORMATION,*PSYSTEM_MODULE_INFORMATION;<br />typedef struct { DWORD dwNumberOfModules; SYSTEM_MODULE_INFORMATION<br />smi;<br />} MODULES, *PMODULES;<br />#define SystemModuleInformation 11<br />DWORD GetHeaders(PCHAR ibase, PIMAGE_FILE_HEADER *pfh,<br />PIMAGE_OPTIONAL_HEADER *poh, PIMAGE_SECTION_HEADER *psh)<br />{ PIMAGE_DOS_HEADER mzhead=(PIMAGE_DOS_HEADER)ibase; if<br />((mzhead-&amp;gt;e_magic!=IMAGE_DOS_SIGNATURE) ||<br />(ibaseDD[mzhead-&amp;gt;e_lfanew]!=IMAGE_NT_SIGNATURE)) return FALSE;<br />*pfh=(PIMAGE_FILE_HEADER)&amp;amp;ibase[mzhead-&amp;gt;e_lfanew]; if<br />(((PIMAGE_NT_HEADERS)*pfh)-&amp;gt;Signature!=IMAGE_NT_SIGNATURE) return<br />FALSE;<br /> *pfh=(PIMAGE_FILE_HEADER)((PBYTE)*pfh+sizeof(IMAGE_NT_SIGNATURE));<br /> <br /> *poh=(PIMAGE_OPTIONAL_HEADER)((PBYTE)*pfh+sizeof(IMAGE_FILE_HEADER));<br /> if ((*poh)-&amp;gt;Magic!=IMAGE_NT_OPTIONAL_HDR32_MAGIC)<br /> return FALSE;<br /> <br /> *psh=(PIMAGE_SECTION_HEADER)((PBYTE)*poh+sizeof(IMAGE_OPTIONAL_HEADER));<br /> return TRUE;<br />}<br /><br /><br />DWORD FindKiServiceTable(HMODULE hModule,DWORD dwKSDT)<br />{<br /> PIMAGE_FILE_HEADER pfh;<br /> PIMAGE_OPTIONAL_HEADER poh;<br /> PIMAGE_SECTION_HEADER psh;<br /> PIMAGE_BASE_RELOCATION pbr;<br /> PIMAGE_FIXUP_ENTRY pfe; <br /> <br /> DWORD dwFixups=0,i,dwPointerRva,dwPointsToRva,dwKiServiceTable;<br /> BOOL bFirstChunk;<br /><br /> GetHeaders((PBYTE)hModule,&amp;amp;pfh,&amp;amp;poh,&amp;amp;psh);<br /><br /> // loop thru relocs to speed up the search<br /> if ((poh-&amp;gt;DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress) &amp;amp;&amp;amp;<br /> (!((pfh-&amp;gt;Characteristics)&amp;amp;IMAGE_FILE_RELOCS_STRIPPED))) {<br /> <br /> pbr=(PIMAGE_BASE_RELOCATION)RVATOVA(poh-&amp;gt;DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress,hModule);<br /><br /> bFirstChunk=TRUE;<br /> // 1st IMAGE_BASE_RELOCATION.VirtualAddress of ntoskrnl is 0<br /> while (bFirstChunk || pbr-&amp;gt;VirtualAddress) {<br /> bFirstChunk=FALSE;<br /><br /> pfe=(PIMAGE_FIXUP_ENTRY)((DWORD)pbr+sizeof(IMAGE_BASE_RELOCATION));<br /><br /> for (i=0;i&amp;lt;(pbr-&amp;gt;SizeOfBlock-sizeof(IMAGE_BASE_RELOCATION))&amp;gt;&amp;gt;1;i++,pfe++) {<br /> if (pfe-&amp;gt;type==IMAGE_REL_BASED_HIGHLOW) {<br /> dwFixups++;<br /> dwPointerRva=pbr-&amp;gt;VirtualAddress+pfe-&amp;gt;offset;<br /> // DONT_RESOLVE_DLL_REFERENCES flag means relocs aren\'t fixed<br /> dwPointsToRva=*(PDWORD)((DWORD)hModule+dwPointerRva)-(DWORD)poh-&amp;gt;ImageBase;<br /><br /> // does this reloc point to KeServiceDescriptorTable.Base?<br /> if (dwPointsToRva==dwKSDT) {<br /> // check for mov [mem32],imm32. we are trying to find <br /> // \"mov ds:_KeServiceDescriptorTable.Base, offset _KiServiceTable\"<br /> // from the KiInitSystem.<br /> if (*(PWORD)((DWORD)hModule+dwPointerRva-2)==0x05c7) {<br /> // should check for a reloc presence on KiServiceTable here<br /> // but forget it<br /> dwKiServiceTable=*(PDWORD)((DWORD)hModule+dwPointerRva+4)-poh-&amp;gt;ImageBase;<br /> return dwKiServiceTable;<br /> }<br /> }<br /> <br /> } else<br /> if (pfe-&amp;gt;type!=IMAGE_REL_BASED_ABSOLUTE)<br /> // should never get here<br /> printf(\" relo type %d found at .%X<br />\",pfe-&amp;gt;type,pbr-&amp;gt;VirtualAddress+pfe-&amp;gt;offset);<br /> }<br /> *(PDWORD)&amp;amp;pbr+=pbr-&amp;gt;SizeOfBlock;<br /> }<br /> } <br /> <br /> if (!dwFixups) <br /> // should never happen - nt, 2k, xp kernels have relocation data<br /> printf(\"No fixups!<br />\");<br /> return 0;<br />}<br /><br />void main(int argc,char *argv[])<br />{ <br /> HMODULE hKernel;<br /> DWORD dwKSDT; // rva of KeServiceDescriptorTable<br /> DWORD dwKiServiceTable; // rva of KiServiceTable<br /> PMODULES pModules=(PMODULES)&amp;amp;pModules;<br /> DWORD dwNeededSize,rc;<br /> DWORD dwKernelBase,dwServices=0;<br /> PCHAR pKernelName;<br /> PDWORD pService;<br /> PIMAGE_FILE_HEADER pfh;<br /> PIMAGE_OPTIONAL_HEADER poh;<br /> PIMAGE_SECTION_HEADER psh;<br /><br /> <br /> // get system modules - ntoskrnl is always first there<br /> rc=NtQuerySystemInformation(SystemModuleInformation,pModules,4,&amp;amp;dwNeededSize);<br /> if (rc==STATUS_INFO_LENGTH_MISMATCH) {<br /> pModules=GlobalAlloc(GPTR,dwNeededSize);<br /> rc=NtQuerySystemInformation(SystemModuleInformation,pModules,dwNeededSize,NULL);<br /> } else {<br />strange:<br /> printf(\"strange NtQuerySystemInformation()!<br />\");<br /> return;<br /> }<br /> if (!NT_SUCCESS(rc)) goto strange;<br /> <br /> // imagebase<br /> dwKernelBase=(DWORD)pModules-&amp;gt;smi.Base;<br /> // filename - it may be renamed in the boot.ini<br /> pKernelName=pModules-&amp;gt;smi.ModuleNameOffset+pModules-&amp;gt;smi.ImageName;<br /> <br /> // map ntoskrnl - hopefully it has relocs<br /> hKernel=LoadLibraryEx(pKernelName,0,DONT_RESOLVE_DLL_REFERENCES);<br /> if (!hKernel) {<br /> printf(\"Failed to load! LastError=%i<br />\",GetLastError());<br /> return; <br /> }<br /><br /> GlobalFree(pModules);<br /><br /> // our own export walker is useless here - we have GetProcAddress :) <br /> if (!(dwKSDT=(DWORD)GetProcAddress(hKernel,\"KeServiceDescriptorTable\"))) {<br /> printf(\"Can\'t find KeServiceDescriptorTable<br />\");<br /> return;<br /> }<br /><br /> // get KeServiceDescriptorTable rva<br /> dwKSDT-=(DWORD)hKernel; <br /> // find KiServiceTable<br /> if (!(dwKiServiceTable=FindKiServiceTable(hKernel,dwKSDT))) {<br /> printf(\"Can\'t find KiServiceTable...<br />\");<br /> return;<br /> }<br /><br /> printf(\"&amp;amp;KiServiceTable==%08X<br /><br />Dumping \'old\' ServiceTable:<br /><br />\",<br /> dwKiServiceTable+dwKernelBase); <br /> <br /> // let\'s dump KiServiceTable contents <br /> <br /> // MAY FAIL!!!<br /> // should get right ServiceLimit here, but this is trivial in the kernel mode<br /> GetHeaders((PBYTE)hKernel,&amp;amp;pfh,&amp;amp;poh,&amp;amp;psh);<br /> <br /> for (pService=(PDWORD)((DWORD)hKernel+dwKiServiceTable);<br /> *pService-poh-&amp;gt;ImageBase&amp;lt;poh-&amp;gt;SizeOfImage;<br /> pService++,dwServices++)<br /> printf(\"%08X<br />\",*pService-poh-&amp;gt;ImageBase+dwKernelBase); <br /><br /> printf(\"<br /><br />Possibly KiServiceLimit==%08X<br />\",dwServices);<br /><br /> FreeLibrary(hKernel);<br /><br />}<br /><br />

No hay comentarios: