Use functions from shared libraries, like the puts,
Opt for dynamic linking, and
Opt for lazy binding,
the object code for puts is not included in the binary, but instead is linked at runtime.
The linker adds a placeholder that will be patched at runtime with the real address of puts.
That address is obtained by the dynamic linker from the shared library libc.so.
But this is only done after the first call to puts (thus the lazy binding).
Moreover, when the program calls puts, it does so via a “trampoline”, in the form of a PLT stub.
This stub is a short piece of code (3 instructions only) that runs everytime puts is called.
The first instruction jumps to the address currently in the placeholder (GOT slot).
When the program starts, this address is the address of the next (second) instruction of the stub.
See the illustration below.
The second instruction pushes an identifier for the dynamic linker, and the third jumps to run the dynamic linker itself.
The dynamic linker uses that identifier to fill the GOT slot with the real address of puts in libc.so.
Then the program jumps to puts, which is executed, and the program resumes normal execution.
The next time puts is called, the first instruction jumps to the address in the GOT slot, which is the real address of puts.
This runs puts, and resumes normal execution, and avoids further unnecessary calls to the resolver.
See the illustration below.
We need to disassemble main to get the address of puts’ PLT stub.
In order to get the adresses, we run the program.
But first we add a breakpoint in main:
(gdb)break main
Breakpoint 1 at 0x113d
Then run the program:
(gdb) run
Starting program: /home/rafa/cybersec-dojo/_drafts/hello
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, 0x000055555555513d in main ()
We examine main to get puts’ PLT stub address.
The symbol is aptly named puts@plt:
(gdb) disassemble main
Dump of assembler code for function main:
0x0000555555555139 <+0>: push %rbp
0x000055555555513a <+1>: mov %rsp,%rbp
=> 0x000055555555513d <+4>: lea 0xec0(%rip),%rax # 0x555555556004
0x0000555555555144 <+11>: mov %rax,%rdi
0x0000555555555147 <+14>: call 0x555555555030 <puts@plt>
0x000055555555514c <+19>: mov $0x0,%eax
0x0000555555555151 <+24>: pop %rbp
0x0000555555555152 <+25>: ret
End of assembler dump.
We disassemble the stub.
The first instruction is the jump to the address GOT points to.
(gdb) disassemble 0x555555555030
Dump of assembler code for function puts@plt:
0x0000555555555030 <+0>: jmp *0x2fca(%rip)# 0x555555558000 <[email protected]>
0x0000555555555036 <+6>: push $0x0
0x000055555555503b <+11>: jmp 0x555555555020
End of assembler dump.
We saw that the GOT’s address is 0x555555558000.
To see the address it points to, we examine the contents of that memory address.