過 DNF TP 驅動保護(二)

過 DNF TP 驅動保護(二)

 

文章目錄:php

                 

01. 博文簡介:html

02. 環境及工具準備:數組

03. 分析 TP 所作的保護:函數

04. 幹掉 NtOpenProcess 中的 Deep InLine Hook:工具

05. 幹掉 NtOpenThread 中的 Deep InLine Hook:post

06. 幹掉 NtReadVirtualMemory 中的 InLine Hook:編碼

07. 幹掉 NtWriteVirtualMemory 中的 InLine Hook:spa

08. 幹掉 KiAttachProcess 的 InLine Hook:調試

09. 幹掉 NtGetContextThread 中的 InLine Hook:code

10. 幹掉 NtSetContextThread 中的 InLine Hook:

11. 幹掉 DbgkpQueueMessage 中的 InLine Hook:

12. 幹掉 DbgkpSetProcessDebugObject 中的 InLine Hook:

13. 幹掉 Debug 清零:

                                            

                                            

共四篇,本篇爲第二篇。

                                            

                                

06. 幹掉 NtReadVirtualMemory 中的 InLine Hook:

                                

前面已經幹掉了 TP 對 NtOpenProcess 以及 TP 對 NtOpenThread 所作的 Hook,

這樣的話,咱們已經可使用 OD 或者 CE 看到 DNF 的遊戲進程了,

弄過一些遊戲方面內容的朋友應該都是知道 CE 的,這東東是開源的,不過是基於 Delphi 的,

工具作得很不錯,能夠經過 CE 來修改一些遊戲進程的內存數據,好比能夠修改一些簡單的遊戲的血值啊之類的,

可是,此時咱們能夠用 CE 來掃描一下 DNF 遊戲進程的內存,你會發現根本掃描不到任何數據,

其實緣由也很簡單,TP 對 NtReadVirtualMemory 進行了淺層的 InLine Hook,

從而能夠達到防止其餘進程讀取 DNF 遊戲進程內存的目的,

而 CE 的話,在 Ring3 下是經過 ReadProcessMemory 來讀取遊戲內存的,

而 ReadProcessMemory 進入 Ring0 後又是調用的 NtReadVirtualMemory,

因爲 NtReadVirtualMemory 被 TP 幹掉了,因此天然用 CE 是掃描不出任何東西的,

要幹掉 TP 對 NtReadVirtualMemory 所做的淺層 InLine Hook 實際上是比較簡單的,

由於 TP 並無對 NtReadVirtualMemory 作檢測,因此能夠直接用 SSDT 來對抗掉淺層的 InLine Hook 就 OK。

至於原理的話,很簡單,直接用 SSDT Hook 替換掉系統服務 NtReadVirtualMemory,

而後在 SSDTHookNtReadVirtualMemory 這個咱們本身寫的系統服務中判斷,

若是是 DNF 進程的話,直接調用原來的 SSDT 系統服務就好,若是不是 DNF 進程的話,

我就跳過 TP 對 NtReadVirtualMemory 所作的 InLine Hook,也就是跳過前面 7 個字節就 OK 了。

對於這種幹掉 InLine Hook 的原理,墮落天才的文章講的最清楚了,我這裏就再也不班門弄斧了。

(繼續爲墮落天才打廣告)

對於用 SSDT Hook 幹掉淺層的 InLine Hook 能夠參考看雪上墮落天才的文章:

文章名稱:《SSDT Hook 的妙用 - 對抗 Ring0 InLine Hook》

文章地址:http://bbs.pediy.com/showthread.php?t=40832

有的童鞋可能會問,咱是如何知道 NtReadVirtualMemory 被 TP 幹掉了呢 ?

很簡單,仍是採用前面的作法,用 Kernel Detective 就能夠看到了,具體能夠看下面的截圖:

image_thumb[108]

image_thumb[103]

                                

至於如何安裝 SSDT Hook 或者 SSDT Hook 是啥玩意來着的話,你們有興趣的能夠參考個人下面博文系列:

《進程隱藏與進程保護(SSDT Hook 實現)》系列,共三篇。

http://www.cnblogs.com/BoyXiao/archive/2011/09/03/2164574.html

http://www.cnblogs.com/BoyXiao/archive/2011/09/04/2166596.html

http://www.cnblogs.com/BoyXiao/archive/2011/09/05/2168115.html

                                

