又是一篇内核函数分析的博文,我个人觉得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. .Net并行编程系列之三:创建带时间限制(Timeout)的异步任务并取得异步任务的结果

    尝试创建基于MVVM三层架构的异步任务: 场景:View层触发ViewModel层的动作请求,ViewModel层异步的从Model层查询数据,当数据返回或者请求超时时正确更新ViewModel层数据 ...

  2. SpringBoot(十三):springboot 小技巧

    原文出处: 纯洁的微笑 一些springboot小技巧.小知识点. 初始化数据 我们在做测试的时候经常需要初始化导入一些数据,如何来处理呢?会有两种选择,一种是使用Jpa,另外一种是Spring JD ...

  3. 介绍——基于类的视图(class-based view)

    ​刚开始的时候,django只有基于函数的视图(Function-based views).为了解决开发视图中繁杂的重复代码,基于函数的通用视图( Class-based generic views) ...

  4. 2018ICPC赛后总结

    南京: 南京是我们队第一场区域赛,而且和一队二队一起,心里总有种踏实感.之前我们仨在一起讨论过我们打大型比赛出现的问题,晨哥说我们在封榜后总是出不了题,确实是这样,我觉得在之前的比赛中,我们队胜负欲都 ...

  5. src.rpm包的解压

    有时候,我们在找源码包时候,发现有src.rpm的包:而不是tar.gz/tgz/zip结尾的. 那么如何去看这个src.rpm里面的详细信息呢? 看完下面这个例子,基本上明白了. 1,首先,生成sp ...

  6. 【leetcode 简单】 第九十九题 字符串相加

    给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和. 注意: num1 和num2 的长度都小于 5100. num1 和num2 都只包含数字 0-9. num1 和num2 都不包 ...

  7. Codeforces Round #540 (Div. 3)题解

    题目链接: https://codeforces.com/contest/1118 A题: 题意: q次查询,给你一个n,要你用1和2来凑出n,1的花费为a,2的花费为b,求花费的最小值. 思路: 我 ...

  8. 升级lamp中php5.6到php7.0过程

    升级过程我就直接摘录博友,http://www.tangshuang.net/1765.html,几乎问题和解决办法都是参照他的,所以我也就不另外写了.谢谢!! 周末看了一下php7的一些情况,被其强 ...

  9. sru源码--language model

    import sys import os import argparse import time import random import math import numpy as np import ...

  10. shell变量$#,$@,$0,$1,$2的含义

    linux中shell变量$#,$@,$0,$1,$2的含义解释: 变量说明: $$ Shell本身的PID(ProcessID) $! Shell最后运行的后台Process的PID $? 最后运行 ...