2012-10-27 40 views
1

要簡短的問題是這樣的。我正在編寫一個內核模式的Windows驅動程序,在加載內核模式DLL(或其他可執行模塊)時得到通知。在某些情況下,我必須攔截DLL入口點例程。也就是說,重寫它以便我的例程首先被調用,然後我可以將控制權交給原始入口點。64位DLL入口點覆蓋

對於32位(準確地說是x86),這樣做沒有問題。我得到了模塊基本映射地址,它實際上是從標準PE頭開始的(由Windows可執行文件使用)。這裏有一個RVA(相對於圖像庫的地址)DLL入口點。我只是通過我的例程地址減去模塊基址來覆蓋它。瞧!

現在,事情在64位更復雜。問題是RVAs仍然是32位整數。這樣的RVA覆蓋從圖像基地址開始並以4GB偏移結束的地址範圍。在同一個可執行模塊內引用任何符號沒有問題(假設它不超過4GB大小),但是這會給跨模塊攔截帶來問題。當然,我的可執行模塊和我試圖掛鉤的模塊不必落入相同的4GB範圍,因此存在問題。

暫時我解決了這個問題,通過將無條件的jmp覆蓋原始的例程序言代碼到我的代碼中。這在64位平臺上需要12個字節。然後,爲了從我的例程中調用原始代碼,我還原了重寫的12個字節(意思是 - 在覆蓋前我保存它們)。

到目前爲止 - 沒有問題。但現在情況正在發生變化,我將不得不支持對入口點例程的多線程訪問(請不要問爲什麼,它涉及將多會話DLL加載到所謂的「用戶空間」中,單獨爲每個終端會話)。

其中一個解決方案是使用全局鎖,但我想避免這種情況。

我知道所謂的「蹦牀功能」,但我想避免這種情況。這樣做需要對函數prolog代碼進行運行時解碼,以正確識別指令邊界和可能的分支。

最近我想到了另外一個想法。如果我能找到原始DLL的一些「不需要」的部分,它至少有12個字節的長度(大小爲mov RAX addr + jmp RAX)。然後這個部分可以被我的手覆蓋jmp。然後入口點RVA可以設置爲這個部分!

這個工作所需要的只是可以被覆蓋的適當部分。我想有這樣的可能性,因爲PE標題包含很多幾十年來不再使用的歷史字段。

這個想法值得嘗試,還是這是一個衆所周知的技術?安迪其他建議?

在此先感謝。

+0

你爲什麼不使用蹦牀? –

+0

這聽起來像一個rootkit - 不像我說的那樣。 – Linuxios

+0

@Linuxios:希望不是:) – valdo

回答

2

你有幾個選擇。不幸的是,你只能從這3箇中選擇2個:100%固體;易於實施;低廉。

很有可能在.TEXT部分末尾會發現未使用的空間。這是因爲Windows將圖像部分以4k塊的形式映射到內存中,通常.text部分不是一個精確的乘法。

另一個容易實現的就是使用PE頭。一個非常安全的區域是DOS存根。問題在於不能保證PE頭與入口例程位於同一部分(Microsoft鏈接器將它放在同一節中,但不知道GNU或其他)。

另一個簡單但只適用於系統DLL的工作是做'熱修補',並在每個函數前面重複使用15個字節設置爲'nop','mov edi,edi'指令。所有與Windows一起發佈的DLL都支持Hot Patching。

可靠但很難的選擇是做@David Heffeman的建議。這種技術被稱爲「登陸功能」,你可以將前12個字節複製到登陸功能中,然後跳轉到原來的功能。

簡單而可靠的選擇是使用MS Detour。 Microsoft Detour是微軟研究院的一款產品,它的確如此,並且效果很好,並且得到了支持,並且它處理了可能會出現的一系列角落案例和競爭條件(以及其他內容),並且其x86版本已打開資源。缺點是商業用途非常昂貴 - 上次我檢查它是10k。