下面先貼出安裝 SSDT 鉤子的代碼,該代碼用來幹掉 TP 對 NtReadVirtualMemory 的 InLine Hook:

 1: /************************************************************************/
 2: /* 安裝鉤子從而過掉 TP 保護所 Hook 的 NtReadVirtualMemory - 讓 TP 失效
 3: /* 保存 NtReadVirtualMemory 第 4,5,6,7 個字節(其實就是一個 ULONG 跳轉地址)
 4: /* 由於這幾個字節在不一樣的 XP 上會改變,因此在 SSDT Hook 以前保存下來
 5: /* 從而避免在此處進行硬編碼
 6: /************************************************************************/
 7: VOID InstallPassTPNtReadVirtualMemory()
 8: {
 9: if(g_SSDTHookNtReadVirtualMemory > 0)
 10: {
 11: /* 得到 NtReadVirtualMemory 的地址 */
 12: ULONG uNtReadVirtualMemoryAddr = oldSysServiceAddr[g_SSDTHookNtReadVirtualMemory];
 13:  
 14: /* 若是是 DNF 進程,則跳到 NtReadVirtualMemory 執行,即不處理,從而讓 DNF InLine Hook 生效 */
 15: uTPHookedNtReadVirtualMemoryJmpAddr = uNtReadVirtualMemoryAddr;
 16: /* 若是不是 DNF 進程,則跳過 TP 的 InLine Hook,從而使 TP 失效 */
 17: uMyHookedNtReadVirtualMemoryJmpAddr = uNtReadVirtualMemoryAddr + 7;
 18: /* 保存下未 Hook 以前的 NtReadVirtualMemory 的第 4,5,6,7 個字節 */
 19: uNtReadVirtualMemoryAddr_3 = *((ULONG *)(uNtReadVirtualMemoryAddr + 3));
 20:  
 21: InstallSysServiceHookByIndex(g_SSDTHookNtReadVirtualMemory, SSDTHookNtReadVirtualMemory);
 22:  
 23: KdPrint(("Pass TP - NtReadVirtualMemory Installed."));
 24: }
 25: }

                                

下面再給出 SSDT Hook 的中繼 API 的實現代碼:

 1: /************************************************************************/
 2: /* 自定義的 NtReadVirtualMemory,用來實現 SSDT Hook Kernel API
 3: /************************************************************************/
 4: NTSYSHOOKAPI VOID SSDTHookNtReadVirtualMemory()
 5: {
 6: /* 開始過濾 */
 7: if(ValidateCurrentProcessIsDNF() == TRUE)
 8: {
 9: __asm
 10: {
 11: /* 若是是 DNF 進程調用的話,則調用已經被 TP Hook 的 NtReadVirtualMemory */
 12: jmp uTPHookedNtReadVirtualMemoryJmpAddr
 13: }
 14: }
 15:  
 16: __asm
 17: {
 18: /* 已經作了針對硬編碼的處理 */
 19: /* 若是不是 DNF 進程調用的話,則跳過 TP Hook 的 NtReadVirtualMemory */
 20: push 0x1C
 21: push uNtReadVirtualMemoryAddr_3
 22: jmp uMyHookedNtReadVirtualMemoryJmpAddr
 23: }
 24: }

                              

好,到這裏就已經幹掉了 TP 對 NtReadVirtualMemory 所作的 InLine Hook了,

對此最直白的效果就是用 CE 打開 DNF 遊戲進程進行內存掃描,你會發現,On Year,能夠正常掃描到 DNF 內存了。

image

image

                                                

                                                

07. 幹掉 NtWriteVirtualMemory 中的 InLine Hook:

                                                

上面又幹掉了 TP 對 NtReadVirtualMemory 的 InLine Hook 了,從而實現了 CE 讀取 DNF 進程的內存。

可是你能夠試着用 CE 修改 DNF 進程的內存,你很快就會發現,雖然能夠掃描內存,可是並不能夠讀取內存,

然後你也確定可以想到,既然有防止讀取內存,那確定也有防止寫入內存,

然後你又天然會找到 NtWriteVirtualMemory 上來,等你找到 NtWriteVirtualMemory 後,

你又基本可以肯定,搞定這個 API 和搞定 NtReadVirtualMemory 應該是差很少的,

由於這兩個 API 就是一對啊,一個讀,一個寫,因此 TP 確定也是採用相同的作法來處理這兩個 API,

固然,你上面的猜測都是正確的,因此咱仍是用 SSDT 來幹掉 TP 對 NtWriteVirtualMemory 所作的 InLine。

代碼和上面的 NtReadVirtualMemory 差很少,這裏也仍是貼出來一下吧,俺先放兩幅截圖,而後再貼代碼出來:

image_thumb[98]

image_thumb[93]

                                                

下面先貼出安裝 SSDT 鉤子的代碼,該代碼用來幹掉 TP 對 NtWriteVirtualMemory 的 InLine Hook:

 1: /************************************************************************/
 2: /* 安裝鉤子從而過掉 TP 保護所 Hook 的 NtWriteVirtualMemory - 讓 TP 失效
 3: /* 保存 NtWriteVirtualMemory 第 4,5,6,7 個字節(其實就是一個 ULONG 跳轉地址)
 4: /* 由於這幾個字節在不一樣的 XP 上會改變,因此在 SSDT Hook 以前保存下來
 5: /* 從而避免在此處進行硬編碼
 6: /************************************************************************/
 7: VOID InstallPassTPNtWriteVirtualMemory()
 8: {
 9: if(g_SSDTHookNtWriteVirtualMemory > 0)
 10: {
 11: /* 得到 NtWriteVirtualMemory 的地址 */
 12: ULONG uNtWriteVirtualMemoryAddr = oldSysServiceAddr[g_SSDTHookNtWriteVirtualMemory];
 13:  
 14: /* 若是是 DNF 進程,則跳到 NtWriteVirtualMemory 執行,即不處理,從而讓 DNF InLine Hook 生效 */
 15: uTPHookedNtWriteVirtualMemoryJmpAddr = uNtWriteVirtualMemoryAddr;
 16: /* 若是不是 DNF 進程,則跳過 TP 的 InLine Hook,從而使 TP 失效 */
 17: uMyHookedNtWriteVirtualMemoryJmpAddr = uNtWriteVirtualMemoryAddr + 7;
 18: /* 保存下未 Hook 以前的 NtReadVirtualMemory 的第4,5,6,7 個字節 */
 19: uNtWriteVirtualMemoryAddr_3 = *((ULONG *)(uNtWriteVirtualMemoryAddr + 3));
 20:  
 21: InstallSysServiceHookByIndex(g_SSDTHookNtWriteVirtualMemory, SSDTHookNtWriteVirtualMemory);
 22:  
 23: KdPrint(("Pass TP - NtWriteVirtualMemory Installed."));
 24: }
 25: }

                               

下面再給出 SSDT Hook 的中繼 API 的實現代碼:

 1: /************************************************************************/
 2: /* 自定義的 NtWriteVirtualMemory,用來實現 SSDT Hook Kernel API
 3: /************************************************************************/
 4: NTSYSHOOKAPI VOID SSDTHookNtWriteVirtualMemory ()
 5: {
 6: /* 開始過濾 */
 7: if(ValidateCurrentProcessIsDNF() == TRUE)
 8: {
 9: __asm
 10: {
 11: /* 若是是 DNF 進程調用的話,則調用已經被 TP Hook 的 NtWriteVirtualMemory */
 12: jmp uTPHookedNtWriteVirtualMemoryJmpAddr
 13: }
 14: }
 15:  
 16: __asm
 17: {
 18: /* 已經作了針對硬編碼的處理 */
 19: /* 若是不是 DNF 進程調用的話,則跳過 TP Hook 的 NtWriteVirtualMemory */
 20: push 0x1C
 21: push uNtWriteVirtualMemoryAddr_3
 22: jmp uMyHookedNtWriteVirtualMemoryJmpAddr
 23: }
 24: }

                                                

好,到這裏就已經拿下了 TP 對 NtWriteVirtualMemory 所作的 InLine Hook了,

對此最直白的效果你們也均可以想象獲得了,那就是直接用 CE 打開 DNF 遊戲進程進行內存修改,

你會發現,此時能夠正常修改掉 DNF 遊戲的內存了。

image

                                     

                                

08. 幹掉 KiAttachProcess 中的 InLine Hook:

                                                

前面已經幹掉 NtOpenProcess 和 NtOpenThread 了,按道理來講,咱能夠開始用 OD 來調試 DNF 遊戲進程了,

不過你能夠用 OD 嘗試附加一下 DNF 的遊戲進程,然後你會發現根本附加不上去,這又是爲什麼呢 ?

image

首先須要明白 Ring3 在何種操做下,會致使內核調用 KiAttachProcess,

從字面意思上看,就是附加進程,在 Ring3 下的附加進程操做通常會出如今調試進程的時候,

好比用 OD 附加進程來進行調試,或者用 Visual Studio 附加進程進行調試,

其實咱猜的沒錯,就是當附加進程的時候會致使內核調用 KiAttachProcess,

