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 )
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)
and compare them with the real SSDT contents in kernel memory. ( Shows in windbg)
as mentioned in SSDT HOOK, the real kernel function address equals value >> 4 plus table base address.
for example the first SSDT function offset is
and the first 8 bytes in SSDT in ntoskrnl.exe file buffer showed above are
// Get default function address with index
defaultAddress = (PULONGULONG)ssdtFileBase + SSDTIndex;
originalOffset = defaultAddress - (ntDefaultBase + tableRva);
originalValue = (LONG)(originalOffset << 4);
// Remember original value is LONG type
LONG originalValue =
((PULONG_PTR)ssdtFileBase + Index - ntDefaultBase - tableRva) << 4
0: kd> lmDmnt
Browse full module list
start end module name
83e4b000 8425d000 nt (pdb symbols) C:\Program Files (x86)\Windows Kits\10\Debuggers\x86\sym\ntkrpamp.pdb\C820DD65C4BC4499A56D7610BE16FD082\ntkrpamp.pdb
0: kd> dps nt!KeServiceDescriptorTable
83fb4b00 83ec9d9c nt!KiServiceTable
83fb4b04 00000000
83fb4b08 00000191
83fb4b0c 83eca3e4 nt!KiArgumentTable
ntBase = 0x83e4b000 ;
ntTableBase = 0x83ec9d9c ;
// So the offset is
tableRva = ntTableBase - ntBase; // --> 0x7ed9c
// table offset to file base in ntoskrnl.exe file
tableOffset = RvaToOffset(tableRva); // --> 0x7e59c
// Get pSystemInfoBuffer by ZwQuerySystemInformation routine
PUCHAR ntFilename = pSystemInfoBuffer->Module[0].FullPathName;
PVOID pNtFileBase = ReadFile(ntFilename);
ULONG_PTR ssdtFileBase = pNtFileBase + tableOffset;
// Get default function address with index
defaultAddress = (PULONG)ssdtFileBase + SSDTIndex;
originalValue = defaultAddress - (ntDefaultBase + tableRva);
LONG originalValue =
((PULONG_PTR)w32kTableFileBase + Index - w32kDefaultBase - w32kTableRva) << 4
LONG originalValue =
(PULONG_PTR)w32kTableFileBase + Index - w32kDefaultBase - w32kTableRva