Swap out an ELF executable's main function with another function from its symbol table, without touching its code at all.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

101 lines
3.2 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. # relocmain
  2. Swap out an ELF executable's `main` function with another function from its
  3. symbol table, without touching its code at all.
  4. ## How it works
  5. The typical ELF entrypoint isn't `main` directly, but rather `_start`, an
  6. assembly stub that first does some runtime initialization before calling
  7. `main`. But instead of directly calling the latter function, it is often
  8. passed as a parameter to `__libc_start_main` (which in turn calls `main` after
  9. doing *more* initialization stuff).
  10. 'Relocations' are instructions for an ELF linker or loader on how to patch a
  11. binary when moving it around in memory or when resolving functions. Some
  12. examples are "this value here is an absolute address, so when you move me
  13. around, please keep it updated" (`R_<arch>_RELATIVE`), or "I'm using this
  14. external function, and I'm accessing it through a PLT, so when you resolve
  15. the symbol, please put it in the PLT" (`R_<arch>_JUMP_SLOT`).
  16. This programs adds another relocation entry that replaces the code that loads
  17. the address `main` (to pass it to `__libc_start_main`) with the address of a
  18. different symbol. Due to how relocations work, it is restricted to replacing
  19. it with another symbol that is either imported from another library, or
  20. exported by the executable itself.
  21. Currently, only x86_64 is supported (but it's not too hard to add support for
  22. other instruction sets, 32-bit ELF support is a bit harder). The code looks for
  23. a `lea rdi, [rel <pcrel32>]` instruction near `_start`, this is often the
  24. instruction that loads `main` to pass it to `__libc_start_main` (at least on
  25. glibc and musl it is). This then gets overwritten *at runtime by* `ld.so` due
  26. to a relocation of type `R_X86_64_PC32` targetting the `pcrel32` operand of
  27. that instruction --- all without touching any code of the executable.
  28. The result:
  29. ```c
  30. int main(int argc, char** argv[]){
  31. printf("hello, world\n");
  32. return 42;
  33. }
  34. extern int main2(int argc, char** argv[]);
  35. int main2(int argc, char** argv[]) {
  36. printf("be gay do crimes\n");
  37. return 69;
  38. }
  39. ```
  40. ```
  41. $ ./hello.glibc
  42. hello, world
  43. $ ./manip-exe ./hello.glibc{,.manip} main2
  44. ```
  45. ```asm
  46. $ objdump -d hello.glibc.manip
  47. 0000000000001050 <_start>:
  48. 1050: 31 ed xor %ebp,%ebp
  49. 1052: 49 89 d1 mov %rdx,%r9
  50. 1055: 5e pop %rsi
  51. 1056: 48 89 e2 mov %rsp,%rdx
  52. 1059: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
  53. 105d: 50 push %rax
  54. 105e: 54 push %rsp
  55. 105f: 4c 8d 05 6a 01 00 00 lea 0x16a(%rip),%r8 # 11d0 <__libc_csu_fini>
  56. 1066: 48 8d 0d 03 01 00 00 lea 0x103(%rip),%rcx # 1170 <__libc_csu_init>
  57. 106d: 48 8d 3d c1 00 00 00 lea 0xc1(%rip),%rdi # 1135 <main>
  58. 1074: ff 15 66 2f 00 00 callq *0x2f66(%rip) # 3fe0 <__libc_start_main@GLIBC_2.2.5>
  59. ```
  60. ```
  61. $ ./hello.glibc.manip
  62. be gay do crimes
  63. ```
  64. ```asm
  65. $ objdump -drR hello.glibc.manip
  66. 106d: 48 8d 3d c1 00 00 00 lea 0xc1(%rip),%rdi # 1135 <main>
  67. 1070: R_X86_64_PC32 main2@@Base-0x4 # !!!
  68. ```
  69. ## Usage
  70. ```sh
  71. # build only the tool
  72. make manip-exe
  73. # usage: ./manip-exe <input> <output> <symbol>
  74. # also show some example stuff
  75. make all
  76. ```
  77. ## License
  78. ```
  79. be gay, do crimes, death to america
  80. ```