VAD樹結構體的屬性以及遍歷


Windows內核分析索引目錄:https://www.cnblogs.com/onetrainee/p/11675224.html

VAD樹的屬性以及遍歷

 

前面學習過的PFNDATABSAE是管理物理頁的,整個操作系統僅維護一個PFNDATABASE。

現在的VAD是管理虛擬內存的,每一個進程有自己單獨的一個VAD樹。

VAD樹:

  1. 比如你使用VirtualAllocate函數申請一個內存,則會在VAD樹上增加一個結點,其是_MMVAD結構體。
  2. 一個VAD結點可以有多個頁,這在StartingVpn和EndingVpn會介紹。
  3. 當以MEN_SERVIED保留屬性提交時,其只是在VAD樹上掛上一個節點,真正提交時這棵樹才是由意義的。

 

一、VAD結構體介紹

  kd> dt _MMVAD
  nt!_MMVAD
     +0x000 StartingVpn      : Uint4B
     +0x004 EndingVpn        : Uint4B
     +0x008 Parent           : Ptr32 _MMVAD
     +0x00c LeftChild        : Ptr32 _MMVAD
     +0x010 RightChild       : Ptr32 _MMVAD
     +0x014 u                : __unnamed
     +0x018 ControlArea      : Ptr32 _CONTROL_AREA
     +0x01c FirstPrototypePte : Ptr32 _MMPTE
     +0x020 LastContiguousPte : Ptr32 _MMPTE
     +0x024 u2               : __unnamed

1. StringVpn 起始頁 / EndingVpn結束頁

  1)兩者算法是不同的。起始頁:startingVpn*0x1000/結束頁:EndVpn*0x1000+0xfff。

2. Parent、LeftChild、RightChild - 其父節點、左子樹、右子樹。

  1)我們遍歷這些樹時用到的就是這些結構體成員。

3. u - 其是_MMVAN_FLAGS屬性,非常重要的。

  kd> dt _MMVAD_FLAGS
   nt!_MMVAD_FLAGS
      +0x000 CommitCharge     : Pos 0, 19 Bits
      +0x000 PhysicalMapping  : Pos 19, 1 Bit
      +0x000 ImageMap         : Pos 20, 1 Bit
      +0x000 UserPhysicalPages : Pos 21, 1 Bit
      +0x000 NoChange         : Pos 22, 1 Bit
      +0x000 WriteWatch       : Pos 23, 1 Bit
      +0x000 Protection       : Pos 24, 5 Bits
      +0x000 LargePages       : Pos 29, 1 Bit
      +0x000 MemCommit        : Pos 30, 1 Bit
      +0x000 PrivateMemory    : Pos 31, 1 Bit
  1)CommitCharge  實際提交的頁數。

    其19Bits,我們內存低字節7ffffff,正好十九位。

    比如我們以MEN_RESERVED保留形式提交了4頁大小的內存,此時這里為2,將一頁改為EXECUTE屬性,這時這里就會變成2。

  2)PyhsicalMapping:內核物理頁映射。

  3)UserPhysicalPages:內核物理頁映射。

  4)PrivateMemory:如果私有設置為1。

  5)ImageMap:對dll/exe等文件進行保護,防止其被修改(使用映射寫拷貝之類的原理)

     如果ImageMap為1,PrivateMemory為0,說明其為DLL。

  6)NoChange:關於鎖頁技術。當置為1,像VirtualProtect等函數不會改變其頁的屬性。

  7)LargePage:標志是否為大頁。

  8)MemCommit:提交狀態,只要提交就會置為1(CommitCharge存儲提交了多少頁)

  9)Protection:3bit,關於保護(比如頁的讀寫、可執行等)。

  下面介紹了其對應相關文件的對應關系,看這個很好理解。

  

  這篇博客詳細介紹了R3與R0中頁面保護的對應關系:R3環申請內存時頁面保護與_MMVAD_FLAGS位的對應關系

4. ContraArea 控制結構

  其指向一個_CONTROL_AREA的數據結構,該結構就暫不表述了。

  1)_CONTROL_AREA+ 0x24 FilePointer,文件指針,指向一個_FILE_OBJECT結構體。

  2)_FILE_OBJECT結構體中,保存着文件對象很多關鍵的信息。

    a> +0x30 FileName 文件名

      若想知道該頁屬於哪個文件,可以查看這里。

      將.sys文件偽裝成.dll文件,則必須修改這里。

    b> +0x26-0x28 文件保護屬性

      比如一個文件被獨占無法刪除,在內核中你可以將DeleteAccess位置1,之后強制刪除。

 

