When linking a binary with -Wl,-z,relro,-z,now, all relocations are performed at start-up before passing control to the binary.

Because of this, there is no need for the .got.plt segment. Normally, a pointer to the linker's link_map structure is stored in this segment.

When compiled with full RELRO, where, if anywhere, can a copy of the link_map be obtained without consulting other loaded libraries, or libdl?

The place one would expect it to be -- in the segment marked with the tag DT_PLTGOT -- it does not appear. Instead, there's just a link back to Program Header of type PT_DYNAMIC. The slot in the segment marked DT_GOTPLT starts with the offset of the DYNAMIC section, and does not contain any pointers to the link map.

Headers

$ readelf -a amd64-pwntest-relro | egrep -i '(_dynamic|pltgot)'
 0x0000000000000003 (PLTGOT)             0x202eb8
    48: 0000000000202ca8     0 OBJECT  LOCAL  DEFAULT   21 _DYNAMIC

Binary is RELRO

$ checksec.sh --file amd64-relro
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Full RELRO      No canary found   NX disabled   PIE enabled     No RPATH   No RUNPATH   amd64-relro

GDB shows that the data at the specified offset, at runtime, does not contain a link map pointer.

$ gdb ./amd64-relro
gdb-peda$ start
gdb-peda$ vmmap relro
Start              End                Perm      Name
0x0000555555554000 0x0000555555556000 r-xp      /home/user/pwntools-regression/src/amd64-relro
0x0000555555756000 0x0000555555757000 r-xp      /home/user/pwntools-regression/src/amd64-relro
0x0000555555757000 0x0000555555758000 rwxp      /home/user/pwntools-regression/src/amd64-relro
gdb-peda$ telescope 0x0000555555554000+0x202eb8 5
00:0000|  0x555555756eb8 --> 0x202ca8 
01:0008|  0x555555756ec0 --> 0x0 
02:0016|  0x555555756ec8 --> 0x0 
03:0024|  0x555555756ed0 --> 0x7ffff7675870 (<__GI___libc_free>:        mov    rax,QWORD PTR [rip+0x33b671]        # 0x7ffff79b0ee8)
04:0032|  0x555555756ed8 --> 0x7ffff79c0430 (<__pthread_create_2_1>:    push   rbp)
up vote 4 down vote accepted

If the binary has a DT_DEBUG entry in the PT_DYNAMIC area, it will be filled with a pointer to the r_debug symbol in the dynamic linker.

test:00007F17ED7DDDB0 Elf64_Dyn <DT_SYMENT, 18h>
test:00007F17ED7DDDB0 Elf64_Dyn <DT_DEBUG, offset _r_debug>
test:00007F17ED7DDDB0 Elf64_Dyn <DT_PLTGOT, offset _GLOBAL_OFFSET_TABLE_>

The second field in r_debug is the pointer to link_map:

debug001:00007F17ED5DC1A0 _r_debug dd 1                                    ; r_version
debug001:00007F17ED5DC1A0 db 0, 0, 0, 0
debug001:00007F17ED5DC1A0 dq offset _link_map_head                ; r_map
debug001:00007F17ED5DC1A0 dq offset _dl_debug_state               ; r_brk
debug001:00007F17ED5DC1A0 dd RT_ADD                               ; r_state
debug001:00007F17ED5DC1A0 db 0, 0, 0, 0
debug001:00007F17ED5DC1A0 dq 7F17ED3B8000h                        ; r_ldbase
  • Bummer that there doesn't appear to be any additional ways for a stripped binary. – Zach Riggle Oct 25 '14 at 22:50
  • Even stripped binaries have dynamic section, and usually it includes the DT_DEBUG entry, so what's the problem? – Igor Skochinsky Oct 26 '14 at 0:24
  • After revisiting, it appears that I misunderstood your post. The DT_DEBUG entry is populated at runtime. I mistook the output of readelf -a binary showing 0 for that slot to mean that it didn't contain useful data. Examining under GDB shows that is it populated. – Zach Riggle Oct 28 '14 at 2:17
  • 1
    Awesome, thanks again! Just landed a pull request for pwntools to take advantage of this. Saves about thirty seconds (50% of runtime; slow network comms) vs. leaking a GOT pointer into libc and doing a scandown then parsing the elf header. – Zach Riggle Oct 28 '14 at 9:01

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.