码迷,mamicode.com
首页 > 其他好文 > 详细

Sysenter hook 检测与恢复

时间:2016-04-18 19:01:56      阅读:587      评论:0      收藏:0      [点我收藏+]

标签:

关于Sysenter、Kifastcallentry、中断之类的内核入口hook技术早就烂大街了,可是对hook的检测与恢复代码却是寥寥无几,一切抛开代码将原理的行为都是耍流氓。  

下面以Sysenter hook技术为例子,重点分析下这类钩子的检测与恢复技术。

 

Sysenter简述 :

  Windows2000以前用int 2e作为中断指令进入内核发起系统调用。从Windows2000以后,准确的说是Pentium II处理器开始,为了避免int 2e指令对模式切换的巨大开销,Intel引入了一对新的指令,sysenter/sysexit,实现快速的模式切换,所以也叫快速系统调用。 关于两者之间的具体异同请参考潘爱民老师《Windows内核原理与实现》第8章Windows系统服务,再次不是分析重点,不在累赘。

 

  sysenter使用三个MSR(Model Specific Register)寄存器来指定跳转目标地址和栈位置。操作系统在内核模式下通过rdmsr/wrmsr特权指令来设置这三个寄存器,当然必须在系统初始化时(第一次系统调用发生以前)完成。

 

MSR寄存器                                MSR地址        含义

IA32_SYSENTRY_CS                174h           低16位值制订了特权级0的代码段和栈段的段选择符

IA32_SYSENTRY_ESP      175h           内核栈指针的32位偏移  

IA32_SYSENTRY_EIP       176h             目标例程的32位偏移(Kifastcallentry地址)

 

根据这三个MSR寄存器的属性我们看一段代码

    _asm
    {
        mov ecx,0x176
        rdmsr
        mov KifastcalllAddress,eax
    }

 

获取Kifastcallentry地址其实只需要三行汇编就可以搞定,但是有很多人说看不懂。  

RDMSR将64位由ECX寄存器指定的MSR(model specific register,模式指定寄存器)的内容读出至寄存器EDX:EAX中(在支持intel64架构的处理器中RCX的高32位忽略)。MSR的高32位内容存放在EDX寄存器中,MSR的低32位内容存放在EAX寄存器(在支持intel64架构的处理器中RDX和RAX的高32位忽略)。

说这么多其实就是把MSR寄存器地址放进ECX,然后通过RDMSR特权指令就能把相应MSR寄存器的值读进EAX中。而IA32_SYSENTRY_EIP寄存器中保存的就是Kifastcallentry地址。

 

Sysenter hook:

下面看一个替换IA32_SYSENTRY_EIP值进行sysentry hook小Demo。

 

#include "HookSysenter.h"


ULONG  OriginalAddress = 0;
ULONG  i = 0;

__declspec(naked)FakeAddress()
{

    _asm
    {
        mov i,eax
    }

    __asm
    {
        pushad
        push fs
        push 0x30
        pop fs
    }

    if (i==0x101)
    {
        DbgPrint("Terminate\r\n");
    }


    _asm
    { 
        pop fs   
        popad
        jmp [OriginalAddress]    
    }
}


NTSTATUS
DriverEntry(PDRIVER_OBJECT  DriverObject,
            PUNICODE_STRING RegisterPath)
{

    DriverObject->DriverUnload = UnloadDriver;



    DbgPrint("Hello\r\n");


    Hook();
    return STATUS_SUCCESS;
}

VOID
UnloadDriver(PDRIVER_OBJECT  DriverObject)
{
    UnHook();
}

VOID
Hook()
{
    _asm
    {
        mov ecx,0x176
        rdmsr
        mov OriginalAddress,eax
        mov eax, FakeAddress
        wrmsr
    }
}

VOID
UnHook()
{
    KIRQL oldIrql;
    oldIrql=KeRaiseIrqlToDpcLevel();
    _asm
    {
        mov ecx,0x176
        mov eax,OriginalAddress
        wrmsr
    }
    KeLowerIrql(oldIrql);
}

 

只是替换Kifastcallentry地址,然后简单判断了下是否发起对NTterminateProcess的系统调用,很简单。

 

那么问题来了,我们如何通过内核文件去寻找原始的Kifastcallentry呢?

#include "CheckKiFastCall.h"
#include "Asm.H"


