代码之家  ›  专栏  ›  技术社区  ›  tr9sh

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

  •  8
  • tr9sh  · 技术社区  · 15 年前

    在我的学士学位论文中,我希望看到内存中的数据残留,以及在重新启动系统后它是如何持续存在的。

    我有一个简单的想法,把一张图片放到内存中,关闭我的电脑,等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钩子或内存分配器黑客的情况下获取我想要的数据,我现在就想这样做。

    事先谢谢, 戴维

    6 回复  |  直到 15 年前
        1
  •  20
  •   Chris Arguin    15 年前

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

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

    如果您有权限(如果您重新启动计算机,我假设您可以是该计算机上的根目录),那么您可以查看/dev/mem以查看实际的种类布局。也许您应该尝试对值进行采样,重新启动,然后查看这些值中有多少是持久的。

        2
  •  11
  •   rodion    15 年前

    有一个 similar project 演示冷启动攻击。这个 source code 有,也许你能从中得到一些灵感。

    但是,他们在没有先加载操作系统的情况下读取内存,因此不必破坏OSS内存保护。也许您也应该尝试这样做,以避免在引导后内存被操作系统覆盖或清除。

    (还可以在网站上查看视频,这非常令人印象深刻;)

        3
  •  5
  •   Community CDub    7 年前

    在这个问题上 Direct Memory Access in Linux 我们解决了实现这一目标所需的大部分基本问题。注意,mmap()并不是这个问题的答案,因为其他人所说的原因正是如此。你需要一个真实的地址,而不是虚拟的,你只能进入内核(或者通过编写一个驱动程序来将其中继到用户空间)。

    最简单的方法是编写一个可以读写的字符设备驱动程序,并使用ioctl为您提供一个有效的起始或结束地址。同样,如果您希望在内核中使用内存管理函数的指针,请参阅我链接到的问题。其中大部分是在第一个(并被接受的)答案的评论中得出的。

        4
  •  2
  •   nos    15 年前

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

    文件*地址=(文件*)0x12346000;
    fwrite((file*)fd_out,(size_t)1, (size_t)lenna_size,(file*)addr);

    您不能将一个整数强制转换为一个文件指针,并期望得到一些健全的东西。 你也把第一个和最后一个论点转换成了fwrite吗?最后一个参数应该是要写入的文件*。

        5
  •  2
  •   MarkR    15 年前

    为此,您可能需要尽可能少的操作系统;加载的软件越多,覆盖您要检查的内容的机会就越大。

    DOS可能是个不错的选择;它使用640K内存。如果您不加载himem,而是编写自己的(需要汇编的)例程来跳转到pmode,将一块高内存复制到低内存,然后跳转回实模式,您可以编写一个基本上是实模式的程序,该程序可以转储物理RAM(减去bios、dos和应用程序的使用量)。它可以把它丢到一个闪盘或别的什么东西上。

    当然,真正的问题可能是在开机自检期间BIOS会清除内存。

        6
  •  1
  •   Michael Burr    15 年前

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