【问题标题】:How do you read directly from physical memory?你如何直接从物理内存中读取?
【发布时间】:2012-01-14 06:33:39
【问题描述】:

在 C 或 C++(Windows)中,如何通过提供物理(非虚拟)地址来读取 RAM? 这意味着无需通过虚拟内存系统(mmu 表),并且特定于一个进程。

我已经知道 API ReadProcessMemory,它从 ram(大多数培训师使用)读取,但它仅用于特定进程。

我在MSDN上搜索,发现Device\PhysicalMemory似乎提供了这种可能性,但我没有找到实际的例子,而且这个功能似乎已被Windows服务包关闭(修复一些漏洞)。

我知道这是可能的,因为 WinHex 可以做到(如果你选择“工具”>“打开内存”>“物理内存”)。然后它将显示从 0x00000000 到 your_ram_size 的 RAM 内容,就像打开传统文件时一样。它需要管理员权限,但无需安装驱动程序(这意味着 WinHex 在用户模式下安装)。

编辑:添加了有关操作系统的信息。

【问题讨论】:

  • 在 WinHex 上尝试“link -dump -imports”,看看它调用了哪些函数。
  • @Alexandre C. :我想为游戏写一个教练。我从来不知道值会在哪里(哪个进程),所以我认为扫描所有内容会更容易(winhex 做得非常快)。我已经可以手动完成(使用winhex),但最好有一些自动程序来完成。
  • 物理内存映射比较不稳定,包含的内存远大于游戏进程。所以,这似乎比ReadProcessMemory 倒退了一步。
  • 如果您正在为游戏编写作弊程序,那么您至少知道您想要的值将在那个进程的内存中。此外,该进程的内存在您查找时不一定在物理 RAM 中,因此 ReadProcessMemory 确实是满足您需求的理想功能。它会从页面文件中读取,它会让你使用稳定的地址。
  • 对于您的培训师,无论如何您都希望从基本 VA 中进行偏移,因为可执行文件的内存中的图像会随着执行而变化。因此,您最好确定感兴趣变量的位置,计算偏移量,并使用它来获得过程 VA。

标签: c++ c windows memory readprocessmemory


【解决方案1】:

在 Windows 下,您应该使用 NativeAPI 调用 NtOpenSectionNtMapViewOfSection

Mark Russinovich 的例子

static BOOLEAN MapPhysicalMemory( HANDLE PhysicalMemory,
                            PDWORD Address, PDWORD Length,
                            PDWORD VirtualAddress )
{
    NTSTATUS            ntStatus;
    PHYSICAL_ADDRESS    viewBase;
    char                error[256];

    *VirtualAddress = 0;
    viewBase.QuadPart = (ULONGLONG) (*Address);
    ntStatus = NtMapViewOfSection (PhysicalMemory,
                               (HANDLE) -1,
                               (PVOID) VirtualAddress,
                               0L,
                               *Length,
                               &viewBase,
                               Length,
                               ViewShare,
                               0,
                               PAGE_READONLY );

    if( !NT_SUCCESS( ntStatus )) {

        sprintf_s( error, "Could not map view of %X length %X",
                *Address, *Length );
        PrintError( error, ntStatus );
        return FALSE;                   
    }

    *Address = viewBase.LowPart;
    return TRUE;
}

static HANDLE OpenPhysicalMemory()
{
    NTSTATUS        status;
    HANDLE          physmem;
    UNICODE_STRING  physmemString;
    OBJECT_ATTRIBUTES attributes;
    WCHAR           physmemName[] = L"\\device\\physicalmemory";

    RtlInitUnicodeString( &physmemString, physmemName );    

    InitializeObjectAttributes( &attributes, &physmemString,
                                OBJ_CASE_INSENSITIVE, NULL, NULL );         
    status = NtOpenSection( &physmem, SECTION_MAP_READ, &attributes );

    if( !NT_SUCCESS( status )) {

        PrintError( "Could not open \\device\\physicalmemory", status );
        return NULL;
    }

    return physmem;
}

\device\physicalmemory 是 Linux 下/dev/mem 的模拟,您也可以直接访问物理内存。顺便说一句,不确定 Windows,但在 Linux 下只有 1 Mb 的物理地址空间可用,因为它可能包含一些服务低级数据,如 BIOS 表。访问其他物理内存可能会损坏由操作系统管理的虚拟内存,这就是不允许这样做的原因

