如何验证一个地址可否使用—— 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.根据描述来看这个函数的功能只是去检查读写操作会不会触发一个页错误,但是作为一个常用函数,我们常常用这个函数来检查地址合不合法,这次就在源码里看下具体的流程,主要目的是搞清楚这个函数是怎么判断一个函数会不会触发页错误的。
BOOLEAN
MiIsAddressValid (
IN PVOID VirtualAddress,
IN LOGICAL UseForceIfPossible
)
{
PMMPTE PointerPte; //
// If the address is not canonical then return FALSE as the caller (which
// may be the kernel debugger) is not expecting to get an unimplemented
// address bit fault.
// if (MI_RESERVED_BITS_CANONICAL(VirtualAddress) == FALSE) {
return FALSE;
} PointerPte = MiGetPdeAddress (VirtualAddress);
if (PointerPte->u.Hard.Valid == ) {
return FALSE;
} if (MI_PDE_MAPS_LARGE_PAGE (PointerPte)) {
return TRUE;
} PointerPte = MiGetPteAddress (VirtualAddress);
if (PointerPte->u.Hard.Valid == ) {
return FALSE;
} //
// Make sure we're not treating a page directory as a page table here for
// the case where the page directory is mapping a large page. This is
// because the large page bit is valid in PDE formats, but reserved in
// PTE formats and will cause a trap. A virtual address like c0200000 (on
// x86) triggers this case.
// if (MI_PDE_MAPS_LARGE_PAGE (PointerPte)) {
return FALSE;
} return TRUE;
}
代码出人意外的简单,很明显,这是利用了分页机制去查询。先查下页目录项是否为空,然后再看一下页表项是否为空。至于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.我们来看下这个函数的实现。
VOID
ProbeForWrite (
__inout_bcount(Length) PVOID Address,
__in SIZE_T Length,
__in ULONG Alignment
) /*++ Routine Description: This function probes a structure for write accessibility and ensures
correct alignment of the structure. If the structure is not accessible
or has incorrect alignment, then an exception is raised. Arguments: Address - Supplies a pointer to the structure to be probed. Length - Supplies the length of the structure. Alignment - Supplies the required alignment of the structure expressed
as the number of bytes in the primitive datatype (e.g., 1 for char,
2 for short, 4 for long, and 8 for quad). Return Value: None. --*/ { ULONG_PTR EndAddress;
ULONG_PTR StartAddress; #define PageSize PAGE_SIZE //
// If the structure has zero length, then do not probe the structure for
// write accessibility or alignment.
// if (Length != ) { //
// If the structure is not properly aligned, then raise a data
// misalignment exception.
// ASSERT((Alignment == ) || (Alignment == ) ||
(Alignment == ) || (Alignment == ) ||
(Alignment == )); StartAddress = (ULONG_PTR)Address;
if ((StartAddress & (Alignment - )) == ) { //
// Compute the ending address of the structure and probe for
// write accessibility.
// EndAddress = StartAddress + Length - ;
if ((StartAddress <= EndAddress) &&
(EndAddress < MM_USER_PROBE_ADDRESS)) { //
// N.B. Only the contents of the buffer may be probed.
// Therefore the starting byte is probed for the
// first page, and then the first byte in the page
// for each succeeding page.
//
// If this is a Wow64 process, then the native page is 4K, which
// could be smaller than the native page size/
// EndAddress = (EndAddress & ~(PageSize - )) + PageSize;
do {
*(volatile CHAR *)StartAddress = *(volatile CHAR *)StartAddress;
StartAddress = (StartAddress & ~(PageSize - )) + PageSize;
} while (StartAddress != EndAddress); return; } else {
ExRaiseAccessViolation();
} } else {
ExRaiseDatatypeMisalignment();
}
} return;
}
VOID
ProbeForRead(
__in_bcount(Length) VOID *Address,
__in SIZE_T Length,
__in ULONG Alignment
) /*++ Routine Description: This function probes a structure for read accessibility and ensures
correct alignment of the structure. If the structure is not accessible
or has incorrect alignment, then an exception is raised. Arguments: Address - Supplies a pointer to the structure to be probed. Length - Supplies the length of the structure. Alignment - Supplies the required alignment of the structure expressed
as the number of bytes in the primitive datatype (e.g., 1 for char,
2 for short, 4 for long, and 8 for quad). Return Value: None. --*/ { PAGED_CODE(); ASSERT((Alignment == ) || (Alignment == ) ||
(Alignment == ) || (Alignment == ) ||
(Alignment == )); if (Length != ) {
if (((ULONG_PTR)Address & (Alignment - )) != ) {
ExRaiseDatatypeMisalignment(); } else if ((((ULONG_PTR)Address + Length) > (ULONG_PTR)MM_USER_PROBE_ADDRESS) ||
(((ULONG_PTR)Address + Length) < (ULONG_PTR)Address)) { *(volatile UCHAR * const)MM_USER_PROBE_ADDRESS = ;
}
}
}
以分页来管理内存,以页为单位,如果一个内存页的第一个字节可写,那么整个内存页就可写,所以就验证页的一个字节就可以了。(xxx & ~(PageSize - 1)) + PageSize就是以页为单位移动。这里也可以看到字节对齐Alignment仅仅是起到了一个验证的作用,而读写验证也只是一个简单的指针操作*(volatile CHAR *)StartAddress = *(volatile CHAR *)StartAddress;
如何验证一个地址可否使用—— MmIsAddressValid函数分析的更多相关文章
- 笔记——malloc、free、不同数据类型操作、.pyc文件、python安装第三方包、验证一个网站的所有链接有效性
C — malloc( ) and free( ) C 语言中使用malloc( )函数申请的内存空间,为什么一定要使用free释放? **malloc()函数功能:是从堆区申请一段连续的空间,函数结 ...
- Java实现 LeetCode 468 验证IP地址
468. 验证IP地址 编写一个函数来验证输入的字符串是否是有效的 IPv4 或 IPv6 地址. IPv4 地址由十进制数和点来表示,每个地址包含4个十进制数,其范围为 0 - 255, 用(&qu ...
- 微信公众号开发(一)--验证服务器地址的Java实现
现在主流上都用php写微信公众号后台,其实作为后端语言之一的java也可以实现. 这篇文章将对验证服务器地址这一步做出实现. 参考资料:1.慕课网-<初识java微信公众号开发>,2.微信 ...
- 类虚函数表原理实现分析(当我们将虚表地址[n]中的函数替换,那么虚函数的实现就由我们来控制了)
原理分析 当调用一个虚函数时, 编译器生成的代码会调用 虚表地址[0](param1, param2)这样的函数. 已经不是在调用函数名了. 当我们将虚表地址[n]中的函数实现改为另外的函数, 虚函数 ...
- 如何验证 Email 地址:SMTP 协议入门教程
http://www.ruanyifeng.com/blog/2017/06/smtp-protocol.html 作者: 阮一峰 日期: 2017年6月25日 Email 是最常用的用户识别手段 ...
- C++11标准 STL正则表达式 验证电子邮件地址
转自:http://www.cnblogs.com/yejianfei/archive/2012/10/07/2713715.html 我们最经常遇到的验证,就是电子邮件地址验证.网站上常见.各种网页 ...
- C++ Daily 《3》----构造函数可否是虚函数
C++ 中构造函数可否是虚函数? 绝不要!! 而且,在构造函数中调用虚函数也是不提倡的行为,因为会引发预想不到的结果. 因为,在 derived class 对象构造的过程中,首先调用的是基类的构造函 ...
- 此集合已经采用方案 http 的地址。此集合中每个方案中最多只能包含一个地址。
错误信息:此集合已经采用方案 http 的地址.此集合中每个方案中最多只能包含一个地址.如果服务承载于 IIS 中,则可以通过将“system.serviceModel/serviceHostingE ...
- 不定参数函数原理以及实现一个属于自己的printf函数
一.不定参数函数原理 二.实现一个属于自己的printf函数 参考博文:王爽汇编语言综合研究-函数如何接收不定数量的参数
随机推荐
- svn断开链接后,重新share提交代码报错
前言:svn怎样断开链接并清除干净请查看此地址-->android studio中断开SVN连接,并彻底清理项目中的.svn文件 1.每次把项目重新关联到新的svn地址上,我都抓狂一样的烦躁,因 ...
- 主角场景Shader效果:描边
基本思路:Shader用两个Pass,一个渲染描边部分,一个渲染物体部分. Pass1:剔除正面,渲染背面,把顶点延法线方向外围扩展一定宽度,用来表现描边的粗细,这部分用自己设定的颜色. Pass2: ...
- 1.Qt简介
附件列表 1.Qt简介.png
- 2017年Java面试题整理
原文出处:CSDN邓帅 面试是我们每个人都要经历的事情,大部分人且不止一次,这里给大家总结最新的2016年面试题,让大家在找工作时候能够事半功倍. 1.Switch能否用string做参数? a.在 ...
- Java Socket Timeout 总结
原文出处:囚兔 摘要: Java的网络编程Socket常常用于各种网络工具,比如数据库的jdbc客户端,redis客户端jedis,各种RPC工具java客户端,这其中存在一些参数来配置timeout ...
- MVVM模式原则
1.MVVM简介 这个模式的核心是ViewModel,它是一种特殊的model类型,用于表示程序的UI状态.它包含描述每个UI控件的状态的属性.例如,文本输入域的当前文本,或者一个特定按钮是否可用.它 ...
- python---重点(设计模式)
前戏:设计模式简介 设计模式是面向对象设计的解决方案,是复用性程序设计的经验总结.(与语言无关,任何语言都可以实现设计模式) 设计模式根据使用目的的不同而分为创建型模式(Creational Patt ...
- Liunx操作指令搜素引擎
链接:http://wangchujiang.com/linux-command/c/vi.html
- ajax状态码--转他人的
var getXmlHttpRequest = function () { try{ //主流浏览器提供了XMLHttpRequest对象 return new XMLHttpRequest(); } ...
- bzoj千题计划170:bzoj1968: [Ahoi2005]COMMON 约数研究
http://www.lydsy.com/JudgeOnline/problem.php?id=1968 换个角度 一个数可以成为几个数的约数 #include<cstdio> #incl ...