NTSTATUS
DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath )
{

    CheckKiFastCallHook(TRUE);


    DriverObject->DriverUnload = UnloadDriver;

    return STATUS_SUCCESS;
}

VOID
UnloadDriver(PDRIVER_OBJECT  DriverObject)
{

}



int  CheckKiFastCallHook(BOOLEAN UnHook)
{
    MODULE_INFO  KernelModule;
    PULONG       KernelBaseAddress = NULL;
    PIMAGE_NT_HEADERS     Ntheader = NULL;
    PUCHAR       EntryPointer = NULL;    //OEP
    int          Strlen = 0;
    PRAW_IDT     rawIDT = NULL;
    ULONG        IDTBaseAddr = 0;
    ULONG        ImageBase = 0;
    int          Diff = 0;
    PULONG       Temp = NULL;
    ULONG        Address = 0;
    ULONG        KiFmAddr = 0;
    KIRQL         Irq;

    if (GetKernelBase(&KernelModule)==0)
    {
        DbgPrint("Get ntkrnlpa.exe Error\r\n");
        return -1;
    }
    

    KernelBaseAddress = (PVOID)MapViewOfImage(KernelModule.FullPath);
    if(!KernelBaseAddress)
    {
        return -1;
    }

    Ntheader = (PIMAGE_NT_HEADERS)((ULONG)KernelBaseAddress+((PIMAGE_DOS_HEADER)(KernelBaseAddress))->e_lfanew);

    EntryPointer = (PUCHAR)((ULONG)KernelBaseAddress+Ntheader->OptionalHeader.AddressOfEntryPoint);   //获得代码的入口OEP


    DbgPrint("OEP:%x\r\n",EntryPointer);


    while (1)
    {

        PUCHAR Code = 0;

        Strlen = SizeOfCode((VOID*)EntryPointer,&Code);

        if (Strlen<=0)   //没有获得指令
        {
            break;
        }

        if (*Code==0xc2)
        {
            break;
        }

        if (*Code==0xc3)
        {
            break;             
        }
        //以上两条Opcode都是return 可以使用查看Demo

        /*

  kd> u kisystemstartup l 100
  806909b2 8b7df0          mov     edi,dword ptr [ebp-10h]
  806909b5 befc0a6980      mov     esi,offset nt!IDT (80690afc)
  806909ba b900080000      mov     ecx,800h
  806909bf c1e902          shr     ecx,2
  806909c2 f3a5            rep movs dword ptr es:[edi],dword ptr [esi]

       */
        if (Strlen==5)
        {
            rawIDT = (PRAW_IDT)EntryPointer;

    
            DbgPrint("%x       %x            %x\r\n",rawIDT->d_800h,rawIDT->Opcode1,rawIDT->Opcode2);

            if((rawIDT->d_800h==0x800)&&(rawIDT->Opcode1==0xbe)&&(rawIDT->Opcode2==0xb9))
            {

                IDTBaseAddr = rawIDT->OrigBase;   //该值实际上也是 一个 相对偏移

                //DbgPrint("%x\r\n",IDTBaseAddr);

                break;
            }


        }


        EntryPointer+=Strlen;

    }

    if (IDTBaseAddr==0)
    {
        ZwUnmapViewOfSection((HANDLE)-1,(PVOID)KernelBaseAddress);

        return -1;
    }


    ImageBase = GetImageBase(KernelBaseAddress);                 //获得PE文件中的预加载基地址

    Diff = (ULONG)KernelBaseAddress - (ULONG)ImageBase;          //获得差值

    IDTBaseAddr += Diff;                                         //获得真正的IDT表

/*

  kd> dq /c 1 80690afc
  80690afc  80538e00`0008f19c
  80690b04  80538e00`0008f314
  80690b0c  80538e00`0008f3fc
  80690b14  8053ee00`0008f6e4
  80690b1c  8053ee00`0008f864
  80690b24  80538e00`0008f9c0
  80690b2c  80538e00`0008fb34
  80690b34  80548e00`0008019c
  80690b3c  80548e00`000804ce
  80690b44  80548e00`000805c0
  80690b4c  80548e00`000806e0
  80690b54  80548e00`00080820
  80690b5c  80548e00`00080a7c
  80690b64  80548e00`00080d60
  80690b6c  80548e00`00081450
  80690b74  80548e00`00081780

 */

    Temp = (PULONG)IDTBaseAddr;

    Temp+=2;   //80538e00`0008f314  =  取第一个成员的高16 和 第二成员的低16位    

    Address = *(Temp)+Diff;


    DbgPrint("%x\r\n",Address);  //我们这里得到的值就是那个地址了



    Address = GetKiFastCallEntryOrigEntry((PUCHAR)Address);  //此时的Address 也是一个RVA


    if (Address==0)
    {
        ZwUnmapViewOfSection((HANDLE)-1,(PVOID)KernelBaseAddress);
        return -1;
    }


    //Address 地址中存储的就是KiFastCallEntry 的OpCode 指令



    //获得现在的KiFastCallEntry

    _asm
    {
        mov ecx,0x176
        rdmsr
        mov KiFmAddr,eax
    }


    //进行比较


    if((Address+(ULONG)KernelModule.Base-ImageBase)!=KiFmAddr)//转换到内存值再比较
    {
        DbgPrint("KiFastCallEntry Hooked!\n");
        KdPrint(("KiFastCallEntry Current Addr :%08X Orig Address:%08X\n",KiFmAddr,Address+(ULONG)KernelModule.Base-ImageBase));
        
        if(UnHook)
        {
            Irq=WPOFF();
            HookKiFastCallEntry(Address+(ULONG)KernelModule.Base-ImageBase);
            WPON(Irq);
        }

    }


    ZwUnmapViewOfSection((HANDLE)-1,(PVOID)KernelBaseAddress);
    return 1;
}


