elf文件解析(cpp版)1

Elf.h

#ifndef H_ELF_PARSER
#define H_ELF_PARSER
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
#include <fcntl.h>    /* O_RDONLY */
#include <sys/stat.h> /* For the size of the file. , fstat */
#include <sys/mman.h> /* mmap, MAP_PRIVATE */
#include <vector>
#include <elf.h>      // Elf64_Shdr
#include <fcntl.h>
//-----说明通过sections Header定位的以下称为节,通过Program Header定位的以下称为段 -----//
namespace elf_parser {
//节
typedef struct {
    //当前节的索引
    int section_index = 0;
    //节的偏移,虚拟地址
    std::intptr_t section_offset, section_addr;
    //节名称
    std::string section_name;
    //节类型
    std::string section_type;
    //节大小 ,节中单个表项的大小,节对齐方式
    int section_size, section_ent_size, section_addr_align;
} section_t;
//段
typedef struct {
    //段类型,段的权限属性标志
    std::string segment_type, segment_flags;
    //段的偏移,段的内存线性地址,段的内存物理地址,段在文件中的大小,段在内存中的大小
    long segment_offset, segment_virtaddr, segment_physaddr, segment_filesize, segment_memsize;
    //段的内存中的对齐方式
    int segment_align;
} segment_t;
//符号
typedef struct {
    //符号索引
    std::string symbol_index;
    //符号的值
    std::intptr_t symbol_value;
    //符号表项顺序 ,大小
    int symbol_num = 0, symbol_size = 0;
    //符号类型,绑定信息,其它,名称,所属节名称
    std::string symbol_type, symbol_bind, symbol_visibility, symbol_name, symbol_section;      
} symbol_t;
//重定位
typedef struct {
    //指向需要进行重定位操作的位置 ,信息,值
    std::intptr_t relocation_offset, relocation_info, relocation_symbol_value;
    //类型,名称,所属字名称
    std::string   relocation_type, relocation_symbol_name, relocation_section_name;
    //单个重定位条目所在地址
    std::intptr_t relocation_plt_address;
} relocation_t;
//ELF
class Elf_parser {
    public:
        Elf_parser (std::string &program_path): m_program_path{program_path} {   
            load_memory_map();
        }
        //获取所有的节信息
        std::vector<section_t> get_sections();
        //获取所有的段信息
        std::vector<segment_t> get_segments();
        //获取所有符号信息
        std::vector<symbol_t> get_symbols();
        //获取所有重位条目信息
        std::vector<relocation_t> get_relocations();
        //获取映射内存地址
        uint8_t *get_memory_map();

    private:
        //申请映射
        void load_memory_map();
        //获取节类型
        std::string get_section_type(int tt);
        //获取段类型
        std::string get_segment_type(uint32_t &seg_type);
        //获取段的权限属性
        std::string get_segment_flags(uint32_t &seg_flags);
        //获取符号类型
        std::string get_symbol_type(uint8_t &sym_type);
        //获取符号绑定信息
        std::string get_symbol_bind(uint8_t &sym_bind);
        //获取符号其它信息
        std::string get_symbol_visibility(uint8_t &sym_vis);
        //获取符号在字符串的中索引
        std::string get_symbol_index(uint16_t &sym_idx);
        //获取重定位类型
        std::string get_relocation_type(uint64_t &rela_type);
        //获取重定位值(在符号表中找)
        std::intptr_t get_rel_symbol_value(uint64_t &sym_idx, std::vector<symbol_t> &syms);
        //获取重定位的名称(在符号表中找)
        std::string get_rel_symbol_name(
            uint64_t &sym_idx, std::vector<symbol_t> &syms);
        //文件路径
        std::string m_program_path;
        //映射内存首地址
        uint8_t *m_mmap_program;
};

}
#endif

Elf.cpp