更新:提供的代码在从 Windows Vista 开始的用户模式下不起作用。相反,您可以调用 GetSystemFirmwareTable() 从 1st MB 的原始内存中获取有用的信息,而无需寻找它。

奖励:在 Linux (Debian 9) 下使用 Boost IO 内存映射文件读取物理内存,课程的一部分:

NativePhysicalMemory::NativePhysicalMemory(size_t base, size_t length)
    : physical_memory_map_(std::make_unique<boost::iostreams::mapped_file_source>())
{
    map_physical_memory(base, length);
}

// ...

void NativePhysicalMemory::map_physical_memory(size_t base, size_t length)
{
#ifdef _SC_PAGESIZE
    size_t mempry_page_offset = base % sysconf(_SC_PAGESIZE);
#else
    size_t mempry_page_offset = base % getpagesize();
#endif /* _SC_PAGESIZE */

    boost_io::mapped_file_params params = {};
    params.path = "/dev/mem";
    params.flags = boost_io::mapped_file::mapmode::readonly;
    params.length = length + mempry_page_offset;
    params.offset = base - mempry_page_offset;
    params.hint = nullptr;
    physical_memory_map_->open(params);
}

【讨论】:

  • 在管理员权限和root权限下完美运行
  • 是的,对不起,我的错 - 从 Windows 7 开始,它在用户模式下确实不起作用(适用于 XP 等旧系统)。取而代之的是,您可以使用 WinAPI 调用读取 SMBIOS 表。虽然,在 Linux 下从 /dev/mem 读取仍然是可能的 :)
  • 再一次,不是从Windows 7开始,当然是从Vista开始(我的记忆,我不是说RAM =))更新了答案。
【解决方案2】:

您必须编写内核模式驱动程序并使用内存管理器函数将物理内存范围映射到内核驱动程序的系统空间,然后将功能导出到用户 API 或驱动程序。

在 Windows 98 之后,大多数情况下无法从用户模式访问物理内存。正如其他人所说的那样,任何旧程序都不能仅仅破坏人们的计算机。您必须编写一个内核驱动程序,该驱动程序只有在它被签名并首先加载到窗口的存储区时才能安装。仅此一项就不像链接 DLL 那样简单。

总之MmAllocateContiguousMemory()是一个windows内核模式函数,它将连续的物理内存映射到系统内存,是ntoskrnl.exe的一部分。

您也不能从用户模式应用程序调用这些 API。只有司机可以使用它们。没有驱动程序的帮助,用户模式应用程序无法访问物理内存。驱动程序可以处理来自用户 API 的请求,也可以使用 IOCTL 并将其资源映射到 API 的虚拟内存。无论哪种方式,您都需要由即插即用管理器安装的驱动程序的帮助。 PnP 必须选择通过硬件激活(即热插拔)或其他方法(如始终打开的总线驱动程序)自行安装驱动程序。

更多窗口随机分配虚拟地址,因此不容易识别任何模式或计算出它的物理位置。

【讨论】:

  • DOLLx8KD 是一个 dll,您可以使用它来访问 ram 直到 windows 7(实际上它是一个内核模式驱动程序 ^ 带有用户模式驱动程序,您可以通过 dll 调用它的功能。在 c 中)
  • 另一个常见的用途是直接内存访问或 DMA。在这种情况下,独立硬件直接访问物理内存,无需通过处理器或内存管理器。
  • 另外看起来 ReadProcessMemory() 只是一种允许通过内存进行进程间通信的方法。它仍然是一个虚拟内存地址。
  • 第二个链接似乎是微软制作的类型,用于管理物理内存并设计用于某些设置。它不是实际获取这些对象的方法。
  • 不确定winHex,你看过源码吗?你怎么知道它不叫司机?
【解决方案3】:

C 语言和 C++ 都没有定义术语“内存”。事物是用抽象术语定义的,例如“存储”和“存储分类器”。指针是抽象的东西——它们的值可以是任何东西,与物理或虚拟地址完全无关。

只有在系统及其实现的上下文中才会引入内存和地址空间等术语。由于这些是系统特定的东西,因此必须使用操作系统提供的方法来访问它们。

即使在实现操作系统内核时,您也必须通过特定于实现和体系结构的方法来访问最低级别的东西,而不是通过 C(因为它根本不能)。通常这是通过一组用汇编语言编写的低级函数来完成的,这些函数的编写方式与编译器生成的机器代码类型相匹配。这允许从 C 中调用那些用汇编编写的函数,就好像它们是由编译器编译的一样。

