以编程方式访问核心文件内存映像

时间:2015-02-19 17:37:40

标签: c core elf

从相应的ELF核心转储文件访问进程的内存映像的(正确)方法是什么?在某种程度上,我可以检查特定的地址,比如0x12345678。

请记住,不能使用gdb,只需使用纯C方法。不建议使用libelf以外的图书馆使用。

1 个答案:

答案 0 :(得分:5)

  

从相应的ELF核心转储文件访问进程的内存映像的(正确)方法是什么?

这并非完全无足轻重。此外,特定地址甚至可能不在core开头。

让我们考虑一个例子:

// t.c

#include <stdio.h>
#include <stdlib.h>

int main() {
    int i = 42;
    printf("&i = %p\n", &i);
    abort();
}

用以下内容编译:

gcc -g t.c && ulimit -c unlimited && ./a.out
&i = 0x7fffdfb20e1c
Aborted (core dumped)

让我们来看看核心:

readelf -l core.19477

Elf file type is CORE (Core file)
Entry point 0x0
There are 18 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  NOTE           0x0000000000000430 0x0000000000000000 0x0000000000000000
                 0x000000000000084c 0x0000000000000000         0
  LOAD           0x0000000000001000 0x0000000000400000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  R E    1000
  LOAD           0x0000000000002000 0x0000000000600000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  R      1000
  LOAD           0x0000000000003000 0x0000000000601000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  RW     1000
  LOAD           0x0000000000004000 0x00007f85c6abf000 0x0000000000000000
                 0x0000000000001000 0x00000000001bb000  R E    1000
  LOAD           0x0000000000005000 0x00007f85c6c7a000 0x0000000000000000
                 0x0000000000000000 0x00000000001ff000         1000
  LOAD           0x0000000000005000 0x00007f85c6e79000 0x0000000000000000
                 0x0000000000004000 0x0000000000004000  R      1000
  LOAD           0x0000000000009000 0x00007f85c6e7d000 0x0000000000000000
                 0x0000000000002000 0x0000000000002000  RW     1000
  LOAD           0x000000000000b000 0x00007f85c6e7f000 0x0000000000000000
                 0x0000000000005000 0x0000000000005000  RW     1000
  LOAD           0x0000000000010000 0x00007f85c6e84000 0x0000000000000000
                 0x0000000000001000 0x0000000000023000  R E    1000
  LOAD           0x0000000000011000 0x00007f85c7084000 0x0000000000000000
                 0x0000000000003000 0x0000000000003000  RW     1000
  LOAD           0x0000000000014000 0x00007f85c70a3000 0x0000000000000000
                 0x0000000000003000 0x0000000000003000  RW     1000
  LOAD           0x0000000000017000 0x00007f85c70a6000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  R      1000
  LOAD           0x0000000000018000 0x00007f85c70a7000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  RW     1000
  LOAD           0x0000000000019000 0x00007f85c70a8000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  RW     1000
  LOAD           0x000000000001a000 0x00007fffdfb00000 0x0000000000000000
                 0x0000000000022000 0x0000000000022000  RW     1000
  LOAD           0x000000000003c000 0x00007fffdfbfc000 0x0000000000000000
                 0x0000000000002000 0x0000000000002000  R E    1000
  LOAD           0x000000000003e000 0xffffffffff600000 0x0000000000000000
                 0x0000000000001000 0x0000000000001000  R E    1000

如您所见,核心包含NOTE段,后跟几个LOAD段。

NOTE段包含一些Elf64_Note,描述崩溃时的寄存器以及其他内容。它本身非常有趣(使用readelf -n来检查它),但与此处的具体问题无关。

其中一个LOAD段“涵盖”我们感兴趣的地址:0x7fffdfb20e1c,这一个:

  LOAD           0x000000000001a000 0x00007fffdfb00000 0x0000000000000000
                 0x0000000000022000 0x0000000000022000  RW     1000

请注意,它是可写的(正如人们所期望的那样)和

0x7fffdfb00000 < 0x7fffdfb20e1c < 0x7fffdfb22000 (0x7fffdfb00000+0x22000)

因此&i位于LOAD段的内部,偏移

0x7fffdfb20e1c - 0x7fffdfb00000 == 0x20e1c

段本身位于文件偏移量 0x1a000,它告诉我们我们寻找的值是文件偏移量0x1a000 + 0x20e1c == 0x3ae1c

实际上,我们在核心的偏移处找到了值42:

hexdump -s 0x3ae1c -n 4  -e  '4/1 "%02X "' core.19477
2A 00 00 00

那你怎么能以编程方式做到这一点?

非常简单:从Elf64_Ehdr开头阅读core。这将告诉你Elf64_Phdr s的偏移量和数量。迭代它们,直到找到一个“覆盖”你的地址。现在计算文件偏移量(正如我上面所做的那样),lseek(2)read(2)数据。