TP 對這個未導出的 API 進行了 InLine Hook,從而使得 DNF 遊戲進程不能被附加,

這樣的話,咱的 OD 或者 Visual Studio 都是不可以附加上 DNF 的遊戲進程來進行調試了。

正如前面分析的 NtReadVirtualMemory 和 NtWriteVirtualMemory 這兩個 API 同樣,

TP 對 KiAttachProcess 也是作的淺層的 InLine Hook,也就是隻是 Hook 了函數頭 7 個字節,

且 TP 對 KiAttachProcess 的 InLine Hook 也沒有檢測,因此幹掉這個 API 仍是比較簡單的,

不過咱們不能夠像幹掉 NtRead/WriteVirtualMemory 同樣用 SSDT Hook 來對抗掉,

由於 KiAttachProcess 並無在 SSDT 表中,不過咱們能夠直接恢復 TP 對 KiAttachProcess 的 Hook 便可。

                                                

前面也提到了 KiAttachProcess 是一個未導出的內核 API,

因此咱們不能使用 MmGetSystemRoutineAddress 來得到它的地址,

不過好在 KeAttachProcess 這是一個導出的內核 API,

(通常 Kixxx 都是未導出的 API,而 Kexxx 則是導出的 API)

在 KeAttachProcess 的內部實質上是調用的 KiAttachProcess 來完成功能的,

因此咱能夠經過 KeAttachProcess 來定位到 KiAttachProcess 的地址,

而且讓人欣喜的是在 KeAttachProcess 中的第一個 call 就是 call KiAttachProcess,

因此搜索特徵碼也更加簡單了,直接搜索第一個 e8 就 OK。

若是用 WinDbg 的話,你能夠輸幾個命令就能夠把 KeAttachProcess 的地址打印出來,可是如今咱走點彎路,

在驅動裏面用 KdPrint 打印出 KeAttachProcess 的地址,而後咱們再分析。

 1: KdPrint(("KeAttachProcess: %x.", MmGetSystemFunAddress(L"KeAttachProcess")));

                                

image

                                

好,這裏獲得了 KeAttachProcess 的地址了,

那麼咱就能夠在 Kernel Detective 中來看看 KeAttachProcess 的反彙編代碼了,

具體的就看圖說話,俺也就很少說了,由於截圖裏面都明明白白擺着在哪裏,

image

image

image

image

image

                                 
下面給出獲取 KiAttachProcess 地址的代碼:

 1: /************************************************************************/
 2: /* 獲取函數 KiAttachProcess 的地址
 3: /* KiAttachProcess 在 KeAttachProcess 中的第一個 Call(e8 指令) 位置
 4: /************************************************************************/
 5: ULONG GetKiAttachProcessAddr()
 6: {
 7: ULONG uCallAddr = 0;
 8: ULONG uKeAttachProcessAddr = 0;
 9: ULONG uKiAttachProcessAddr = 0;
 10: CHAR szCode[1] = 
 11: {
 12: (char)0xe8
 13: };
 14:  
 15: /* 獲取 KeAttachProcess 的地址 */
 16: uKeAttachProcessAddr = MmGetSystemFunAddress(L"KeAttachProcess");
 17:  
 18: /* 搜索特徵碼 e8 */
 19: uCallAddr = SearchFeature(uKeAttachProcessAddr, szCode, 1);
 20: if (uCallAddr == 0)
 21: {
 22: uKiAttachProcessAddr = 0;
 23: }
 24: else
 25: {
 26: /* 獲取 KiAttachProcess 的地址 */
 27: uKiAttachProcessAddr = *((ULONG *)uCallAddr) + uCallAddr + 4;
 28: }
 29:  
 30: return uKiAttachProcessAddr;
 31: }

                              

獲得了 KiAttachProcess 的地址,那麼下面就要來幹掉 KiAttachProcess 了,

爲了簡單實現,我一開始幹掉 KiAttachProcess 的作法是採用的硬編碼,

在獲得 KiAttachProcess 地址後,能夠用 Xuetr 來反彙編這個地址,

從而看到 KiAttachProcess 的反彙編指令,因此個人作法就是,

直接將 Xuetr 中 KiAttachProcess 的頭 7 個字節以硬編碼的形式保存在數組中,

而後等 TP 啓動後,我用保存下來的這 7 個字節直接去恢復 KiAttachProcess 就 OK,

這種方式在個人虛擬機 XP 裏面是 OK 的,由於我就是從這臺 XP 上獲取的 7 個字節的硬編碼,