【讨论】:

  • 日期这是正确的吗?唯一不能用 C 做的就是访问指令指针。一旦你设置好它并告诉 CPU 从哪里开始执行,你基本上就可以开始执行编译的代码了。现在你可能不想在没有像样的、称职的 API 的情况下这样做,但是如果你希望尽可能多地使用汇编,你可以通过编译的代码轻松地直接指向物理内存 - 对于现代机器来说,这当然完全依赖于 MMU。但在较小的机器或较旧的机器上,您可以通过编译代码访问物理内存。
  • @cdcdcd:是的,编译后的代码,即架构二进制,当然可以访问内存。但是 C 编程语言(以及 C++)没有内置的机制来直接访问 任意 内存位置。必须始终通过在有效的 C 存储对象上应用地址运算符 (&amp;) 来获取指针。 或者实现可以提供对任意内存位置的实现依赖访问。例如在 Linux 内核中,每个架构都有一个内存访问的实现,例如对于 x86:lxr.free-electrons.com/source/arch/x86/include/asm/io.h
  • @cdcdcd: 说清楚一点,Linux内核源码使用内联汇编来定义C函数接口,函数体不是用C写的,而是直接用机器语言写的,因为C不具有语言定义的内存访问机制。
  • 你确定吗?能否提供参考。物理内存是否可以直接访问与语言无关。你的论点是一种心态。您可以轻松地编写一个编译器来从 .c 生成相同的机器代码,就像您所指的手工制作的机器代码一样。鉴于您的逻辑,人们可能会争辩说程序集无法访问内存地址,因为它需要一个汇编程序。当然,在 C 和汇编的情况下,源代码并不直接寻址内存(直到编译和加载),但机器代码在执行之前也不会直接寻址。
  • 仅仅因为你可以并不意味着你应该。鉴于此,某些系统允许,并且您可以读取 0x00 处的字节值 - 这里没有未定义的行为,事件的顺序是可预测的,并且对于允许这样做的系统,并且是编译器的目标;它会正确处理它(读取内容并将其写入v)。现代平台的编译器可能会抛出警告。请记住,许多系统都有定制的编译器。同样,您可以编写程序集来做本质上相同的事情。如果您担心优化,请使用 volatile。
【解决方案4】:

我认为设备驱动程序必须允许物理内存访问,因为 PCI 卡等设备需要以这种方式访问​​。如果您可以从驱动程序中执行此操作,则为您的“用户”(更像管理员)模式程序编写一个自定义分配器,以便轻松链接到 C++。

【讨论】:

  • PCI 驱动程序使用 DMA 电路进行数据传输,因此不涉及 CPU(用于编码)。因此,出于所有实际目的,这应该无济于事。
【解决方案5】:

查看此链接:Access Physical Memory, Port and PCI Configuration Space

但是从 Windows Vista 开始,即使 WinHex 也无法打开物理内存。

【讨论】:

  • 这确实需要编写驱动程序。 OP 声称 WinHex 在没有驱动程序的情况下也能做到这一点——不过我对此持怀疑态度。
【解决方案6】:

我猜它不可能直接访问物理地址。甚至没有管理权限。

应用程序访问的每个地址都是虚拟地址,由硬件 MMU 转换为物理地址。

一种方法是将 MMU 配置为一对一映射虚拟地址到物理地址。这通常在没有操作系统的嵌入式系统中或在加载操作系统之前完成。

加载了窗口。我相信你的要求是不可能的。

【讨论】:

  • POSIX - /dev/mem 和 Windows NtOpenSection(L"\\Device\\PhysicalMemory") 都有可能
【解决方案7】:

简短回答:否

长答案:

C/C++ 标准用非常简单的术语定义机器。没有虚拟内存的概念(只是内存)。这些概念更多地属于硬件领域,并且可能通过操作系统访问(如果它知道操作系统这样的东西)。

我会根据您的操作系统/硬件提供的设施重新提出问题。

【讨论】:

  • 我认为问题是已经就操作系统提供的设施而言,因为它正在谈论操作系统提供的功能和设备。
  • 我忘了说明:这是针对Win32环境的。我同意你的观点,如果不是特定于操作系统,这个问题就是无意义的。
  • 这不是 C++ 新闻组吧。操作系统细节完全没问题
猜你喜欢
  • 2013-04-09
  • 2020-05-18
  • 1970-01-01
  • 2014-05-05
  • 1970-01-01
  • 1970-01-01
  • 2012-12-25
  • 2013-02-27
  • 1970-01-01
相关资源
最近更新 更多