在内存中发现程序的图像

时间:2011-06-24 17:34:51

标签: c memory

这是我最近一直在思考的重要内容:

我知道我的程序的虚拟地址空间包含堆栈(每个线程)和堆以及一些静态分配的内存以及所有这些。但它是否也包含程序的图像和所有指令?并且有可能以某种方式(无论平台如何依赖于技巧)找出我自己的图像的地址范围?内存是只读的吗?

简而言之:我可以创建一个自己打印的程序吗?

如果无法做到这一点,那么问题是,我可以打印自己的堆栈吗?我在想这样的事情:

const char * BASE;

void print_stack();

int main(int argc, char * argv[]) {
  BASE = &argc;
  /* do stuff */
  print_stack();
  return 0;
}

void print_stack() {
  int sentinel;
  const char * bottom = &sentinel;
  while (bottom < BASE)
    printf("%02X ", *bottom++);
}

2 个答案:

答案 0 :(得分:1)

要回答您的第一个问题,当然它包含您的程序说明:您只能执行您可以访问的内容。要获取说明的地址,您可以获取功能的地址并从那里开始打印。然后,您可以使用udis86之类的库来反汇编它们。但请注意,您的编译器不需要以任何特定方式对函数进行排序,因此从main开始并且从那里读取并不能保证获得所有内容,可能会在未分配的内存上进行操作。

要获得整个指令存储器范围(您正在寻找.text段),您可以从操作系统中查找地址+大小(在Linux中,该信息将位于{{1在OS X中,您可以使用/proc/[pid]/maps或通过vmmap内核陷阱请求内核),然后直接读取内存。您还可以使用mach_vm_region()转储程序的符号,将所有指向nm段(它们应在.text输出中标记为T)并转储那些。这不是一个好方法,因为你必须反汇编所有内容以确定它们之间的填充位置。

可以访问所有映射内存,但并非所有映射内存都可写(nm段不可写)。需要注意的一点是,如果您的操作系统实现ASLR,地址可能不会稳定地调用调用。

要解决您的第二个问题,是的,您可以打印自己的堆栈并在第三方库的帮助下对其进行符号化,但不是您尝试这样做的方式。堆栈通常会增长 down (即从高地址开始向低地址移动。作为读者的练习,通过.text或其他反汇编程序反汇编您的一个函数,看看内存是如何堆栈在你的函数prolog期间被分配),因此你的for循环将永远不会运行,因为gdb可能总是大于BASE的地址。

答案 1 :(得分:1)

是的,代码字节(通常在此上下文中称为程序“text”)是虚拟地址空间的一部分。

您可以确定某个功能的地址,例如main(),并使用它来确定一系列文本页面中的单个有效地址。然后,您必须调用特定于虚拟内存的API以确定该地址的映射范围。

共享库(.so文件)将其文本映射到不连续的VM区域。