在C或C ++(windows)中,如何通过提供物理(非虚拟)地址来读取RAM? 这意味着无需通过虚拟内存系统(mmu表),并且特定于一个进程。
我已经知道API ReadProcessMemory
,它从ram读取(由大多数培训师使用),但它仅用于特定的过程。
我在MSDN上搜索并发现Device\PhysicalMemory似乎提供了这种可能性,但我发现没有实际的例子,这个功能似乎已被Windows服务包关闭(以修复某些漏洞)。
我知道可以这样做,因为WinHex会这样做(如果你选择“工具”>“打开ram”>“物理内存”)。然后它将显示RAM内容从0x00000000到your_ram_size,就像打开传统文件一样。它需要管理员权限,但没有安装驱动程序(这意味着WinHex从用户模式执行)。
编辑:添加了有关操作系统的信息。
答案 0 :(得分:6)
您必须编写内核模式驱动程序并使用内存管理器函数将物理内存范围映射到内核驱动程序的系统空间,然后将功能导出到用户API或驱动程序。
在Windows 98之后,在大多数情况下无法从用户模式访问物理内存。正如其他人所说的那样,任何旧程序都无法破坏人们的计算机。您必须编写内核驱动程序,只有在签名并首次加载到窗口的存储区中才能安装该驱动程序。仅此一点并不像链接DLL那样简单。
总之MmAllocateContiguousMemory()
是一个Windows内核模式函数,它将连续的物理内存映射到系统内存,是ntoskrnl.exe
的一部分。
此外,您无法从用户模式应用程序调用这些API。只有司机可以使用它们。用户模式应用程序无法在驱动程序的帮助下访问物理内存。驱动程序可以处理来自用户API的请求,也可以使用IOCTL并将其资源映射到API的虚拟内存。无论哪种方式,您都需要一个必须由插件管理器安装的驱动程序的帮助。 PnP必须选择通过硬件激活(即热插拔或其他方法,如总是打开的总线驱动程序)自行安装驱动程序。
其他窗口随机分配虚拟地址,以便不容易识别任何模式或计算出它的物理位置。
答案 1 :(得分:5)
语言C和C ++都没有定义术语“记忆”。事物用抽象术语定义,如“存储”和“存储分类器”。指针是抽象的东西 - 它们的值可以是任何东西,与物理或虚拟地址完全无关。
仅在系统及其实现的上下文中引入了诸如内存和地址空间之类的术语。由于这些是系统特定的事情,一个必须使用操作系统提供的方法来访问它们。
即使在实现操作系统内核时,您也必须通过C(因为它根本不能)访问最低级别的内容,而是通过特定于实现和体系结构的方法。通常这是通过在汇编中编程的一组低级函数来完成的,这些函数的编写方式与编译器生成的机器代码类型相匹配。这允许从C调用那些用汇编编写的函数,就好像它们是由编译器编译的一样。
答案 2 :(得分:5)
点击此链接:Access Physical Memory, Port and PCI Configuration Space
但是从Windows Vista开始,即使是WinHex也无法打开物理内存。答案 3 :(得分:0)
我认为设备驱动程序必须允许物理内存访问,因为需要以这种方式访问PCI卡等设备。如果您可以从驱动程序执行此操作,则为您的“用户”(更像是管理员)模式程序编写自定义分配器,以便轻松链接到C ++。
答案 4 :(得分:0)
在Windows下,您应该使用NativeAPI调用NtOpenSection和NtMapViewOfSection
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()从第1 MB的原始内存中获取有用的信息,而无需查找。
Bonus:使用Boost IO内存映射文件读取Linux下的物理内存(Debian 9),该类的一部分:
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);
}
答案 5 :(得分:-2)
我猜它不可能直接访问物理地址。甚至没有行政特权。
应用程序访问的每个地址都是虚拟地址,由硬件MMU转换为物理地址。
一种方法是将MMU配置为一对一映射虚拟地址到物理地址。这通常在没有OS的嵌入式系统中或在加载OS之前完成。
加载Windows。我相信你的要求是不可能的。
答案 6 :(得分:-3)
简答:否
长答案:
C / C ++标准以非常简单的术语定义机器。没有虚拟内存的概念(只是内存)。这些概念更像是硬件的领域,可能通过操作系统进行访问(如果它知道OS这样的东西)。
我会根据您的操作系统/硬件提供的设施重新提出问题。