又是一篇内核函数分析的博文,我个人觉得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函数分析的更多相关文章

  1. 笔记——malloc、free、不同数据类型操作、.pyc文件、python安装第三方包、验证一个网站的所有链接有效性

    C — malloc( ) and free( ) C 语言中使用malloc( )函数申请的内存空间,为什么一定要使用free释放? **malloc()函数功能:是从堆区申请一段连续的空间,函数结 ...

  2. Java实现 LeetCode 468 验证IP地址

    468. 验证IP地址 编写一个函数来验证输入的字符串是否是有效的 IPv4 或 IPv6 地址. IPv4 地址由十进制数和点来表示,每个地址包含4个十进制数,其范围为 0 - 255, 用(&qu ...

  3. 微信公众号开发(一)--验证服务器地址的Java实现

    现在主流上都用php写微信公众号后台,其实作为后端语言之一的java也可以实现. 这篇文章将对验证服务器地址这一步做出实现. 参考资料:1.慕课网-<初识java微信公众号开发>,2.微信 ...

  4. 类虚函数表原理实现分析(当我们将虚表地址[n]中的函数替换,那么虚函数的实现就由我们来控制了)

    原理分析 当调用一个虚函数时, 编译器生成的代码会调用 虚表地址[0](param1, param2)这样的函数. 已经不是在调用函数名了. 当我们将虚表地址[n]中的函数实现改为另外的函数, 虚函数 ...

  5. 如何验证 Email 地址:SMTP 协议入门教程

    http://www.ruanyifeng.com/blog/2017/06/smtp-protocol.html 作者: 阮一峰 日期: 2017年6月25日   Email 是最常用的用户识别手段 ...

  6. C++11标准 STL正则表达式 验证电子邮件地址

    转自:http://www.cnblogs.com/yejianfei/archive/2012/10/07/2713715.html 我们最经常遇到的验证,就是电子邮件地址验证.网站上常见.各种网页 ...

  7. C++ Daily 《3》----构造函数可否是虚函数

    C++ 中构造函数可否是虚函数? 绝不要!! 而且,在构造函数中调用虚函数也是不提倡的行为,因为会引发预想不到的结果. 因为,在 derived class 对象构造的过程中,首先调用的是基类的构造函 ...

  8. 此集合已经采用方案 http 的地址。此集合中每个方案中最多只能包含一个地址。

    错误信息:此集合已经采用方案 http 的地址.此集合中每个方案中最多只能包含一个地址.如果服务承载于 IIS 中,则可以通过将“system.serviceModel/serviceHostingE ...

  9. 不定参数函数原理以及实现一个属于自己的printf函数

    一.不定参数函数原理 二.实现一个属于自己的printf函数 参考博文:王爽汇编语言综合研究-函数如何接收不定数量的参数

随机推荐

  1. Codeforces Round #302 (Div. 2) C 简单dp

    C. Writing Code time limit per test 3 seconds memory limit per test 256 megabytes input standard inp ...

  2. python函数的 全局变量与局部变量

    一.函数的全局变量 1.什么是全局变量 顶着头开始写,没有任何缩进,在py文件的任何位置都能调用 #!/usr/bin/env python # _*_ coding:utf8 _*_ name=&q ...

  3. go语言从零学起(三) -- chat实现的思考

    要通过go实现一个应用场景: 1 建立一个websocket服务 2 维护在线用户的链接 3 推送消息和接受用户的操作 列出需求,很显然的想到了chat模型.于是研究了revel框架提供的sample ...

  4. jquery validate 增加过滤特殊字符的方法

    jQuery.validator.addMethod("specialCharFilter", function(value, element) { var pattern = n ...

  5. 转:String StringBuffer StringBuilder区别

    转自:http://www.iteye.com/topic/522167 作者:每次上网冲杯Java时,都能看到关于String无休无止的争论.还是觉得有必要让这个讨厌又很可爱的String美眉,赤裸 ...

  6. Bzoj1939 [Croatian2010] Zuma

    Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 43  Solved: 31 Description 有一行 N 个弹子,每一个都有一个颜色.每次可以让超过 ...

  7. 20155331 2016-2017-2 《Java程序设计》第5周学习总结

    20155331 2016-2017-2 <Java程序设计>第5周学习总结 教材学习内容总结 一.Java异常的基础知识 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时 ...

  8. shell脚本安装部署反向代理 监控进程 计划任务

    1.编写脚本自动部署反向代理.web.nfs: 要求: I.部署nginx反向代理三个web服务,调度算法使用加权轮询: 反向代理服务器脚本配置脚本 #!/bin/bash #安装eple和nginx ...

  9. MySQL Sakila样本数据库

    Sakila样本数据库介绍 Sakila样本数据库是MySQL官方提供的一个模拟DVD租赁信息管理的数据库,提供了一个标准模式,可作为书中例子,教程.文章.样品,等等,对学习测试来说是个不错的选择. ...

  10. Linux基础-rpm软件包管理

    任务:挂载光盘文件到/media目录,进去/media目录下的Packages目录,查看系统已安装的所有rpm包,查看系统是否安装dhcp软件包,安装dhcp软件包,查看dhcp软件包的信息,查看dh ...