#define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * mostly based on code from hijack.c (for x86) * by Victor Zandy * */ /* memory map for libraries */ #define MAX_NAME_LEN 256 #define MEMORY_ONLY "[memory]" struct mm { char name[MAX_NAME_LEN]; unsigned long start, end; }; typedef struct symtab *symtab_t; struct symlist { Elf32_Sym *sym; /* symbols */ char *str; /* symbol strings */ unsigned num; /* number of symbols */ }; struct symtab { struct symlist *st; /* "static" symbols */ struct symlist *dyn; /* dynamic symbols */ }; static void* xmalloc(size_t size) { void *p; p = malloc(size); if (!p) { printf("Out of memory\n"); exit(1); } return p; } static struct symlist* get_syms(int fd, Elf32_Shdr *symh, Elf32_Shdr *strh) { struct symlist *sl, *ret; int rv; ret = NULL; sl = (struct symlist *) xmalloc(sizeof(struct symlist)); sl->str = NULL; sl->sym = NULL; /* sanity */ if (symh->sh_size % sizeof(Elf32_Sym)) { //printf("elf_error\n"); goto out; } /* symbol table */ sl->num = symh->sh_size / sizeof(Elf32_Sym); sl->sym = (Elf32_Sym *) xmalloc(symh->sh_size); rv = pread(fd, sl->sym, symh->sh_size, symh->sh_offset); if (0 > rv) { //perror("read"); goto out; } if (rv != symh->sh_size) { //printf("elf error\n"); goto out; } /* string table */ sl->str = (char *) xmalloc(strh->sh_size); rv = pread(fd, sl->str, strh->sh_size, strh->sh_offset); if (0 > rv) { //perror("read"); goto out; } if (rv != strh->sh_size) { //printf("elf error"); goto out; } ret = sl; out: return ret; } static int do_load(int fd, symtab_t symtab) { int rv; size_t size; Elf32_Ehdr ehdr; Elf32_Shdr *shdr = NULL, *p; Elf32_Shdr *dynsymh, *dynstrh; Elf32_Shdr *symh, *strh; char *shstrtab = NULL; int i; int ret = -1; /* elf header */ rv = read(fd, &ehdr, sizeof(ehdr)); if (0 > rv) { //perror("read"); goto out; } if (rv != sizeof(ehdr)) { //printf("elf error\n"); goto out; } if (strncmp(ELFMAG, ehdr.e_ident, SELFMAG)) { /* sanity */ //printf("not an elf\n"); goto out; } if (sizeof(Elf32_Shdr) != ehdr.e_shentsize) { /* sanity */ //printf("elf error\n"); goto out; } /* section header table */ size = ehdr.e_shentsize * ehdr.e_shnum; shdr = (Elf32_Shdr *) xmalloc(size); rv = pread(fd, shdr, size, ehdr.e_shoff); if (0 > rv) { //perror("read"); goto out; } if (rv != size) { //printf("elf error"); goto out; } /* section header string table */ size = shdr[ehdr.e_shstrndx].sh_size; shstrtab = (char *) xmalloc(size); rv = pread(fd, shstrtab, size, shdr[ehdr.e_shstrndx].sh_offset); if (0 > rv) { //perror("read"); goto out; } if (rv != size) { //printf("elf error\n"); goto out; } /* symbol table headers */ symh = dynsymh = NULL; strh = dynstrh = NULL; for (i = 0, p = shdr; i < ehdr.e_shnum; i++, p++) if (SHT_SYMTAB == p->sh_type) { if (symh) { //printf("too many symbol tables\n"); goto out; } symh = p; } else if (SHT_DYNSYM == p->sh_type) { if (dynsymh) { //printf("too many symbol tables\n"); goto out; } dynsymh = p; } else if (SHT_STRTAB == p->sh_type && !strncmp(shstrtab+p->sh_name, ".strtab", 7)) { if (strh) { //printf("too many string tables\n"); goto out; } strh = p; } else if (SHT_STRTAB == p->sh_type && !strncmp(shstrtab+p->sh_name, ".dynstr", 7)) { if (dynstrh) { //printf("too many string tables\n"); goto out; } dynstrh = p; } /* sanity checks */ if ((!dynsymh && dynstrh) || (dynsymh && !dynstrh)) { //printf("bad dynamic symbol table"); goto out; } if ((!symh && strh) || (symh && !strh)) { //printf("bad symbol table"); goto out; } if (!dynsymh && !symh) { //printf("no symbol table"); goto out; } /* symbol tables */ if (dynsymh) symtab->dyn = get_syms(fd, dynsymh, dynstrh); if (symh) symtab->st = get_syms(fd, symh, strh); ret = 0; out: free(shstrtab); free(shdr); return ret; } static symtab_t load_symtab(char *filename) { int fd; symtab_t symtab; symtab = (symtab_t) xmalloc(sizeof(*symtab)); memset(symtab, 0, sizeof(*symtab)); fd = open(filename, O_RDONLY); if (0 > fd) { //perror("open"); return NULL; } if (0 > do_load(fd, symtab)) { //printf("Error ELF parsing %s\n", filename); free(symtab); symtab = NULL; } close(fd); return symtab; } static int load_memmap(pid_t pid, struct mm *mm, int *nmmp) { char raw[50000]; // increase this if needed for larger "maps" char name[MAX_NAME_LEN]; char *p; unsigned long start, end; struct mm *m; int nmm = 0; int fd, rv; int i; sprintf(raw, "/proc/%d/maps", pid); fd = open(raw, O_RDONLY); if (0 > fd) { //printf("Can't open %s for reading\n", raw); return -1; } /* Zero to ensure data is null terminated */ memset(raw, 0, sizeof(raw)); p = raw; while (1) { rv = read(fd, p, sizeof(raw)-(p-raw)); if (0 > rv) { //perror("read"); return -1; } if (0 == rv) break; p += rv; if (p-raw >= sizeof(raw)) { //printf("Too many memory mapping\n"); return -1; } } close(fd); p = strtok(raw, "\n"); m = mm; while (p) { /* parse current map line */ rv = sscanf(p, "%08lx-%08lx %*s %*s %*s %*s %s\n", &start, &end, name); p = strtok(NULL, "\n"); if (rv == 2) { m = &mm[nmm++]; m->start = start; m->end = end; strcpy(m->name, MEMORY_ONLY); continue; } /* search backward for other mapping with same name */ for (i = nmm-1; i >= 0; i--) { m = &mm[i]; if (!strcmp(m->name, name)) break; } if (i >= 0) { if (start < m->start) m->start = start; if (end > m->end) m->end = end; } else { /* new entry */ m = &mm[nmm++]; m->start = start; m->end = end; strcpy(m->name, name); } } *nmmp = nmm; return 0; } /* Find libc in MM, storing no more than LEN-1 chars of its name in NAME and set START to its starting address. If libc cannot be found return -1 and leave NAME and START untouched. Otherwise return 0 and null-terminated NAME. */ static int find_libname(char *libn, char *name, int len, unsigned long *start, struct mm *mm, int nmm) { int i; struct mm *m; char *p; for (i = 0, m = mm; i < nmm; i++, m++) { if (!strcmp(m->name, MEMORY_ONLY)) continue; p = strrchr(m->name, '/'); if (!p) continue; p++; if (strncmp(libn, p, strlen(libn))) continue; p += strlen(libn); /* here comes our crude test -> 'libc.so' or 'libc-[0-9]' */ if (!strncmp(".so", p, 3)) // || (p[0] == '-' && isdigit(p[1]))) break; } if (i >= nmm) /* not found */ return -1; *start = m->start; strncpy(name, m->name, len); if (strlen(m->name) >= len) name[len-1] = '\0'; mprotect((void*)m->start, m->end - m->start, PROT_READ|PROT_WRITE|PROT_EXEC); return 0; } static int lookup2(struct symlist *sl, unsigned char type, char *name, unsigned long *val) { Elf32_Sym *p; int len; int i; len = strlen(name); for (i = 0, p = sl->sym; i < sl->num; i++, p++) { //printf("name: %s %x\n", sl->str+p->st_name, p->st_value); if (!strncmp(sl->str+p->st_name, name, len) && ELF32_ST_TYPE(p->st_info) == type) { //if (p->st_value != 0) { *val = p->st_value; return 0; //} } } return -1; } static int lookup_sym(symtab_t s, unsigned char type, char *name, unsigned long *val) { if (s->dyn && !lookup2(s->dyn, type, name, val)) return 0; if (s->st && !lookup2(s->st, type, name, val)) return 0; return -1; } static int lookup_func_sym(symtab_t s, char *name, unsigned long *val) { return lookup_sym(s, STT_FUNC, name, val); } int find_name(pid_t pid, char *name, char *libn, unsigned long *addr) { struct mm mm[250]; unsigned long libcaddr; int nmm; char libc[256]; symtab_t s; if (0 > load_memmap(pid, mm, &nmm)) { //printf("cannot read memory map\n"); return -1; } if (0 > find_libname(libn, libc, sizeof(libc), &libcaddr, mm, nmm)) { //printf("cannot find libc\n"); return -1; } s = load_symtab(libc); if (!s) { //printf("cannot read symbol table\n"); return -1; } if (0 > lookup_func_sym(s, name, addr)) { //printf("cannot find %s\n", name); return -1; } *addr += libcaddr; return 0; } .