If the SSDT(Shadow) was hooked by bad guys, how can we restore it to the original state?
Unhook SSDT
x64
To unhook SSDT, we must obtain the original value of the SSDT function offset which has been replaced. Well it's a little trick to do this.
First, we have to calculate the offset from SSDT base address to kernel base address, for example in WinDbg ( Machine OS is Win7 x64):
1: kd> lmDmnt
Browse full module list
start end module name
fffff800`03e50000 fffff800`0443a000 nt (pdb symbols) C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\sym\ntkrnlmp.pdb\3844DBB920174967BE7AA4A2C20430FA2\ntkrnlmp.pdb
1: kd> dps nt!KeServiceDescriptorTable
fffff800`04101840 fffff800`03ed1300 nt!KiServiceTable
fffff800`04101848 00000000`00000000
fffff800`04101850 00000000`00000191
fffff800`04101858 fffff800`03ed1f8c nt!KiArgumentTable
We have
ntBase =0xfffff80003e19000;ntTableBase =0xfffff80003e9a300;// So the offset is tableRva = ntTableBase - ntBase; // --> 0x81300// table offset to file base in ntoskrnl.exe filetableOffset =RvaToOffset(tableRva); // --> 0x80900
Then read the kernel executable to memory (just like we read ntdll.dll in SSDT HOOK) to get original SSDT base in file ( the default path is "c:\windows\system32\ntoskrnl.exe", but to double-check, we get its image path from kernel module information )
// Get pSystemInfoBuffer by ZwQuerySystemInformation routinePUCHAR ntFilename = pSystemInfoBuffer->Module[0].FullPathName;PVOID pNtFileBase =ReadFile(ntFilename); ULONG_PTR ssdtFileBase = pNtFileBase + tableOffset;
and analyze its PE header to get its ImageBase field.
Then read the first bytes of the SSDT in ntoskrnl.exe file buffer. ( For simplicity, I read them in a binary file editor which is the same as in a program)
// bytes begin at address of ssdtFileBaseA0 EC 484001000000 C0 68374001000000A0 8107400100000080 9A 364001000000A0 B7 394001000000 A0 29394001000000
and compare them with the real SSDT contents in kernel memory. ( Shows in windbg)
and the first 8 bytes in SSDT in ntoskrnl.exe file buffer showed above are
ULONGLONG originalBytes =0x000000014048ECA0
Here comes the climax of the play.
which is
So we can conclude that the originalBytes in nt file is actually the default address of its corresponding kernel function .
Now we can calculate the original SSDT value of any SSDT function with its index even if SSDT has been modified in runtime.
// Get default function address with indexdefaultAddress = (PULONGULONG)ssdtFileBase + SSDTIndex; originalOffset = defaultAddress - (ntDefaultBase + tableRva); originalValue = (LONG)(originalOffset <<4);// Remember original value is LONG type
Combine together
So the steps to get the original value of any SSDT function is:
Get kernel base address ntBase and SSDT base address ssdtBase ( like in Hook SSDT(Shadow)),
Calculate the RVA between them: tableRva
Read ntoskrnl.exe in memory and get its default image base address ntDefaultBase and original SSDT buffer ssdtFileBase
For any function we can calculate its default SSDT value with its SSDT index ,which is
LONG originalValue =((PULONG_PTR)ssdtFileBase + Index - ntDefaultBase - tableRva) <<4
x86
In X86 platform the thing is very similar.
First we need to get the kernel base and SSDT base , as shown in Windbg:
0: kd> lmDmntBrowse full module liststart end module name83e4b000 8425d000 nt (pdb symbols) C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\sym\ntkrpamp.pdb\C820DD65C4BC4499A56D7610BE16FD082\ntkrpamp.pdb
0: kd> dps nt!KeServiceDescriptorTable83fb4b00 83ec9d9c nt!KiServiceTable83fb4b040000000083fb4b080000019183fb4b0c 83eca3e4 nt!KiArgumentTable
we have
ntBase =0x83e4b000 ;ntTableBase =0x83ec9d9c ;// So the offset is tableRva = ntTableBase - ntBase; // --> 0x7ed9c// table offset to file base in ntoskrnl.exe filetableOffset =RvaToOffset(tableRva); // --> 0x7e59c
Then read the nt file into memory( note that we need to get the right file path from SystemModuleInformation by ZwQuerySystemInformation)
// Get pSystemInfoBuffer by ZwQuerySystemInformation routinePUCHAR ntFilename = pSystemInfoBuffer->Module[0].FullPathName;PVOID pNtFileBase =ReadFile(ntFilename); ULONG_PTR ssdtFileBase = pNtFileBase + tableOffset;
and analyze its PE header to get its ImageBase field.