标签:
又是一篇内核函数分析的博文,我个人觉得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给复制一份,以便于访问。
如何验证一个地址可否使用—— MmIsAddressValid函数分析
标签:
原文地址:http://www.cnblogs.com/Ox9A82/p/5571217.html