埠訪問監控原理

2022-11-24 09:24:02 字數 3529 閱讀 1252

標題: 【原創】埠訪問監控原理

作者: 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;