//-----说明通过sections Header定位的以下称为节,通过Program Header定位的以下称为段 -----//
#include "elf_parser.hpp"
using namespace elf_parser;
//获取所有节
std::vector<section_t> Elf_parser::get_sections() {
    //ELF头
    Elf64_Ehdr *ehdr = (Elf64_Ehdr*)m_mmap_program;
    //Section Header Table
    Elf64_Shdr *shdr = (Elf64_Shdr*)(m_mmap_program + ehdr->e_shoff);
    //Section Header Table个数
    int shnum = ehdr->e_shnum;
    //字符串表
    Elf64_Shdr *sh_strtab = &shdr[ehdr->e_shstrndx];
    //字符串表在文件中的偏移
    const char *const sh_strtab_p = (char*)m_mmap_program + sh_strtab->sh_offset;
    //保存所有的节
    std::vector<section_t> sections;
    //循环遍历所有节表
    for (int i = 0; i < shnum; ++i) {
        section_t section;
        //当前节在所有节中在顺序
        section.section_index= i;
        //节的名称  sh_strtab_p字符串表的起始地址 +  节的名称的在字符串中的索引(偏移)
        section.section_name = std::string(sh_strtab_p + shdr[i].sh_name);
        //节的类型
        section.section_type = get_section_type(shdr[i].sh_type);
        //节的虚拟地址
        section.section_addr = shdr[i].sh_addr;
        //节的文件偏移
        section.section_offset = shdr[i].sh_offset;
        //节的大小
        section.section_size = shdr[i].sh_size;
        //节中表项的大小
        section.section_ent_size = shdr[i].sh_entsize;
        //节对齐方式
        section.section_addr_align = shdr[i].sh_addralign; 
        //保存起来
        sections.push_back(section);
    }
    //返回所有节向量
    return sections;
}
//获取所有段
std::vector<segment_t> Elf_parser::get_segments() {
    //映射基址
    Elf64_Ehdr *ehdr = (Elf64_Ehdr*)m_mmap_program;
    //程序头基址
    Elf64_Phdr *phdr = (Elf64_Phdr*)(m_mmap_program + ehdr->e_phoff);
    //程序头表的数量
    int phnum = ehdr->e_phnum;
    //节表基址
    Elf64_Shdr *shdr = (Elf64_Shdr*)(m_mmap_program + ehdr->e_shoff);
    //字符串表索引
    Elf64_Shdr *sh_strtab = &shdr[ehdr->e_shstrndx];
    //字符串表基址
    const char *const sh_strtab_p = (char*)m_mmap_program + sh_strtab->sh_offset;
    //声名保存所有段的向量
    std::vector<segment_t> segments;
    //遍历所有段
    for (int i = 0; i < phnum; ++i) {
        segment_t segment;
        //段的类型
        segment.segment_type     = get_segment_type(phdr[i].p_type);
        //段的偏移
        segment.segment_offset   = phdr[i].p_offset;
        //段的内存线性地址
        segment.segment_virtaddr = phdr[i].p_vaddr;
        //段的内存物理地址
        segment.segment_physaddr = phdr[i].p_paddr;
        //段在文件中的大小
        segment.segment_filesize = phdr[i].p_filesz;
        //段在内存中的大小
        segment.segment_memsize  = phdr[i].p_memsz;
        //段的权限属性标志
        segment.segment_flags    = get_segment_flags(phdr[i].p_flags);
        //段的内存中的对齐方式
        segment.segment_align    = phdr[i].p_align;
        //保存段
        segments.push_back(segment);
    }
    //返回所有节向量
    return segments;
}
//获取所有符号
std::vector<symbol_t> Elf_parser::get_symbols() {
    //先获取所有节
    std::vector<section_t> secs = get_sections();
    // get headers for offsets
    Elf64_Ehdr *ehdr = (Elf64_Ehdr*)m_mmap_program;
    //获取所有节表头基址
    Elf64_Shdr *shdr = (Elf64_Shdr*)(m_mmap_program + ehdr->e_shoff);
    //符号字符串表
    // get strtab
    char *sh_strtab_p = nullptr;
    //遍历所有的节
    for(auto &sec: secs) {
        //判断节的类型与名称
        if((sec.section_type == "SHT_STRTAB") && (sec.section_name == ".strtab")){
            //获取符号基址
            sh_strtab_p = (char*)m_mmap_program + sec.section_offset;
            break;
        }
    }
    //动态符号字符串表
    // get dynstr
    char *sh_dynstr_p = nullptr;
    //遍历所有的节
    for(auto &sec: secs) {
        //判断节的类型与名称
        if((sec.section_type == "SHT_STRTAB") && (sec.section_name == ".dynstr")){
            //获取符号基址
            sh_dynstr_p = (char*)m_mmap_program + sec.section_offset;
            break;
        }
    }
    //用于保存所有符号
    std::vector<symbol_t> symbols;
    //遍历所有节
    for(auto &sec: secs) {
        //判断类型
        if((sec.section_type != "SHT_SYMTAB") && (sec.section_type != "SHT_DYNSYM"))
            continue;
        //计算节中的所有符号个数
        auto total_syms = sec.section_size / sizeof(Elf64_Sym);
        //符号节的基址
        auto syms_data = (Elf64_Sym*)(m_mmap_program + sec.section_offset);
        //遍历符号表
        for (int i = 0; i < total_syms; ++i) {
            symbol_t symbol;
            //符号表序号
            symbol.symbol_num       = i;
            //符号的值
            symbol.symbol_value     = syms_data[i].st_value;
            //符号的大小 (字节)
            symbol.symbol_size      = syms_data[i].st_size;
            //获取符号的类型 (info表示相关信息,低4字节表示符号类型,可以用宏获取)
            symbol.symbol_type      = get_symbol_type(syms_data[i].st_info);
            //获取符号绑定信息
            symbol.symbol_bind      = get_symbol_bind(syms_data[i].st_info);
            //获取符号其它信息(可见性)
            symbol.symbol_visibility= get_symbol_visibility(syms_data[i].st_other);
            //符号索引
            symbol.symbol_index     = get_symbol_index(syms_data[i].st_shndx);
            //符号所在节的名称
            symbol.symbol_section   = sec.section_name;
            //判断字符串表
            if(sec.section_type == "SHT_SYMTAB")
                symbol.symbol_name = std::string(sh_strtab_p + syms_data[i].st_name);
            //判断动态字符串表
            if(sec.section_type == "SHT_DYNSYM")
                symbol.symbol_name = std::string(sh_dynstr_p + syms_data[i].st_name);
            //保存起来
            symbols.push_back(symbol);
        }
    }
    //返回向量
    return symbols;
}
//获取所有重定位表
std::vector<relocation_t> Elf_parser::get_relocations() {
    //获取所有节
    auto secs = get_sections();
    //获取所有字符串表
    auto syms = get_symbols();
    //plt 节表项大小
    int  plt_entry_size = 0;
    //plt 节的虚拟地址
    long plt_vma_address = 0;
    //遍历所有的节
    for (auto &sec : secs) {
        //判断节名称
        if(sec.section_name == ".plt") {
          //节中表项的大小
          plt_entry_size = sec.section_ent_size;
          //节的虚拟地址
          plt_vma_address = sec.section_addr;
          break;
        }
    }
    //用于保存所有重定位表
    std::vector<relocation_t> relocations;
    //遍历所有的节
    for (auto &sec : secs) {
        //判断节的类型
        if(sec.section_type != "SHT_RELA") 
            continue;
        //计算节的所有重定位条目数
        auto total_relas = sec.section_size / sizeof(Elf64_Rela);
        //重定位节的基址
        auto relas_data  = (Elf64_Rela*)(m_mmap_program + sec.section_offset);
        //遍历所有的重定位条目
        for (int i = 0; i < total_relas; ++i) {
            relocation_t rel;
            //指向需要进行重定位操作的位置
            //解释:
            //表示重定位地址,对应可重定位目标文件来说,表示重定位位置相对于被重定位段的基址的偏移,而对于
            //可执行文件或共享目标文件来说,表示重定位位置对应的线性地址,这个与动态相关。
            rel.relocation_offset = static_cast<std::intptr_t>(relas_data[i].r_offset);
            //重定位相关信息
            //描述了重定位类型和符号,低8位表示重定位类型,高24位表示重定位符号对应符号表项在符号表内的索引
            //不同的处理器体系结构都属于自己的一套重定位类型。
            rel.relocation_info   = static_cast<std::intptr_t>(relas_data[i].r_info);
            //类型
            rel.relocation_type   = \
                get_relocation_type(relas_data[i].r_info);
            //值 (在字符串表中找)
            rel.relocation_symbol_value = \
                get_rel_symbol_value(relas_data[i].r_info, syms);
            //符号名称(存字符串表中找)
            rel.relocation_symbol_name  = \
                get_rel_symbol_name(relas_data[i].r_info, syms);
            // plt项的地址
            rel.relocation_plt_address = plt_vma_address + (i + 1) * plt_entry_size;
            //节名称
            rel.relocation_section_name = sec.section_name;
            //保存起来
            relocations.push_back(rel);
        }
    }
    //返回向量
    return relocations;
}
//获取映射内存首地址
uint8_t *Elf_parser::get_memory_map() {
    return m_mmap_program;
}
//申请内存映射
void Elf_parser::load_memory_map() {
    int fd, i;
    struct stat st;
    //打开文件
    if ((fd = open(m_program_path.c_str(), O_RDONLY)) < 0) {
        printf("Err: open\n");
        exit(-1);
    }
    //获取文件大小
    if (fstat(fd, &st) < 0) {
        printf("Err: fstat\n");
        exit(-1);
    }
    //映射内存
    m_mmap_program = static_cast<uint8_t*>(mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0));
    //判断是否成功
    if (m_mmap_program == MAP_FAILED) {
        printf("Err: mmap\n");
        exit(-1);
    }
    auto header = (Elf64_Ehdr*)m_mmap_program;
    //判断是否为64位ELF文件
    if (header->e_ident[EI_CLASS] != ELFCLASS64) {
        printf("Only 64-bit files supported\n");
        exit(1);
    }
}

