win10,2018新年版后好像不支持了?
MSR hook
MSR (Model Specific Registers)是CPU 的一组64 位寄存器,可以分别通过RDMSR 和WRMSR 两条指令进行读和写的操作,前提要在ECX 中写入MSR 的地址(MSR地址就像一个宏STAR,LSTAR,CSTAR,SFMASK)。对于RDMSR 指令,将会返回相应的MSR 中64bit 信息到(EDX:EAX)寄存器中;对于WRMSR 指令,把要写入的信息存入(EDX:EAX)中,执行写指令后,即可将相应的信息存入ECX 指定的MSR 中。MSR 的指令必须执行在level 0 或实模式下。
MSR 总体来是为了设置CPU 的工作环境和标示CPU 的工作状态,包括温度控制,性能监控等,具体来说,分为以下几项:
1. Thermal
2. Frequency
3. C State
4. Microcode
5. EIST
6. TM
7. Key Features Of CPU
8. Voltage
9. Cache Control
10. MTRR
11. DCA(Direct Cache Access)
12. Machine Check
13. 硬件联机控制
14.other
其中MSR_LSTAR里存放着SYSCALL调用的KiSystemCall64的地址。(从应用层到内核层调用的函数,x86的kifastcall),所以我们可以把这个位置的函数通过WRMSR替换,就可以替换SSDT表,然后就实现了X64hook。因为如果直接修改,不经过VT,patchguard会定期检查这里,所以使用VT修改欺骗。当RDMSR会引发exit事件,被VT捕获,VT可以把原来的值给回去。
所以X64HOOk的流程是先做业务流程的x64hook(SSDTHOOK),在VT上hookMSR。此外还要禁用WRMSR,防止别人替换。
X64上syscall流程
NTXXX Ntdll.dll
-》syscall(快速系统调用,intelx用sysenter和sysexit,amd:syscall,SYSRETURN)
-》去msr_lstar地址调用KisystemCall64
-》去KiSystemServiceStart
-》 KiSystemServiceRepeat,通过r10(函数地址),rax(参数个数)来调用SSDT表中的函数。
所以我们急速要替换KisystemCall64,所以我们也要去找到KiSystemServiceStart地址,在我们的函数里调用。需要暴力搜索特征码。
SSDTHOOK思路
X86上
拿到ssdt表首地址
拿到函数索引号(函数地址第二个到第五个字节就是索引号,或者可以解析ntdll)
用新的hook函数替换原函数
X64
拿到未导出SSDT,可以通过KiSystemCall64来定位它,这是Syscall时64位程序进入的RIP,可以通过readmsr(0xc0000082)来获取,向下搜索0x100个字节左右就有KeSerciveDescriptorTable和ShadowTable
拿到函数索引号,计算函数地址,索引号不在第一条指令的,在中间,需要反汇编。另外这里索引号也变了,这里ssdt表函数地址是相对于ssdt表首地址的偏移左移了四位,所以计算函数地址,我们需要根据索引号找到ssdt表记录的地址,。右移四位加上ssdt首地址,才是函数地址。即ServiceTableBase+ServiceTableBase[x]>>4
构造自己的SSDT表,并替换SYSCALL入口,进入自己的SSDT表。
SSDT索引的计算
X86:&XwXxxxx+1,或者解析ntdll.dll,x64:&XwXxxxx+n,or解析ntdll.dll
可以在函数里找mov eax机器码是B8,后面就是索引号(?如果函数本身就有mov eax呢?)
代码思路
先备份原来的NtSyscallhander=(ULONG64)__readmsr(MSR_LSTAR),(欺骗patchguard用和恢复要用)
writemsr,就是自己写一个kisystemcall64替换msr中msr_lstar中的值
vt exit事件中,欺骗系统的readmsr,并禁止别人再writemsr
那syscall如何不被欺骗,因为syscall不是使用readmr读取这里值,是直接获取msr_lstar中的值,PG才是使用readmsr,readmsr才会陷入vt,所以syscall不会被欺骗。
注意要先hook,再VT,不然readmsr读不到VT值,而且vt对writemsr禁用,所以不能在替换了。所以要先hook。(pg不是试试的),所以卸载时候先关vt,在unhook
代码流程
1.备份NtSyscallhander=(ULONG64)__readmsr(MSR_LSTAR)
2.构造自己的SSDT表数组
SyscallPointTable[4096](HOOK之后的SSDT函数),SyscallHookEnabled[4096](标识对应的索引号有没被我们hook),SyscallParamTable[4096](每个索引号需要的函数参数个数)
3.提供汇编版的SyscallEntryPoint(GuestSyscallHandler,用来替换原来的KisystemCall64),所以
原来函数走kisystemcall64-》kisystemservicestart-》kisystemservicerepeat-》oldssdt
现在函数走syscallentrypoint-》kisystemcall64_Emulate->kisystemservicestart_Emulate->kisystemServiceReat(暴力搜索r10,rax确定函数地址,参数)-》syscallpointtable(newssdt)
4.__writemsr(MSR_LSTAR,GuestSyscallHandler),完成替换,开启vt
5.在readmsr的vt exit事件中,返回备份的NtSyscallHandler,欺骗系统,禁用writemsr。
往VThook框架添加要hook函数
流程
获取函数原型
Nt.c/h 宏定义index和old函数声明,index计算old函数地址计算,封装函数实现
Ssdt.c/ssdt.h(hook函数实现)
hook.c 导入index变量,addServicecallHook,removeServiceCallHook。
以NtCreateSection为例
__kernel_entry NTSYSCALLAPI NTSTATUS NtCreateSection(
PHANDLE SectionHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
PLARGE_INTEGER MaximumSize,
ULONG SectionPageProtection,
ULONG AllocationAttributes,
HANDLE FileHandle
);
现在nt.c中添加
ECL_NT(VtNtCreateSection,
OUT PHANDLE SectionHandle,
IN ULONG DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN PLARGE_INTEGER MaximumSize OPTIONAL,
IN ULONG PageAttributess,
IN ULONG SectionAttributes,
IN HANDLE FileHandle OPTIONAL);
在InitializeSection里加上获取ntcreatesection索引号
NTSTATUS InitializeSSDTHook()
{
if (GetSSDTFunctionIndex((ULONG_PTR)&ZwClose, &IdVtNtClose) &&
GetSSDTFunctionIndex((ULONG_PTR)&ZwOpenSection, &IdVtNtOpenSection)&&
GetSSDTFunctionIndex((ULONG_PTR)&ZwCreateFile, &IdVtNtCreateFile) &&
GetSSDTFunctionIndex((ULONG_PTR)&ZwCreateSection,&IdVtNtCreateSection))
{
KdPrint(("get index ok"));
}
else
{
KdPrint(("get index failed"));
return STATUS_UNSUCCESSFUL;
}
*(ULONG_PTR *)&pVtNtClose = GetSSDTEntry(IdVtNtClose);//pVtNtClose original address
*(ULONG_PTR *)&pVtNtOpenSection = GetSSDTEntry(IdVtNtOpenSection);
*(ULONG_PTR *)&pVtNtCreateFile = GetSSDTEntry(IdVtNtCreateFile);
*(ULONG_PTR *)&pVtNtCreateSection = GetSSDTEntry(IdVtNtCreateSection);
if (!pVtNtClose || !pVtNtOpenSection || !pVtNtCreateFile || !pVtNtCreateSection)
return STATUS_UNSUCCESSFUL;
return STATUS_SUCCESS;
}
然后比照之前函数包装一个相应函数,这个是用于放行的
NTSTATUS NTAPI VtNtCreateSection(
OUT PHANDLE SectionHandle,
IN ULONG DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN PLARGE_INTEGER MaximumSize OPTIONAL,
IN ULONG PageAttributess,
IN ULONG SectionAttributes,
IN HANDLE FileHandle OPTIONAL)
{
KdPrint(("NtCreateSection called\n"));
return pVtNtCreateSection(
SectionHandle,
DesiredAccess,
ObjectAttributes,
MaximumSize,
PageAttributess,
SectionAttributes,
FileHandle);
}
在nt.h加上这个函数,用于其他模块调用
NTSTATUS NTAPI VtNtCreateSection(
OUT PHANDLE SectionHandle,
IN ULONG DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN PLARGE_INTEGER MaximumSize OPTIONAL,
IN ULONG PageAttributess,
IN ULONG SectionAttributes,
IN HANDLE FileHandle OPTIONAL);
然后在ssdt.h/c添加相应函数,实现新的hook
ssdt.c
NTSTATUS hk_NtCreateSection(
OUT PHANDLE SectionHandle,
IN ULONG DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN PLARGE_INTEGER MaximumSize OPTIONAL,
IN ULONG PageAttributess,
IN ULONG SectionAttributes,
IN HANDLE FileHandle OPTIONAL)
{
KdPrint(("NtCreateSection called\n"));
return VtNtCreateSection(
SectionHandle,
DesiredAccess,
ObjectAttributes,
MaximumSize,
PageAttributess,
SectionAttributes,
FileHandle);
}
ssdt.h
NTSTATUS NTAPI hk_NtCreateSection(
OUT PHANDLE SectionHandle,
IN ULONG DesiredAccess,
IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL,
IN PLARGE_INTEGER MaximumSize OPTIONAL,
IN ULONG PageAttributess,
IN ULONG SectionAttributes,
IN HANDLE FileHandle OPTIONAL);
修改hook.c
导入id
extern UINT32 IdVtNtCreateSection;
函数 ServiceCallInitialize()里添加
AddServiceCallHook(IdVtNtCreateSection, 7, (PVOID)&hk_NtCreateSection);
NTSTATUS ServiceCallUnload()添加
RemoveServiceCallHook(IdVtNtCreateSection);
发现一家hook成功,但是pchunter没有发现钩子,已达成vt欺骗。
EPThook
原理:如果GuestPA转HostPA期间发生权限异常,客户机发生VmExit事件
,控制权回到宿主机手中(设置guesttrip,调到hook函数等),优点是无痕,缺点是控制粒度大,影响性能。
可以去单页权限控制,去执行权限,取消原函数所在物理页对应的物理扩展页表PTE执行权限,当执行到该函数的时候翻书异常,判断发生溢出的线性地址是否是HOOK函数位置,如果是恢复可执行权限,将客户机RIP寄存器设置为hook函数地址,设置MTF标志,以便返回到客户机执行完这几指令之后返回到宿主机中将该物理页可执行权限去除。(MTFMonitor trap flag,用来做程序调试的)
或者双页权限互斥(读写和执行权限分离),
总之VT下可以有针对内存读写的事件回调,也就是访问内存时(读写执行),会陷入到根模式,此时做处理自然就可以了 。