如何使用C获取特定的内存地址

时间:2009-06-07 16:45:32

标签: c linux memory memory-management

对于我的学士论文,我想要想象一下内存的数据剩余以及重启系统后它是如何存在的。

我有一个简单的想法是将图片映射到内存,关闭计算机,等待x秒,启动计算机并查看图片是否仍在那里。

int mmap_lena(void)
{
    FILE *fd = NULL;
    size_t lena_size;
    void *addr = NULL;

    fd = fopen("lena.png", "r");

    fseek(fd, 0, SEEK_END);
    lena_size = ftell(fd);

    addr = mmap((void *) 0x12345678, (size_t) lena_size, (int) PROT_READ, (int) MAP_SHARED, (int) fileno(fd), (off_t) 0);
    fprintf(stdout, "Addr = %p\n", addr);
    munmap((void *) addr, (size_t) lena_size);
    fclose(fd);
    fclose(fd_log);
    return EXIT_SUCCESS;
}

为了清楚起见,我省略了检查返回值。

所以在mmap之后我试图以某种方式获取地址,但我通常最终会遇到分段错误,因为我的理解是内存受我的操作系统保护。

int fetch_lena(void)
{
    FILE *fd = NULL;
    FILE *fd_out = NULL;
    size_t lenna_size;
    FILE *addr = (FILE *) 0x12346000;

    fd = fopen("lena.png", "r");
    fd_out = fopen("lena_out.png", "rw");

    fseek(fd, 0, SEEK_END);
    lenna_size = ftell(fd);

    // Segfault 
    fwrite((FILE *) addr, (size_t) 1, (size_t) lenna_size, (FILE *) fd_out);

    fclose(fd);
    fclose(fd_out);

    return 0;

}

请注意我在这个例子中对地址进行了硬编码,因此每当你运行mmap_lena时,我在fetch_lena中使用的值可能是错误的,因为操作系统将第一个参数仅作为提示采用mmap(在我的系统上它总是以某种方式默认为0x12346000。

如果有任何微不足道的编码错误,我很抱歉,因为我的C技能还没有完全发展。

我想现在,如果有任何方法可以获得我想要的数据而不实现任何malloc挂钩或内存分配器黑客。

提前致谢, 大卫

7 个答案:

答案 0 :(得分:20)

您遇到的一个问题是您正在获取虚拟地址,而不是内存所在的物理地址。下次启动时,映射可能不一样。

这肯定可以在Linux的内核模块中完成,但我不认为你可以使用的用户空间中有任何类型的API。

如果你有权限(并且我假设如果你重新启动它可能是这台机器上的root用户),那么你可以查看/ dev / mem以查看实际的phyiscal布局。也许您应该尝试采样值,重新启动,并查看其中有多少值仍然存在。

答案 1 :(得分:11)

有一个similar project,其中演示了冷启动攻击。 source code可用,也许你可以在那里得到一些灵感。

然而,AFAIR他们首先读取内存而不加载操作系统,因此不必弄乱操作系统内存保护。也许您也应该尝试这样做以避免在启动后被操作系统覆盖或清除内存。

(另请查看网站上的视频,这非常令人印象深刻;)

答案 2 :(得分:5)

在问题Direct Memory Access in Linux中,我们找出了实现这一目标所需的大部分基础知识。注意,mmap()不是这个的答案,正是由于其他人所说的原因......你需要一个真正的地址,而不是虚拟的,你只能进入内核(或者通过编写驱动程序将一个地址转发给用户空间) )。

最简单的方法是编写一个可以读取或写入的字符设备驱动程序,使用ioctl为您提供有效的开始或结束地址。同样,如果你想要在内核中使用内存管理函数的指针,请参阅我链接到的问题..大多数问题都是在第一个(并接受)答案的注释中解决的。

答案 3 :(得分:2)

您的测试代码看起来很奇怪

  

FILE * addr =(FILE *)0x12346000;
  fwrite((FILE *)fd_out,(size_t)1,   (size_t)lenna_size,(FILE *)addr);

你不能只是将一个整数强制转换为FILE指针,并希望得到一些理智的东西。 你还把第一个和最后一个参数切换到fwrite吗?最后一个参数应该是要写入的FILE *。

答案 4 :(得分:2)

您可能希望尽可能少的操作系统用于此目的;您加载的软件越多,覆盖您想要检查的内容的机会就越多。

DOS可能是一个不错的选择;它使用< 640k的内存。如果您不加载HIMEM而是编写自己的(需要程序集)例程以跳转到pmode,将高内存块复制到低内存中,然后跳回到实模式,您可以编写一个大多数实模式程序可以转储物理内存(减去BIOS,DOS和你的应用程序使用的多少)。它可以将它转储到闪存盘或其他东西。

当然,真正的问题可能是BIOS在POST期间清除了内存。

答案 5 :(得分:1)

我不熟悉Linux,但您可能需要编写设备驱动程序。设备驱动程序必须有一些方法将虚拟内存地址转换为物理内存地址以用于DMA目的(DMA控制器仅处理物理内存地址)。您应该能够使用这些接口直接处理物理内存。

答案 6 :(得分:0)

我不是说这是最省力的,但是为了完整起见,
您可以Compile a MMU-less Linux Kernel
然后,由于所有地址都是真实的,因此您可能会大吃一惊,就像您点击存储芯片本身一样。

注意1:如果您访问某些地址,仍然可能会收到错误消息,因此只需跳过这些地址即可。

注2:在这种情况下,您不使用文件访问内存,您只需为指针分配一个地址并读取其内容即可。

auto pStart = reinterpert_cast<uint32_t*>(0x600000);
auto pEnd = reinterpert_cast<uint32_t*>(0x800000);
for(auto p = pStart; p < pEnd; ++p)
{
    // do what you want with *p
}