01 ELF Header组成
纯手搓C++实现读取ELF文件头信息、段表信息和节表信息_c代码实现解析elf文件-CSDN博客
- elf header
- programm header table
- section header table
- section
- …
02 read elf
#include <iostream>
using namespace std;using ELF64_Addr = uint64_t;
using ELF64_Off = uint64_t;
using ELF64_Half = uint16_t;
using ELF64_Word = uint32_t;
using ELF64_Sword = int32_t;
using ELF64_Xword = uint64_t;
using ELF64_Sxword = int64_t;/*
e_ident[16]
包含了Maigc Number用于表明这是一个ELF文件,共16字节。
0~3:前4字节为Magic Number,固定为、'\\0x7f' 'E' 'L' 'F'。
4(EI_CLASS):00为非法文件,01位32位,02为64位。
5(EI_DATA):00为非法数据编码,01为小端序,02为大端序。
6(EI_VERSION):文件版本,固定为01。
7(EI_OSABI):操作系统ABI标识(实际未使用)。
8(EI_ABIVERSION):ABI版本(实际 未使用)。
9~15:对齐填充,无实际意义。
*/typedef struct {unsigned char e_ident[16]; ELF64_Half e_type; // 可重定位、可执行、共享目标文件ELF64_Half e_machine; // CPU架构ELF64_Word e_version;ELF64_Addr e_entry;ELF64_Off e_phoff;ELF64_Off e_shoff;ELF64_Word e_flags;ELF64_Half e_ehsize;ELF64_Half e_phentsize;ELF64_Half e_phnum;ELF64_Half e_shentsize;ELF64_Half e_shnum;ELF64_Half e_shstrndx;
} ELF64_Ehdr;typedef struct {ELF64_Half p_type;ELF64_Half p_flags;ELF64_Off p_offset;ELF64_Half p_offset;ELF64_Addr p_vaddr;ELF64_Addr p_paddr;ELF64_Xword p_filesz;ELF64_Xword p_memsz;ELF64_Xword p_align;
} ELF64_Phdr;typedef struct {ELF64_Word sh_name;ELF64_Word sh_type;ELF64_Xword sh_flags;ELF64_Addr sh_addr;ELF64_Off sh_offset;ELF64_Xword sh_size;ELF64_Word sh_link;ELF64_Word sh_info;ELF64_Xword sh_addralign;ELF64_Xword sh_entsize;
} ELF64_Shdr;int main() {char filename[10010];cin >> filename;FILE *fp = fopen(filename, "r");if (fp == NULL) {cout << "fail to open file" << endl;exit(0);}ELF64_Ehdr elf_head;int shnum, status;status = fread(&elf_head, sizeof(ELF64_Ehdr), 1, fp);if (status == 0) {cout << "fail to read elf header" << endl;exit(0);}if (elf_head.e_ident[0] != 0x7f ||elf_head.e_ident[1] != 'E' ||elf_head.e_ident[2] != 'L' ||elf_head.e_ident[3] != 'F') {cout << "not a elf file" << endl;exit(0);}printf("程序入口(start标签位置) %#x\\n\\n",elf_head.e_entry);printf("Program header table文件中偏移 %#x\\n",elf_head.e_phoff);printf("Program header table大小 %#x\\n",elf_head.e_phentsize*elf_head.e_phnum);printf("Program header数量 %d\\n\\n",elf_head.e_phnum);printf("Section header table文件中偏移 %#x\\n",elf_head.e_shoff);printf("Section header table大小 %#x\\n",elf_head.e_shentsize*elf_head.e_shnum);printf("Section header数量 %d\\n\\n",elf_head.e_shnum);// 解析Segment Header// 制作Segment Header数组用来存储每一个Segment HeaderELF64_Phdr *phdr = (ELF64_Phdr*)malloc(sizeof(ELF64_Phdr) * elf_head.e_phnum);if (NULL == phdr){printf("phdr malloc failed\\n");exit(0);}// 设置文件偏移量,定位到e_phoffstatus = fseek(fp, elf_head.e_phoff, SEEK_SET);if (0 != status){printf("\\nfaile to fseek\\n");exit(0);}// 读取所有Segment Header 到 phdr, 大小为sizeof(Elf64_Phdr) * 数量status = fread(phdr, sizeof(ELF64_Phdr) * elf_head.e_phnum, 1, fp);if (0 == status){printf("\\nfail to read segment\\n");exit(0);}// 重置指针位置到文件流开头rewind(fp);// 遍历每一个Segment Headerfor (int i = 0; i < elf_head.e_phnum; i++){printf("段首的偏移: %#x\\n", phdr[i].p_offset);printf("段在文件中的大小: %#x\\n", phdr[i].p_filesz);printf("段的运行时虚拟内存地址: %#x\\n", phdr[i].p_vaddr);printf("段在内存中的大小: %#x\\n", phdr[i].p_memsz);printf("\\n");}// 解析Section Header// 制作Section Header数组用来存储每一个Section HeaderELF64_Shdr *shdr = (ELF64_Shdr*)malloc(sizeof(ELF64_Shdr) * elf_head.e_shnum);if (NULL == shdr){printf("shdr malloc failed\\n");exit(0);}// 设置文件偏移量,定位到e_shoffstatus = fseek(fp, elf_head.e_shoff, SEEK_SET);if (0 != status){printf("\\nfaile to fseek\\n");exit(0);}// 读取所有Segment Header 到 phdr, 大小为sizeof(Elf64_Phdr) * 数量status = fread(shdr, sizeof(ELF64_Shdr) * elf_head.e_shnum, 1, fp);if (0 == status){printf("\\nfail to read section\\n");exit(0);}// 重置指针位置到文件流开头rewind(fp);// 读取每个Section的名字// 将fp指针移到字符串表(.shstrtab)内容的偏移位置处fseek(fp, shdr[elf_head.e_shstrndx].sh_offset, SEEK_SET);// e_shstrndx项是字符串表(.shstrtab)的下标// 把这个段的内容全部存储到shstrtab数组里面uint64_t size = shdr[elf_head.e_shstrndx].sh_size; char *shstrtab = (char*) malloc(size);char *temp = shstrtab;// 读取内容status = fread(shstrtab, shdr[elf_head.e_shstrndx].sh_size, 1, fp);if (0 == status){printf("\\nfaile to read\\n");}// 遍历每一个节for (int i = 0; i < elf_head.e_shnum; i++){// temp指针用于定位当前节的名字在.shstrtab节内容中的首地址temp = shstrtab;// shdr[i].sh_name表示这个节名字在(.shstrtab)节内容中的偏移temp = temp + shdr[i].sh_name;printf("节的名称: %s\\n", temp);printf("节首的偏移: %#x\\n", shdr[i].sh_offset);printf("节的大小: %#x\\n", shdr[i].sh_size);printf("节的运行时虚拟内存地址: %#x\\n", shdr[i].sh_addr);printf("\\n");}system("pause");return 0;
}