static const pid_t _gen_child() { uint8_t buf[PAGE_SIZE]; mem_assign(buf, PAGE_SIZE, TRAP, TRAP_SZ); uint8_t *elf; const size_t elf_sz = gen_elf(&elf, options.start, (uint8_t *)buf, PAGE_SIZE); const int exe_fd = write_exe(elf, elf_sz, options.savefile); free(elf); const pid_t tracee = fork(); if (tracee < 0) { perror("fork"); exit(EXIT_FAILURE); } else if (tracee == 0) { ptrace_child(exe_fd); abort(); } // Parent close(exe_fd); return tracee; }
const size_t gen_elf_x86( uint8_t **out, const unsigned long start, const uint8_t *const code, const size_t code_sz) { /* We give the elf header and phdr an entire page, because the elf loader can * only map the file at PAGE_SIZE offsets. So our file will look like this * for an invocation with some code and 2 data segments. * * +----------+ * | 1st page | * | ehdr | * | phdr | * | shdr | * | shdr | * |----------| * | 2nd page | * | code | * |----------| * | 3rd page | * | data 1 | * |----------| * | 4th page | * | data 2 | * +----------+ * * TODO add data section, section headers */ const size_t pg_align_dist = start - (start & ~0xffff); const size_t pad_sz = PAGE_SIZE - (PAGE_SIZE % code_sz); const size_t sz = PAGE_SIZE + pg_align_dist + code_sz + pad_sz; uint8_t *const e = xmalloc(sz); mem_assign(e, sz, TRAP, TRAP_SZ); Elf32_Ehdr *const ehdr = (Elf32_Ehdr *) e; ehdr->e_ident[0] = ELFMAG0; ehdr->e_ident[1] = ELFMAG1; ehdr->e_ident[2] = ELFMAG2; ehdr->e_ident[3] = ELFMAG3; ehdr->e_ident[4] = ELFCLASS32; ehdr->e_ident[5] = ELFDATA2LSB; ehdr->e_ident[6] = EV_CURRENT; ehdr->e_ident[7] = ELFOSABI_NONE; ehdr->e_ident[9] = 0; // Padding ehdr->e_type = ET_EXEC; ehdr->e_machine = EM_386; ehdr->e_version = EV_CURRENT; ehdr->e_entry = start; ehdr->e_phoff = sizeof(Elf32_Ehdr); ehdr->e_shoff = 0; // XXX ehdr->e_flags = 0; ehdr->e_ehsize = sizeof(Elf32_Ehdr); ehdr->e_phentsize = sizeof(Elf32_Phdr); ehdr->e_phnum = 1; ehdr->e_shentsize = 0; ehdr->e_shnum = 0; ehdr->e_shstrndx = 0; Elf32_Phdr *const phdr = (Elf32_Phdr *) ((uint8_t *) e + sizeof(Elf32_Ehdr)); phdr->p_type = PT_LOAD; phdr->p_flags = PF_X | PF_R; phdr->p_offset = PAGE_SIZE; phdr->p_vaddr = start - pg_align_dist; phdr->p_paddr = 0; phdr->p_filesz = code_sz + pg_align_dist; phdr->p_memsz = code_sz + pg_align_dist; phdr->p_align = 0x4; uint8_t *const data = (uint8_t *) e + PAGE_SIZE + pg_align_dist; memcpy(data, code, code_sz); *out = e; return sz; }
const size_t assemble_arm( uint8_t *const bytecode, const size_t bytecode_sz, const char *const assembly, const size_t asm_sz) { size_t sz = 0; char path[PATH_MAX]; snprintf(path, sizeof(path), "/tmp/rappel-output.XXXXXX"); const int t = mkstemp(path); if (t == -1) { perror("mkstemp"); exit(EXIT_FAILURE); } REQUIRE (unlink(path) == 0); int fildes[2]; REQUIRE (pipe(fildes) == 0); const pid_t asm_pid = fork(); if (asm_pid < 0) { perror("fork"); exit(EXIT_FAILURE); } else if (asm_pid == 0) { _child_assemble(fildes, t); // Not reached abort(); } verbose_printf("as is pid %d\n", asm_pid); REQUIRE (close(fildes[0]) == 0); write_data(fildes[1], (uint8_t *)assembly, asm_sz); REQUIRE (close(fildes[1]) == 0); int asm_status; REQUIRE (waitpid(asm_pid, &asm_status, 0) != -1); if (WIFSIGNALED(asm_status)) { fprintf(stderr, "as exited with signal %d.\n", WTERMSIG(asm_status)); } else if (WIFEXITED(asm_status) && WEXITSTATUS(asm_status)) { fprintf(stderr, "as exited %d.\n", WEXITSTATUS(asm_status)); goto exit; } REQUIRE (lseek(t, SEEK_SET, 0) != -1); int results[2]; REQUIRE (pipe(results) == 0); const pid_t objcopy_pid = fork(); if (objcopy_pid < 0) { perror("fork"); exit(EXIT_FAILURE); } else if (objcopy_pid == 0) { _child_objcopy(results, t); // Not reached abort(); } verbose_printf("objcopy is pid %d\n", objcopy_pid); REQUIRE (close(results[1]) == 0); mem_assign(bytecode, bytecode_sz, TRAP, TRAP_SZ); sz = read_data(results[0], bytecode, bytecode_sz); if (sz >= bytecode_sz) { fprintf(stderr, "Too much bytecode to handle, exiting...\n"); exit(EXIT_FAILURE); } int objcopy_status; REQUIRE (waitpid(objcopy_pid, &objcopy_status, 0) != -1); if (WIFEXITED(objcopy_status) && WIFSIGNALED(objcopy_status)) fprintf(stderr, "objcopy exited with signal %d.\n", WTERMSIG(objcopy_status)); else if (WIFEXITED(objcopy_status) && WEXITSTATUS(objcopy_status)) fprintf(stderr, "objcopy exited %d.\n", WEXITSTATUS(objcopy_status)); exit: REQUIRE (close(t) == 0); return sz; }
const size_t assemble( uint8_t *const bytecode, const size_t bytecode_sz, const char *const assembly, const size_t asm_sz) { char path[PATH_MAX]; snprintf(path, sizeof(path), "/tmp/rappel-input.XXXXXX"); const int t = mkstemp(path); if (t == -1) { perror("mkstemp"); exit(EXIT_FAILURE); } REQUIRE (unlink(path) == 0); dprintf(t, BITSTR); dprintf(t, "section .text vstart=%ld\n", options.start); write_data(t, (uint8_t *)assembly, asm_sz); int fildes[2]; REQUIRE (pipe(fildes) == 0); const pid_t asm_pid = fork(); if (asm_pid < 0) { perror("fork"); exit(EXIT_FAILURE); } else if (asm_pid == 0) { _child(fildes, t); // Not reached abort(); } verbose_printf("nasm is pid %d\n", asm_pid); REQUIRE (close(fildes[1]) == 0); mem_assign(bytecode, bytecode_sz, TRAP, TRAP_SZ); size_t sz = read_data(fildes[0], bytecode, bytecode_sz); if (sz >= bytecode_sz) { fprintf(stderr, "Too much bytecode to handle, exiting...\n"); exit(EXIT_FAILURE); } int status; REQUIRE (waitpid(asm_pid, &status, 0) != -1); if (WIFSIGNALED(status)) fprintf(stderr, "nasm exited with signal %d.\n", WTERMSIG(status)); else if (WIFEXITED(status) && WEXITSTATUS(status)) fprintf(stderr, "nasm exited %d.\n", WEXITSTATUS(status)); REQUIRE (close(t) == 0); return sz; }