《Windows內核安全與驅動編程》-第八章-鍵盤的過濾學習-day3

鍵盤的過濾

8.5 Hook 分發函數

​ 前面講述了進行鍵盤的過濾。本節開始更加深入地探討鍵盤的過濾與反過濾的對抗。無論是過濾還是反過濾,其原理都是進行過濾。取勝的關鍵在於: 誰將第一個得到消息。

​ 前面學到的鍵盤過濾方法,是通過在設備棧上綁定一個新的設備實現的。這樣就有了一個簡單的設想來防止黑客對鍵盤進行過濾: 檢查設備棧,看上面是否有不明的設備。

​ 但是一般的黑客軟件如果想進行過濾的話,不會採用往設備棧中插入設備進行過濾的方法。一個可能的途徑是,黑客不會去插入一個能被安全軟件輕易監控到的設備,但是依然能通過修改一個已經存在的驅動對象(比如 KdbClass) 的分發函數的指針來實現過濾所有請求的目的。黑客可以將這些函數指針替換成自己的驅動中的函數,這樣請求將被黑客的程序首先解惑。然後爲了讓程序繼續進行,黑客可以調用原來被替換過的舊的指針函數指針,讓Windwos 的擊鍵過程正常運作下去。

8.5.1 獲得類驅動對象

​ 當然,首先要獲得鍵盤類的驅動對象,才能去替換下面的分發函數。但是這個操作相對簡單,因爲這個驅動的名字 “\\Driver\\Kdbclass”, 所以可以直接用函數 ObReferenceObjectByName 來獲得。這個函數在前面的例子中已經用到過,但是還沒有詳細介紹。

// 驅動的名字
#define KBD_DRIVER_NAME L"\\Driver\\kbdclass"
// 當我們求得驅動對象的指針時,將其放到這裏
PDRIVER_OBJECT KbdDriverObject;
UNICODE_STRING uniNtNameString;

// 初始化驅動的名字字符串
RtlInitUnicodeString(&uniNtNameString,KBD_DRIVER_NAME);
// 根據名字字符串來獲得驅動對象
status = ObReferenceObjectByName(
	&uniNtNameString,
	OBJ_CASE_INSENSITIVE,
	NULL,
	0,
	IoDriverObjectType,
	KernelMode,
	NULL,
	&KbdDriverObject
);
if(!NT_SUCCESS(status))
{
    // 如果失敗了
    DbgPrint("MyAttach: Couldn't get the kbd driver Object\n");
    return STATUS_UNSUCCESSFUL;
}
else
{
    // 凡是調用了 Reference 系列的函數都要通過調用 ObDereferenceObject
    // 來解除引用
    ObDereferenceObject(kbdDriverObject);
}

​ 這樣就得到了驅動對象,然後只要替換其分發函數就行了。這個操作比想象的更加容易。在前面的幾個例子中,我們都給自己的驅動對象設置的分發函數。而這裏,要設置分發函數的不再是自己的驅動對象了,而是剛剛打開的鍵盤類驅動對象。

8.5.2 修改類驅動的分發函數指針

​ 雖然驅動對象不同,但是替換方法還是一樣的。值得注意的是,必須保存原有的驅動對象的分發函數的指針: 否則,第一,替換之後將無法回覆;第二,完成我們自己的處理後無法繼續調用原有的分發函數。

​ 這裏用到一個原子操作: InterlockedExchangePointer。這個操作的好處是,作者設置的新的函數指針的操作是原子的,不會被打斷,插入其他可能要執行到調用這些分發函數的代碼。

// 這個數組用來保存所有舊的指針
ULONG i;
PDRIVER_DISPATCH OldDispatchFuncitons[IPR_MJ_MAXIMUM_FUNCTION+1];

...
// 把所有的分發函數指針都替換成我們自己編寫的同一個分發函數
for(i=0;i<=IRP_MJ_MAXIMUM_FUNCTION;++i)
{
    // 假設 MyFilterDispatch 是筆者已經寫好的一個分發函數
    OldDispatchFunction[i] = KbdDriverObject->MajorFunction[i];
    //進行原子交換操作
    InterlockedExchangerPointer(
    	&KbdDriverObject->MajorFunction[i];,
    	MyFilterDispatch
    );
}

