MSR_HOOK

32 位下 HOOk SYSENTER 实现进程保护。

OpenProcess的调用过程如下:

kernel32.OpenProcess -> kernelBa.OpenProcess -> ntdll.ZwOpenProcess -> ntdll.KiFastSystemCall ->(Ring0) _KiFastCallEntry -> (Ring0)NtOpenProcess

ZwOpenProcess中的代码如下:

mov eax,0xeb  //调用号
mov edx,KUSER_SHARED_SYSCALL 
// cpu初始化时,根据架构不同,KUSER_SHARED_SYSCALL里面的实现的内核函数指针接口也不同,可能是 KiIntSystemCall中断式系统调用 也可能是 KiFastSystemCall快速系统调用
call [edx]
ret 0x10

这里edx保存的是KiFastSystemCall函数,继续跟进KiFastSystemCall函数

mov edx,esp
sysenter 
retn

简单的三条指令,mov edx,esp 因为下一条指令SYSENTER就是进入内核层,由于每个线程都有一套线程上下文,都有一个独立的栈.。进入到内核后,内核也会使用自己的内核栈,所以这里先用edx保存栈顶esp。

SYSENTER 执行的时候,会读取三个特殊寄存器,从这三个特殊寄存器中取出内核栈的栈顶( esp ) ,内核代码段段选择子( cs ) ,以及代码的首地址( eip ),保存这三个值得寄存器是MSR寄存器组。这组寄存器没有名字,只有编号,由于没有名字,无法通过正常的汇编指令来存取值,Intel提供了两条指令来读写这些寄存器:

  • rdmsr 读取MSR寄存器 其中高32位存放在EDX 低32位存放在EAX(64位和32位是一样,只是64位时rdx和rcx的高32位会被清零),使用ECX传递寄存器编号

  • wrmsr 写入MSR寄存器,和读取一样写入时是用EDX表示高32位,EAX表示低32位,使用ECX传递寄存器编号

也就是说, Windows在启动,进行初始化的时候会将内核栈栈顶,内核CS段选择子,以及代码段地址(KiFastCallEntry 函数)的地址一一存放到MSR寄存器组的这几个编号的寄存器中。当 SYSENTER 被执行,,CPU就直接使用这些寄存器的值来初始化真正的CS , EIP , ESP 寄存器。因此, SYSENTER 执行之后, 就跑到内核的 KiFastCallEntry 函数中执行代码了。

而进行SYSENTER-HOOK时我们只需要关注代码的地址( SYSENTER_EIP_MSR )即可,它的编号是0x176。用类似于3环的Inline-Hook的方法,直接把该地址改为我们自己的函数地址,过滤检查传入的参数,这样就能实现HOOK保护进程了。具体用法如下:

#include <ntddk.h>

ULONG OldAddr;
VOID DriverUnload(PDRIVER_OBJECT pDriver_Object);
VOID OnHook();
UINT32 g_Pid = 2652;

void _declspec(naked) MyKiFastCallEntry()  //过滤参数
{
    __asm
    {
        cmp eax, 0xbe;//对比是否是NtOpenProcess的调用号
        jne _End;     //不是则不处理
        push eax;     //保存寄存器
        mov eax, [edx + 0x14];//获取第4个参数PCLIENT_ID
        mov eax, [eax];//获取PCLIENT_ID第一个字段PID
        //PCLIENT_ID->UniqueProcess的值       
        cmp eax, g_Pid;//判断是否是要保护的进程
        pop eax;
        jne _End;
        cmp[edx + 0xc], 1;//判断是否是关闭操作
        jne _End;
        mov[edx + 0xc], 0;//是就把访问权限设为无
    _End:
        jmp OldAddr;//调用原来的_KiFastCallEntry函数
    }
}

VOID OnHook(){
    KAFFINITY ActiveProcessors, CurrentAffinity;
    ActiveProcessors = KeQueryActiveProcessors();
    for (CurrentAffinity = 1; ActiveProcessors; CurrentAffinity <<= 1)  //考虑多核同步下,msr逻辑分离,所以修改每个内核的msr
    {
        if (ActiveProcessors & CurrentAffinity)
        {
            ActiveProcessors &= ~CurrentAffinity;
            KeSetSystemAffinityThread(CurrentAffinity);
            _asm
            {
                cli      // 锁,防止中断
                    push ecx
                    push eax
                    mov ecx, 0x176
                    rdmsr
                    mov OldAddr, eax //保存原来的 SYSENTER_EIP_MSR中的_KiFastCallEntry
                    xor eax, eax
                    mov eax, MyKiFastCallEntry // 将 SYSENTER_EIP_MSR寄存器的值设置为我们的过滤函数
                    wrmsr
                    xor eax, eax
                    xor ecx, ecx
                    pop eax
                    pop ecx
                    sti
            }
        }
    }
    DbgPrint("NewKiFastCallEntry Addr:%08x\n", MyKiFastCallEntry);

}


VOID DriverUnload(PDRIVER_OBJECT pDriver_Object)  //恢复HOOK
{
    KAFFINITY ActiveProcessors, CurrentAffinity;
    ActiveProcessors = KeQueryActiveProcessors();
    for (CurrentAffinity = 1; ActiveProcessors; CurrentAffinity <<= 1)
    {
        if (ActiveProcessors & CurrentAffinity)
        {
            ActiveProcessors &= ~CurrentAffinity;
            KeSetSystemAffinityThread(CurrentAffinity);
            _asm
            {
                cli
                    push ecx
                    push eax
                    mov ecx, 0x176
                    mov eax, OldAddr
                    wrmsr
                    xor ecx, ecx
                    xor eax, eax
                    pop eax
                    pop ecx
                    sti
            }
        }
    }
    DbgPrint("驱动卸载成功\n");
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver_Object, PUNICODE_STRING pRegstryString)
{
    OnHook();
    pDriver_Object->DriverUnload = DriverUnload;
    return STATUS_SUCCESS;

}

Last updated