久久r热视频,国产午夜精品一区二区三区视频,亚洲精品自拍偷拍,欧美日韩精品二区

您的位置:首頁(yè)技術(shù)文章
文章詳情頁(yè)

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

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

第四章 探索 Windows 2000 的內(nèi)存管理機(jī)制

翻譯: Kendiv( fcczj@263.net )

更新: Sunday, February 17, 2005

聲明:轉(zhuǎn)載請(qǐng)注明出處,并保證文章的完整性,本人保留譯文的所有權(quán)利。

IOCTL 函數(shù) SPY_IO_PDE_ARRAY

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

在無(wú)法保證源數(shù)據(jù)頁(yè)存在于物理內(nèi)存時(shí),就開(kāi)始復(fù)制內(nèi)存中的數(shù)據(jù)通常并不是一個(gè)好主意。不過(guò),頁(yè)目錄是少數(shù)列外中的一個(gè)。在當(dāng)前任務(wù)處于運(yùn)行狀態(tài)時(shí),它的頁(yè)目錄總是存在于物理內(nèi)存中。它不會(huì)被置換到頁(yè)面文件中,除非另一個(gè)任務(wù)被置換進(jìn)來(lái)。這就是為什么 CPU 的頁(yè)目錄基地址寄存器( PDBR )沒(méi)有 P ( present )位的原因, PDE 和 PTE 也類似。請(qǐng)參考 列表 4-3 中的 X86_PDBR 結(jié)構(gòu)的定義,以驗(yàn)證這一點(diǎn)。

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 結(jié)構(gòu)的定義

IOCTL 函數(shù) SPY_IO_PAGE_ENTRY

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

SpyMemoryPageEntry() 首先假定目標(biāo)頁(yè)是 4MB 頁(yè),然后,從系統(tǒng)的 PDE 數(shù)組(此數(shù)組起始于 0xC0300000 )中復(fù)制指定線性地址的 PDE 到 SPY_PAGE_ENTRY 結(jié)構(gòu)體的 pe 成員。如果 P 位不為 0 ,則肯定存在下一級(jí)的頁(yè)或頁(yè)表,所以接下來(lái)檢查 PS 位以確定頁(yè)面大小。如果 PS 位不為 0 ,則表示此 PDE 指向一個(gè) 4MB 數(shù)據(jù)頁(yè),工作到此就可結(jié)束了 ------SpyMemoryPageEntry() 返回 TRUE ,并且 SPY_PAGE_ENTRY 結(jié)構(gòu)體的 fPresent 成員也同時(shí)被設(shè)為 TRUE 。如果 PS 位為 0 ,則 PDE 指向的是一個(gè) PTE ,所以代碼從起始于 0xC0000000 的數(shù)組中提取該 PTE ,并檢查它的 P 位。如果不為 0 ,則包含指定線性地址的 4KB 頁(yè)存在于物理內(nèi)存中,此時(shí), SpyMemoryPageEntry() 和 fPresent 都會(huì)報(bào)告 TRUE 。否則,找到的必定是一個(gè) page-not-present entry ,因此 SpyMemoryPageEntry() 返回 TRUE ,不過(guò)僅當(dāng) PageFile 位不為 0 時(shí), fPresent 成員才會(huì)被設(shè)為 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() 不能識(shí)別被置換出物理內(nèi)存的 4MB 頁(yè)。如果 PDE 指向的 4MB 頁(yè)并不存在,將無(wú)法判斷給定的線性地址是否有效的,以及該頁(yè)是否還保存在當(dāng)前頁(yè)面文件中。 4MB 頁(yè)僅用于內(nèi)核內(nèi)存范圍: 0x80000000----0x9FFFFFFF 。不過(guò)我從來(lái)沒(méi)見(jiàn)過(guò)這樣的一個(gè)頁(yè)被置換出去,即使物理內(nèi)存極端少的時(shí)候也沒(méi)有過(guò),因此我不需要檢查任何與此相關(guān)的 page-not-present entrIEs 。

IOCTL 函數(shù) SPY_IO_MEMORY_DATA

SPY_IO_MEMORY_DATA 函數(shù)是重量級(jí)函數(shù)中的一個(gè),因?yàn)樗梢詮?fù)制任意數(shù)量的內(nèi)存數(shù)據(jù)到調(diào)用者提供的緩沖區(qū)中。正如你可能還記得的那樣,用戶模式下的應(yīng)用程序很容易傳入一個(gè)無(wú)效的地址。因此,該函數(shù)在觸及源地址之前,會(huì)非常謹(jǐn)慎的檢驗(yàn)這些地址的有效性。記住,藍(lán)屏可以潛伏在內(nèi)核模式的任何地方。