ULONG GetKernelBase(PMODULE_INFO OutInfo)
{

    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    ULONG Base = 0;
    ULONG BufferSize = 0x3000;
    PVOID Buffer = NULL;
    char  NameBuffer[256] = {0};
    __try
    {    
        
        Buffer= ExAllocatePool(PagedPool,BufferSize);
        if(Buffer==0)
        {
            return 0;
        }

        NtQuerySystemInformation(SystemModuleInformation,Buffer,BufferSize,&Base);

        
        Base = 0;
        

        if(*(ULONG*)Buffer>0)
        {
            PSYSTEM_MODULE_INFORMATION_ENTRY  pInfo=
                (PSYSTEM_MODULE_INFORMATION_ENTRY)((char*)Buffer+sizeof(ULONG));
            Base = (ULONG)pInfo[0].Base;
        
            if(OutInfo)
            {
                OutInfo->Base=pInfo[0].Base;
                OutInfo->ImageSize=pInfo[0].Size;//ImageSize;


                DbgPrint("%x\r\n",OutInfo->Base);
                DbgPrint("%d\r\n",OutInfo->ImageSize);


                DbgPrint("%s\r\n",pInfo[0].ImageName);

                ConvertFileName(pInfo[0].ImageName,NameBuffer);   //查看注册表  Regedit
                ByteToWChar(NameBuffer,OutInfo->FullPath,256*2);
            }
        }
        if(Buffer!=NULL)
        {
            ExFreePool(Buffer);
        }
        
        return Base;
    }
    __except(1)
    {
        DbgPrint("Can‘t Get KernelBase!\r\n");
        return 0;
    }
}



VOID ConvertFileName(CHAR *ImageName, CHAR* FileName)
{
    int Pos = strlen(ImageName)-1;
    for(;Pos>0;Pos--)
    {
        if(ImageName[Pos]==\\)
        {
            Pos++;
            break;
        }
    }

    if (ImageName[0]==\\&&(_strnicmp(ImageName, "\\windows\\system32\\", 18)==0))
    {
        strcpy(FileName, "\\SystemRoot\\System32\\");
        strcat(FileName,ImageName + Pos);
    }
}





