日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

《Undocumented Windows 2000 Secrets》翻譯 --- 第四章(6)

瀏覽:193日期:2023-08-27 10:15:51

第四章 探索 Windows 2000 的內存管理機制

翻譯: Kendiv( fcczj@263.net )

更新: Sunday, February 17, 2005

聲明:轉載請注明出處,并保證文章的完整性,本人保留譯文的所有權利。

IOCTL 函數 SPY_IO_PDE_ARRAY

SPY_IO_PDE_ARRAY 是另一個普通的函數,它只是簡單的把整個頁目錄(開始于地址 0xC0300000 )復制到調用者提供的輸出緩沖區中。該緩沖區采用 列表 4-21 所示的 SPY_PDE_ARRAY 結構。你可能已猜到,該結構的大小正好是 4KB ,它由 1,024 個 32 位的 PDE 組成。 X86_PE 結構將在這里使用, X86_PE 結構代表一個一般化的頁項( page entry ),可在 列表 4-3 中找到該結構的定義,常量 X86_PAGES_4M 定義在 列表 4-5 。 SPY_PDE_ARRAY 的結構體成員總是頁目錄項( PDE ), X86_PE 結構可以是 X86_PDE_4M 類型,也可以是 X86_PDE_4KB 類型,這取決于 PDE 的 PS 位的取值。

在無法保證源數據頁存在于物理內存時,就開始復制內存中的數據通常并不是一個好主意。不過,頁目錄是少數列外中的一個。在當前任務處于運行狀態時,它的頁目錄總是存在于物理內存中。它不會被置換到頁面文件中,除非另一個任務被置換進來。這就是為什么 CPU 的頁目錄基地址寄存器( PDBR )沒有 P ( present )位的原因, PDE 和 PTE 也類似。請參考 列表 4-3 中的 X86_PDBR 結構的定義,以驗證這一點。

typedef struct _SPY_PDE_ARRAY

{

X86_PE apde [X86_PAGES_4M];

}

SPY_PDE_ARRAY, *PSPY_PDE_ARRAY, **PPSPY_PDE_ARRAY;

#define SPY_PDE_ARRAY_ sizeof (SPY_PDE_ARRAY)

列表 4-21. SPY_PDE_ARRY 結構的定義

IOCTL 函數 SPY_IO_PAGE_ENTRY

如果你對給定線性地址的 page entry 感興趣的話,這個函數就是一個很好的選擇。 列表 4-22 給出了 SpyMemoryPageEntry() 的內部細節,該函數就是用來處理 SPY_IO_PAGE_ENTRY 請求的。該函數返回的 SPY_PAGE_ENTRY 結構本質上是一個 X86_PE page entry (定義于 列表 4-3 ),不過這里增加了兩個新成員(為了使用方便): dSize 和 fPresent 。其中 dSize 成員用于說明頁的大小(以字節為單位),其值不是 X86_PAGE_4KB ( 4,096 字節)就是 X86_PAGE_4MB ( 4,194,304 字節); fPresent 成員用來說明頁是否存在于物理內存中。這個標志必須和 SpyMemoryPageEntry() 自身的返回值進行對比,即使 fPresent 為 FALSE ,函數自身的返回值也可為 TRUE 。此時,提供的線性地址時有效的,但它指向的數據頁已被置換到了頁面文件中。這種情況可通過設置 page entry 的第 10 位(即 列表 4-22 中出現的 PageFile )來表示。當 P 位(該位屬于 X86_PNPE 結構)被置 0 時, PageFile 就會被設置。請參考本章稍早討論過的 X86_PNPE 結構的細節。 X86_PNPE 結構代表一個 page-not-persent entry ,該結構定義于 列表 4-3

SpyMemoryPageEntry() 首先假定目標頁是 4MB 頁,然后,從系統的 PDE 數組(此數組起始于 0xC0300000 )中復制指定線性地址的 PDE 到 SPY_PAGE_ENTRY 結構體的 pe 成員。如果 P 位不為 0 ,則肯定存在下一級的頁或頁表,所以接下來檢查 PS 位以確定頁面大小。如果 PS 位不為 0 ,則表示此 PDE 指向一個 4MB 數據頁,工作到此就可結束了 ------SpyMemoryPageEntry() 返回 TRUE ,并且 SPY_PAGE_ENTRY 結構體的 fPresent 成員也同時被設為 TRUE 。如果 PS 位為 0 ,則 PDE 指向的是一個 PTE ,所以代碼從起始于 0xC0000000 的數組中提取該 PTE ,并檢查它的 P 位。如果不為 0 ,則包含指定線性地址的 4KB 頁存在于物理內存中,此時, SpyMemoryPageEntry() 和 fPresent 都會報告 TRUE 。否則,找到的必定是一個 page-not-present entry ,因此 SpyMemoryPageEntry() 返回 TRUE ,不過僅當 PageFile 位不為 0 時, fPresent 成員才會被設為 FALSE 。

typedef struct _SPY_PAGE_ENTRY

{

X86_PE pe;

DWord dSize;

BOOL fPresent;

}

SPY_PAGE_ENTRY, *PSPY_PAGE_ENTRY, **PPSPY_PAGE_ENTRY;

#define SPY_PAGE_ENTRY_ sizeof (SPY_PAGE_ENTRY)

// -----------------------------------------------------------------

BOOL SpyMemoryPageEntry (PVOID pVirtual,

PSPY_PAGE_ENTRY pspe)

{

SPY_PAGE_ENTRY spe;

BOOL fOk = FALSE;

spe.pe = X86_PDE_ARRAY [X86_PDI (pVirtual)];

spe.dSize = X86_PAGE_4M;

spe.fPresent = FALSE;

if (spe.pe.pde4M.P)

{

if (spe.pe.pde4M.PS)

{

fOk = spe.fPresent = TRUE;

}

else

{

spe.pe = X86_PTE_ARRAY [X86_PAGE (pVirtual)];

spe.dSize = X86_PAGE_4K;

if (spe.pe.pte4K.P)

{

fOk = spe.fPresent = TRUE;

}

else

{

fOk = (spe.pe.pnpe.PageFile != 0);

}

}

}

if (pspe != NULL) *pspe = spe;

return fOk;

}

列表 4-22. 查詢 PDE 和 PTE

需要注意的是, SpyMemoryPageEntry() 不能識別被置換出物理內存的 4MB 頁。如果 PDE 指向的 4MB 頁并不存在,將無法判斷給定的線性地址是否有效的,以及該頁是否還保存在當前頁面文件中。 4MB 頁僅用于內核內存范圍: 0x80000000----0x9FFFFFFF 。不過我從來沒見過這樣的一個頁被置換出去,即使物理內存極端少的時候也沒有過,因此我不需要檢查任何與此相關的 page-not-present entrIEs 。

IOCTL 函數 SPY_IO_MEMORY_DATA

SPY_IO_MEMORY_DATA 函數是重量級函數中的一個,因為它可以復制任意數量的內存數據到調用者提供的緩沖區中。正如你可能還記得的那樣,用戶模式下的應用程序很容易傳入一個無效的地址。因此,該函數在觸及源地址之前,會非常謹慎的檢驗這些地址的有效性。記住,藍屏可以潛伏在內核模式的任何地方。

調用程序通過傳入一個 SPY_MEMORY_BLOCK 結構來請求一個內存塊中的數據,在 列表 4-23 的頂部給出了該結構體的定義,該結構體會指定內存塊的地址和大小。為了方便,此處的地址被定義為一個 union ,以允許將其解釋為一個字節類型的數組( PBYTE pbAddress )或解釋為一個無類型的指針( PVOID pAddress )。 列表 4-23 中的 SpyInputMemory() 函數將從 IOCTL 的輸入緩沖區中復制該結構。其搭檔函數 SpyOutputMemory() (在 列表 4-23 的末尾處)只是 SpyMemoryReadBlock() 的一個外包而已, 列表 4-24 給出了 SpyMemoryReadBlock() 函數。 SpyOutputMemory() 的主要職責是在 SpyMemoryReadBlock() 讀取數據后,返回適當的 NTSTATUS 值。

SpyMemoryReadBlock() 通過一個 SPY_MEMORY_DATA 結構返回它讀到的內存數據。該結構定義于 列表 4-25 。我選擇了一中不同的定義方式,因為 SPY_MEMORY_DATA 是一個針對變量大小的數據類型。基本上,它包含一個名為 smb 的 SPY_MEMORY_BLOCK 結構,隨后是一個 WORD 類型的數組,名為 awData[] 。該數組的長度由 smb 的 dBytes 成員給出。為了允許方便的按預定大小定義 SPY_MEMORY_DATA 的全局或局部實體,該結構的定義采用了一個宏 ----SPY_MEMORY_DATA_N() 。該宏的唯一參數用于指定 awData[] 數組的大小。實際的結構體定義在宏定義之后,它提供的結構體中包含一個長度為 0 的 awData[] 數組。 SPY_MEMORY_DATA__() 宏首先計算 SPY_MEMORY_DATA 結構的全部大小,然后按這一大小分配結構中的數組,剩下的定義允許將 WORD 型的數據加入數組或從數組中取出。顯然,每個 WORD 的低半位包含內存數據的字節數,高半位作為標志位。現在,僅有第 8 位有意義,用于表示位于 0—7 位的內存字節數是否有效。

typedef struct _SPY_MEMORY_BLOCK

{

union

{

PBYTE pbAddress;

PVOID pAddress;

};

DWORD dBytes;

}

SPY_MEMORY_BLOCK, *PSPY_MEMORY_BLOCK, **PPSPY_MEMORY_BLOCK;

#define SPY_MEMORY_BLOCK_ sizeof (SPY_MEMORY_BLOCK)

// -----------------------------------------------------------------

NTSTATUS SpyInputMemory (PSPY_MEMORY_BLOCK psmb,

PVOID pInput,

DWORD dInput)

{

return SpyInputBinary (psmb, SPY_MEMORY_BLOCK_, pInput, dInput);

}

// -----------------------------------------------------------------

NTSTATUS SpyOutputMemory (PSPY_MEMORY_BLOCK psmb,

PVOID pOutput,

DWORD dOutput,

PDWORD pdInfo)

{

NTSTATUS ns = STATUS_BUFFER_TOO_SMALL;

if (*pdInfo = SpyMemoryReadBlock (psmb, pOutput, dOutput))

{

ns = STATUS_SUCCESS;

}

return ns;

}

列表 4-23. 處理內存塊

DWORD SpyMemoryReadBlock (PSPY_MEMORY_BLOCK psmb,

PSPY_MEMORY_DATA psmd,

DWORD dSize)

{

DWORD i;

DWORD n = SPY_MEMORY_DATA__ (psmb->dBytes);

if (dSize >= n)

{

psmd->smb = *psmb;

for (i = 0; i < psmb->dBytes; i++)

{

psmd->awData [i] =

(SpyMemoryTestAddress (psmb->pbAddress + i)

? SPY_MEMORY_DATA_VALUE (psmb->pbAddress [i], TRUE)

: SPY_MEMORY_DATA_VALUE (0, FALSE));

}

}

else

{

if (dSize >= SPY_MEMORY_DATA_)

{

psmd->smb.pbAddress = NULL;

psmd->smb.dBytes = 0;

}

n = 0;

}

return n;

}

// -----------------------------------------------------------------

BOOL SpyMemoryTestAddress (PVOID pVirtual)

{

return SpyMemoryPageEntry (pVirtual, NULL);

}

// -----------------------------------------------------------------

BOOL SpyMemoryTestBlock (PVOID pVirtual,

DWORD dBytes)

{

PBYTE pbData;

DWORD dData;

BOOL fOk = TRUE;

if (dBytes)

{

pbData = (PBYTE) ((DWORD_PTR) pVirtual & X86_PAGE_MASK);

dData = (((dBytes + X86_OFFSET_4K (pVirtual) - 1)

/ PAGE_SIZE) + 1) * PAGE_SIZE;

do {

fOk = SpyMemoryTestAddress (pbData);

pbData += PAGE_SIZE;

dData -= PAGE_SIZE;

}

while (fOk && dData);

}

return fOk;

}

列表 4-24. 復制內存塊中的數據

SpyMemoryTestAddress() 用于測試數據的有效性, SpyMemoryReadBlock() 針對要讀取的每個字節都會調用 SpyMemoryTestAddress() 。 SpyMemoryTestAddress() 在 列表 4-24 的下半部分給出,該函數只是簡單的調用 SpyMemoryPageEntry() ,不過傳入的第二個參數為 NULL 。 SpyMemoryPageEntry() 在討論 SPY_IO_PAGE_ENTRY 時已經介紹過( 列表 4-22 )。將其 PSPY_PAGE_ENTRY 指針參數設為 NULL ,意味著調用者不關心指定線性地址對應的 page entry ,因此,如果線性地址有效,函數將返回 TRUE 。在 SpyMemoryPageEntry() 的上下文中,僅當一個線性地址對應的數據頁存在于物理內存中,或者位于頁面文件中,該地址才是有效的。注意,這種行為與 ntoskrnl.exe 中的 API 函數 MmIsAddressValid() 并不一致,當指定的頁不存在于物理內存中時, MmIsAddressValid() 總是返回 FALSE ,即使這個有效的數據據頁位于頁面文件中也會如此。 列表 4-24 中的另一個函數 SpyMemoryTestBlock() 是 SpyMemoryTestAddress() 的增強版。它可測試一個內存區域的有效性,它每次可測試指定塊中的 4,096 個字節,直到測試完區域中的所有頁為止。

#define SPY_MEMORY_DATA_N(_n)

struct _SPY_MEMORY_DATA_##_n

{

SPY_MEMORY_BLOCK smb;

WORD awData [_n];

}

typedef SPY_MEMORY_DATA_N (0)

SPY_MEMORY_DATA, *PSPY_MEMORY_DATA, **PPSPY_MEMORY_DATA;

#define SPY_MEMORY_DATA_ sizeof (SPY_MEMORY_DATA)

#define SPY_MEMORY_DATA__(_n) (SPY_MEMORY_DATA_ + ((_n) * WORD_))

#define SPY_MEMORY_DATA_BYTE 0x00FF

#define SPY_MEMORY_DATA_VALID 0x0100

#define SPY_MEMORY_DATA_VALUE(_b,_v)

((WORD) (((_b) & SPY_MEMORY_DATA_BYTE ) |

((_v) ? SPY_MEMORY_DATA_VALID : 0)))

列表 4-25. SPY_MEMORY_DATA 的定義

將置換出去的頁作為有效的地址范圍有一個很重要的好處:當 SpyMemoryReadBlock() 試圖讀取這些頁中的第一個字節時,這些頁就會被很快的再次調入內存中。稍后給出的內存 Dump 工具如果依賴 MmIsAddressValid() ,有時就會拒絕顯示指定地址范圍中的數據(即使 5 分鐘之前,它還可以顯示這些數據),而這僅僅是因為這些頁可能已被傳送到了頁面文件中。

IOCTL 函數 SPY_IO_MEMORY_BLOCK

SPY_IO_MEMORY_BLOCK 依賴于 SPY_IO_MEMORY_DATA ,因為它也是從任意地址復制內存塊到調用者的緩沖區中。主要的區別是: SPY_IO_MEMORY_DATA 試圖復制所有可讀取的字節,而對于 SPY_IO_MEMORY_BLOCK 來說,只要請求的范圍中包含無效地址它就會失敗,一個字節也不會復制。第 6 章中需要這個函數來將位于內核空間中的數據結構傳遞給用戶模式下的程序。這一要求顯然會大大限制這個函數,所以若一個結構體中包含無法讀取的字節,就跳過它們,僅復制可讀取的字節。

和 SPY_IO_MEMORY_DATA 類似, SPY_IO_MEMORY_BLOCK 期望輸入一個 SPY_MEMORY_BLOCK 結構來指定要復制的內存塊的基地址和大小。返回的數據,將是原始數據的 1:1 復制品。輸出緩沖區必須足夠容納要復制的全部內容。否則,將會報告一個錯誤,并且不會返回任何數據。

IOCTL 函數 SPY_IO_HANDLE_INFO

和前面介紹的 SPY_IO_PHSICAL 類似,這個函數允許用戶模式下的程序調用其他途經無法調用的內核模式 API 。內核驅動程序可通過 ntoskrnl.exe 導出的 obReferenceObjectByHandle() 來獲取由句柄描述的對象的指針。而在 Win32 下沒有對等的函數。不過,應用程序可以命令 Spy 設備執行這一函數,并返回對象的指針。 列表 4-26 展示了由 SpyDispatcher() 調用的 SpyOutputHandleInfo() 函數。可通過 SpyInputHandle() 獲(定義于 列表 4-10 )取輸入的句柄。

列表 4-26 頂部的 SPY_HANDLE_INFO 結構包含與句柄相關的對象體的指針,以及該句柄的屬性,這兩個都會由 ObReferenceObjectByHandle() 返回。特別重要的一點是:如果 ObReferenceObjectByHandle() 調用成功,就必須調用 ObDereferenceObject() 來將對象的引用計數器恢復到先前的值。如果沒有這樣做,將會導致“對象引用漏洞”。

typedef struct _SPY_HANDLE_INFO

{

PVOID pObjectBody;

DWORD dHandleAttributes;

}

SPY_HANDLE_INFO, *PSPY_HANDLE_INFO, **PPSPY_HANDLE_INFO;

#define SPY_HANDLE_INFO_ sizeof (SPY_HANDLE_INFO)

// -----------------------------------------------------------------

NTSTATUS SpyOutputHandleInfo (HANDLE hObject,

PVOID pOutput,

DWORD dOutput,

PDWORD pdInfo)

{

SPY_HANDLE_INFO shi;

OBJECT_HANDLE_INFORMATION ohi;

NTSTATUS ns = STATUS_INVALID_PARAMETER;

if (hObject != NULL)

{

ns = ObReferenceObjectByHandle (hObject,

STANDARD_RIGHTS_READ,

NULL, KernelMode,

&shi.pObjectBody, &ohi);

}

if (ns == STATUS_SUCCESS)

{

shi.dHandleAttributes = ohi.HandleAttributes;

ns = SpyOutputBinary (&shi, SPY_HANDLE_INFO_,

pOutput, dOutput, pdInfo);

ObDereferenceObject (shi.pObjectBody);

}

return ns;

}

列表 4-26. 通過句柄引用一個對象