二、利用windbg遍歷VAD樹

1. 每個進程的VAD樹存儲在_EPROCESS+0x11c結構體中,其是ROOTVAD根結點。

  

2. 獲取每個進程的 _EPROCESS,使用指令!process 0 0,得到PROCESS的值就是指向_EPROCESS的指針。

  

3. 當獲取VadRoot之后,可以使用 !vad VadRoot來顯示該進程的VAD樹。

 

 

三、使用代碼實現VAD樹的遍歷

代碼核心就是先遍歷進程找出目標進程(這里默認 test.exe),之后對目標進程的VAD樹進行遍歷。

 1 #include <ntddk.h>
 2 
 3 
 4 //---------------------//
 5 // MMVAD結構體簡單定義 //
 6 //---------------------//
 7 typedef struct _MMVAD {
 8     ULONG StartingVpn;
 9     ULONG EndingVpn;
10     struct _MMVAD * Parent;
11     struct _MMVAD * LeftChild;
12     struct _MMVAD * RightChild;
13 }MMVAD,*PMMVAD;
14 
15 
16 
17 VOID Unload(IN PDRIVER_OBJECT pDriverObject) {
18     DbgPrint("Driver UnLoad!");
19 }
20 
21 //-----------//
22 // 遍歷VAD樹 //
23 //-----------//
24 VOID vad_enum(PMMVAD pVad) {
25     if (pVad) {
26         DbgPrint("Start: %x | End: %x | \r\n", pVad->StartingVpn, pVad->EndingVpn);
27         if (pVad->LeftChild)
28             vad_enum(pVad->LeftChild);
29         if (pVad->RightChild)
30             vad_enum(pVad->RightChild);
31     }
32 }
33 
34 
35 //-------------------------------------------------------------//
36 //  在內核中進程遍歷的原理就是先獲取系統進程EPROCESS結構       //
37 //        然后依照其鏈表來獲取其他的進程                         //
38 //        依次遍歷出來                                           //
39 //-------------------------------------------------------------//
40 NTSTATUS process_enum() {
41 
42     PEPROCESS pEprocess = NULL; // 得到系統進程地址
43     PEPROCESS pFirstEprocess = NULL;
44     ULONG ulProcessName = 0; // 字符串指針,指向進程名稱
45     ULONG ulProcessID = 0;    // 進程ID
46     ANSI_STRING target_str; // 帶檢測進程的名稱
47     ANSI_STRING ansi_string; // 
48     ULONG VadRoot;
49 
50     //----------------------------//
51     // 得到當前系統進程的EPROCESS //
52     //----------------------------//
53     pEprocess = PsGetCurrentProcess();
54     if (pEprocess == NULL) {
55         DbgPrint("獲取當前系統進程EPROCESS錯誤..");
56         return STATUS_SUCCESS;
57     }
58     DbgPrint("pEprocess addr is %x0x8\r\n", pEprocess);
59     pFirstEprocess = pEprocess;
60 
61     while (pEprocess) {
62 
63         ulProcessName = (ULONG)pEprocess + 0x174;
64         ulProcessID = *(ULONG*)((ULONG)pEprocess + 0x84);
65         VadRoot = *(ULONG*)((ULONG)pEprocess + 0x11c);
66 
67         //--------------------------------------//
68         // 將目標進程與當前進程的進程名進行對比 //
69         //--------------------------------------//
70         RtlInitAnsiString(&ansi_string, (PCSTR)ulProcessName);    
71         RtlInitAnsiString(&target_str, "test.exe");
72         if (RtlEqualString(&ansi_string, &target_str, TRUE)) {
73             DbgPrint("檢測到進程字符串,%x", ulProcessID);
74             vad_enum((PMMVAD)VadRoot); // 開始遍歷目標進程的VAD樹
75             return STATUS_SUCCESS;
76         }
77         pEprocess = (PEPROCESS)(*(ULONG*)((ULONG)pEprocess + 0x88) - 0x88);
78 
79         if (pEprocess == pFirstEprocess || *(ULONG*)((ULONG)pEprocess + 0x84) <= 0) {
80             DbgPrint("遍歷結束!未檢測到進程ID!\r\n");
81             break;
82         }
83     }
84     return STATUS_SUCCESS;
85 }
86 
87 NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING registeryPat) {
88     DbgPrint("Driver Loaded!");
89     pDriverObject->DriverUnload = Unload;
90     process_enum();
91     return STATUS_SUCCESS;
92 }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM