在 C/C++ 中,如何在虚拟内存空间中获取数据?

Posted

技术标签:

【中文标题】在 C/C++ 中,如何在虚拟内存空间中获取数据?【英文标题】:In C/C++, how do I get the data in a virtual memory space? 【发布时间】:2012-09-24 23:31:11 【问题描述】:

我正在处理调试信息。我正在尝试编写类似于“调试信息解析器”的方式,我正在使用 DWARF 和 ELF 库来执行此操作,但是除了内存空间的信息之外,它们不提供任何东西,我正在尝试获取该内存空间中的数据.我迷上了这个程序。我正在使用一个名为 Pin 的工具,所以我实际上是在另一个程序中运行代码。这就是为什么我可以访问它的变量。

假设我有一个指向地址的指针,我想获取存储在该地址中的所有数据以及接下来的 4 个字节(例如)。

例如,假设我有一个地址 0xDEADBEEF,我想从该地址开始遍历接下来的 4 个字节并读取数据(取消引用每个字节上的指针)

我对 C 比较陌生,我想做的是:

char * address = "0xDEADBEEF";
unsigned int bytesize = 4;

ptr = (void *) address;
ptr_limit = ptr + bytesize;

for(ptr; ptr < ptr_limit; ptr++)
     cout << ptr;

我知道这可能是完全错误的,而且我遇到了很多编译错误,但这只是为了展示我正在尝试使用的一些逻辑......

【问题讨论】:

您是否正在尝试编写调试器? @therefromhere 不,但我正在处理调试信息。我正在尝试编写类似于“调试信息解析器”的方式,我正在使用 DWARF 和 ELF 库来执行此操作,但是除了内存空间的信息之外,它们不提供任何东西,我正在尝试获取该内存空间中的数据 C 和 C++ 是不同的语言,请告诉我们您使用的是哪一种。另外,ptrptr_limit 的类型是什么? 我将 C 和 C++ 用于一个名为 Pin 的工具以及读取 ELF 和 DWARF 库。 ptr 和 ptr_limit 只是 void 指针,但我可以更改它们并使它们成为我需要的任何类型 一方面,"0xDEADBEEF" 不是地址,也不是数字。 0xDEADBEEF 是一个数字。试试看。 【参考方案1】:

好吧,C 和 C++ 是低级的,但它们不是狂野的西部。您不能只是编造一个地址并访问它。在大多数操作系统上,您不能在 assembly 中这样做;这就是 SegFaults 的来源。

你获得内存的方式是分配它。这个过程包括告诉操作系统你想要一块一定大小的内存。此时,操作系统会执行它的工作,以便您可以访问一定范围的虚拟内存。尝试访问此范围之外的内存(或操作系统允许您访问的任何范围)将导致操作系统终止您的程序。

在 C 语言中,您通常使用 malloc/calloc/realloc 分配内存并使用 free 告诉操作系统您已经完成了它。 C++ 使用new 分配对象并使用delete 释放它们。

我正在尝试编写类似于“调试信息解析器”的方式,我正在使用 DWARF 和 ELF 库来执行此操作,但是除了内存空间信息之外它们不提供任何内容,我正在尝试获取数据那个内存空间

如果你能在你的问题中加入这样的东西,那就太好了。

在任何情况下,您都在谈论访问其他人的内存,这并没有完成。好吧,标准 C 和 C++ 的规则不允许这样做。各种操作系统都有调用,可以让你将另一个进程的一些地址空间映射到你的。但这要复杂得多,而且是特定于操作系统的。

【讨论】:

