Elf二进制文件解析
生活随笔
收集整理的這篇文章主要介紹了
Elf二进制文件解析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Elf文件解析
ELF頭部結構
ELF文件頭從文件的0偏移量開始,是除了文件頭剩余部分文件的一個映射。
typedef struct {unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */Elf64_Half e_type; /* Object file type */Elf64_Half e_machine; /* Architecture */Elf64_Word e_version; /* Object file version */Elf64_Addr e_entry; /* Entry point virtual address */Elf64_Off e_phoff; /* Program header table file offset */Elf64_Off e_shoff; /* Section header table file offset */Elf64_Word e_flags; /* Processor-specific flags */Elf64_Half e_ehsize; /* ELF header size in bytes */Elf64_Half e_phentsize; /* Program header table entry size */Elf64_Half e_phnum; /* Program header table entry count */Elf64_Half e_shentsize; /* Section header table entry size */Elf64_Half e_shnum; /* Section header table entry count */Elf64_Half e_shstrndx; /* Section header string table index */ } Elf64_Ehdr;Elf程序頭
ELF程序頭是對二進制文件中段的描述,是程序裝載必須的一部分。段是在內核裝載是被解析的,描述了磁盤上可執行文件的內存布局以及如何映射到內存中。可以通過引用原始ELF頭中成員e_phoff(程序頭表偏移量)的偏移量來獲取程序頭表。
typedef struct {Elf64_Word p_type; /* Segment type */Elf64_Word p_flags; /* Segment flags */Elf64_Off p_offset; /* Segment file offset */Elf64_Addr p_vaddr; /* Segment virtual address */Elf64_Addr p_paddr; /* Segment physical address */Elf64_Xword p_filesz; /* Segment size in file */Elf64_Xword p_memsz; /* Segment size in memory */Elf64_Xword p_align; /* Segment alignment */ } Elf64_Phdr;ELF節頭
節,不是段,段是程序執行的必要組成部分,在每個段中,會有代碼歐哲數據被劃分成為不同的節。節頭表是對這些節位置和大小的描述,主要用于鏈接和調試。
typedef struct {Elf64_Word sh_name; /* Section name (string tbl index) */Elf64_Word sh_type; /* Section type */Elf64_Xword sh_flags; /* Section flags */Elf64_Addr sh_addr; /* Section virtual addr at execution */Elf64_Off sh_offset; /* Section file offset */Elf64_Xword sh_size; /* Section size in bytes */Elf64_Word sh_link; /* Link to another section */Elf64_Word sh_info; /* Additional section information */Elf64_Xword sh_addralign; /* Section alignment */Elf64_Xword sh_entsize; /* Entry size if section holds table */ } Elf64_Shdr;ELF符號
符號是對某些類型的數據或者代碼(如全局變量或函數)的符號引用。如printf()函數會在動態符號表.dynsym中存有一個指向該函數的符號條目。在大多數共享庫和動態鏈接可執行文件中,存在兩個符號表即.dynsym和.symtab連個節,Elf64_Sym中字段st_name保存了指向符號表中字符串表.dynstr和.strtab。
符號表對應的結構體為:
typedef struct {Elf64_Word st_name; /* Symbol name (string tbl index) */unsigned char st_info; /* Symbol type and binding */unsigned char st_other; /* Symbol visibility */Elf64_Section st_shndx; /* Section index */Elf64_Addr st_value; /* Symbol value */Elf64_Xword st_size; /* Symbol size */ } Elf64_Sym;ELF文件解析實現(簡易readelf實現)
#include <stdlib.h> #include <stdio.h> #include <elf.h> #include <string.h> #include <error.h> #include <unistd.h> #include <sys/mman.h> #include <stdint.h> #include <sys/stat.h> #include <fcntl.h>/* To printf dynamic symbol */ void print_dynamic_symbol(uint8_t *mem, Elf64_Shdr *dynsym, Elf64_Shdr *dynstr) {unsigned int i = 0;unsigned int nsym = 0;Elf64_Sym *sym = NULL;printf("%d %d\n", dynsym->sh_size, dynsym->sh_entsize);nsym = dynsym->sh_size / dynsym->sh_entsize;printf("The nsym size is %d\n", nsym);for (i = 0; i < nsym; i++) {sym = (Elf64_Sym *)(&mem[dynsym->sh_offset] + i * sizeof(Elf64_Sym));printf("sybmol -> %s\n", (&mem[dynstr->sh_offset] + sym->st_name));}printf("Nothing here\n"); }/* To printf all symbol */ void print_all_symbol(uint8_t *mem, Elf64_Shdr *symtab, Elf64_Shdr *strtab) {unsigned int i = 0;unsigned int nsym = 0;Elf64_Sym *sym = NULL;nsym = symtab->sh_size / symtab->sh_entsize;printf("The nsym size is %d\n", nsym);for (i = 0; i < nsym; i++) {sym = (Elf64_Sym *)(&mem[symtab->sh_offset] + i * sizeof(Elf64_Sym));printf("sybmol -> %s\n", (&mem[strtab->sh_offset] + sym->st_name));}printf("Nothing here\n"); }/* gcc elfparse.c -o elfparse */ int main(int argc, char *argv[]) {int fd, i;uint8_t *mem;struct stat st;char *StringTable, *interp;Elf64_Ehdr *ehdr;Elf64_Phdr *phdr;Elf64_Shdr *shdr;Elf64_Shdr *dynsym = NULL;Elf64_Shdr *dynstr = NULL;Elf64_Shdr *symtab = NULL;Elf64_Shdr *strtab = NULL;if (argc != 2) {printf("Usage: %s <exe>\n", argv[0]); return -1;}fd = open(argv[1], O_RDONLY);if (fd <= 0) {perror("open"); return -1;}if (fstat(fd, &st) < 0) {perror("fstat"); goto close;}/* Map the executable into memory */mem = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);if (mem == NULL) {perror("mmap"); goto close;}/** The initial ELF Header starts at offset 0* of out mapped memory*/ehdr = (Elf64_Ehdr *)mem;/** The shdr table and phdr table offset are* given by e_shoff and e_phoff members of the Elf64_Ehdr*/phdr = (Elf64_Phdr *)&mem[ehdr->e_phoff];shdr = (Elf64_Shdr *)&mem[ehdr->e_shoff];/** Check to see if the ELF magic (The first 4 bytes)* match up as 0x7f E L F*/if (mem[0] != 0x7f && strcmp(&mem[1], "ELF")) {fprintf(stderr, "%s is not an ELF file\n", argv[1]);goto out; }/** We are only parsing executables wite this code.* so ET_EXEC marks an executable*/#if 0printf("%d\n", ehdr->e_type);if (ehdr->e_type != ET_EXEC) {fprintf(stderr, "%s is not an excutable file\n", argv[1]);return -1;} #endifprintf("Program Entry point 0x%lx\n", ehdr->e_entry);/** We find the string table for the section header* names with e_shstrndx which gives the index of* which section holds the string table.*/StringTable = &mem[shdr[ehdr->e_shstrndx].sh_offset];/** Print each section header name and address.* Notice we get the index into the string table* that contain each section header name with * the shdr.sh_name member.*/printf("Section header list:\n\n");for (i = 1; i < ehdr->e_shnum; i++) {printf("%s: addr: 0x%lx offset: 0x%lx size: %d\n", &StringTable[shdr[i].sh_name],shdr[i].sh_addr, shdr[i].sh_offset, shdr[i].sh_entsize);if (!strcmp(&StringTable[shdr[i].sh_name], ".dynsym")) {dynsym = (Elf64_Shdr *)&shdr[i];}if (!strcmp(&StringTable[shdr[i].sh_name], ".dynstr")) {dynstr = (Elf64_Shdr *)&shdr[i];}if (!strcmp(&StringTable[shdr[i].sh_name], ".symtab")) {symtab = (Elf64_Shdr *)&shdr[i];}if (!strcmp(&StringTable[shdr[i].sh_name], ".strtab")) {strtab = (Elf64_Shdr *)&shdr[i];}}print_dynamic_symbol(mem, dynsym, dynstr);print_all_symbol(mem, symtab, strtab);/** Print out each segment name, and address.* Except for PT_INTERRP we print the path to * the dynamic linker (Interpreter)*/printf("Segment header list:\n\n");for (i = 1; i < ehdr->e_phnum; i++) {switch(phdr[i].p_type) {/* * We know that text segment statrs* at offset 0. And only one other* possible loadable segment exitis* which is the data segment.*/case PT_LOAD:if (phdr[i].p_offset == 0) {printf("Text segment: 0x%lx\n", phdr[i].p_vaddr);} else {printf("Data segment: 0x%lx\n", phdr[i].p_vaddr);}break;case PT_INTERP:interp = strdup((char *)&mem[phdr[i].p_offset]);printf("Interpreter: %s\n", interp);break;case PT_NOTE:printf("Note segment: 0x%lx\n", phdr[i].p_vaddr);break;case PT_DYNAMIC:printf("Dynamic segment: 0x%lx\n", phdr[i].p_vaddr);break;case PT_PHDR:printf("Phdr segment: 0x%lx\n", phdr[i].p_vaddr);break;} } munmap(mem, st.st_size);close(fd);return 0;out:munmap(mem, st.st_size); close:close(fd);return -1; }總結
以上是生活随笔為你收集整理的Elf二进制文件解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle指令df, oracle
- 下一篇: jdbc mysql demo_JDBC