inject.c (8795B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #include <stdint.h> 6 #include <unistd.h> 7 #include <sys/mman.h> 8 #include <elf.h> 9 10 /* The Android NDK headers define those */ 11 #undef Elf_Ehdr 12 #undef Elf_Addr 13 14 #if defined(__LP64__) 15 # define Elf_Ehdr Elf64_Ehdr 16 # define Elf_Phdr Elf64_Phdr 17 # define Elf_Addr Elf64_Addr 18 # define Elf_Word Elf64_Word 19 # define Elf_Dyn Elf64_Dyn 20 #else 21 # define Elf_Phdr Elf32_Phdr 22 # define Elf_Ehdr Elf32_Ehdr 23 # define Elf_Addr Elf32_Addr 24 # define Elf_Word Elf32_Word 25 # define Elf_Dyn Elf32_Dyn 26 #endif 27 28 #ifdef RELRHACK 29 # include "relrhack.h" 30 # define mprotect_cb mprotect 31 # define sysconf_cb sysconf 32 33 #else 34 // On ARM, PC-relative function calls have a limit in how far they can jump, 35 // which might not be enough for e.g. libxul.so. The easy way out would be 36 // to use the long_call attribute, which forces the compiler to generate code 37 // that can call anywhere, but clang doesn't support the attribute yet 38 // (https://bugs.llvm.org/show_bug.cgi?id=40623), and while the command-line 39 // equivalent does exist, it's currently broken 40 // (https://bugs.llvm.org/show_bug.cgi?id=40624). So we create a manual 41 // trampoline, corresponding to the code GCC generates with long_call. 42 # ifdef __arm__ 43 __attribute__((section(".text._init_trampoline"), naked)) int init_trampoline( 44 int argc, char** argv, char** env) { 45 __asm__ __volatile__( 46 // thumb doesn't allow to use r12/ip with ldr, and thus would require an 47 // additional push/pop to save/restore the modified register, which would 48 // also change the call into a blx. It's simpler to switch to arm. 49 ".arm\n" 50 " ldr ip, .LADDR\n" 51 ".LAFTER:\n" 52 " add ip, pc, ip\n" 53 " bx ip\n" 54 ".LADDR:\n" 55 " .word real_original_init-(.LAFTER+8)\n"); 56 } 57 # endif 58 59 // On aarch64, a similar problem exists, but long_call is not an option at all 60 // (even GCC doesn't support them on aarch64). 61 # ifdef __aarch64__ 62 __attribute__((section(".text._init_trampoline"), naked)) int init_trampoline( 63 int argc, char** argv, char** env) { 64 __asm__ __volatile__( 65 " adrp x8, .LADDR\n" 66 " add x8, x8, :lo12:.LADDR\n" // adrp + add gives us the full address 67 // for .LADDR 68 " ldr x0, [x8]\n" // Load the address of real_original_init relative to 69 // .LADDR 70 " add x0, x8, x0\n" // Add the address of .LADDR 71 " br x0\n" // Branch to real_original_init 72 ".LADDR:\n" 73 " .xword real_original_init-.LADDR\n"); 74 } 75 # endif 76 77 extern __attribute__((visibility("hidden"))) void original_init(int argc, 78 char** argv, 79 char** env); 80 81 extern __attribute__((visibility("hidden"))) Elf_Addr relhack[]; 82 extern __attribute__((visibility("hidden"))) Elf_Addr relhack_end[]; 83 84 extern __attribute__((visibility("hidden"))) int (*mprotect_cb)(void* addr, 85 size_t len, 86 int prot); 87 extern __attribute__((visibility("hidden"))) long (*sysconf_cb)(int name); 88 extern __attribute__((visibility("hidden"))) char relro_start[]; 89 extern __attribute__((visibility("hidden"))) char relro_end[]; 90 #endif 91 92 extern __attribute__((visibility("hidden"))) Elf_Ehdr __ehdr_start; 93 94 static inline __attribute__((always_inline)) void do_relocations( 95 Elf_Addr* relhack, Elf_Addr* relhack_end) { 96 Elf_Addr* ptr; 97 for (Elf_Addr* entry = relhack; entry < relhack_end; entry++) { 98 if ((*entry & 1) == 0) { 99 ptr = (Elf_Addr*)((intptr_t)&__ehdr_start + *entry); 100 *ptr += (intptr_t)&__ehdr_start; 101 } else { 102 Elf_Addr bits = *entry; 103 Elf_Addr* end = ptr + 8 * sizeof(Elf_Addr) - 1; 104 do { 105 ptr++; 106 bits >>= 1; 107 if (bits & 1) { 108 *ptr += (intptr_t)&__ehdr_start; 109 } 110 } while (ptr < end); 111 } 112 } 113 } 114 115 #ifndef RELRHACK 116 __attribute__((section(".text._init_noinit"))) int init_noinit(int argc, 117 char** argv, 118 char** env) { 119 do_relocations(relhack, relhack_end); 120 return 0; 121 } 122 123 __attribute__((section(".text._init"))) int init(int argc, char** argv, 124 char** env) { 125 do_relocations(relhack, relhack_end); 126 original_init(argc, argv, env); 127 // Ensure there is no tail-call optimization, avoiding the use of the 128 // B.W instruction in Thumb for the call above. 129 return 0; 130 } 131 #endif 132 133 static inline __attribute__((always_inline)) void do_relocations_with_relro( 134 Elf_Addr* relhack, Elf_Addr* relhack_end, char* relro_start, 135 char* relro_end) { 136 long page_size = sysconf_cb(_SC_PAGESIZE); 137 uintptr_t aligned_relro_start = ((uintptr_t)relro_start) & ~(page_size - 1); 138 // The relro segment may not end at a page boundary. If that's the case, the 139 // remainder of the page needs to stay read-write, so the last page is never 140 // set read-only. Thus the aligned relro end is page-rounded down. 141 uintptr_t aligned_relro_end = ((uintptr_t)relro_end) & ~(page_size - 1); 142 // By the time the injected code runs, the relro segment is read-only. But 143 // we want to apply relocations in it, so we set it r/w first. We'll restore 144 // it to read-only in relro_post. 145 mprotect_cb((void*)aligned_relro_start, 146 aligned_relro_end - aligned_relro_start, PROT_READ | PROT_WRITE); 147 148 do_relocations(relhack, relhack_end); 149 150 mprotect_cb((void*)aligned_relro_start, 151 aligned_relro_end - aligned_relro_start, PROT_READ); 152 #ifndef RELRHACK 153 // mprotect_cb and sysconf_cb are allocated in .bss, so we need to restore 154 // them to a NULL value. 155 mprotect_cb = NULL; 156 sysconf_cb = NULL; 157 #endif 158 } 159 160 #ifndef RELRHACK 161 __attribute__((section(".text._init_noinit_relro"))) int init_noinit_relro( 162 int argc, char** argv, char** env) { 163 do_relocations_with_relro(relhack, relhack_end, relro_start, relro_end); 164 return 0; 165 } 166 167 __attribute__((section(".text._init_relro"))) int init_relro(int argc, 168 char** argv, 169 char** env) { 170 do_relocations_with_relro(relhack, relhack_end, relro_start, relro_end); 171 original_init(argc, argv, env); 172 return 0; 173 } 174 #else 175 176 extern __attribute__((visibility("hidden"))) Elf_Dyn _DYNAMIC[]; 177 178 static void _relrhack_init(void) { 179 // Get the location of the SHT_RELR data from the PT_DYNAMIC segment. 180 uintptr_t elf_header = (uintptr_t)&__ehdr_start; 181 Elf_Addr* relhack = NULL; 182 Elf_Word size = 0; 183 for (Elf_Dyn* dyn = _DYNAMIC; dyn->d_tag != DT_NULL; dyn++) { 184 if ((dyn->d_tag & ~DT_RELRHACK_BIT) == DT_RELR) { 185 relhack = (Elf_Addr*)(elf_header + dyn->d_un.d_ptr); 186 } else if ((dyn->d_tag & ~DT_RELRHACK_BIT) == DT_RELRSZ) { 187 size = dyn->d_un.d_val; 188 } 189 } 190 191 Elf_Addr* relhack_end = (Elf_Addr*)((uintptr_t)relhack + size); 192 193 // Find the location of the PT_GNU_RELRO segment in the program headers. 194 Elf_Phdr* phdr = (Elf_Phdr*)(elf_header + __ehdr_start.e_phoff); 195 char* relro_start = NULL; 196 char* relro_end = NULL; 197 for (int i = 0; i < __ehdr_start.e_phnum; i++) { 198 if (phdr[i].p_type == PT_GNU_RELRO) { 199 relro_start = (char*)(elf_header + phdr[i].p_vaddr); 200 relro_end = (char*)(relro_start + phdr[i].p_memsz); 201 break; 202 } 203 } 204 205 if (relro_start != relro_end) { 206 do_relocations_with_relro(relhack, relhack_end, relro_start, relro_end); 207 } else { 208 do_relocations(relhack, relhack_end); 209 } 210 } 211 212 # ifdef ANDROID 213 // This creates a value that uses a relative relocation. 214 // In the case we've not set DT_RELRHACK_BIT on the RELR tags in the 215 // dynamic section, if the dynamic loader applied the relocation, 216 // the value will correctly point to the function address in memory. 217 __attribute__((visibility("hidden"))) void (*__relrhack_init)(void) = 218 _relrhack_init; 219 # endif 220 221 // The Android CRT doesn't contain an init function. 222 # ifndef ANDROID 223 extern __attribute__((visibility("hidden"))) void _init(int argc, char** argv, 224 char** env); 225 # endif 226 227 void _relrhack_wrap_init(int argc, char** argv, char** env) { 228 # ifdef ANDROID 229 // When the dynamic loader has applied the relocations itself, do nothing. 230 // (see comment above __relrhack_init) 231 if (__relrhack_init == _relrhack_init) { 232 return; 233 } 234 # endif 235 _relrhack_init(); 236 # ifndef ANDROID 237 _init(argc, argv, env); 238 # endif 239 } 240 #endif