在上篇文章讲到了利用TF的方式来检测是否存在MsrHook,这一篇继续完善一些其他的检测方式
NewIDT[1] = PgCtx->IdtEntryIdx1;
NewIDT[2] = PgCtx->IdtEntryIdx2;
NewIDT[0x12] = PgCtx->IdtEntryIdx18h;
NewIdtr.Size = 303;
NewIdtr.Base = NewIDT;
NewIDT[1].OffsetLow = &PgCtx->NewIdtDescriptor1;
NewIDT[1].OffsetMiddle = &PgCtx->NewIdtDescriptor1 >> 16;
NewIDT[1].OffsetHigh = &PgCtx->NewIdtDescriptor1 >> 32;
_disable();
__sidt(OriginalIdt);
__lidt(&NewIdtr);
__writedr(7u, 0i64);
NewIDT[1].OffsetLow = &PgCtx->NewIdtDescriptor2; // unclear why this is done again?
NewIDT[1].OffsetMiddle = &PgCtx->NewIdtDescriptor2 >> 16;
NewIDT[1].OffsetHigh = &PgCtx->NewIdtDescriptor2 >> 32;
if ( PgCtx->FeaturesActiveFlags & 0x20 )
{
PgCtx->StackSegmentReg = KiGetSs();
__writedr(0, &PgCtx->StackSegmentReg);
__writedr(7u, 0x70001ui64);
PgCtx->pSyscall = KiErrataSkx55Present(&PgCtx->StackSegmentReg);
__writedr(7u, 0i64);
__writedr(0, 0i64);
}
else
{
PgCtx->pSyscall = KiErrata704Present();
}
PgCtx->PrcbNumber = KeGetPcr()->Prcb.Number;
__lidt(OriginalIdt);
_enable();
_disable();
pNewIdt[1] = PgCtx->IdtEntryIdx1;
pNewIdt[2] = PgCtx->IdtEntryIdx2;
pNewIdt[0x12] = PgCtx->IdtEntryIdx18h;
NewIdtr.Size = 303;
NewIdtr.Base = pNewIdt;
pNewIdt[1].OffsetLow = &PgCtx->NewIdtDescriptor1;
pNewIdt[1].OffsetMiddle = &PgCtx->NewIdtDescriptor1 >> 16;
pNewIdt[1].OffsetHigh = &PgCtx->NewIdtDescriptor1 >> 32;
__sidt(OriginalIdtr);
__lidt(&NewIdtr);
if ( !(PgCtx->ChecksStatusFlags & 0x20000) )
{
CurrentPcrb = KeGetCurrentPrcb();
*PgCtx->qword_FFFFF80629E0F0C0 = &PgCtx + A3A03F5891C8B4E8h;
*PgCtx->qword_FFFFF80629E12DE8 = CurrentPcrb;
*PgCtx->qword_FFFFF80629E12DF0 = 0i64;
*PgCtx->qword_FFFFF80629E12DF8 = 0x115i64;
}
KiErrata361Present();
if ( !(PgCtx->ChecksStatusFlags & 0x20000) )
{
*PgCtx->qword_FFFFF80629E0F0C0 = 0xA3A03F5891C8B4E8ui64;
*PgCtx->qword_FFFFF80629E12DE8 = 0i64;
*PgCtx->qword_FFFFF80629E12DF0 = 0i64;
*PgCtx->qword_FFFFF80629E12DF8 = 0i64;
}
__lidt(OriginalIdtr);
_enable();
if ( PgCtx->FeaturesActiveFlags & 1 )
{
_disable();
IA32MSR = 0xC0000082i64; // IA32_LSTAR_MSR
pKiSyscall64 = __readmsr(0xC0000082);
__writemsr(0xC0000082, &PgCtx->PgSyscallHook); // 0xC3 -> ret
if ( !(PgCtx->ChecksStatusFlags & 0x20000) ) // (1)
{
Prcb = KeGetCurrentPrcb();
*PgCtx->qword_FFFFF80629E0F0C0 = &PgCtx + 0xA3A03F5891C8B4E8;
*PgCtx->qword_FFFFF80629E12DE8 = Prcb;
*PgCtx->qword_FFFFF80629E12DF0 = 0xC0000082i64;
Temp = PgCtx->qword_FFFFF80629E12DF8;
*Temp = 0x112i64; // (1)
}
KeGuardDispatchICall(&PgCtx->SyscallOpcode1); // 0F 05 -> syscall
if ( !(PgCtx->ChecksStatusFlags & 0x20000) ) // (2)
{
*PgCtx->qword_FFFFF80629E0F0C0 = 0xA3A03F5891C8B4E8ui64;
*PgCtx->qword_FFFFF80629E12DE8 = 0i64;
*PgCtx->qword_FFFFF80629E12DF0 = 0i64;
*PgCtx->qword_FFFFF80629E12DF8 = 0i64; // (2)
}
__writemsr(0xC0000082, pKiSyscall64);
_enable();
}
另外还有一种方法,对比上面的方法,只能监视到SystemCallNumber,并推断出Hook的函数地址,以360晶核为例
不过这种方法 对地址进行检测 对性能是有影响的。
只能监视SSDT,因为我发现高版本win10 SSSDT SystemCallNumber与函数地址不对等。 比如NtGdiExtEscape变成stub_GdiExtEscape
并且需要过滤NtCallbackReturn这个SystemCallNumber,否则函数地址是SSSDT的
.text:0000000140408418 89 83 80 00 00 00 mov [rbx+80h], eax
.text:000000014040841E 66 90 xchg ax, ax
.text:0000000140408420
.text:0000000140408420 KiSystemServiceStart: ; DATA XREF: KiServiceInternal+5A↑o
.text:0000000140408420 ; .data:0000000140C00340↓o
.text:0000000140408420 48 89 A3 90 00 00 00 mov [rbx+90h], rsp
.text:0000000140408427 8B F8 mov edi, eax
.text:0000000140408429 C1 EF 07 shr edi, 7
.text:000000014040842C 83 E7 20 and edi, 20h
.text:000000014040842F 25 FF 0F 00 00 and eax, 0FFFh
(SystemCallNumber >> 7) & 0x20 = 服务表的索引,也是判断是否是SSSDT
函数地址=KeServiceDescriptorTable + 服务表的索引[SystemCallNumber & 0xFFF] >> 4
至于这种方法原理是什么,请参考别人实现KiSystemCall64的ASM