過 DNF TP 驅動保護(二)

文章目錄:

                 

01. 博文簡介:

02. 環境及工具準備:

03. 分析 TP 所做的保護:

04. 幹掉 NtOpenProcess 中的 Deep InLine Hook:

05. 幹掉 NtOpenThread 中的 Deep InLine Hook:

06. 幹掉 NtReadVirtualMemory 中的 InLine Hook:

07. 幹掉 NtWriteVirtualMemory 中的 InLine Hook:

08. 幹掉 KiAttachProcess 的 InLine Hook:

09. 幹掉 NtGetContextThread 中的 InLine Hook:

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 驅動保護還比較遠的,

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

                                    

現已支持過掉最新版本的 TP 保護(2013年04月23日),

如有需要者(有償提供),請聯繫 QQ:1751048662

        

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

 

 

            

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