//获取节的类型(参数为类型值)
std::string Elf_parser::get_section_type(int tt) {
    if(tt < 0)
        return "UNKNOWN";

    switch(tt) {
        case 0: return "SHT_NULL";      /* Section header table entry unused */
        case 1: return "SHT_PROGBITS";  /* Program data */
        case 2: return "SHT_SYMTAB";    /* Symbol table */
        case 3: return "SHT_STRTAB";    /* String table */
        case 4: return "SHT_RELA";      /* Relocation entries with addends */
        case 5: return "SHT_HASH";      /* Symbol hash table */
        case 6: return "SHT_DYNAMIC";   /* Dynamic linking information */
        case 7: return "SHT_NOTE";      /* Notes */
        case 8: return "SHT_NOBITS";    /* Program space with no data (bss) */
        case 9: return "SHT_REL";       /* Relocation entries, no addends */
        case 11: return "SHT_DYNSYM";   /* Dynamic linker symbol table */
        default: return "UNKNOWN";
    }
    return "UNKNOWN";
}

//段的类型
std::string Elf_parser::get_segment_type(uint32_t &seg_type) {
    switch(seg_type) {
        case PT_NULL:   return "NULL";                  /* Program header table entry unused */ 
        case PT_LOAD: return "LOAD";                    /* Loadable program segment */
        case PT_DYNAMIC: return "DYNAMIC";              /* Dynamic linking information */
        case PT_INTERP: return "INTERP";                /* Program interpreter */
        case PT_NOTE: return "NOTE";                    /* Auxiliary information */
        case PT_SHLIB: return "SHLIB";                  /* Reserved */
        case PT_PHDR: return "PHDR";                    /* Entry for header table itself */
        case PT_TLS: return "TLS";                      /* Thread-local storage segment */
        case PT_NUM: return "NUM";                      /* Number of defined types */
        case PT_LOOS: return "LOOS";                    /* Start of OS-specific */
        case PT_GNU_EH_FRAME: return "GNU_EH_FRAME";    /* GCC .eh_frame_hdr segment */
        case PT_GNU_STACK: return "GNU_STACK";          /* Indicates stack executability */
        case PT_GNU_RELRO: return "GNU_RELRO";          /* Read-only after relocation */
        //case PT_LOSUNW: return "LOSUNW";
        case PT_SUNWBSS: return "SUNWBSS";              /* Sun Specific segment */
        case PT_SUNWSTACK: return "SUNWSTACK";          /* Stack segment */
        //case PT_HISUNW: return "HISUNW";
        case PT_HIOS: return "HIOS";                    /* End of OS-specific */
        case PT_LOPROC: return "LOPROC";                /* Start of processor-specific */
        case PT_HIPROC: return "HIPROC";                /* End of processor-specific */
        default: return "UNKNOWN";
    }
}

