int main(int argc, char **argv) { Elf32_Ehdr *e_hdr_in, *e_hdr_out, *e_hdr_inj; Elf32_Phdr *p_hdr_in, *p_hdr_out, *code_hdr, *data_hdr, *dyn_hdr; Elf32_Shdr *s_hdr_out, *s_hdr_str_out; int fd_in; int fd_out; off_t fd_in_sz, fd_out_sz; uint32_t pltgot, *fix, msk, user_offset, base_block, hook_sz, inj_sz, over_sz; char *s_str_out; int i; if (argc != 3) { printf("usage: %s <ld-linux.so> <pie.elf>\n", argv[0]); return -1; } /* check input file */ if ((fd_in = open(argv[1], O_RDONLY)) == -1) { printf("can't open file %s\n", argv[1]); return -1; } fd_in_sz = lseek(fd_in, 0, SEEK_END); lseek(fd_in, 0, SEEK_SET); e_hdr_in = (Elf32_Ehdr*)mmap(NULL, fd_in_sz, PROT_READ, MAP_PRIVATE, fd_in, 0); p_hdr_in = (Elf32_Phdr*)((uint32_t)e_hdr_in + e_hdr_in->e_phoff); /* load file to inject */ if (load_code_from_file(argv[2], &e_hdr_inj, &inj_sz) == -1) return -1; hook_sz = (uint32_t)payload - (uint32_t)shatner; over_sz = hook_sz + inj_sz; /* build out file */ if ((fd_out = open(LDOUTFILE, O_RDWR|O_CREAT|O_TRUNC, 0755)) == -1) { printf("can't open file "LDOUTFILE"\n"); return -1; } fd_out_sz = fd_in_sz + over_sz; lseek(fd_out, fd_out_sz - 1, SEEK_SET); write(fd_out, "\x00", 1); lseek(fd_out, 0, SEEK_SET); e_hdr_out = (Elf32_Ehdr*)mmap(NULL, fd_out_sz, PROT_READ|PROT_WRITE, MAP_SHARED, fd_out, 0); /* copy elf hdr */ uint32_t offset = (uint32_t)e_hdr_out; memcpy((void*)offset, (void*)e_hdr_in, e_hdr_in->e_ehsize); /* install hook */ offset += e_hdr_in->e_ehsize; memcpy((void*)offset, (void*)shatner, hook_sz); base_block = offset; /* save fixed ld entry point */ fix = (uint32_t*)((uint32_t)base_block + 2); *fix = e_hdr_out->e_entry + over_sz; /* inject external code */ inject_code_from_file(base_block, offset+hook_sz, e_hdr_inj); /* copy remaining */ offset += over_sz; memcpy((void*)offset, (void*)e_hdr_in+e_hdr_in->e_ehsize, fd_in_sz - e_hdr_in->e_ehsize); /* ** Fix Elf header */ e_hdr_out->e_entry = e_hdr_out->e_ehsize; e_hdr_out->e_phoff += over_sz; e_hdr_out->e_shoff += over_sz; /* ** Fix Program headers */ printf("p_hdr #%d\n", e_hdr_out->e_phnum); p_hdr_out = (Elf32_Phdr*)((uint32_t)e_hdr_out + e_hdr_out->e_phoff); code_hdr = (Elf32_Phdr*)0; data_hdr = (Elf32_Phdr*)0; for (i=0 ; i<e_hdr_out->e_phnum ; i++, p_hdr_out++) { if (!p_hdr_out->p_memsz) continue; if (p_hdr_out->p_type == PT_LOAD && (p_hdr_out->p_flags & (PF_R|PF_X)) && i == 0) { code_hdr = p_hdr_out; code_hdr->p_filesz += over_sz; code_hdr->p_memsz += over_sz; continue; } if (p_hdr_out->p_type == PT_LOAD && (p_hdr_out->p_flags & (PF_R|PF_W)) && i == 1) { data_hdr = p_hdr_out; msk = data_hdr->p_align - 1; } if (p_hdr_out->p_type == PT_DYNAMIC) dyn_hdr = p_hdr_out; printf("fixing phdr %d\n", i); p_hdr_out->p_offset += over_sz; p_hdr_out->p_vaddr += over_sz; p_hdr_out->p_paddr += over_sz; } if (!code_hdr || !data_hdr || !dyn_hdr) { printf("bad program headers\n"); return -1; } /* ** Fix Section headers */ s_hdr_out = (Elf32_Shdr*)((uint32_t)e_hdr_out + e_hdr_out->e_shoff); s_hdr_str_out = &s_hdr_out[e_hdr_out->e_shstrndx]; s_str_out = (uint8_t*)e_hdr_out + s_hdr_str_out->sh_offset + over_sz; printf("s_hdr #%d | s_hdr_str 0x%x\n", e_hdr_out->e_shnum, s_str_out); for (i=0 ; i<e_hdr_out->e_shnum ; i++, s_hdr_out++) { if (s_hdr_out->sh_size == 0) continue; s_hdr_out->sh_offset += over_sz; if (s_hdr_out->sh_flags & SHF_ALLOC) s_hdr_out->sh_addr += over_sz; printf("\\_fix shdr %s addr 0x%x off 0x%x\n", &s_str_out[s_hdr_out->sh_name], s_hdr_out->sh_addr, s_hdr_out->sh_offset); /* fix .dynamic */ if (s_hdr_out->sh_type == SHT_DYNAMIC) { Elf32_Dyn *dyn_s = (Elf32_Dyn*)((uint32_t)e_hdr_out + s_hdr_out->sh_offset); Elf32_Dyn *dyn = dyn_s; while (dyn->d_tag != DT_NULL) { switch (dyn->d_tag) { case DT_PLTGOT: case DT_JMPREL: case DT_GNU_HASH: case DT_HASH: case DT_STRTAB: case DT_SYMTAB: case DT_REL: case DT_RELA: case DT_VERDEF: case DT_VERSYM: dyn->d_un.d_ptr += over_sz; printf(" \\_fix 0x%x\n", dyn->d_un.d_ptr); break; case DT_SONAME: printf(" \\_soname 0x%x\n", dyn->d_un.d_val); break; case DT_INIT_ARRAY: case DT_FINI_ARRAY: printf("!!!! WARNING need to patch ARRAY !!!!\n"); break; } if (dyn->d_tag == DT_PLTGOT) { pltgot = dyn->d_un.d_ptr; printf(" \\_found .got.plt @ 0x%x\n", pltgot); } dyn++; } /* compute user offset to save old user entry into .dynamic[NULL].d_un.d_ptr */ user_offset = ((code_hdr->p_memsz + msk) & (~msk)) + (dyn_hdr->p_vaddr & msk) + ((uint32_t)dyn - (uint32_t)dyn_s) + 4; printf("computed user_offset 0x%x\n", user_offset); fix = (uint32_t*)((uint32_t)base_block + 6); *fix = user_offset; } /* fix .dynsym */ if (s_hdr_out->sh_type == SHT_DYNSYM) { Elf32_Sym *sym = (Elf32_Sym*)((uint32_t)e_hdr_out + s_hdr_out->sh_offset); uint32_t nr = s_hdr_out->sh_size/s_hdr_out->sh_entsize; int z; for (z=0 ; z<nr ; z++) { if (sym->st_size) { sym->st_value += over_sz; printf(" \\_fix 0x%x\n", sym->st_value); } sym++; } } /* fix .got.plt */ if (s_hdr_out->sh_addr == pltgot) { uint32_t *got = (uint32_t*)((uint32_t)e_hdr_out + s_hdr_out->sh_offset); uint32_t nr = s_hdr_out->sh_size/s_hdr_out->sh_entsize; int z; for (z=0 ; z<nr ; z++) { *got += over_sz; printf(" \\_fix 0x%x\n", *got); got++; } } } /* ** now that s_hdr are fixed, process reloc entries */ s_hdr_out = (Elf32_Shdr*)((uint32_t)e_hdr_out + e_hdr_out->e_shoff); for (i=0 ; i<e_hdr_out->e_shnum ; i++, s_hdr_out++) { if (s_hdr_out->sh_type != SHT_REL) continue; printf("\\_reloc %s fix\n", &s_str_out[s_hdr_out->sh_name]); Elf32_Rel *rel = (Elf32_Rel*)((uint32_t)e_hdr_out + s_hdr_out->sh_offset); uint32_t nr = s_hdr_out->sh_size/s_hdr_out->sh_entsize; int z; for (z=0 ; z<nr ; z++) { rel->r_offset += over_sz; printf(" \\_fix 0x%x\n", rel->r_offset); if (ELF32_R_TYPE(rel->r_info) == R_386_RELATIVE) { uint32_t rfix = (uint32_t)e_hdr_out + rel->r_offset; uint32_t diff = reloc_lookup_section(e_hdr_out, rel->r_offset); if (diff) rfix -= diff; *(uint32_t*)rfix += over_sz; } rel++; } } munmap((void*)e_hdr_in, fd_in_sz); close(fd_in); munmap((void*)e_hdr_out, fd_out_sz); close(fd_out); return 0; }
list_t* do_prim_op(char *name, list_t *args) { int i = 0; int j; int val = 0; list_t *l1; list_t* nl = c_malloc(sizeof(list_t)); char *buf; if (!strcmp(name, "+")) { val = 0; for (i = 0; i < args->cc; ++i) { if (args->c[i]->type != NUMBER) { error_msg("+ expects numbers"); code_error(); } val += args->c[i]->val; } nl->type = NUMBER; nl->val = val; return nl; } if (!strcmp(name, "-")) { if (args->cc == 1) { /* single argument: unary minus sign */ if (args->c[0]->type != NUMBER) { error_msg("- expects numbers"); code_error(); } val = -args->c[0]->val; } else { /* otherwise, standard N-ary subtraction */ for (i = 0; i < args->cc; ++i) { if (args->c[i]->type != NUMBER) { error_msg("- expects numbers"); code_error(); } if (i == 0) val = args->c[i]->val; else val -= args->c[i]->val; } } nl->val = val; nl->type = NUMBER; return nl; } if (!strcmp(name, "*")) { val = 1; for (i = 0; i < args->cc; ++i) { if (args->c[i]->type != NUMBER) { error_msg("* expects numbers"); code_error(); } val *= args->c[i]->val; } nl->type = NUMBER; nl->val = val; return nl; } if (!strcmp(name, "remainder")) { if (args->cc != 2 || args->c[0]->type != NUMBER || args->c[1]->type != NUMBER) { error_msg("`remainder' expects two numbers"); code_error(); } nl->type = NUMBER; nl->val = args->c[0]->val % args->c[1]->val; return nl; } if (!strcmp(name, "=")) { if (args->cc != 2 || args->c[0]->type != NUMBER || args->c[1]->type != NUMBER) { error_msg("= expects two numbers"); code_error(); } return makebool(args->c[0]->val == args->c[1]->val); } if (!strcmp(name, ">")) { if (args->cc != 2 || args->c[0]->type != NUMBER || args->c[1]->type != NUMBER) { error_msg("> expects two numbers"); code_error(); } return makebool(args->c[0]->val > args->c[1]->val); } if (!strcmp(name, "<")) { if (args->cc != 2 || args->c[0]->type != NUMBER || args->c[1]->type != NUMBER) { error_msg("< expects two numbers"); code_error(); } return makebool(args->c[0]->val < args->c[1]->val); } if (!strcmp(name, "<=")) { if (args->cc != 2 || args->c[0]->type != NUMBER || args->c[1]->type != NUMBER) { error_msg("<= expects two numbers"); code_error(); } return makebool(args->c[0]->val <= args->c[1]->val); } if (!strcmp(name, ">=")) { if (args->cc != 2 || args->c[0]->type != NUMBER || args->c[1]->type != NUMBER) { error_msg(">= expects two numbers"); code_error(); } return makebool(args->c[0]->val >= args->c[1]->val); } if (!strcmp(name, "not")) { val = 0; if (args->cc != 1) { error_msg("`not' expects one argument"); code_error(); } /* r6rs.pdf section 11.8, page 47 */ val = args->c[0]->type == BOOL && !args->c[i]->val; return makebool(val); } if (!strcmp(name, "cons")) { if (args->cc != 2) { error_msg("`cons' expects 2 arguments"); code_error(); } /* just return the list as-is for now */ memcpy(nl, args, sizeof(list_t)); nl->type = CONS; return nl; } if (!strcmp(name, "car")) { if (args->cc != 1) { error_msg("`car' expects 1 argument"); code_error(); } if (args->c[0]->type != CONS) { error_msg("`car' expects a linked-list"); code_error(); } if (args->c[0]->cc < 1) { error_msg("`car' has failed"); code_error(); } return args->c[0]->c[0]; } if (!strcmp(name, "cdr")) { if (args->cc != 1) { error_msg("`cdr' expects 1 argument"); code_error(); } if (args->c[0]->type != CONS) { error_msg("`cdr' expects a linked-list"); code_error(); } if (args->c[0]->cc < 2) { error_msg("`cdr' has failed"); code_error(); } return args->c[0]->c[1]; } if (!strcmp(name, "null?")) { return makebool(args->cc == 1 && ( ((args->c[0]->type == SYMBOL && !strcmp(args->c[0]->head, "NIL")) || (args->c[0]->type == CONS && args->c[0]->cc == 0)))); } if (!strcmp(name, "display")) { buf = malloc(LINEBUFSIZ); if (!buf) { error_msg("malloc failed"); code_error(); } *buf = 0; printout(args->c[0], buf); #ifdef JS_GUI c_writeback(buf); #else printf("%s", buf); #endif free(buf); return args->c[0]; } if (!strcmp(name, "pair?")) { /* check for null first */ j = args->cc == 1 && ( ((args->c[0]->type == SYMBOL && !strcmp(args->c[0]->head, "NIL")) || (args->c[0]->type == CONS && args->c[0]->cc == 0))); return makebool(!j /* not null, then the rest */ && args->cc == 1 && args->c[0]->type == CONS); } /* the following bit deals with (eq? A B) -- * apparently, eq? checks if two things evaluate * to the same memory pointer. but further hackery * is sufficient to deal with the case (eq? 'foo 'foo) */ if (!strcmp(name, "eq?")) { if (args->cc != 2) { error_msg("`eq?' expects two arguments"); code_error(); } return makebool(args->c[0] == args->c[1] || (args->c[0]->type == SYMBOL && args->c[1]->type == SYMBOL && !strcmp(args->c[0]->head, args->c[1]->head)) /* (eq? 'NIL '()) => #t */ || (args->c[0]->type == SYMBOL && !strcmp(args->c[0]->head, "NIL") && args->c[1]->type == CONS && args->c[1]->cc == 0) /* (eq? '() 'NIL) => #t */ || (args->c[1]->type == SYMBOL && !strcmp(args->c[1]->head, "NIL") && args->c[0]->type == CONS && args->c[0]->cc == 0) /* (eq? '() '()) => #t */ || (args->c[0]->type == CONS && args->c[0]->cc == 0 && args->c[1]->type == CONS && args->c[1]->cc == 0)); } if (!strcmp(name, "symbol?")) { return makebool(args->cc == 1 && args->c[0]->type == SYMBOL); } if (!strcmp(name, "number?")) { return makebool(args->cc == 1 && args->c[0]->type == NUMBER); } if (!strcmp(name, "newline")) { #ifdef JS_GUI c_writeback_nl(""); #else puts(""); #endif return mksym("NIL"); } if (!strcmp(name, "save-to")) { if (save_mode) { return mksym("NIL"); } save_file = fopen(args->c[0]->head, "w"); if (!save_file) { error_msg("failed to open file for writing"); code_error(); } save_mode = 1; return mksym("savefile-ok"); } if (!strcmp(name, "load")) { buf = malloc(strlen(args->c[0]->head) + 1); if (!buf) { error_msg("malloc failed"); code_error(); } strcpy(buf, args->c[0]->head); load_code_from_file(buf); free(buf); return mksym("HERP-DERP"); } if (!strcmp(name, "cons2list")) { return cons2list(args->c[0]); } if (!strcmp(name, "debuglog")) { stacktracer_barf(); return mksym("herp-derp"); } if (!strcmp(name, "reverse")) { /* r6rs.pdf, page 48 */ if (args->c[0]->type == CONS) l1 = cons2list(args->c[0]); else if (args->c[0]->type == LIST) l1 = args->c[0]; else return mksym("NIL"); if (l1->cc == 0) return mksym("NIL"); nl = new_list(); nl->type = LIST; for (i = l1->cc - 1; i >= 0; --i) add_child(nl, l1->c[i]); return makelist(nl); } return NULL; }