build/unix/elfhack/elfhack.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #undef NDEBUG
michael@0 6 #include <assert.h>
michael@0 7 #include <cstring>
michael@0 8 #include <cstdlib>
michael@0 9 #include <cstdio>
michael@0 10 #include "elfxx.h"
michael@0 11
michael@0 12 #define ver "0"
michael@0 13 #define elfhack_data ".elfhack.data.v" ver
michael@0 14 #define elfhack_text ".elfhack.text.v" ver
michael@0 15
michael@0 16 #ifndef R_ARM_V4BX
michael@0 17 #define R_ARM_V4BX 0x28
michael@0 18 #endif
michael@0 19 #ifndef R_ARM_CALL
michael@0 20 #define R_ARM_CALL 0x1c
michael@0 21 #endif
michael@0 22 #ifndef R_ARM_JUMP24
michael@0 23 #define R_ARM_JUMP24 0x1d
michael@0 24 #endif
michael@0 25 #ifndef R_ARM_THM_JUMP24
michael@0 26 #define R_ARM_THM_JUMP24 0x1e
michael@0 27 #endif
michael@0 28
michael@0 29 char *rundir = nullptr;
michael@0 30
michael@0 31 template <typename T>
michael@0 32 struct wrapped {
michael@0 33 T value;
michael@0 34 };
michael@0 35
michael@0 36 class Elf_Addr_Traits {
michael@0 37 public:
michael@0 38 typedef wrapped<Elf32_Addr> Type32;
michael@0 39 typedef wrapped<Elf64_Addr> Type64;
michael@0 40
michael@0 41 template <class endian, typename R, typename T>
michael@0 42 static inline void swap(T &t, R &r) {
michael@0 43 r.value = endian::swap(t.value);
michael@0 44 }
michael@0 45 };
michael@0 46
michael@0 47 typedef serializable<Elf_Addr_Traits> Elf_Addr;
michael@0 48
michael@0 49 class Elf_RelHack_Traits {
michael@0 50 public:
michael@0 51 typedef Elf32_Rel Type32;
michael@0 52 typedef Elf32_Rel Type64;
michael@0 53
michael@0 54 template <class endian, typename R, typename T>
michael@0 55 static inline void swap(T &t, R &r) {
michael@0 56 r.r_offset = endian::swap(t.r_offset);
michael@0 57 r.r_info = endian::swap(t.r_info);
michael@0 58 }
michael@0 59 };
michael@0 60
michael@0 61 typedef serializable<Elf_RelHack_Traits> Elf_RelHack;
michael@0 62
michael@0 63 class ElfRelHack_Section: public ElfSection {
michael@0 64 public:
michael@0 65 ElfRelHack_Section(Elf_Shdr &s)
michael@0 66 : ElfSection(s, nullptr, nullptr)
michael@0 67 {
michael@0 68 name = elfhack_data;
michael@0 69 };
michael@0 70
michael@0 71 void serialize(std::ofstream &file, char ei_class, char ei_data)
michael@0 72 {
michael@0 73 for (std::vector<Elf_RelHack>::iterator i = rels.begin();
michael@0 74 i != rels.end(); ++i)
michael@0 75 (*i).serialize(file, ei_class, ei_data);
michael@0 76 }
michael@0 77
michael@0 78 bool isRelocatable() {
michael@0 79 return true;
michael@0 80 }
michael@0 81
michael@0 82 void push_back(Elf_RelHack &r) {
michael@0 83 rels.push_back(r);
michael@0 84 shdr.sh_size = rels.size() * shdr.sh_entsize;
michael@0 85 }
michael@0 86 private:
michael@0 87 std::vector<Elf_RelHack> rels;
michael@0 88 };
michael@0 89
michael@0 90 class ElfRelHackCode_Section: public ElfSection {
michael@0 91 public:
michael@0 92 ElfRelHackCode_Section(Elf_Shdr &s, Elf &e, unsigned int init)
michael@0 93 : ElfSection(s, nullptr, nullptr), parent(e), init(init) {
michael@0 94 std::string file(rundir);
michael@0 95 file += "/inject/";
michael@0 96 switch (parent.getMachine()) {
michael@0 97 case EM_386:
michael@0 98 file += "x86";
michael@0 99 break;
michael@0 100 case EM_X86_64:
michael@0 101 file += "x86_64";
michael@0 102 break;
michael@0 103 case EM_ARM:
michael@0 104 file += "arm";
michael@0 105 break;
michael@0 106 default:
michael@0 107 throw std::runtime_error("unsupported architecture");
michael@0 108 }
michael@0 109 file += ".o";
michael@0 110 std::ifstream inject(file.c_str(), std::ios::in|std::ios::binary);
michael@0 111 elf = new Elf(inject);
michael@0 112 if (elf->getType() != ET_REL)
michael@0 113 throw std::runtime_error("object for injected code is not ET_REL");
michael@0 114 if (elf->getMachine() != parent.getMachine())
michael@0 115 throw std::runtime_error("architecture of object for injected code doesn't match");
michael@0 116
michael@0 117 ElfSymtab_Section *symtab = nullptr;
michael@0 118
michael@0 119 // Find the symbol table.
michael@0 120 for (ElfSection *section = elf->getSection(1); section != nullptr;
michael@0 121 section = section->getNext()) {
michael@0 122 if (section->getType() == SHT_SYMTAB)
michael@0 123 symtab = (ElfSymtab_Section *) section;
michael@0 124 }
michael@0 125 if (symtab == nullptr)
michael@0 126 throw std::runtime_error("Couldn't find a symbol table for the injected code");
michael@0 127
michael@0 128 // Find the init symbol
michael@0 129 entry_point = -1;
michael@0 130 Elf_SymValue *sym = symtab->lookup(init ? "init" : "init_noinit");
michael@0 131 if (!sym)
michael@0 132 throw std::runtime_error("Couldn't find an 'init' symbol in the injected code");
michael@0 133
michael@0 134 entry_point = sym->value.getValue();
michael@0 135
michael@0 136 // Get all relevant sections from the injected code object.
michael@0 137 add_code_section(sym->value.getSection());
michael@0 138
michael@0 139 // Adjust code sections offsets according to their size
michael@0 140 std::vector<ElfSection *>::iterator c = code.begin();
michael@0 141 (*c)->getShdr().sh_addr = 0;
michael@0 142 for(ElfSection *last = *(c++); c != code.end(); c++) {
michael@0 143 unsigned int addr = last->getShdr().sh_addr + last->getSize();
michael@0 144 if (addr & ((*c)->getAddrAlign() - 1))
michael@0 145 addr = (addr | ((*c)->getAddrAlign() - 1)) + 1;
michael@0 146 (*c)->getShdr().sh_addr = addr;
michael@0 147 // We need to align this section depending on the greater
michael@0 148 // alignment required by code sections.
michael@0 149 if (shdr.sh_addralign < (*c)->getAddrAlign())
michael@0 150 shdr.sh_addralign = (*c)->getAddrAlign();
michael@0 151 }
michael@0 152 shdr.sh_size = code.back()->getAddr() + code.back()->getSize();
michael@0 153 data = new char[shdr.sh_size];
michael@0 154 char *buf = data;
michael@0 155 for (c = code.begin(); c != code.end(); c++) {
michael@0 156 memcpy(buf, (*c)->getData(), (*c)->getSize());
michael@0 157 buf += (*c)->getSize();
michael@0 158 }
michael@0 159 name = elfhack_text;
michael@0 160 }
michael@0 161
michael@0 162 ~ElfRelHackCode_Section() {
michael@0 163 delete elf;
michael@0 164 }
michael@0 165
michael@0 166 void serialize(std::ofstream &file, char ei_class, char ei_data)
michael@0 167 {
michael@0 168 // Readjust code offsets
michael@0 169 for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); c++)
michael@0 170 (*c)->getShdr().sh_addr += getAddr();
michael@0 171
michael@0 172 // Apply relocations
michael@0 173 for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); c++) {
michael@0 174 for (ElfSection *rel = elf->getSection(1); rel != nullptr; rel = rel->getNext())
michael@0 175 if (((rel->getType() == SHT_REL) ||
michael@0 176 (rel->getType() == SHT_RELA)) &&
michael@0 177 (rel->getInfo().section == *c)) {
michael@0 178 if (rel->getType() == SHT_REL)
michael@0 179 apply_relocations((ElfRel_Section<Elf_Rel> *)rel, *c);
michael@0 180 else
michael@0 181 apply_relocations((ElfRel_Section<Elf_Rela> *)rel, *c);
michael@0 182 }
michael@0 183 }
michael@0 184
michael@0 185 ElfSection::serialize(file, ei_class, ei_data);
michael@0 186 }
michael@0 187
michael@0 188 bool isRelocatable() {
michael@0 189 return true;
michael@0 190 }
michael@0 191
michael@0 192 unsigned int getEntryPoint() {
michael@0 193 return entry_point;
michael@0 194 }
michael@0 195 private:
michael@0 196 void add_code_section(ElfSection *section)
michael@0 197 {
michael@0 198 if (section) {
michael@0 199 /* Don't add section if it's already been added in the past */
michael@0 200 for (auto s = code.begin(); s != code.end(); ++s) {
michael@0 201 if (section == *s)
michael@0 202 return;
michael@0 203 }
michael@0 204 code.push_back(section);
michael@0 205 find_code(section);
michael@0 206 }
michael@0 207 }
michael@0 208
michael@0 209 /* Look at the relocations associated to the given section to find other
michael@0 210 * sections that it requires */
michael@0 211 void find_code(ElfSection *section)
michael@0 212 {
michael@0 213 for (ElfSection *s = elf->getSection(1); s != nullptr;
michael@0 214 s = s->getNext()) {
michael@0 215 if (((s->getType() == SHT_REL) ||
michael@0 216 (s->getType() == SHT_RELA)) &&
michael@0 217 (s->getInfo().section == section)) {
michael@0 218 if (s->getType() == SHT_REL)
michael@0 219 scan_relocs_for_code((ElfRel_Section<Elf_Rel> *)s);
michael@0 220 else
michael@0 221 scan_relocs_for_code((ElfRel_Section<Elf_Rela> *)s);
michael@0 222 }
michael@0 223 }
michael@0 224 }
michael@0 225
michael@0 226 template <typename Rel_Type>
michael@0 227 void scan_relocs_for_code(ElfRel_Section<Rel_Type> *rel)
michael@0 228 {
michael@0 229 ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink();
michael@0 230 for (auto r = rel->rels.begin(); r != rel->rels.end(); r++) {
michael@0 231 ElfSection *section = symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection();
michael@0 232 add_code_section(section);
michael@0 233 }
michael@0 234 }
michael@0 235
michael@0 236 class pc32_relocation {
michael@0 237 public:
michael@0 238 Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
michael@0 239 Elf32_Word addend, unsigned int addr)
michael@0 240 {
michael@0 241 return addr + addend - offset - base_addr;
michael@0 242 }
michael@0 243 };
michael@0 244
michael@0 245 class arm_plt32_relocation {
michael@0 246 public:
michael@0 247 Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
michael@0 248 Elf32_Word addend, unsigned int addr)
michael@0 249 {
michael@0 250 // We don't care about sign_extend because the only case where this is
michael@0 251 // going to be used only jumps forward.
michael@0 252 Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr) >> 2;
michael@0 253 tmp = (addend + tmp) & 0x00ffffff;
michael@0 254 return (addend & 0xff000000) | tmp;
michael@0 255 }
michael@0 256 };
michael@0 257
michael@0 258 class arm_thm_jump24_relocation {
michael@0 259 public:
michael@0 260 Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
michael@0 261 Elf32_Word addend, unsigned int addr)
michael@0 262 {
michael@0 263 /* Follows description of b.w and bl instructions as per
michael@0 264 ARM Architecture Reference Manual ARM® v7-A and ARM® v7-R edition, A8.6.16
michael@0 265 We limit ourselves to Encoding T4 of b.w and Encoding T1 of bl.
michael@0 266 We don't care about sign_extend because the only case where this is
michael@0 267 going to be used only jumps forward. */
michael@0 268 Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr);
michael@0 269 unsigned int word0 = addend & 0xffff,
michael@0 270 word1 = addend >> 16;
michael@0 271
michael@0 272 /* Encoding T4 of B.W is 10x1 ; Encoding T1 of BL is 11x1. */
michael@0 273 unsigned int type = (word1 & 0xd000) >> 12;
michael@0 274 if (((word0 & 0xf800) != 0xf000) || ((type & 0x9) != 0x9))
michael@0 275 throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for B.W <label> and BL <label>");
michael@0 276
michael@0 277 /* When the target address points to ARM code, switch a BL to a
michael@0 278 * BLX. This however can't be done with a B.W without adding a
michael@0 279 * trampoline, which is not supported as of now. */
michael@0 280 if ((addr & 0x1) == 0) {
michael@0 281 if (type == 0x9)
michael@0 282 throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for BL <label> when label points to ARM code");
michael@0 283 /* The address of the target is always relative to a 4-bytes
michael@0 284 * aligned address, so if the address of the BL instruction is
michael@0 285 * not 4-bytes aligned, adjust for it. */
michael@0 286 if ((base_addr + offset) & 0x2)
michael@0 287 tmp += 2;
michael@0 288 /* Encoding T2 of BLX is 11x0. */
michael@0 289 type = 0xc;
michael@0 290 }
michael@0 291
michael@0 292 unsigned int s = (word0 & (1 << 10)) >> 10;
michael@0 293 unsigned int j1 = (word1 & (1 << 13)) >> 13;
michael@0 294 unsigned int j2 = (word1 & (1 << 11)) >> 11;
michael@0 295 unsigned int i1 = j1 ^ s ? 0 : 1;
michael@0 296 unsigned int i2 = j2 ^ s ? 0 : 1;
michael@0 297
michael@0 298 tmp += ((s << 24) | (i1 << 23) | (i2 << 22) | ((word0 & 0x3ff) << 12) | ((word1 & 0x7ff) << 1));
michael@0 299
michael@0 300 s = (tmp & (1 << 24)) >> 24;
michael@0 301 j1 = ((tmp & (1 << 23)) >> 23) ^ !s;
michael@0 302 j2 = ((tmp & (1 << 22)) >> 22) ^ !s;
michael@0 303
michael@0 304 return 0xf000 | (s << 10) | ((tmp & (0x3ff << 12)) >> 12) |
michael@0 305 (type << 28) | (j1 << 29) | (j2 << 27) | ((tmp & 0xffe) << 15);
michael@0 306 }
michael@0 307 };
michael@0 308
michael@0 309 class gotoff_relocation {
michael@0 310 public:
michael@0 311 Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset,
michael@0 312 Elf32_Word addend, unsigned int addr)
michael@0 313 {
michael@0 314 return addr + addend;
michael@0 315 }
michael@0 316 };
michael@0 317
michael@0 318 template <class relocation_type>
michael@0 319 void apply_relocation(ElfSection *the_code, char *base, Elf_Rel *r, unsigned int addr)
michael@0 320 {
michael@0 321 relocation_type relocation;
michael@0 322 Elf32_Addr value;
michael@0 323 memcpy(&value, base + r->r_offset, 4);
michael@0 324 value = relocation(the_code->getAddr(), r->r_offset, value, addr);
michael@0 325 memcpy(base + r->r_offset, &value, 4);
michael@0 326 }
michael@0 327
michael@0 328 template <class relocation_type>
michael@0 329 void apply_relocation(ElfSection *the_code, char *base, Elf_Rela *r, unsigned int addr)
michael@0 330 {
michael@0 331 relocation_type relocation;
michael@0 332 Elf32_Addr value = relocation(the_code->getAddr(), r->r_offset, r->r_addend, addr);
michael@0 333 memcpy(base + r->r_offset, &value, 4);
michael@0 334 }
michael@0 335
michael@0 336 template <typename Rel_Type>
michael@0 337 void apply_relocations(ElfRel_Section<Rel_Type> *rel, ElfSection *the_code)
michael@0 338 {
michael@0 339 assert(rel->getType() == Rel_Type::sh_type);
michael@0 340 char *buf = data + (the_code->getAddr() - code.front()->getAddr());
michael@0 341 // TODO: various checks on the sections
michael@0 342 ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink();
michael@0 343 for (typename std::vector<Rel_Type>::iterator r = rel->rels.begin(); r != rel->rels.end(); r++) {
michael@0 344 // TODO: various checks on the symbol
michael@0 345 const char *name = symtab->syms[ELF32_R_SYM(r->r_info)].name;
michael@0 346 unsigned int addr;
michael@0 347 if (symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection() == nullptr) {
michael@0 348 if (strcmp(name, "relhack") == 0) {
michael@0 349 addr = getNext()->getAddr();
michael@0 350 } else if (strcmp(name, "elf_header") == 0) {
michael@0 351 // TODO: change this ungly hack to something better
michael@0 352 ElfSection *ehdr = parent.getSection(1)->getPrevious()->getPrevious();
michael@0 353 addr = ehdr->getAddr();
michael@0 354 } else if (strcmp(name, "original_init") == 0) {
michael@0 355 addr = init;
michael@0 356 } else if (strcmp(name, "_GLOBAL_OFFSET_TABLE_") == 0) {
michael@0 357 // We actually don't need a GOT, but need it as a reference for
michael@0 358 // GOTOFF relocations. We'll just use the start of the ELF file
michael@0 359 addr = 0;
michael@0 360 } else if (strcmp(name, "") == 0) {
michael@0 361 // This is for R_ARM_V4BX, until we find something better
michael@0 362 addr = -1;
michael@0 363 } else {
michael@0 364 throw std::runtime_error("Unsupported symbol in relocation");
michael@0 365 }
michael@0 366 } else {
michael@0 367 ElfSection *section = symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection();
michael@0 368 assert((section->getType() == SHT_PROGBITS) && (section->getFlags() & SHF_EXECINSTR));
michael@0 369 addr = symtab->syms[ELF32_R_SYM(r->r_info)].value.getValue();
michael@0 370 }
michael@0 371 // Do the relocation
michael@0 372 #define REL(machine, type) (EM_ ## machine | (R_ ## machine ## _ ## type << 8))
michael@0 373 switch (elf->getMachine() | (ELF32_R_TYPE(r->r_info) << 8)) {
michael@0 374 case REL(X86_64, PC32):
michael@0 375 case REL(386, PC32):
michael@0 376 case REL(386, GOTPC):
michael@0 377 case REL(ARM, GOTPC):
michael@0 378 case REL(ARM, REL32):
michael@0 379 apply_relocation<pc32_relocation>(the_code, buf, &*r, addr);
michael@0 380 break;
michael@0 381 case REL(ARM, CALL):
michael@0 382 case REL(ARM, JUMP24):
michael@0 383 case REL(ARM, PLT32):
michael@0 384 apply_relocation<arm_plt32_relocation>(the_code, buf, &*r, addr);
michael@0 385 break;
michael@0 386 case REL(ARM, THM_PC22 /* THM_CALL */):
michael@0 387 case REL(ARM, THM_JUMP24):
michael@0 388 apply_relocation<arm_thm_jump24_relocation>(the_code, buf, &*r, addr);
michael@0 389 break;
michael@0 390 case REL(386, GOTOFF):
michael@0 391 case REL(ARM, GOTOFF):
michael@0 392 apply_relocation<gotoff_relocation>(the_code, buf, &*r, addr);
michael@0 393 break;
michael@0 394 case REL(ARM, V4BX):
michael@0 395 // Ignore R_ARM_V4BX relocations
michael@0 396 break;
michael@0 397 default:
michael@0 398 throw std::runtime_error("Unsupported relocation type");
michael@0 399 }
michael@0 400 }
michael@0 401 }
michael@0 402
michael@0 403 Elf *elf, &parent;
michael@0 404 std::vector<ElfSection *> code;
michael@0 405 unsigned int init;
michael@0 406 int entry_point;
michael@0 407 };
michael@0 408
michael@0 409 unsigned int get_addend(Elf_Rel *rel, Elf *elf) {
michael@0 410 ElfLocation loc(rel->r_offset, elf);
michael@0 411 Elf_Addr addr(loc.getBuffer(), Elf_Addr::size(elf->getClass()), elf->getClass(), elf->getData());
michael@0 412 return addr.value;
michael@0 413 }
michael@0 414
michael@0 415 unsigned int get_addend(Elf_Rela *rel, Elf *elf) {
michael@0 416 return rel->r_addend;
michael@0 417 }
michael@0 418
michael@0 419 void set_relative_reloc(Elf_Rel *rel, Elf *elf, unsigned int value) {
michael@0 420 ElfLocation loc(rel->r_offset, elf);
michael@0 421 Elf_Addr addr;
michael@0 422 addr.value = value;
michael@0 423 addr.serialize(const_cast<char *>(loc.getBuffer()), Elf_Addr::size(elf->getClass()), elf->getClass(), elf->getData());
michael@0 424 }
michael@0 425
michael@0 426 void set_relative_reloc(Elf_Rela *rel, Elf *elf, unsigned int value) {
michael@0 427 // ld puts the value of relocated relocations both in the addend and
michael@0 428 // at r_offset. For consistency, keep it that way.
michael@0 429 set_relative_reloc((Elf_Rel *)rel, elf, value);
michael@0 430 rel->r_addend = value;
michael@0 431 }
michael@0 432
michael@0 433 void maybe_split_segment(Elf *elf, ElfSegment *segment, bool fill)
michael@0 434 {
michael@0 435 std::list<ElfSection *>::iterator it = segment->begin();
michael@0 436 for (ElfSection *last = *(it++); it != segment->end(); last = *(it++)) {
michael@0 437 // When two consecutive non-SHT_NOBITS sections are apart by more
michael@0 438 // than the alignment of the section, the second can be moved closer
michael@0 439 // to the first, but this requires the segment to be split.
michael@0 440 if (((*it)->getType() != SHT_NOBITS) && (last->getType() != SHT_NOBITS) &&
michael@0 441 ((*it)->getOffset() - last->getOffset() - last->getSize() > segment->getAlign())) {
michael@0 442 // Probably very wrong.
michael@0 443 Elf_Phdr phdr;
michael@0 444 phdr.p_type = PT_LOAD;
michael@0 445 phdr.p_vaddr = 0;
michael@0 446 phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff();
michael@0 447 phdr.p_flags = segment->getFlags();
michael@0 448 phdr.p_align = segment->getAlign();
michael@0 449 phdr.p_filesz = (unsigned int)-1;
michael@0 450 phdr.p_memsz = (unsigned int)-1;
michael@0 451 ElfSegment *newSegment = new ElfSegment(&phdr);
michael@0 452 elf->insertSegmentAfter(segment, newSegment);
michael@0 453 ElfSection *section = *it;
michael@0 454 for (; it != segment->end(); ++it) {
michael@0 455 newSegment->addSection(*it);
michael@0 456 }
michael@0 457 for (it = newSegment->begin(); it != newSegment->end(); it++) {
michael@0 458 segment->removeSection(*it);
michael@0 459 }
michael@0 460 // Fill the virtual address space gap left between the two PT_LOADs
michael@0 461 // with a new PT_LOAD with no permissions. This avoids the linker
michael@0 462 // (especially bionic's) filling the gap with anonymous memory,
michael@0 463 // which breakpad doesn't like.
michael@0 464 // /!\ running strip on a elfhacked binary will break this filler
michael@0 465 // PT_LOAD.
michael@0 466 if (!fill)
michael@0 467 break;
michael@0 468 // Insert dummy segment to normalize the entire Elf with the header
michael@0 469 // sizes adjusted, before inserting a filler segment.
michael@0 470 {
michael@0 471 memset(&phdr, 0, sizeof(phdr));
michael@0 472 ElfSegment dummySegment(&phdr);
michael@0 473 elf->insertSegmentAfter(segment, &dummySegment);
michael@0 474 elf->normalize();
michael@0 475 elf->removeSegment(&dummySegment);
michael@0 476 }
michael@0 477 ElfSection *previous = section->getPrevious();
michael@0 478 phdr.p_type = PT_LOAD;
michael@0 479 phdr.p_vaddr = (previous->getAddr() + previous->getSize() + segment->getAlign() - 1) & ~(segment->getAlign() - 1);
michael@0 480 phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff();
michael@0 481 phdr.p_flags = 0;
michael@0 482 phdr.p_align = 0;
michael@0 483 phdr.p_filesz = (section->getAddr() & ~(newSegment->getAlign() - 1)) - phdr.p_vaddr;
michael@0 484 phdr.p_memsz = phdr.p_filesz;
michael@0 485 if (phdr.p_filesz) {
michael@0 486 newSegment = new ElfSegment(&phdr);
michael@0 487 assert(newSegment->isElfHackFillerSegment());
michael@0 488 elf->insertSegmentAfter(segment, newSegment);
michael@0 489 } else {
michael@0 490 elf->normalize();
michael@0 491 }
michael@0 492 break;
michael@0 493 }
michael@0 494 }
michael@0 495 }
michael@0 496
michael@0 497 template <typename Rel_Type>
michael@0 498 int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type2, bool force, bool fill)
michael@0 499 {
michael@0 500 ElfDynamic_Section *dyn = elf->getDynSection();
michael@0 501 if (dyn == nullptr) {
michael@0 502 fprintf(stderr, "Couldn't find SHT_DYNAMIC section\n");
michael@0 503 return -1;
michael@0 504 }
michael@0 505
michael@0 506 ElfSegment *relro = elf->getSegmentByType(PT_GNU_RELRO);
michael@0 507
michael@0 508 ElfRel_Section<Rel_Type> *section = (ElfRel_Section<Rel_Type> *)dyn->getSectionForType(Rel_Type::d_tag);
michael@0 509 assert(section->getType() == Rel_Type::sh_type);
michael@0 510
michael@0 511 Elf32_Shdr relhack32_section =
michael@0 512 { 0, SHT_PROGBITS, SHF_ALLOC, 0, (Elf32_Off)-1, 0, SHN_UNDEF, 0,
michael@0 513 Elf_RelHack::size(elf->getClass()), Elf_RelHack::size(elf->getClass()) }; // TODO: sh_addralign should be an alignment, not size
michael@0 514 Elf32_Shdr relhackcode32_section =
michael@0 515 { 0, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, 0, (Elf32_Off)-1, 0,
michael@0 516 SHN_UNDEF, 0, 1, 0 };
michael@0 517
michael@0 518 unsigned int entry_sz = Elf_Addr::size(elf->getClass());
michael@0 519
michael@0 520 // The injected code needs to be executed before any init code in the
michael@0 521 // binary. There are three possible cases:
michael@0 522 // - The binary has no init code at all. In this case, we will add a
michael@0 523 // DT_INIT entry pointing to the injected code.
michael@0 524 // - The binary has a DT_INIT entry. In this case, we will interpose:
michael@0 525 // we change DT_INIT to point to the injected code, and have the
michael@0 526 // injected code call the original DT_INIT entry point.
michael@0 527 // - The binary has no DT_INIT entry, but has a DT_INIT_ARRAY. In this
michael@0 528 // case, we interpose as well, by replacing the first entry in the
michael@0 529 // array to point to the injected code, and have the injected code
michael@0 530 // call the original first entry.
michael@0 531 // The binary may have .ctors instead of DT_INIT_ARRAY, for its init
michael@0 532 // functions, but this falls into the second case above, since .ctors
michael@0 533 // are actually run by DT_INIT code.
michael@0 534 ElfValue *value = dyn->getValueForType(DT_INIT);
michael@0 535 unsigned int original_init = value ? value->getValue() : 0;
michael@0 536 ElfSection *init_array = nullptr;
michael@0 537 if (!value || !value->getValue()) {
michael@0 538 value = dyn->getValueForType(DT_INIT_ARRAYSZ);
michael@0 539 if (value && value->getValue() >= entry_sz)
michael@0 540 init_array = dyn->getSectionForType(DT_INIT_ARRAY);
michael@0 541 }
michael@0 542
michael@0 543 Elf_Shdr relhack_section(relhack32_section);
michael@0 544 Elf_Shdr relhackcode_section(relhackcode32_section);
michael@0 545 ElfRelHack_Section *relhack = new ElfRelHack_Section(relhack_section);
michael@0 546
michael@0 547 ElfSymtab_Section *symtab = (ElfSymtab_Section *) section->getLink();
michael@0 548 Elf_SymValue *sym = symtab->lookup("__cxa_pure_virtual");
michael@0 549
michael@0 550 std::vector<Rel_Type> new_rels;
michael@0 551 Elf_RelHack relhack_entry;
michael@0 552 relhack_entry.r_offset = relhack_entry.r_info = 0;
michael@0 553 size_t init_array_reloc = 0;
michael@0 554 for (typename std::vector<Rel_Type>::iterator i = section->rels.begin();
michael@0 555 i != section->rels.end(); i++) {
michael@0 556 // We don't need to keep R_*_NONE relocations
michael@0 557 if (!ELF32_R_TYPE(i->r_info))
michael@0 558 continue;
michael@0 559 ElfLocation loc(i->r_offset, elf);
michael@0 560 // __cxa_pure_virtual is a function used in vtables to point at pure
michael@0 561 // virtual methods. The __cxa_pure_virtual function usually abort()s.
michael@0 562 // These functions are however normally never called. In the case
michael@0 563 // where they would, jumping to the null address instead of calling
michael@0 564 // __cxa_pure_virtual is going to work just as well. So we can remove
michael@0 565 // relocations for the __cxa_pure_virtual symbol and null out the
michael@0 566 // content at the offset pointed by the relocation.
michael@0 567 if (sym) {
michael@0 568 if (sym->defined) {
michael@0 569 // If we are statically linked to libstdc++, the
michael@0 570 // __cxa_pure_virtual symbol is defined in our lib, and we
michael@0 571 // have relative relocations (rel_type) for it.
michael@0 572 if (ELF32_R_TYPE(i->r_info) == rel_type) {
michael@0 573 Elf_Addr addr(loc.getBuffer(), entry_sz, elf->getClass(), elf->getData());
michael@0 574 if (addr.value == sym->value.getValue()) {
michael@0 575 memset((char *)loc.getBuffer(), 0, entry_sz);
michael@0 576 continue;
michael@0 577 }
michael@0 578 }
michael@0 579 } else {
michael@0 580 // If we are dynamically linked to libstdc++, the
michael@0 581 // __cxa_pure_virtual symbol is undefined in our lib, and we
michael@0 582 // have absolute relocations (rel_type2) for it.
michael@0 583 if ((ELF32_R_TYPE(i->r_info) == rel_type2) &&
michael@0 584 (sym == &symtab->syms[ELF32_R_SYM(i->r_info)])) {
michael@0 585 memset((char *)loc.getBuffer(), 0, entry_sz);
michael@0 586 continue;
michael@0 587 }
michael@0 588 }
michael@0 589 }
michael@0 590 // Keep track of the relocation associated with the first init_array entry.
michael@0 591 if (init_array && i->r_offset == init_array->getAddr()) {
michael@0 592 if (init_array_reloc) {
michael@0 593 fprintf(stderr, "Found multiple relocations for the first init_array entry. Skipping\n");
michael@0 594 return -1;
michael@0 595 }
michael@0 596 new_rels.push_back(*i);
michael@0 597 init_array_reloc = new_rels.size();
michael@0 598 } else if (!(loc.getSection()->getFlags() & SHF_WRITE) || (ELF32_R_TYPE(i->r_info) != rel_type) ||
michael@0 599 (relro && (i->r_offset >= relro->getAddr()) &&
michael@0 600 (i->r_offset < relro->getAddr() + relro->getMemSize()))) {
michael@0 601 // Don't pack relocations happening in non writable sections.
michael@0 602 // Our injected code is likely not to be allowed to write there.
michael@0 603 new_rels.push_back(*i);
michael@0 604 } else {
michael@0 605 // TODO: check that i->r_addend == *i->r_offset
michael@0 606 if (i->r_offset == relhack_entry.r_offset + relhack_entry.r_info * entry_sz) {
michael@0 607 relhack_entry.r_info++;
michael@0 608 } else {
michael@0 609 if (relhack_entry.r_offset)
michael@0 610 relhack->push_back(relhack_entry);
michael@0 611 relhack_entry.r_offset = i->r_offset;
michael@0 612 relhack_entry.r_info = 1;
michael@0 613 }
michael@0 614 }
michael@0 615 }
michael@0 616 if (relhack_entry.r_offset)
michael@0 617 relhack->push_back(relhack_entry);
michael@0 618 // Last entry must be nullptr
michael@0 619 relhack_entry.r_offset = relhack_entry.r_info = 0;
michael@0 620 relhack->push_back(relhack_entry);
michael@0 621
michael@0 622 unsigned int old_end = section->getOffset() + section->getSize();
michael@0 623
michael@0 624 if (init_array) {
michael@0 625 if (! init_array_reloc) {
michael@0 626 fprintf(stderr, "Didn't find relocation for DT_INIT_ARRAY's first entry. Skipping\n");
michael@0 627 return -1;
michael@0 628 }
michael@0 629 Rel_Type *rel = &new_rels[init_array_reloc - 1];
michael@0 630 unsigned int addend = get_addend(rel, elf);
michael@0 631 // Use relocated value of DT_INIT_ARRAY's first entry for the
michael@0 632 // function to be called by the injected code.
michael@0 633 if (ELF32_R_TYPE(rel->r_info) == rel_type) {
michael@0 634 original_init = addend;
michael@0 635 } else if (ELF32_R_TYPE(rel->r_info) == rel_type2) {
michael@0 636 ElfSymtab_Section *symtab = (ElfSymtab_Section *)section->getLink();
michael@0 637 original_init = symtab->syms[ELF32_R_SYM(rel->r_info)].value.getValue() + addend;
michael@0 638 } else {
michael@0 639 fprintf(stderr, "Unsupported relocation type for DT_INIT_ARRAY's first entry. Skipping\n");
michael@0 640 return -1;
michael@0 641 }
michael@0 642 }
michael@0 643
michael@0 644 section->rels.assign(new_rels.begin(), new_rels.end());
michael@0 645 section->shrink(new_rels.size() * section->getEntSize());
michael@0 646
michael@0 647 ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf, original_init);
michael@0 648 relhackcode->insertBefore(section);
michael@0 649 relhack->insertAfter(relhackcode);
michael@0 650 if (section->getOffset() + section->getSize() >= old_end) {
michael@0 651 fprintf(stderr, "No gain. Skipping\n");
michael@0 652 return -1;
michael@0 653 }
michael@0 654
michael@0 655 // Adjust PT_LOAD segments
michael@0 656 for (ElfSegment *segment = elf->getSegmentByType(PT_LOAD); segment;
michael@0 657 segment = elf->getSegmentByType(PT_LOAD, segment)) {
michael@0 658 maybe_split_segment(elf, segment, fill);
michael@0 659 }
michael@0 660
michael@0 661 // Ensure Elf sections will be at their final location.
michael@0 662 elf->normalize();
michael@0 663 ElfLocation *init = new ElfLocation(relhackcode, relhackcode->getEntryPoint());
michael@0 664 if (init_array) {
michael@0 665 // Adjust the first DT_INIT_ARRAY entry to point at the injected code
michael@0 666 // by transforming its relocation into a relative one pointing to the
michael@0 667 // address of the injected code.
michael@0 668 Rel_Type *rel = &section->rels[init_array_reloc - 1];
michael@0 669 rel->r_info = ELF32_R_INFO(0, rel_type); // Set as a relative relocation
michael@0 670 set_relative_reloc(&section->rels[init_array_reloc - 1], elf, init->getValue());
michael@0 671 } else if (!dyn->setValueForType(DT_INIT, init)) {
michael@0 672 fprintf(stderr, "Can't grow .dynamic section to set DT_INIT. Skipping\n");
michael@0 673 return -1;
michael@0 674 }
michael@0 675 // TODO: adjust the value according to the remaining number of relative relocations
michael@0 676 if (dyn->getValueForType(Rel_Type::d_tag_count))
michael@0 677 dyn->setValueForType(Rel_Type::d_tag_count, new ElfPlainValue(0));
michael@0 678
michael@0 679 return 0;
michael@0 680 }
michael@0 681
michael@0 682 static inline int backup_file(const char *name)
michael@0 683 {
michael@0 684 std::string fname(name);
michael@0 685 fname += ".bak";
michael@0 686 return rename(name, fname.c_str());
michael@0 687 }
michael@0 688
michael@0 689 void do_file(const char *name, bool backup = false, bool force = false, bool fill = false)
michael@0 690 {
michael@0 691 std::ifstream file(name, std::ios::in|std::ios::binary);
michael@0 692 Elf elf(file);
michael@0 693 unsigned int size = elf.getSize();
michael@0 694 fprintf(stderr, "%s: ", name);
michael@0 695 if (elf.getType() != ET_DYN) {
michael@0 696 fprintf(stderr, "Not a shared object. Skipping\n");
michael@0 697 return;
michael@0 698 }
michael@0 699
michael@0 700 for (ElfSection *section = elf.getSection(1); section != nullptr;
michael@0 701 section = section->getNext()) {
michael@0 702 if (section->getName() &&
michael@0 703 (strncmp(section->getName(), ".elfhack.", 9) == 0)) {
michael@0 704 fprintf(stderr, "Already elfhacked. Skipping\n");
michael@0 705 return;
michael@0 706 }
michael@0 707 }
michael@0 708
michael@0 709 int exit = -1;
michael@0 710 switch (elf.getMachine()) {
michael@0 711 case EM_386:
michael@0 712 exit = do_relocation_section<Elf_Rel>(&elf, R_386_RELATIVE, R_386_32, force, fill);
michael@0 713 break;
michael@0 714 case EM_X86_64:
michael@0 715 exit = do_relocation_section<Elf_Rela>(&elf, R_X86_64_RELATIVE, R_X86_64_64, force, fill);
michael@0 716 break;
michael@0 717 case EM_ARM:
michael@0 718 exit = do_relocation_section<Elf_Rel>(&elf, R_ARM_RELATIVE, R_ARM_ABS32, force, fill);
michael@0 719 break;
michael@0 720 }
michael@0 721 if (exit == 0) {
michael@0 722 if (!force && (elf.getSize() >= size)) {
michael@0 723 fprintf(stderr, "No gain. Skipping\n");
michael@0 724 } else if (backup && backup_file(name) != 0) {
michael@0 725 fprintf(stderr, "Couln't create backup file\n");
michael@0 726 } else {
michael@0 727 std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc);
michael@0 728 elf.write(ofile);
michael@0 729 fprintf(stderr, "Reduced by %d bytes\n", size - elf.getSize());
michael@0 730 }
michael@0 731 }
michael@0 732 }
michael@0 733
michael@0 734 void undo_file(const char *name, bool backup = false)
michael@0 735 {
michael@0 736 std::ifstream file(name, std::ios::in|std::ios::binary);
michael@0 737 Elf elf(file);
michael@0 738 unsigned int size = elf.getSize();
michael@0 739 fprintf(stderr, "%s: ", name);
michael@0 740 if (elf.getType() != ET_DYN) {
michael@0 741 fprintf(stderr, "Not a shared object. Skipping\n");
michael@0 742 return;
michael@0 743 }
michael@0 744
michael@0 745 ElfSection *data = nullptr, *text = nullptr;
michael@0 746 for (ElfSection *section = elf.getSection(1); section != nullptr;
michael@0 747 section = section->getNext()) {
michael@0 748 if (section->getName() &&
michael@0 749 (strcmp(section->getName(), elfhack_data) == 0))
michael@0 750 data = section;
michael@0 751 if (section->getName() &&
michael@0 752 (strcmp(section->getName(), elfhack_text) == 0))
michael@0 753 text = section;
michael@0 754 }
michael@0 755
michael@0 756 if (!data || !text) {
michael@0 757 fprintf(stderr, "Not elfhacked. Skipping\n");
michael@0 758 return;
michael@0 759 }
michael@0 760 if (data != text->getNext()) {
michael@0 761 fprintf(stderr, elfhack_data " section not following " elfhack_text ". Skipping\n");
michael@0 762 return;
michael@0 763 }
michael@0 764
michael@0 765 ElfSegment *first = elf.getSegmentByType(PT_LOAD);
michael@0 766 ElfSegment *second = elf.getSegmentByType(PT_LOAD, first);
michael@0 767 ElfSegment *filler = nullptr;
michael@0 768 // If the second PT_LOAD is a filler from elfhack --fill, check the third.
michael@0 769 if (second->isElfHackFillerSegment()) {
michael@0 770 filler = second;
michael@0 771 second = elf.getSegmentByType(PT_LOAD, filler);
michael@0 772 }
michael@0 773 if (second->getFlags() != first->getFlags()) {
michael@0 774 fprintf(stderr, "Couldn't identify elfhacked PT_LOAD segments. Skipping\n");
michael@0 775 return;
michael@0 776 }
michael@0 777 // Move sections from the second PT_LOAD to the first, and remove the
michael@0 778 // second PT_LOAD segment.
michael@0 779 for (std::list<ElfSection *>::iterator section = second->begin();
michael@0 780 section != second->end(); ++section)
michael@0 781 first->addSection(*section);
michael@0 782
michael@0 783 elf.removeSegment(second);
michael@0 784 if (filler)
michael@0 785 elf.removeSegment(filler);
michael@0 786
michael@0 787 if (backup && backup_file(name) != 0) {
michael@0 788 fprintf(stderr, "Couln't create backup file\n");
michael@0 789 } else {
michael@0 790 std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc);
michael@0 791 elf.write(ofile);
michael@0 792 fprintf(stderr, "Grown by %d bytes\n", elf.getSize() - size);
michael@0 793 }
michael@0 794 }
michael@0 795
michael@0 796 int main(int argc, char *argv[])
michael@0 797 {
michael@0 798 int arg;
michael@0 799 bool backup = false;
michael@0 800 bool force = false;
michael@0 801 bool revert = false;
michael@0 802 bool fill = false;
michael@0 803 char *lastSlash = rindex(argv[0], '/');
michael@0 804 if (lastSlash != nullptr)
michael@0 805 rundir = strndup(argv[0], lastSlash - argv[0]);
michael@0 806 for (arg = 1; arg < argc; arg++) {
michael@0 807 if (strcmp(argv[arg], "-f") == 0)
michael@0 808 force = true;
michael@0 809 else if (strcmp(argv[arg], "-b") == 0)
michael@0 810 backup = true;
michael@0 811 else if (strcmp(argv[arg], "-r") == 0)
michael@0 812 revert = true;
michael@0 813 else if (strcmp(argv[arg], "--fill") == 0)
michael@0 814 fill = true;
michael@0 815 else if (revert) {
michael@0 816 undo_file(argv[arg], backup);
michael@0 817 } else
michael@0 818 do_file(argv[arg], backup, force, fill);
michael@0 819 }
michael@0 820
michael@0 821 free(rundir);
michael@0 822 return 0;
michael@0 823 }

mercurial