嗯,内存已经分配好了。我正在做的是使用 DWARF 库来获取一堆变量的地址,以及每个变量的字节大小。但有时我会遇到结构和字符数组。取消引用整数没有问题,因为我只是取消引用指针并使用 int 变量来读取它,但我试图取消引用更大的变量,这就是我得到的大小.. @attis: "内存已经分配好了。" 是在你的进程中分配的吗?目前还不清楚你在这里做什么。我们是在谈论触摸别人的记忆,还是您正在阅读 ELF 和 DWARF 文件并试图以此为基础做一些事情? 我正在读取 ELF 和 DWARF 文件并尝试从我从它们那里获得的内存地址中获取数据。 @attis:每个程序都有自己的地址空间。如果其他程序在地址0xDEADBEEF 分配一个字符,而您在地址0xDEADBEEF 读取一个字符,您将看不到它们的数据,它在内存中的不同位置 @MooingDuck 我迷上了这个程序。我正在使用一个名为 Pin 的工具,所以我实际上是在另一个程序中运行代码。这就是为什么我可以访问它的变量。【参考方案2】:

内存地址是整数类型(读取数字)。 在您的示例中,您有一个 char *(读取 string)。

以下代码:

char * address = "0xDEADBEEF";
void * ptr     = ( void * )address;

只会将该char * 变量的地址作为void * 放入p。 它不会设置指向内存地址0xDEADBEEF的指针。

如果你想访问那个特定的内存位置(假设你知道自己在做什么),你需要这样的东西:

void * ptr = ( void * )0xDEADBEEF;

我说“假设你知道自己在做什么”,因为访问这样一个特定地址最终会导致段错误,因为你基本上不知道这样一个地址在你的地址空间中,除非你正在做的事情在环 0(读取内核)中,例如,使用 DMA。 但是我会假设你知道指针是一个数字,而不是一个字符串......

【讨论】:

好吧,我将地址存储为无符号整数中的十进制数,我只是想在该示例中使用 char * 来处理十六进制值并向您显示它是一个地址..它不是随机地址,而是我可以访问的地址。它来自我连接到 ptrace 的程序中的一个变量,我使用 DWARF 从该程序中获取了所有调试信息【参考方案3】:

代码可以很简单:

char *address, *limit;

for(address = (char *)0xdeadbeef, limit = address+4; address < limit; address++)
     cout << *address;

但是请注意,虽然允许将任意整数转换为地址,但使用结果指针的结果是不可移植的(甚至接近于)。根据您的评论(您通过调试信息获取地址),因此您获取的地址应该是有效的,结果应该正常工作(只是不能保证它是可移植的)。

【讨论】:

这正是我的想法,但每当我取消引用地址变量时,我获取的不是数据而是实际地址。所以打印的只是地址而不是数据.. @attis:那是因为您从 string 中的地址开始。当您执行char *whatever = "whatever"; 时,您正在初始化指向该字符串的指针,因此当您打印出字符时,您会打印出字符串的内容。在上面的例子中,我没有使用字符串,而是一个 int 常量。如果必须以字符串开头,请先使用strtol 之类的内容转换为整数。 是的,我知道,我只是想寻找一种将字符串转换为 int 的方法,因为它给了我编译错误,但该函数工作正常!非常感谢.. @JerryCoffin【参考方案4】:

将您的地址存储为 DWORD => DWORD address = 0xDEADBEEF 然后将此地址转换为指针 => void *ptr = (void *)address

这是一个例子:

char *pointer = "FOO";
DWORD address = (DWORD)pointer;
printf("0x%u\n", address);
printf("%s\n", (char *)address); // prints FOO

address++; //move 1 byte
printf("%s\n", (char *)address); // prints OO

【讨论】:

以上是关于在 C/C++ 中,如何在虚拟内存空间中获取数据?的主要内容,如果未能解决你的问题,请参考以下文章

如何更改另一个进程的内存空间中的值

java String 内存地址问题?如何获取 String 内存地址!

澄清Windows中虚拟内存管理器如何获取内存映射文件数据

Linux如何获取进程在物理内存中的所有内容?当进程在内存中的内容发生变化时,又如何获知?内核中实现

C++ 动态内存开辟

C++ 动态内存开辟