標題: 【原創】埠訪問監控原理
作者: netroc
時間: 2008-06-02,17:48
鏈結:netroc
本來不打算寫文章的,呵呵。既然ahnlab敢用,我當然也敢寫咯,哈哈
安博士的反外掛程式系統最近新增了乙個功能,可以檢查出來按鍵精靈、簡單遊這些用增強版winio直接進行埠讀寫的程式。後來拿來看了一下,其原理就是自己前段時間實現過的那種。通過hook int 1,設定io斷點進行監控的方法。
原理如下:
intel相容cpu都內建了除錯功能。可以設定的斷點型別包括執行斷點、記憶體訪問斷點和io斷點。通過操作drx暫存器和cr4 暫存器,可以在發生特定埠的讀寫操作時觸發斷點。
ahnlab的這種檢測技術就是基於cpu的這種功能。以下的介紹都基於32位處理器。
cpu除錯暫存器簡介:
dr0—dr3暫存器:它們是32位除錯位址暫存器。根據dr7中設定的不同,它們可以包含記憶體位址,也可以包含io埠號。
很多偵錯程式的硬體斷點也是通過這幾個暫存器實現的,所以一般硬體斷點只能設定4個。
dr4和dr5:這兩個暫存器是被系統保留的,當cr4中的de被設定時,訪問這兩個暫存器會產生非法指令錯誤#ud;當cr4中的de被清空時,這兩個暫存器和dr6、dr7關聯,即訪問它們和訪問dr6、dr7一樣。
dr6:除錯狀態暫存器。這個暫存器用於在除錯事件發生時報告狀態資訊。要判斷是哪個斷點被觸發,觸發的原因之類的就是靠它裡面的值。dr6的定義如下:
typedef struct _dr6info
dr6info, *pdr6info;
b0到b3用於指示哪個斷點被觸發。它們分別對應於dr0到dr3中的位址或埠。
bd表示觸發斷點的下一條指令是對除錯暫存器的訪問。當dr7的gd標誌被設定時,對除錯暫存器進行訪問的指令會觸發除錯事件,並且dr6的bd被設定。
bs表示是由於單步執行觸發的除錯事件。當eflags的tf標誌被設定時,這種斷點會被觸發。
bt指示是由於任務切換觸發的除錯事件。當tss中的t標誌被設定時會產生這種事件。
dr7:除錯控制暫存器。對斷點是否啟用、斷點型別等的控制。設定斷點需要配合dr0—dr3和dr7暫存器。定義如下:
typedef struct _dr7info
dr7info, *pdr7info;
l0到l3:設定時為當前任務啟用相應的斷點條件。每次任務切換時cpu都會自動清除lx位,所以這幾位只控制當前任務的斷點。
g0到g3:為所有任務啟用相應的斷點條件。這是針對整個機器的。
le和ge:p6 family和之後的ia32處理器都不支援這兩位。當設定時,使得處理器會檢測觸發資料斷點的精確的指令。
為了相容性,intel建議使用精確斷點時把le和ge都設定為1。
gd:設定gd位時啟用對除錯暫存器的保護,這時對這些暫存器的訪問都會觸發除錯中斷。進入中斷處理函式前,cpu會清掉gd位,使得中斷處理函式能夠訪問drx暫存器。
r/w0到r/w3:指定各個斷點的觸發條件。它們對應於dr0到dr3中的位址以及dr6中的4個斷點條件標誌。這幾位的意義會受到cr4中的de位的影響。
當de位為1時,它們的意義如下:
00 — 僅在指令執行時中斷
01 — 僅資料寫入時中斷
10 — io輸入輸出時中斷
11 — 資料讀取或寫入時中斷,但是不受指令預取的影響
當de位為0時,它們的意義如下:
00 — 僅在指令執行時中斷
01 — 僅資料寫入時中斷
10 —未定義
11 — 資料讀取或寫入時中斷,但是不受指令預取的影響
len0到len3:指定在除錯位址暫存器dr0到dr3中指定的位址位置的大小。如果r/wx位為0,則lenx位也必須為0,否則會產生不確定的行為。這幾位的意義如下:
00 — 1位元組長度
01 — 2位元組長度
10 — 未定義
11 — 四位元組長度
io監控的實現
介紹了上面這些內容,那麼io監控的實現方法就很簡單了,鍵盤io的埠是60和64,比如要監控60埠,就可以這樣進行:
hook掉trap01,自己接管除錯中斷
設定cr4的de,以及dr7中的le和ge。
在dr0到dr3中選乙個來設定埠號,比如選擇dr0設定為0x60。
設定dr7中的r/wx和lenx位,這裡應該設定rw0為10、len0為00
在hook的中端函式中,檢查dr6中的b0到b3,如果是b0的話,表明發生了對0x60埠的讀寫操作。
由於io斷點是trap,即在事件發生後才能觸發中斷,所以這種方法不能阻止對埠的讀寫,而僅能夠進行監控。判斷讀寫的資料以及要精確的判斷是讀還是寫需要更進一步的操作,也是有一些辦法可以實現的,這裡就不說完了,呵呵。
實現的關鍵**
**:hook掉idt
ntstatus hookidt(ulong ulid, pvoid pintproc, pulong poldintproc, pidtentry pstoldentry)
; cpucount = *kenumberprocessors;
while( cpucount > 0)
_asm cli;//禁止中斷
if ( poldintproc)
idtentry[ulid].offsetlow = (unsigned short)pintproc;
idtentry[ulid].offsethigh = (unsigned short)((unsigned int)pintproc>>16);
_asm sti;//開中斷
cpucount--;
} return status_success;
}//解除安裝鉤子
ntstatus unhookidt(ulong ulid, ulong pintproc)
; cpucount = *kenumberprocessors;
while( cpucount > 0)
return status_success;
}//自己的
#pragma optimize( "", off )
void __declspec (naked) newtrap01(void)
ulresult = ontrap01( &stcontext);//實際處理
if ( ulresult == 1) }
else
{ _asm
{ mov ax,
mov ds, ax;
mov ebp,
mov ebx,
mov ecx,
mov edi,
mov edx,
mov esi,
mov ax,
mov es, ax;
mov ax,
mov fs, ax;
mov ax,
mov ss, ax;
mov ax,
mov gs, ax;
mov eax,
mov esp,ebp;
pop ebp;
//不是自己需要的事件,呼叫原來的trap01;
jmp g_poldtrap01;