【问题标题】:Triggering page fault exception on Windows在 Windows 上触发页面错误异常
【发布时间】:2019-08-02 09:23:15
【问题描述】:

我正在尝试对 Windows 上的页面错误异常进行一些测试。要求是将一些数据放入页边界,这样读取数据会触发缺页异常。

具体而言,测试由 7 个字节(例如)组成,这些字节在逻辑上是连续的并已分配。我需要前 5 个字节位于物理分配的页面上,但接下来的 2 个字节位于尚未物理分配的页面上。

所以读取这7个字节会触发缺页异常,而只读取4个字节则不会。

我最初认为我应该分配两个页面,在边界上写入 7 字节数据,然后将第二个页面分页。

我可以使用一些用户模式的 Windows API 来做到这一点吗?

【问题讨论】:

    标签: windows memory-management operating-system virtual-memory page-fault


    【解决方案1】:

    是的,这在用户领域是可能的。您通常会使用VirtualAlloc 一次保留所有页面,然后提交其中的一部分。您可以在提交后使用VirtualProtect 来回更改保护。

    #include <cstdint>
    #include <iostream>
    #include <Windows.h>
    
    int main()
    {
        //Retrieve currently configured page-size
        SYSTEM_INFO info;
        GetSystemInfo(&info);
        DWORD pageSize = info.dwPageSize;
        //Reserve 2 pages
        void *mem = VirtualAlloc(NULL, pageSize*2, MEM_RESERVE, PAGE_NOACCESS);
        //Commit first page with read/write premissions
        VirtualAlloc(mem, pageSize, MEM_COMMIT, PAGE_READWRITE);
        //get pointer with 5 bytes in the first page
        uint8_t* ptrAcross = (uint8_t*)mem + pageSize - 5;
        //Fill first 5 bytes
        FillMemory(ptrAcross, 5, 0x55);
        try
        {
            //Try to fill 6th byte
            FillMemory(ptrAcross+5, 1, 0x55);
        }
        catch(...) // only catches the access violation when compiled with /EHa
        {
            std::cout << "Access violation" << std::endl;
        }
        std::cout << "Program finished" <<std::endl;
        VirtualFree(mem, 0, MEM_RELEASE);
    
        return 0;
    }
    

    【讨论】:

    • 谢谢@PeterT,您的解决方案确实足以解决我的问题。实际上,我需要最后 2 个字节包含一些指定的字节(不是随机的)。我可以通过取消提交(即 VirtualFree 然后再次 VirtualAlloc)第二页来做到这一点吗?
    【解决方案2】:

    基本上,这个答案没有添加任何比@PeterT 更重要的东西。稍作修改以在页面边界上写入任意字节(如我们所愿):

    use std::{
        mem::{transmute, MaybeUninit},
        ptr,
    };
    
    use winapi::{
        shared::{minwindef::TRUE, ntdef::NULL},
        um::{
            memoryapi::{VirtualAlloc, VirtualFree, VirtualProtect},
            sysinfoapi::GetSystemInfo,
            winnt::{
                MEM_COMMIT, MEM_DECOMMIT, MEM_RESERVE, PAGE_EXECUTE_READWRITE,
            },
        },
    };
    
    fn main() {
        let page_size = {
            let mut sys_info = MaybeUninit::uninit();
            let sys_info = unsafe {
                GetSystemInfo(sys_info.as_mut_ptr());
                sys_info.assume_init()
            };
            sys_info.dwPageSize
        } as usize;
    
        let region_base = unsafe {
            VirtualAlloc(
                NULL,
                page_size * 2,
                MEM_RESERVE | MEM_COMMIT,
                PAGE_EXECUTE_READWRITE,
            )
        } as usize;
    
        println!("Allocated region base: 0x{:x}", region_base);
    
        let ud1_addr = region_base + page_size - 0x2;
        print!("Writing 0f b9 27 (ud1 esp, [rdi]) to: 0x{:x}... ", ud1_addr);
    
        let ud1_ptr = ud1_addr as *mut u8;
        unsafe {
            ptr::write(ud1_ptr, 0x0f);
            ptr::write(ud1_ptr.add(1), 0xb9);
            ptr::write(ud1_ptr.add(2), 0x27);
        };
    
        println!("ok");
    
        let last_page_addr: usize = region_base + page_size;
        print!("Decommitting the last page at 0x{:x}... ", last_page_addr);
        let free_ok = unsafe { VirtualFree(last_page_addr as _, page_size, MEM_DECOMMIT) };
    
        if free_ok == TRUE {
            println!("ok. Executing: ud1 esp, [rdi]");
            let ud1: extern "C" fn() = unsafe { transmute(ud1_ptr as *const ()) };
            ud1();
        } else {
            println!("failed");
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-04-05
      • 2011-08-19
      • 1970-01-01
      • 2012-11-18
      • 1970-01-01
      • 1970-01-01
      • 2017-08-31
      相关资源
      最近更新 更多