Lazy-binding

How to get the position of function when the program is dynamic linking? The technique which is called lazy-binding isn’t so difficult to understand so that I want to make a note here …

The purpose of Lazy-Binding

When a program is dynamic linking, it will load library function only which necessary for it after it runs up, and this is also why called Lazy :). Where can we see the working of Lazy-binding? objdump -d elf and you may see something likes call <puts@plt>, that’s where it works.

How to get the real position of function

There is a table called GOT(global offset table) with several sections. With .got section, it stores the position of global variables. With .got.plt section, it is the key how program get the real position of library function:
Generally speaking in layout of .got.plt

  1. stores the address of .dynamic in GOT
  2. link_map: put all the library functioin into linked list
  3. dl_runtime_resolve: find out the position (dl_runtime_resolve(link_map, index))

When we dive into the detail of it.

When program call <puts@plt>, it will comes to .got.plt to search for position (in the left hand side of the picture above). It still not gets the position at the first time, so .got.plt will make request to puts@plt. In first step, jmp puts@GOT will make request back to .got.plt and response with puts@plt+6. What’s interesting is that +6 is the length of jmp put@GOT, so it would continue to execute the next line: push index. index is the argument we need for finding the function position (can be seen in third point above), so we push it into stack in advance, then we jump to start point of plt - plt0.

There are two things needed to be done in plt0. First, push link list into the stack because it is also the argument we need for searching function position (the third point above). Now, dl_runtime_resolve can help us to find the position.

In the part of dl_runtime_resolve, it will can called a function _fix_up to find the address and replace and replace puts@plt+6 in .got.plt with the real address of library function.

Second time when you call <puts@plt> and come to upper middle block, the first step is still jmp puts@GOT. However, previous puts@plt+6 in .got.plt has already been replaced with the real address of the function! This is how lazy-binding works.

Security issue

More convenient the thing is, more dangerous the thing might be. Lazy-binding also means that we all have writing permission to GOT, we can apply this concept to complete GOT-hijacking. But it also comes up with another protection called RELRO. And there are three options of RELRO:

  • disabled: Welcome guys, just writing on my face!
  • partial: only .got.plt can be written.
  • full: replace with function address while loading and change to read-only while running.

Conclusion

In the end, I want to emphasize again on the difference between dymanic linking and static linking. Static linking will make all the necessary library compiled before execution, while dynamic linking load the necessary library in execution. Therefore

  1. The advantage of static linking is that we sometimes we don’t need to compile glibc on system or don’t need external dependency on library. Static linking can help us save a lot of time. For pwner, static linking always can build a complete ropchain. But for developer, static linking must not be a good choice because when library needed to be updated, the developer will need to spend a lof of time on recompiling program.
  2. The advantage of dynamic linking program has already been so clear that even default setting of gcc is dynamic linking. However, it also cause to some security problem. For example, LD_PRELOAD is a issue always talked about in dynamic linking, and I would recommend this article The LD-PRELOAD trick for the detail about it.

Reference