​ 上述代碼存在安全問題: 比如一部分分發函數已經被替換,另一部分分發函數還沒有被替換,此時又剛好有連續的幾個 IRP 要進行處理,中間具有相關性。這種情況下有破壞它們之間的關聯。但是隻要替換完畢,也就安全了。這種安全問題出現的概率非常小。

8.5.3 類驅動之下的端口驅動

​ 前面的過濾方法是替換分發函數指針。但是該方法仍然比較明顯,因爲分發函數的指針本來是已知的,如果安全監控軟件有針對性地對這個指針進行檢查保護,就容易發現這個指針已經被替換掉的情況。但是從分發函數出發,下面的各個調用層出不窮,任何一個地方都可能被替換,安全程序又如何去一一保護呢?

​ 下面是一個比較邪道的例子。介紹該方法是爲了讓我們瞭解盜竊鍵盤信息的黑客所可能使用的手段,而絕不是推薦在商業軟件中使用的方法。它是直接尋找一個用於端口驅動中讀取輸入緩衝區的函數(但是這個函數實際上是由類驅動提供的),這個函數也可以被Hook來實現過濾。

KbdClass 被稱爲鍵盤類驅動,在 Windows 中,類驅動通常是指管理同一類設備的驅動程序。不管是 USB 鍵盤,還是 PS/2 鍵盤均經過它,所以在這層做攔截,能獲得很好的通用性。在類驅動之下和實際硬件交互的驅動被稱爲“端口驅動”。具體到鍵盤,USB 鍵盤Kbdhid, 而i8042prtPS/2 鍵盤的端口驅動。

​ 當鍵盤驅動從端口讀出按鍵的掃描碼,最終順利的將它交給在鍵盤設備棧最頂端等待的那個主功能號爲 IRP_MJ_READIRP。爲了完成這個任務,鍵盤驅動使用了兩個循環使用的緩衝區。

​ 以比較古老的 PS/2 鍵盤作爲例子進行介紹。因此下面的端口驅動都是 i8042prt

i8042prtkbdClass 各有自己的一個可以循環使用的緩衝區。緩衝區的每一個單元是一個 KEYBOARD_INPUT_DATA 結構。用來存放一個掃描碼及其他相關信息。在鍵盤驅動中,把這個循環使用的緩衝區叫做輸入數據隊列。i8042prt 的那個緩衝區被叫做端口鍵盤輸入數據隊列。KbdClass 的緩衝區被叫做類輸入數據隊列。

​ 回憶一下設備拓展。我們曾經使用過的設備拓展, i8042prt 這個驅動生成的設備也有自定義的設備拓展。在它的自定義的設備拓展中,保存着一些指針和計數值,用來使用它的輸入數據隊列。包括

PKEYBOARD_INPUT_DATA 類型的 InputData DataIn DataOut DataEnd
ULONG 類型的 InputCount
InputData 指針,指向輸入數據隊列的開頭。
DataEnd 指針,指向輸入數據隊列的結尾。
DataIn 指針,指向要進入隊列的新數據被放在隊列中的位置。
DataOut 指針,指向要出隊列的數據在隊列中開始的位置。
InputCount 值,爲輸入數據隊列中數據的個數。

​ 同時,在 KbdClass 的自定義設備拓展中,也保存着一些指針和計數值,名字和類型和上面的數據是完全一樣的。

​ 這一段我個人覺得比較難懂。嘗試作圖去解釋一下。

​ 緩衝區是這樣一個一個的單元組成。並且這樣的緩衝區叫做輸入數據隊列。

[KEYBOARD_INPUT_DATA]->[KEYBOARD_INPUT_DATA]->[KEYBOARD_INPUT_DATA]->[KEYBOARD_INPUT_DATA]->[KEYBOARD_INPUT_DATA]->[KEYBOARD_INPUT_DATA]...

​ 其中 InputData 指向該隊列的開頭,DataEnd 指向該隊列的結尾。DataIn 指向即將要進入隊列中的新 [KEYBOARD_INPUT_DATA] 應該在的位置……這樣去想,感覺好理解一些。

明日計劃

今天有點雜事處理,學的不多。明天爭取完結該章節,鍵盤的過濾和反過濾以及用端口操作鍵盤。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章