標簽: Windows系統
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
黄色成人精品网站| 91精品婷婷色在线观看| 亚洲午夜天堂| 97久久中文字幕| 一区在线视频观看| 国产精品jk白丝蜜臀av小说| 亚洲资源av| 在线天堂资源www在线污| 国产精品超碰| 美女视频网站久久| 国产精品精品国产一区二区| 国产毛片一区二区三区| 91在线成人| 亚洲精品影视| 亚洲日产国产精品| 蜜臀国产一区二区三区在线播放| 欧美激情99| 国产精品久久久久9999高清| 91p九色成人| 国产精品永久| 久久久精品国产**网站| 欧美精品91| 久久99久久久精品欧美| 国产精品xxxav免费视频| 97人人精品| 一区在线免费观看| 综合一区av| 国产欧美日韩免费观看| 97se综合| 久久天堂成人| 狠狠爱成人网| 国产亚洲精品久久久久婷婷瑜伽| 午夜精品福利影院| **爰片久久毛片| 欧美国产极品| 不卡专区在线| 欧美91精品| 在线日韩成人| 日韩高清不卡一区| 久久精品伊人| 日韩免费在线| 亚洲欧美久久久| 欧美性www| 久久精品国产精品亚洲毛片| 欧美极品一区二区三区| 午夜精品久久久久久久久久蜜桃| 国产成人精品一区二区三区视频| 中文字幕在线视频久| 蜜桃tv一区二区三区| 综合国产在线| 久久免费精品| 日韩激情综合| 欧美日韩精品免费观看视欧美高清免费大片| 欧美日韩一区二区三区视频播放| 亚洲精品免费观看| 另类中文字幕国产精品| 男女性色大片免费观看一区二区 | 亚洲三级观看| 久久97视频| 免费欧美一区| 国产精品久久久久久久久久齐齐| 电影亚洲精品噜噜在线观看| 一区二区国产在线| 岛国av在线网站| 国产精品嫩草99av在线| 国产精品久久777777毛茸茸| 久久久久久久久丰满| 91精品尤物| 成人看片网站| 日韩高清欧美激情| 久久狠狠婷婷| 国产精品对白| 一区福利视频| 亚洲高清毛片| 青青伊人久久| 日韩视频一区二区三区在线播放免费观看| 日韩毛片一区| 蜜臀91精品国产高清在线观看 | 久久福利毛片| 日韩国产欧美在线视频| 久久精品免费一区二区三区| 日韩精品免费一区二区夜夜嗨 | 成人啊v在线| 欧美日韩1区2区3区| 久久精品国产www456c0m| 成人午夜亚洲| 国产欧美一区二区三区精品观看| 亚洲美洲欧洲综合国产一区| 国产成人调教视频在线观看| 色综合视频一区二区三区日韩 | 久久精品99国产精品| 综合一区av| 亚洲不卡视频| 日韩avvvv在线播放| 91成人精品观看| 国产亚洲精品美女久久| 国产精品嫩草影院在线看| 国产日本亚洲| 牛牛精品成人免费视频| 911精品国产| 国产精品亚洲综合色区韩国| 国产精品毛片久久久| 精品国产乱码| 欧美成人a交片免费看| 香蕉久久精品| 麻豆精品网站| 国产午夜久久av| 欧美精品观看| 国产精品v一区二区三区| 老色鬼精品视频在线观看播放| 免费久久99精品国产自在现线| 国产精品久久久久久模特| 在线日韩欧美| 国产精品久久久久久久久久妞妞| 亚洲ww精品| 亚洲1区在线| 涩涩av在线| 欧美成人亚洲| 日韩不卡一区二区| 亚洲特色特黄| 精品中文字幕一区二区三区四区| 一本一道久久a久久精品蜜桃| 97精品国产福利一区二区三区| 伊人网在线播放| 国产欧美亚洲精品a| 久久三级毛片| 青草国产精品久久久久久| 久久超级碰碰| 日韩中文字幕无砖| 欧美日韩一区二区高清| 久久精品国产www456c0m| 黄色日韩精品| 色爱综合网欧美| 日韩精品一区二区三区免费视频| 亚洲一级少妇| 日韩成人午夜精品| 99久久99久久精品国产片果冰| 欧美精品二区| 国产一区视频在线观看免费| 欧美a一区二区| 亚洲精品一二三区区别| 欧美国产一级| 中文字幕一区二区三区在线视频| 国产aⅴ精品一区二区四区| 日韩精品专区| 麻豆精品视频在线| 高潮一区二区| 高清一区二区三区av| а√天堂8资源中文在线| 欧美a级片一区| 国产91欧美| 伊人久久一区| 蜜臀久久99精品久久久画质超高清 | 日韩精品一级| 国产一区二区三区黄网站| 女人天堂亚洲aⅴ在线观看| 奇米色欧美一区二区三区| 国产精品www994| 成人精品亚洲| 精品亚洲成人| 国产精品亚洲欧美一级在线| 97久久中文字幕| 日韩在线麻豆| 亚洲专区视频| 亚洲日产国产精品| 亚洲开心激情| 亚洲狼人精品一区二区三区| 久久久久久美女精品| 日韩高清欧美| 亚洲综合电影| 日韩成人高清| 91看片一区| 蜜臀av免费一区二区三区| 日韩午夜在线| 久久一区二区三区喷水| 国产精品国产三级国产在线观看| 国产欧美91| 国内精品麻豆美女在线播放视频| 亚洲香蕉久久| 欧美啪啪一区| 综合欧美亚洲| 国产欧美成人| 日韩精品三区四区| 亚洲精品观看| 亚洲男女av一区二区| 日韩成人高清| 另类综合日韩欧美亚洲| 欧美一级二级视频| 日韩国产欧美在线播放| 视频在线观看91| 激情欧美一区二区三区| 好看不卡的中文字幕| 美女精品在线观看| 久久亚洲影院| 日韩高清不卡在线| 国产精品蜜月aⅴ在线| 超碰超碰人人人人精品| 久久久久国产精品一区三寸| 久久青草久久| 精品在线91|