selfrando icon indicating copy to clipboard operation
selfrando copied to clipboard

Add support for static PIE binaries

Open ahomescu opened this issue 6 years ago • 0 comments

gcc and glibc recently added support for the -static-pie option, which produces static self-relocating (PIE) binaries. This is implemented by embedding a lightweight version of the glibc loader in the binary itself, which runs very early in __libc_start_main and moves the main binary in memory, then applies the binary's dynamic relocations just like ld-linux.so.2 would.

The problem is that Selfrando makes a few assumptions about dynamic relocations:

  1. All dynamic relocations have been applied by the time Selfrando runs
  2. There is a clear loader -> program separation and execution flow that Selfrando can intercept, turning it into loader -> Selfrando -> program

Embedding the loader into the program itself breaks assumption 2 above. Since the loader and program itself are now one entity, we have two points where we can inject Selfrando:

  • Hook _start, which runs Selfrando at the very beginning of program execution, before the loader. This conflicts with assumption 1
  • Run Selfrando as part of the regular program initialization process, e.g., via .preinit_array or DT_INIT, which ensures that all dynamic relocations have been applied. However, the call to Selfrando is a few levels deep in the loader call graph, so Selfrando would essentially be randomizing the entire binary while it's running. This currently crashes when Selfrando finishes randomization and tries to return to the loader (the return addresses on the stack still point to the pre-randomization instructions).

We have a few options to solve this, but they're all non-trivial:

  1. Run Selfrando as _start, apply the dynamic relocations ourselves before randomization and undo them afterwards (this part is required, since the loader will then apply them a second time).
  2. Run Selfrando later, and make sure to fix up the return addresses on the call stack before returning from Selfrando. Before fixing up the return addresses, we need to locate them by unwinding the stack. I see two ways to do this: 2a. Embed a lightweight unwinding library inside Selfrando. I looked at libunwind, and it's too complex. 2b. Only handle the common case: no -fomit-frame-pointer, and frames are chained using RBP (on x86).
  3. Add return trampolines for all functions on the call chain from the loader entry point to Selfrando, and have Selfrando fix up the trampolines instead. This would take more work inside TrapLinker, as it would have to identify those functions (maybe we hard-code their names) and the relevant call sites, and then create a trampoline for every call site.

Until we figure out what the best fix is, I'll leave this issue open.

ahomescu avatar Jan 28 '19 21:01 ahomescu