調(diào)用程序通過(guò)傳入一個(gè) SPY_MEMORY_BLOCK 結(jié)構(gòu)來(lái)請(qǐng)求一個(gè)內(nèi)存塊中的數(shù)據(jù),在 列表 4-23 的頂部給出了該結(jié)構(gòu)體的定義,該結(jié)構(gòu)體會(huì)指定內(nèi)存塊的地址和大小。為了方便,此處的地址被定義為一個(gè) union ,以允許將其解釋為一個(gè)字節(jié)類型的數(shù)組( PBYTE pbAddress )或解釋為一個(gè)無(wú)類型的指針( PVOID pAddress )。 列表 4-23 中的 SpyInputMemory() 函數(shù)將從 IOCTL 的輸入緩沖區(qū)中復(fù)制該結(jié)構(gòu)。其搭檔函數(shù) SpyOutputMemory() (在 列表 4-23 的末尾處)只是 SpyMemoryReadBlock() 的一個(gè)外包而已, 列表 4-24 給出了 SpyMemoryReadBlock() 函數(shù)。 SpyOutputMemory() 的主要職責(zé)是在 SpyMemoryReadBlock() 讀取數(shù)據(jù)后,返回適當(dāng)?shù)?NTSTATUS 值。

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

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. 處理內(nèi)存塊

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. 復(fù)制內(nèi)存塊中的數(shù)據(jù)

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

#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 的定義

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

IOCTL 函數(shù) SPY_IO_MEMORY_BLOCK

SPY_IO_MEMORY_BLOCK 依賴于 SPY_IO_MEMORY_DATA ,因?yàn)樗彩菑娜我獾刂窂?fù)制內(nèi)存塊到調(diào)用者的緩沖區(qū)中。主要的區(qū)別是: SPY_IO_MEMORY_DATA 試圖復(fù)制所有可讀取的字節(jié),而對(duì)于 SPY_IO_MEMORY_BLOCK 來(lái)說(shuō),只要請(qǐng)求的范圍中包含無(wú)效地址它就會(huì)失敗,一個(gè)字節(jié)也不會(huì)復(fù)制。第 6 章中需要這個(gè)函數(shù)來(lái)將位于內(nèi)核空間中的數(shù)據(jù)結(jié)構(gòu)傳遞給用戶模式下的程序。這一要求顯然會(huì)大大限制這個(gè)函數(shù),所以若一個(gè)結(jié)構(gòu)體中包含無(wú)法讀取的字節(jié),就跳過(guò)它們,僅復(fù)制可讀取的字節(jié)。

和 SPY_IO_MEMORY_DATA 類似, SPY_IO_MEMORY_BLOCK 期望輸入一個(gè) SPY_MEMORY_BLOCK 結(jié)構(gòu)來(lái)指定要復(fù)制的內(nèi)存塊的基地址和大小。返回的數(shù)據(jù),將是原始數(shù)據(jù)的 1:1 復(fù)制品。輸出緩沖區(qū)必須足夠容納要復(fù)制的全部?jī)?nèi)容。否則,將會(huì)報(bào)告一個(gè)錯(cuò)誤,并且不會(huì)返回任何數(shù)據(jù)。

IOCTL 函數(shù) SPY_IO_HANDLE_INFO

和前面介紹的 SPY_IO_PHSICAL 類似,這個(gè)函數(shù)允許用戶模式下的程序調(diào)用其他途經(jīng)無(wú)法調(diào)用的內(nèi)核模式 API 。內(nèi)核驅(qū)動(dòng)程序可通過(guò) ntoskrnl.exe 導(dǎo)出的 obReferenceObjectByHandle() 來(lái)獲取由句柄描述的對(duì)象的指針。而在 Win32 下沒(méi)有對(duì)等的函數(shù)。不過(guò),應(yīng)用程序可以命令 Spy 設(shè)備執(zhí)行這一函數(shù),并返回對(duì)象的指針。 列表 4-26 展示了由 SpyDispatcher() 調(diào)用的 SpyOutputHandleInfo() 函數(shù)。可通過(guò) SpyInputHandle() 獲(定義于 列表 4-10 )取輸入的句柄。

列表 4-26 頂部的 SPY_HANDLE_INFO 結(jié)構(gòu)包含與句柄相關(guān)的對(duì)象體的指針,以及該句柄的屬性,這兩個(gè)都會(huì)由 ObReferenceObjectByHandle() 返回。特別重要的一點(diǎn)是:如果 ObReferenceObjectByHandle() 調(diào)用成功,就必須調(diào)用 ObDereferenceObject() 來(lái)將對(duì)象的引用計(jì)數(shù)器恢復(fù)到先前的值。如果沒(méi)有這樣做,將會(huì)導(dǎo)致“對(duì)象引用漏洞”。

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. 通過(guò)句柄引用一個(gè)對(duì)象

標(biāo)簽: Windows系統(tǒng)
主站蜘蛛池模板: 鄢陵县| 山东省| 长汀县| 麻城市| 安新县| 镇平县| 巴里| 宜川县| 沙湾县| 那坡县| 沂南县| 永济市| 黑水县| 高清| 依兰县| 门源| 报价| 光山县| 岑溪市| 江山市| 合作市| 中西区| 新野县| 三江| 凤城市| 游戏| 石门县| 平潭县| 旬阳县| 武宁县| 浑源县| 九台市| 巴彦淖尔市| 宽甸| 兴国县| 宜春市| 汉中市| 清苑县| 四会市| 黑水县| 鄂伦春自治旗|