如何驗證一個地址可否使用—— MmIsAddressValid函式分析
又是一篇核心函式分析的博文,我個人覺得Windows的核心是最好的老師,當你想實現一個功能之前可以看看Windows核心是怎麼做的,說不定就有靈感呢:)
首先看下官方的註釋說明:
/*++ Routine Description: For a given virtual address this function returns TRUE if no page fault will occur for a read operation on the address, FALSE otherwise. Note that after this routine was called, if appropriate locks are not held, a non-faulting address could fault. Arguments: VirtualAddress - Supplies the virtual address to check. Return Value: TRUE if no page fault would be generated reading the virtual address, FALSE otherwise. Environment: Kernel mode. --*/
WDK文件中給出的功能描述是這樣的:The MmIsAddressValid routine checks whether a page fault will occur for a read or write operation at a given virtual address.根據描述來看這個函式的功能只是去檢查讀寫操作會不會觸發一個頁錯誤,但是作為一個常用函式,我們常常用這個函式來檢查地址合不合法,這次就在原始碼裡看下具體的流程,主要目的是搞清楚這個函式是怎麼判斷一個函式會不會觸發頁錯誤的。
1 BOOLEAN 2 MiIsAddressValid ( 3 IN PVOID VirtualAddress, 4 IN LOGICAL UseForceIfPossible 5 ) 6 { 7 PMMPTE PointerPte; 8 9 10 // 11 // If the address is not canonical then return FALSE as the caller (which 12 // may be the kernel debugger) is not expecting to get an unimplemented 13 // address bit fault. 14 // 15 16 if (MI_RESERVED_BITS_CANONICAL(VirtualAddress) == FALSE) { 17 return FALSE; 18 } 19 20 21 22 23 PointerPte = MiGetPdeAddress (VirtualAddress); 24 if (PointerPte->u.Hard.Valid == 0) { 25 return FALSE; 26 } 27 28 if (MI_PDE_MAPS_LARGE_PAGE (PointerPte)) { 29 return TRUE; 30 } 31 32 PointerPte = MiGetPteAddress (VirtualAddress); 33 if (PointerPte->u.Hard.Valid == 0) { 34 return FALSE; 35 } 36 37 // 38 // Make sure we're not treating a page directory as a page table here for 39 // the case where the page directory is mapping a large page. This is 40 // because the large page bit is valid in PDE formats, but reserved in 41 // PTE formats and will cause a trap. A virtual address like c0200000 (on 42 // x86) triggers this case. 43 // 44 45 if (MI_PDE_MAPS_LARGE_PAGE (PointerPte)) { 46 return FALSE; 47 } 48 49 return TRUE; 50 }
程式碼出人意外的簡單,很明顯,這是利用了分頁機制去查詢。先查下頁目錄項是否為空,然後再看一下頁表項是否為空。至於28、29行應該是判斷是不是直接使用PDE作為一級表吧,但是現在應該沒有這麼用的吧。
if (MI_PDE_MAPS_LARGE_PAGE (PointerPte)) { return TRUE; }
如上就是一個判斷。
而MiGetPdeAddress和MiGetPteAddress 其實是兩個宏,這個宏我們也可以拿來用。
#define MiGetPdeAddress(va) \ ((PMMPTE)(((((ULONG_PTR)(va) & VIRTUAL_ADDRESS_MASK) >> PDI_SHIFT) << PTE_SHIFT) + PDE_BASE))
#define MiGetPteAddress(va) \ ((PMMPTE)(((((ULONG_PTR)(va) & VIRTUAL_ADDRESS_MASK) >> PTI_SHIFT) << PTE_SHIFT) + PTE_BASE))
#define VIRTUAL_ADDRESS_BITS 48 #define VIRTUAL_ADDRESS_MASK ((((ULONG_PTR)1) << VIRTUAL_ADDRESS_BITS) - 1)
注意,每個程式都有自己的程式頁表和頁目錄但是核心在分配一個程式的地址空間時會把PD給複製一份,以便於訪問。
由以上的分析可以看出並沒有所謂的驗證地址是否可讀寫的功能,我們有時候會把它和ProbeForRead(),ProbeForWrite()這兩個函式相混淆。這兩個函式才是來驗證地址是否可讀寫的函式,但是僅限於使用者地址空間的地址。從WDK中可以看到如下的描述:The ProbeForWrite routine checks that a user-mode buffer actually resides in the user-mode portion of the address space, is writable, and is correctly aligned.我們來看下這個函式的實現。
1 VOID 2 ProbeForWrite ( 3 __inout_bcount(Length) PVOID Address, 4 __in SIZE_T Length, 5 __in ULONG Alignment 6 ) 7 8 /*++ 9 10 Routine Description: 11 12 This function probes a structure for write accessibility and ensures 13 correct alignment of the structure. If the structure is not accessible 14 or has incorrect alignment, then an exception is raised. 15 16 Arguments: 17 18 Address - Supplies a pointer to the structure to be probed. 19 20 Length - Supplies the length of the structure. 21 22 Alignment - Supplies the required alignment of the structure expressed 23 as the number of bytes in the primitive datatype (e.g., 1 for char, 24 2 for short, 4 for long, and 8 for quad). 25 26 Return Value: 27 28 None. 29 30 --*/ 31 32 { 33 34 ULONG_PTR EndAddress; 35 ULONG_PTR StartAddress; 36 37 #define PageSize PAGE_SIZE 38 39 // 40 // If the structure has zero length, then do not probe the structure for 41 // write accessibility or alignment. 42 // 43 44 if (Length != 0) { 45 46 // 47 // If the structure is not properly aligned, then raise a data 48 // misalignment exception. 49 // 50 51 ASSERT((Alignment == 1) || (Alignment == 2) || 52 (Alignment == 4) || (Alignment == 8) || 53 (Alignment == 16)); 54 55 StartAddress = (ULONG_PTR)Address; 56 if ((StartAddress & (Alignment - 1)) == 0) { 57 58 // 59 // Compute the ending address of the structure and probe for 60 // write accessibility. 61 // 62 63 EndAddress = StartAddress + Length - 1; 64 if ((StartAddress <= EndAddress) && 65 (EndAddress < MM_USER_PROBE_ADDRESS)) { 66 67 // 68 // N.B. Only the contents of the buffer may be probed. 69 // Therefore the starting byte is probed for the 70 // first page, and then the first byte in the page 71 // for each succeeding page. 72 // 73 // If this is a Wow64 process, then the native page is 4K, which 74 // could be smaller than the native page size/ 75 // 76 77 EndAddress = (EndAddress & ~(PageSize - 1)) + PageSize; 78 do { 79 *(volatile CHAR *)StartAddress = *(volatile CHAR *)StartAddress; 80 StartAddress = (StartAddress & ~(PageSize - 1)) + PageSize; 81 } while (StartAddress != EndAddress); 82 83 return; 84 85 } else { 86 ExRaiseAccessViolation(); 87 } 88 89 } else { 90 ExRaiseDatatypeMisalignment(); 91 } 92 } 93 94 return; 95 }
1 VOID 2 ProbeForRead( 3 __in_bcount(Length) VOID *Address, 4 __in SIZE_T Length, 5 __in ULONG Alignment 6 ) 7 8 /*++ 9 10 Routine Description: 11 12 This function probes a structure for read accessibility and ensures 13 correct alignment of the structure. If the structure is not accessible 14 or has incorrect alignment, then an exception is raised. 15 16 Arguments: 17 18 Address - Supplies a pointer to the structure to be probed. 19 20 Length - Supplies the length of the structure. 21 22 Alignment - Supplies the required alignment of the structure expressed 23 as the number of bytes in the primitive datatype (e.g., 1 for char, 24 2 for short, 4 for long, and 8 for quad). 25 26 Return Value: 27 28 None. 29 30 --*/ 31 32 { 33 34 PAGED_CODE(); 35 36 ASSERT((Alignment == 1) || (Alignment == 2) || 37 (Alignment == 4) || (Alignment == 8) || 38 (Alignment == 16)); 39 40 if (Length != 0) { 41 if (((ULONG_PTR)Address & (Alignment - 1)) != 0) { 42 ExRaiseDatatypeMisalignment(); 43 44 } else if ((((ULONG_PTR)Address + Length) > (ULONG_PTR)MM_USER_PROBE_ADDRESS) || 45 (((ULONG_PTR)Address + Length) < (ULONG_PTR)Address)) { 46 47 *(volatile UCHAR * const)MM_USER_PROBE_ADDRESS = 0; 48 } 49 } 50 }
以分頁來管理記憶體,以頁為單位,如果一個記憶體頁的第一個位元組可寫,那麼整個記憶體頁就可寫,所以就驗證頁的一個位元組就可以了。(xxx & ~(PageSize - 1)) + PageSize就是以頁為單位移動。這裡也可以看到位元組對齊Alignment僅僅是起到了一個驗證的作用,而讀寫驗證也只是一個簡單的指標操作*(volatile CHAR *)StartAddress = *(volatile CHAR *)StartAddress;
相關文章
- 郵箱地址正規表示式驗證2020-10-22
- C++ Daily 《3》----建構函式可否是虛擬函式2014-04-28C++AI函式
- ORACLE 密碼驗證函式2016-12-22Oracle密碼函式
- 驗證virtual函式的原理2013-04-25函式
- JS函式驗證總結2009-04-04JS函式
- 請寫一個函式驗證電子郵件的格式是否正確(要求使用正則)2024-04-11函式
- mssql sqlserver 驗證整型函式分享2018-08-14SQLServer函式
- 自定義密碼驗證函式2017-10-20密碼函式
- 通用表單驗證函式-FormCheckJavaScriptFunction2010-11-30函式ORMJavaScriptFunction
- 分享一個非常全的php正則驗證車牌格式的函式2019-02-16PHP函式
- 函式名/函式地址/函式指標2017-02-13函式指標
- php 驗證格式的函式總結2019-02-16PHP函式
- 表單驗證的3個函式ISSET()、empty()、is_numeric()的使用方法2011-03-14函式
- 驗證ip地址正規表示式程式碼例項2017-02-17
- 正規表示式驗證Ip地址(絕對正確)2016-02-04
- 如何驗證 Email 地址:SMTP 協議入門教程2017-06-25AI協議
- 【實驗】分析函式之魅力展示2009-10-05函式
- oracle分析函式(一)2011-06-29Oracle函式
- Python如何定義一個函式2021-09-11Python函式
- 核心分析PE獲取DLL匯出函式地址2013-08-10函式
- 分析函式row_number()使用一例2011-02-11函式
- Oracle 分析函式的使用2009-12-10Oracle函式
- vue在一個函式中呼叫另外一個函式2019-02-21Vue函式
- 一個常見的閉包函式的分析2017-02-15函式
- Oracle分析函式多層使用時的一個bug及其解決方案2011-07-08Oracle函式
- js如何判斷一個函式是否存在2017-03-21JS函式
- js正規表示式驗證URL函式程式碼(方便多個正則對比)2022-03-20JS函式
- hive視窗分析函式使用詳解系列一2024-04-07Hive函式
- Ajax 實現驗證郵箱地址唯一性2020-11-16
- 使用bcc分析函式耗時2017-09-22函式
- Oracle分析函式使用總結2008-11-11Oracle函式
- vb如何使用ftp函式,vb如何使用ftp函式要知道這些2020-10-13FTP函式
- 淺析一個函式呼叫另一個函式的變數2018-02-05函式變數
- 安裝crs使用runcluvfy驗證的一個問題2011-06-09
- Oracle分析函式七——分析函式案例2009-08-02Oracle函式
- JS不靠譜系列: 寫一個驗證過期時間的函式,包含jest單元測試2018-08-01JS函式
- 幾個分析函式的比較2007-05-05函式
- oracle分析函式_小貼(一)2010-12-17Oracle函式