如何验证一个地址可否使用—— 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函数 参考博文:王爽汇编语言综合研究-函数如何接收不定数量的参数
随机推荐
- ssh 执行命令并实时显示结果
ssh 执行命令并实时显示结果 import paramiko def main(): sshClient = paramiko.SSHClient() sshClient.set_missing_h ...
- python的匿名函数 lambda的使用方法详解以及使用案例
1.匿名函数是用lambda这个关键字定义 lambda x:x+1 第一个x代表形参,x+1相当于函数的返回值 #lambda x:x+1 第一个x代表形参,x+1相当于函数的返回值 def ...
- sparse representation 与sparse coding 的区别的观点
@G_Auss: 一直觉得以稀疏为目标的无监督学习没有道理.稀疏表示是生物神经系统的一个特性,但它究竟只是神经系统完成任务的副产物,还是一个优化目标,没有相关理论,这里有推理漏洞.实际上,稀疏目标只能 ...
- 把一个IEEE754浮点数转换为IBM370浮点数的C#代码
把一个IEEE754浮点数转换为IBM370浮点数的C#代码. 在这个网页上有古老的IBM370浮点格式的说明. // http://en.wikipedia.org/wiki/IBM_Floatin ...
- Dialog插件artDialog
经本人测试,发现没有layer好用,因为artDialog不能拖拽.不过除了拖拽,其他还蛮简洁的,在自定义上的灵活性也还可以.下面是我自己写的测试demo. <!DOCTYPE html> ...
- 简明python教程笔记
自然字符串 如果你想要指示某些不需要如转义符那样的特别处理的字符串,那么你需要指定一个自然字符串.自然字符串通过给字符串加上前缀r或R来指定. r"Newlines are indicate ...
- 《设计模式》-原则二:里氏代换原则(LSP)
回顾一下上一节说的“开闭原则” 说的是 一个软件要遵循对修改关闭 对新功能扩展的原则. 这一次来说说 “里氏代换原则” 意思是说:子类型必须能代替他们的基类. 看了半天的例子 好像 是懂非懂啊...相 ...
- Sparse AutoEncoder简介
1. AutoEncoder AutoEncoder是一种特殊的三层神经网络, 其输出等于输入:\(y^{(i)}=x^{(i)}\), 如下图所示: 亦即AutoEncoder想学到的函数为\(f_ ...
- opencv产生随机的颜色
//将HSV颜色空间值转换成RGB值,参考这里cv::Scalar HSV2RGB(const float h, const float s, const float v) { ); - h_i; - ...
- Python练习-生成器表达式-筛选与运算
# 编辑者:闫龙 l = [10, 2, 3, 4, 5, 6, 7] l1 = [int(i)**2 for i in l] # 得到一个新列表l1,新列表中每个元素是l中对应每个元素值的平方 pr ...