介绍
Physmeme 是一个驱动程序映射器, 适用于任何形式的物理内存读写. 它是高度模块化的代码, 允许逆向工程师轻松集成他们自己的易受攻击的驱动程序. 如果您能够读写物理内存, 您现在只需编写四个函数就可以将未签名的驱动程序映射到您的内核中.
这个怎么运作
Physmeme 的工作方式类似于 https://github.com/z175/kdmapper 在更高级别. 内联挂钩系统调用, 然后调用系统调用, 我们可以调用内核中的任何函数. 唯一的问题是找到所需系统调用的物理页面. 这可以通过做一个简单的计算来完成. 给定 ntoskrnl.exe 中导出函数的相对虚拟地址, 您可以通过页面大小对虚拟地址进行模运算. 通常页面是 0x1000 (4096) 字节, 但在某些系统上它们可以是 2mb 或 1gb.
在左边你可以看到从 ntoskrnl.exe 的基地址到我们感兴趣的函数的相对虚拟地址. 在图像上你可以看到为获得页面偏移所做的简单计算. 此页偏移量稍后在映射器中用于将字节与物理内存进行比较. 现在我们知道了页面偏移量, 我们可以开始映射每个物理页面. 一次映射 2mb 可以显着提高速度, 而不是一次执行一页, 这是因为 IOCTL 非常慢. 在一次映射 2mb 的基础上, 为每个物理内存范围创建一个线程会将找到正确物理页面的时间降低到不到一秒.
- //--- for each physical memory range, make a thread to search it
- std::vector<std::thread> search_threads;
- for (const auto& ranges : util::pmem_ranges)
- search_threads.emplace_back(std::thread(
- &kernel_ctx::map_syscall,
- this,
- ranges.first,
- ranges.second
- ));
- for (std::thread& search_thread : search_threads)
- search_thread.join();
一旦找到系统调用的物理页面并将其映射到我们的进程中, 我们就可以在其中安装内联钩子, 然后调用该函数. 这引入了一些风险, 从补丁保护到竞争条件. 尽管如此, 由于字节在几微秒 / 纳秒内恢复, 因此发生此类事情的可能性还不足以成为问题.
- template <class T, class... Ts>
- std::invoke_result_t<T, Ts...> syscall(void* addr, Ts ... args) const
- {
- static const auto proc =
- GetProcAddress(
- syscall_hook.second.data(),
- syscall_hook.first.data()
- );
- hook::make_hook(psyscall_func, addr);
- auto result = reinterpret_cast<T>(proc)(args ...);
- hook::remove(psyscall_func);
- return result;
- }
使用此函数, 您可以系统调用内核中的任何函数.
用法
您可能会问, 为什么要在 https://github.com/z175/kdmapper 或 drvmapper 上 https://github.com/not-wlan/drvmap 使用 https://githacks.org/xerox/physmeme .Physmeme 是高度模块化的代码, 易于使用, 并允许用户在几分钟内集成他们自己的易受攻击的驱动程序. 使用 https://githacks.org/xerox/physmeme 只需要编程四个函数.
加载和卸载驱动程序的两个函数.
* load_drv, 加载驱动程序并将句柄返回给驱动程序.
* unload_drv, 关闭驱动程序的句柄, 然后卸载它.
处理物理内存的两个函数.
* map_phys, 将物理内存映射到当前进程的地址空间.
* unmap_phys, 取消映射到当前进程的物理内存的映射.
来源: https://www.qcloud.com/developer/article/1857807