gdbelfobjdumpreadelf

How do I dump the contents of an ELF file at a specific address?


Using GDB, if I load an ELF image and give it an address, I can get GDB to dump the contents of the ELF file at that address. For example:

p *((MYSTRUCT *)0x06f8f5b0)
$1 = {
  filename = 0x6f8f5e0 <bla> "this is a string", format = 0x6f8f640 <pvt> "This is another string!\n", lineNumber = 148, argumentCount = 0 '\000', printLevel = 1 '\001'}

This works because GDB has loaded the ELF image and parsed its relocation tables, and it's also aware of the layout of MYSTRUCT.

How do I do the same thing without GDB? I actually don't really care about parsing MYSTRUCT. I just want a dump of 20 bytes at location 0x06f8f5b0. I've tried playing with readelf and objdump, but I couldn't get what I wanted.

Python code (e.g. using pyelftools) would also be acceptable.


Solution

  • I just want a dump of 20 bytes at location 0x06f8f5b0.

    Your question only makes sense in the context of position-dependent (i.e. ET_EXEC) binary (any other binary can be loaded at arbitrary address).

    For a position-dependent binary, the answer is pretty easy:

    To make this more concrete, here is an example:

    // main.c
    const char foo[] = "This is the song that never ends.";
    int main() { printf("&foo = %p\n", &foo[0]); return 0; }
    
    gcc -w -no-pie main.c
    
    ./a.out ; ./a.out
    &foo = 0x402020
    &foo = 0x402020
    
    readelf -Wl a.out | grep LOAD
    
      Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
      LOAD           0x000000 0x0000000000400000 0x0000000000400000 0x000438 0x000438 R   0x1000
      LOAD           0x001000 0x0000000000401000 0x0000000000401000 0x0001bd 0x0001bd R E 0x1000
      LOAD           0x002000 0x0000000000402000 0x0000000000402000 0x000190 0x000190 R   0x1000
      LOAD           0x002e10 0x0000000000403e10 0x0000000000403e10 0x000220 0x000228 RW  0x1000
    

    Here we see that the address we care about is 0x20 bytes into the 3rd LOAD segment, which starts at offset 0x002000 into the file.

    Therefore the bytes we are interested in are at offset 0x2020 into the file.

    Let's check:

    dd if=a.out bs=1 count=15 skip=$((0x002020)) 2>/dev/null
    This is the son
    

    QED.