可是在別的 XP 系統上就不 OK 了,緣由是 KiAttachProcess 頭 7 個字節並非全部的 XP 都相同的,

直接拿不一致的硬編碼去恢復確定是會 BSOD 的,不事後來我用了一種簡單的辦法就幹掉這個問題了,

辦法很簡單,在 TP 啓動以前,我動態去讀取 KiAttachProcess 的頭 7 個字節,而且保存在數組中,

等 TP 啓動後,我就用數組中的這 7 個字節去恢復 TP 對 KiAttachProcess 所作的 InLine Hook。

                                

實現的具體代碼很簡單,下面也貼出來:

先在 TP 啓動以前保存 KiAttachProcess 的頭 7 個字節:

 1: PUCHAR pKiAttachProcessAddr = NULL;
 2:  
 3: pKiAttachProcessAddr = (PUCHAR)GetKiAttachProcessAddr();
 4:  
 5: KdPrint(("KiAttachProcess: %x.", uKiAttachProcessAddr));
 6:  
 7: /* 保存 KiAttachProcess 被 DNF Hook 以前的頭 API_HOOK_HEADER_LEN 個字節 */
 8: for(uIndex = 0; uIndex < API_HOOK_HEADER_LEN; uIndex++)
 9: {
 10: szKiAttachProcessOriginCode[uIndex] = pKiAttachProcessAddr[uIndex];
 11: }

                                

TP 啓動以後恢復被 TP 修改掉的 KiAttachProcess 的頭 7 個字節:

 1: /************************************************************************/
 2: /* 恢復 KiAttachProcess 頭 9 個字節從而恢復 KiAttachProcess - 讓 TP 失效
 3: /* 這裏使用了硬編碼,去掉硬編碼的思路以下:
 4: /* 在 TP 未啓動以前,先保存下 KiAttachProcess 的頭 9 個字節
 5: /* 而後在 TP 啓動以後,恢復這 9 個字節就 OK
 6: /************************************************************************/
 7: VOID RecoveryTPHookedKiAttachProcess()
 8: {
 9: ULONG uIndex = 0;
 10: ULONG uOldAttr = 0;
 11:  
 12: KIRQL kOldIRQL = PASSIVE_LEVEL;
 13: PUCHAR pKiAttachProcessAddr = NULL;
 14:  
 15: /* 獲取到 KiAttachProcess 的地址 */
 16: pKiAttachProcessAddr = (PUCHAR)GetKiAttachProcessAddr();
 17:  
 18: EnableWriteProtect(&uOldAttr);
 19: kOldIRQL = KeRaiseIrqlToDpcLevel();
 20:  
 21: /* 恢復 KiAttachProcess 的頭 API_HOOK_HEADER_LEN 個字節 */
 22: for(uIndex = 0; uIndex < API_HOOK_HEADER_LEN; uIndex++)
 23: {
 24: pKiAttachProcessAddr[uIndex] = szKiAttachProcessOriginCode[uIndex];
 25: }
 26:  
 27: KeLowerIrql(kOldIRQL);
 28: DisableWriteProtect(uOldAttr);
 29:  
 30: KdPrint(("Pass TP - KiAttachProcess Installed."));
 31: }

 

                                

到這裏,咱們又把 KiAttachProcess 給搞定了,因此此時咱能夠用 OD 來附加 DNF 遊戲進程試試看了,

此時你會發現咱能夠將 OD 附加上去了,不過惋惜的是,就算附加上去了,離使用 OD 來調試 DNF 還遠着呢,

由於 TP 對 DNF 遊戲進程還有其餘的保護措施,它在內核裏面 Hook 的 API 可不止這麼點哦。

                                                

                                                

總結:

                                                

《過 DNF TP 驅動保護》的第二篇到這裏就結束了,通過上面的處理,

咱們已通過掉了 TP 所作 InLine Hook 的 5 個 API 了,

首先是 NtOpenProcess 和 NtOpenThread 的深層 InLine Hook,

而後是 SSDT 系統服務函數 NtReadVirtualMemory 和 NtWriteVirtualMemory 的淺層次 InLine Hook,

最後咱們也幹掉了 TP 對未導出內核函數 KiAttachProcess 所作的淺層次 InLine Hook。

雖然也幹掉了很多 TP 在內核中 Hook 的 API 了,可是這離過 DNF TP 驅動保護還比較遠的,

詳情還得留到下回分解了。

 

版權全部,迎轉載,但轉載請註明: 轉載自  Zachary.XiaoZhen - 夢想的天空

相關文章
相關標籤/搜索