//获取段的权限属性
std::string Elf_parser::get_segment_flags(uint32_t &seg_flags) {
    std::string flags;
    //读
    if(seg_flags & PF_R)
        flags.append("R");
    //写
    if(seg_flags & PF_W)
        flags.append("W");
    //执行
    if(seg_flags & PF_X)
        flags.append("E");

    return flags;
}
//获取符号的类型 (info表示相关信息,低4字节表示符号类型,可以用宏获取)
std::string Elf_parser::get_symbol_type(uint8_t &sym_type) {
    //判断符号类型
    switch(ELF32_ST_TYPE(sym_type)) {
        case 0: return "NOTYPE";
        case 1: return "OBJECT";
        case 2: return "FUNC";
        case 3: return "SECTION";
        case 4: return "FILE";
        case 6: return "TLS";
        case 7: return "NUM";
        case 10: return "LOOS";
        case 12: return "HIOS";
        default: return "UNKNOWN";
    }
}
//获取符号的绑定信息 (info表示相关信息,低4字节表示符号类型,可以用宏获取)
std::string Elf_parser::get_symbol_bind(uint8_t &sym_bind) {
    //判断符号的绑定信息
    switch(ELF32_ST_BIND(sym_bind)) {
        case 0: return "LOCAL";
        case 1: return "GLOBAL";
        case 2: return "WEAK";
        case 3: return "NUM";
        case 10: return "UNIQUE";
        case 12: return "HIOS";
        case 13: return "LOPROC";
        default: return "UNKNOWN";
    }
}
//符号的其它信息
std::string Elf_parser::get_symbol_visibility(uint8_t &sym_vis) {
    switch(ELF32_ST_VISIBILITY(sym_vis)) {
        case 0: return "DEFAULT";
        case 1: return "INTERNAL";
        case 2: return "HIDDEN";
        case 3: return "PROTECTED";
        default: return "UNKNOWN";
    }
}
//索引
std::string Elf_parser::get_symbol_index(uint16_t &sym_idx) {
    switch(sym_idx) {
        case SHN_ABS: return "ABS";
        case SHN_COMMON: return "COM";
        case SHN_UNDEF: return "UND";
        case SHN_XINDEX: return "COM";
        default: return std::to_string(sym_idx);
    }
}
//重定位的类型
std::string Elf_parser::get_relocation_type(uint64_t &rela_type) {
    switch(ELF64_R_TYPE(rela_type)) {
        case 1: return "R_X86_64_32";
        case 2: return "R_X86_64_PC32";
        case 5: return "R_X86_64_COPY";
        case 6: return "R_X86_64_GLOB_DAT";
        case 7:  return "R_X86_64_JUMP_SLOT";
        default: return "OTHERS";
    }
}
//获取符中的值
std::intptr_t Elf_parser::get_rel_symbol_value(
                uint64_t &sym_idx, std::vector<symbol_t> &syms) {

    std::intptr_t sym_val = 0;
    //遍历所有符号表项
    for(auto &sym: syms) {
        //判断(高 24位表示重定位符号对应符号表项在符号表内的索引
        //符号表序号
        //symbol.symbol_num       = i;
        if(sym.symbol_num == ELF64_R_SYM(sym_idx)) {
            //取其值
            sym_val = sym.symbol_value;
            break;
        }
    }
    return sym_val;
}
//符号名称
std::string Elf_parser::get_rel_symbol_name(
                uint64_t &sym_idx, std::vector<symbol_t> &syms) {

    std::string sym_name;
    for(auto &sym: syms) {
        //判断(高 24位表示重定位符号对应符号表项在符号表内的索引
        //符号表序号
        //symbol.symbol_num       = i;
        if(sym.symbol_num == ELF64_R_SYM(sym_idx)) {
            //取其名称
            sym_name = sym.symbol_name;
            break;
        }
    }
    return sym_name;
}

猜你喜欢

转载自blog.51cto.com/haidragon/2134231
今日推荐