Triss b89215abd6 | ||
---|---|---|
.gitignore | ||
LICENSE | ||
Makefile | ||
README.md | ||
hello.c | ||
manip-exe.c | ||
manip-exe.h |
README.md
relocmain
Swap out an ELF executable's main
function with another function from its
symbol table, without touching its code at all.
How it works
The typical ELF entrypoint isn't main
directly, but rather _start
, an
assembly stub that first does some runtime initialization before calling
main
. But instead of directly calling the latter function, it is often
passed as a parameter to __libc_start_main
(which in turn calls main
after
doing more initialization stuff).
'Relocations' are instructions for an ELF linker or loader on how to patch a
binary when moving it around in memory or when resolving functions. Some
examples are "this value here is an absolute address, so when you move me
around, please keep it updated" (R_<arch>_RELATIVE
), or "I'm using this
external function, and I'm accessing it through a PLT, so when you resolve
the symbol, please put it in the PLT" (R_<arch>_JUMP_SLOT
).
This programs adds another relocation entry that replaces the code that loads
the address main
(to pass it to __libc_start_main
) with the address of a
different symbol. Due to how relocations work, it is restricted to replacing
it with another symbol that is either imported from another library, or
exported by the executable itself.
Currently, only x86_64 is supported (but it's not too hard to add support for
other instruction sets, 32-bit ELF support is a bit harder). The code looks for
a lea rdi, [rel <pcrel32>]
instruction near _start
, this is often the
instruction that loads main
to pass it to __libc_start_main
(at least on
glibc and musl it is). This then gets overwritten at runtime by ld.so
due
to a relocation of type R_X86_64_PC32
targetting the pcrel32
operand of
that instruction --- all without touching any code of the executable.
The result:
int main(int argc, char** argv[]){
printf("hello, world\n");
return 42;
}
extern int main2(int argc, char** argv[]);
int main2(int argc, char** argv[]) {
printf("be gay do crimes\n");
return 69;
}
$ ./hello.glibc
hello, world
$ ./manip-exe ./hello.glibc{,.manip} main2
$ objdump -d hello.glibc.manip
0000000000001050 <_start>:
1050: 31 ed xor %ebp,%ebp
1052: 49 89 d1 mov %rdx,%r9
1055: 5e pop %rsi
1056: 48 89 e2 mov %rsp,%rdx
1059: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
105d: 50 push %rax
105e: 54 push %rsp
105f: 4c 8d 05 6a 01 00 00 lea 0x16a(%rip),%r8 # 11d0 <__libc_csu_fini>
1066: 48 8d 0d 03 01 00 00 lea 0x103(%rip),%rcx # 1170 <__libc_csu_init>
106d: 48 8d 3d c1 00 00 00 lea 0xc1(%rip),%rdi # 1135 <main>
1074: ff 15 66 2f 00 00 callq *0x2f66(%rip) # 3fe0 <__libc_start_main@GLIBC_2.2.5>
$ ./hello.glibc.manip
be gay do crimes
$ objdump -drR hello.glibc.manip
106d: 48 8d 3d c1 00 00 00 lea 0xc1(%rip),%rdi # 1135 <main>
1070: R_X86_64_PC32 main2@@Base-0x4 # !!!
Usage
# build only the tool
make manip-exe
# usage: ./manip-exe <input> <output> <symbol>
# also show some example stuff
make all
License
be gay, do crimes, death to america