PULONG
MapViewOfImage(PWCHAR ImagePath)
{

    HANDLE             hFile    = NULL;
    HANDLE             hSection = NULL;
    OBJECT_ATTRIBUTES  oa = {0};
    UNICODE_STRING     FileName;
    IO_STATUS_BLOCK    iosb;
    NTSTATUS           Status;
    PULONG             BaseAddress = NULL;
    ULONG              Size     = 0;

    
    RtlInitUnicodeString(&FileName,ImagePath);
    
    InitializeObjectAttributes(&oa,&FileName,OBJ_CASE_INSENSITIVE |OBJ_KERNEL_HANDLE,NULL,NULL);

/*    Status = NtCreateFile(&hFile,
                          GENERIC_READ,&oa,&iosb,0,FILE_ATTRIBUTE_NORMAL,
                          FILE_SHARE_WRITE|FILE_SHARE_READ,
        FILE_OPEN_IF,FILE_WRITE_THROUGH,0,0);*/


    Status = ZwCreateFile(&hFile,GENERIC_READ,&oa,&iosb,0,FILE_ATTRIBUTE_NORMAL,FILE_SHARE_READ|FILE_SHARE_WRITE,FILE_OPEN,FILE_SYNCHRONOUS_IO_NONALERT,
        0,0);

    if(!NT_SUCCESS(Status))
    {
        return NULL;
    }

    if(hFile==NULL)
    {
        DbgPrint("ZwCreateFile() Failed!\r\n");
        return NULL;
    }
    
     oa.ObjectName = 0;
    

     Status = ZwCreateSection(&hSection,SECTION_MAP_READ,&oa,0,PAGE_READONLY,SEC_IMAGE,hFile);
    
     if(!NT_SUCCESS(Status))
     {

         DbgPrint("ZwCreateSection() Failed!\r\n");
    
         ZwClose(hFile);
        
         return NULL;
     }


    Status = ZwMapViewOfSection(hSection,
        ZwCurrentProcess(),&BaseAddress,0,1000,NULL,&Size,1,MEM_TOP_DOWN,PAGE_READONLY);
    
    if(!NT_SUCCESS(Status))
    {
        DbgPrint("ZwMapViewOfSection() Failed!\r\n");
        ZwClose(hSection);
        ZwClose(hFile);
    }

    ZwClose(hSection);
    
    ZwClose(hFile);
    
    DbgPrint("BaseAddress:%x\r\n",BaseAddress);
    
    return BaseAddress;
}

ULONG
GetImageBase(PULONG  BaseAddr)
{
    PIMAGE_DOS_HEADER  DosHeader = NULL;
    PIMAGE_NT_HEADERS  NtHeader  = NULL;

    DosHeader = (PIMAGE_DOS_HEADER)BaseAddr;

    if (!MmIsAddressValid(DosHeader))
    {
        return 0;
    }

    if (DosHeader->e_magic != IMAGE_DOS_SIGNATURE)
    {
        return 0;
    }

    NtHeader = (PIMAGE_NT_HEADERS)((ULONG)DosHeader + DosHeader->e_lfanew);


    if (!MmIsAddressValid(NtHeader))
    {
        return 0;
    }

    if (NtHeader->Signature!=IMAGE_NT_SIGNATURE)
    {
        return 0;
    }

    return NtHeader->OptionalHeader.ImageBase;


}


ULONG GetKiFastCallEntryOrigEntry(UCHAR* Address)
{
    int i = 0;
    UCHAR* Temp = Address;
    for(i=0;i<1000;i++)
    {
        if(*Temp==0x81)   //查看Windbg
        {
            return *(ULONG*)(Temp+2);
        }
        Temp++;
    }
    return 0;
}


BOOLEAN
ByteToWChar(char* szSour,wchar_t* szDest,ULONG Size)
{
    
    if (mbstowcs(szDest,szSour,Size)>0)
    {
        return TRUE;
    }
    
    return FALSE;

    
}



ULONG HookKiFastCallEntry(ULONG Addr)
{
    ULONG OldAddr;
    _asm
    {
        mov ecx,0x176
        rdmsr
        mov OldAddr,eax
        mov eax,Addr
        wrmsr
    }
    return OldAddr;
}


KIRQL WPOFF()
{
    KIRQL            OldIrql; 
    
    KeRaiseIrql(2, &OldIrql);
    _asm
    {
        push    eax
        mov     eax, cr0
        and     eax, 0xFFFEFFFF
        mov     cr0, eax
        pop        eax
        cli
    }
    
    
    return OldIrql;        
}

VOID WPON(KIRQL Irq)
{
    _asm
    {
    
        push    eax
        mov     eax, cr0
        or      eax, 0x10000
        mov     cr0, eax
        pop        eax
        sti
    }
    KeLowerIrql(Irq);
}

 

Sysenter hook 检测与恢复

标签:

原文地址:http://www.cnblogs.com/zibility/p/5405397.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!