要簡短的問題是這樣的。我正在編寫一個內核模式的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標題包含很多幾十年來不再使用的歷史字段。
這個想法值得嘗試,還是這是一個衆所周知的技術?安迪其他建議?
在此先感謝。
你爲什麼不使用蹦牀? –
這聽起來像一個rootkit - 不像我說的那樣。 – Linuxios
@Linuxios:希望不是:) – valdo