// Finds section by the file name. Section* HIDDEN GetSectionByName(const char *moduleName) { Section *section = FindSectionByName(moduleName); if (section == NULL) { // Update sections info if (!ReloadProcessMemoryInfo()) return NULL; section = FindSectionByName(moduleName); } return section; }
int main(int argc, const char *argv[]) { ssize_t bytes_written; // only used to silence compiler warnings if (argc < 2) errx(EX_USAGE, "Usage: PatchEntry <binary>"); if (elf_version(EV_CURRENT) == EV_NONE) errx(EX_SOFTWARE, "Invalid ELF version: %s", elf_errmsg(-1)); int fd = open(argv[1], O_RDWR); if (fd == -1) err(EX_NOINPUT, "open() failed"); Elf *e; e = elf_begin(fd, ELF_C_RDWR, NULL); if (e == nullptr) errx(EX_SOFTWARE, "Failed to read from ELF file: %s", elf_errmsg(-1)); if (elf_kind(e) != ELF_K_ELF) errx(EX_SOFTWARE, "File is not an ELF object"); // Read the ELF header to find the entry point // FIXME: check the class/architecture, make sure it's the same as ours GElf_Ehdr elf_hdr; if (gelf_getehdr(e, &elf_hdr) == NULL) errx(EX_SOFTWARE, "Failed to read ELF header: %s", elf_errmsg(-1)); auto dt_init = FindDynamicInit(e); auto dt_init_ofs = 0; if (dt_init) { dt_init_ofs = dt_init->info.second.sh_offset + dt_init->data->d_off + dt_init->index * sizeof(Elf_Dyn) + offsetof(Elf_Dyn, d_un); } auto pit_sym = FindSymbol(e, kProgramInfoTableName); auto pit_info = FindSectionDataByAddr(e, pit_sym.st_value); auto pit_ofs = std::get<0>(pit_info).second.sh_offset + // Section file offset std::get<1>(pit_info)->d_off + // Elf_Data offset std::get<2>(pit_info); // Offset relative to Elf_Data #if 1 if (dt_init) printf("Old ELF entry:%p DT_INIT:%p@%x PIT:%p@%lx\n", (void*) elf_hdr.e_entry, (void*) dt_init->dyn_section.d_un.d_ptr, dt_init_ofs, (void*) pit_sym.st_value, pit_ofs); else printf("Old ELF entry:%p DT_INIT:NULL PIT:%p@%lx\n", (void*) elf_hdr.e_entry, (void*) pit_sym.st_value, pit_ofs); #endif // Replace the ProgramInfoTable values auto pit_data = std::get<1>(pit_info); auto orig_pit = reinterpret_cast<TrapProgramInfoTable*>( reinterpret_cast<uint8_t*>(pit_data->d_buf) + std::get<2>(pit_info)); TrapProgramInfoTable pit = *orig_pit; // Need to copy it here, since elf_end releases it if (pit.num_sections != 0) errx(EX_USAGE, "Binary already contains full ProgramInfoTable structure"); if (dt_init) pit.orig_dt_init = dt_init->dyn_section.d_un.d_ptr; else pit.orig_dt_init = 0; pit.orig_entry = elf_hdr.e_entry; // Find executable sections in the binary (.text/.plt/others) // then copy them over to the PIT pit.num_sections = TRAP_NUM_SECTIONS; for (size_t i = 0; i < TRAP_NUM_SECTIONS; i++) { auto sec_info = FindSectionByName(e, kExecSections[i][0]); if (sec_info.first != nullptr) { pit.sections[i].start = sec_info.second.sh_addr; pit.sections[i].size = sec_info.second.sh_size; } auto trap_sec_info = FindSectionByName(e, kExecSections[i][1]); if (trap_sec_info.first != nullptr) { pit.sections[i].trap = trap_sec_info.second.sh_addr; pit.sections[i].trap_size = trap_sec_info.second.sh_size; } } // Set the new entry point addresses auto entry_trampoline_sym = FindSymbol(e, kEntryTrampolineName); auto init_trampoline_sym = FindSymbol(e, kInitTrampolineName); elf_hdr.e_entry = entry_trampoline_sym.st_value; auto new_dt_init = static_cast<ArchPointer>(init_trampoline_sym.st_value); // Find export trampolines auto xptramp_info = FindSectionByName(e, ".xptramp"); DynamicSymbolMap dyn_sym_map; ExportTrampolineVector xptramp_vec; size_t xptramp_shndx = 0; if (xptramp_info.first != nullptr) { pit.xptramp_start = xptramp_info.second.sh_addr; pit.xptramp_size = xptramp_info.second.sh_size; dyn_sym_map = FindDynamicSymbols(e); xptramp_vec = FindExportTrampolines(e, xptramp_info); xptramp_shndx = elf_ndxscn(xptramp_info.first); } // Update the ELF header, then write it out // There's some pretty ugly behavior from libelf here: // if we let it handle the layout, it mis-aligns the data sections // It also doesn't seem to update the contents correctly, // so we do that manually elf_flagelf(e, ELF_C_SET, ELF_F_LAYOUT); gelf_update_ehdr(e, &elf_hdr); if (elf_update(e, ELF_C_WRITE) <= 0) errx(EX_SOFTWARE, "Couldn't update ELF file: %s", elf_errmsg(-1)); elf_end(e); // FIXME: libelf shenanigans force us to // write data out to the file manually // 1) DT_INIT inside .dynamic if (dt_init) { printf("writing new DT_INIT: 0x%lu\n", new_dt_init); lseek(fd, dt_init_ofs, SEEK_SET); bytes_written = write(fd, &new_dt_init, sizeof(new_dt_init)); } // 2) The ProgramInfoTable lseek(fd, pit_ofs, SEEK_SET); bytes_written = write(fd, &pit, sizeof(pit)); // 3) Exported symbols WriteExportSymbols(fd, xptramp_info, xptramp_vec, xptramp_shndx, dyn_sym_map); close(fd); return 0; }