本文为看雪论坛精华文章

看雪论坛作者ID:菜鸟m号

附件链接:[原创] ELF文件格式解析器 原理 + 代码

写在前面:

读《Linux二进制》,发现作者对 ELF文件格式部分并没有做详细介绍,为了加深对elf文件格式理解,我自己着手写了个解析器, 会和readelf 工具协同对比。

原理:

ELF文件(目标文件)格式主要三种:

1. 可重定向文件(Relocatable file):文件保存着代码和适当的数据,用来和其他的目标文件一起来创建一个可执行文件或者是一个共享目标文件。(目标文件或者静态库文件,即linux通常后缀为.a和.o的文件)这是由汇编器汇编生成的 .o 文件。后面的链接器(link editor)拿一个或一些 Relocatable object files 作为输入,经链接处理后,生成一个可执行的对象文件 (Executable file) 或者一个可被共享的对象文件(Shared object file),内核可加载模块 .ko 文件也是 Relocatable object file

2. 可执行文件(Executable file):文件保存着一个用来执行的程序。(例如bash,gcc等)

3. 共享目标文件:

即 .so 文件。如果拿前面的静态库来生成可执行程序,那每个生成的可执行程序中都会有一份库代码的拷贝。如果在磁盘中存储这些可执行程序,那就会占用额外的磁盘空 间;另外如果拿它们放到Linux系统上一起运行,也会浪费掉宝贵的物理内存。如果将静态库换成动态库,那么这些问题都不会出现

一般的ELF文件有三个重要的索引表

1. ELF header:在文件的开始,描述整个文件的组织。。

2. Program header table:告诉系统如何创建进程映像。用来构造进程映像的目标文件必须具有程序头部表,可重定位文件不需要这个表。

3. Section header table :包含了描述文件节区的信息,每个节区在表中都有一项,每一项给出诸如节区名称、节区大小这类信息。用于链接的目标文件必须包含节区头部表,其他目标文件可以有,也可以没有这个表。

4. sections 或者 segments:segments是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件,也就是说,在链接阶段,我们可以忽略program header table来处理此文件,在运行阶段可以忽略section header table来处理此程序(所以很多加固手段删除了section header table)。注意:segments与sections是包含的关系,一个segment包含若干个section。

(图片来自网络)

上面提到的包含关系,我们可以用表格来对比一下结构:

了解整体结构之后,我们就可以看一下具体的结构体和指针了。

代码先写了一个help函数,包含基本信息和指令结构,效果如下:

void help(){printf("这是jentle的解析器demon");printf("-h :头部信息n");printf("-S :节区表信息n");printf("-s :符号表信息n");printf("-l :程序头信息n");printf("-r :重定位表信息n"); }

1.-h指令,查看和打印 程序header函数。

我们查看一下elf headr结构体:

typedef struct{unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */Elf32_Half e_type; /* Object file type */Elf32_Half e_machine; /* Architecture */Elf32_Word e_version; /* Object file version */Elf32_Addr e_entry; /* Entry point virtual address */Elf32_Off e_phoff; /* Program header table file offset */Elf32_Off e_shoff; /* Section header table file offset */Elf32_Word e_flags; /* Processor-specific flags */Elf32_Half e_ehsize; /* ELF header size in bytes */Elf32_Half e_phentsize; /* Program header table entry size */Elf32_Half e_phnum; /* Program header table entry count */Elf32_Half e_shentsize; /* Section header table entry size */Elf32_Half e_shnum; /* Section header table entry count */Elf32_Half e_shstrndx; /* Section header string table index */} Elf32_Ehdr;

这里面包括后面的code都会涉及到elf的数据格式,在这给出:

所以打印elf的头信息可以设计为:

//读取文件头函数void fileheader(const char *pbuff){printf("ELF Header:rn");//Magicprintf(" Magic: ");for(int i = 0;i<EI_NIDENT;++i) //e_ident[EI_NIDENT]{printf("%02X", pbuff[i]);putchar(' ');}printf("rn");//Classprintf(" %-33s:", "Class");switch(pbuff[4]){case 0:printf(" Invalid classrn");break;case 1:printf(" ELF32rn");break;case 2:printf(" ELF64rn");break;default:printf(" ERRORrn");break;}//Dataprintf(" %-33s:", "Data");switch(pbuff[5]){case 0:printf(" Invalid data encodingrn");break;case 1:printf(" 2's complement, little endianrn");break;case 2:printf(" 2's complement, big endianrn");break;default:printf(" ERRORrn");break;}//Versionprintf(" %-33s: %srn", "Version", "1(current)");//OS/ABIprintf(" %-33s: %srn", "OS/ABI", "UNIX - System V");//ABI Versionprintf(" %-33s: %srn", "ABI Version", "0");pbuff += EI_NIDENT;//Typeprintf(" %-33s:", "Type");switch(*(uint16_t*)pbuff){case 0:printf(" No file typern");break;case 1:printf(" Relocatable filern");break;case 2:printf(" Executable filern");break;case 3:printf(" Shared object filern");break;case 4:printf(" Core filern");break;default:printf(" ERRORrn");break;}pbuff += sizeof(uint16_t);//Machineprintf(" %-33s:", "Machine");switch(*(uint16_t*)pbuff){case EM_386:printf(" Intel 80386rn");break;case EM_ARM:printf(" ARMrn");break;case EM_X86_64:printf(" AMD X86-64 arrchitecturern");break;default:printf(" ERRORrn");break;}pbuff += sizeof(uint16_t);//Versionprintf(" %-33s: %srn", "version", "0X1");pbuff += sizeof(uint32_t);//入口点位置printf(" %-33s: 0X%lxrn", "Entry point address", *(uint64_t*)pbuff);pbuff += sizeof(uint64_t);//程序头大小printf(" %-33s: %lu (bytes into file)rn", "Start of program headers", *(uint64_t*)pbuff);pbuff += sizeof(uint64_t);//区段大小printf(" %-33s: %lu (bytes into file)rn", "Start of section headers", *(uint64_t*)pbuff);pbuff += sizeof(uint64_t);//Flagsprintf(" %-33s: 0X0rn", "Flags");pbuff += sizeof(Elf32_Word);//本节大小printf(" %-33s: %d (bytes)rn", "Size of this header", *(Elf32_Half*)pbuff);pbuff += sizeof(Elf32_Half);//程序头大小printf(" %-33s: %d (bytes)rn", "Size of program headers", *(Elf32_Half*)pbuff);pbuff += sizeof(Elf32_Half);//程序头大小printf(" %-33s: %drn", "Number of program headers", *(Elf32_Half*)pbuff);pbuff += sizeof(Elf32_Half);//section大小printf(" %-33s: %d (bytes)rn", "Size of section headers", *(Elf32_Half*)pbuff);pbuff += sizeof(Elf32_Half);//section大小printf(" %-33s: %drn", "Number of section headers", *(Elf32_Half*)pbuff);pbuff += sizeof(Elf32_Half);//下标值printf(" %-33s: %drn", "Section header string table index", *(Elf32_Half*)pbuff);}

效果展示:

readelf对比:

参考https://blog.csdn.net/qq_37431937前辈图解:

2. -S 指令 打印section信息

段表结构体:

typedef struct { Elf32_Word st_name; //符号表项名称。如果该值非0,则表示符号名的字//符串表索引(offset),否则符号表项没有名称。Elf32_Addr st_value; //符号的取值。依赖于具体的上下文,可能是一个绝对值、一个地址等等。Elf32_Word st_size; //符号的尺寸大小。例如一个数据对象的大小是对象中包含的字节数。unsigned char st_info; //符号的类型和绑定属性。unsigned char st_other; //未定义。Elf32_Half st_shndx; //每个符号表项都以和其他节区的关系的方式给出定义。//此成员给出相关的节区头部表索引。} Elf32_sym;

一些成员参数解释:

代码设计:

void sectionheader(const char *pbuff){//60 偏移位置得到节区数量int nNumSec = *(Elf64_Half*)(pbuff + 60);//get shstrndexElf64_Ehdr* pfilehead = (Elf64_Ehdr*)pbuff;Elf64_Half eshstrndx = pfilehead->e_shstrndx;//得到偏移地址Elf64_Shdr* psecheader = (Elf64_Shdr*)(pbuff + pfilehead->e_shoff);Elf64_Shdr* pshstr = (Elf64_Shdr*)(psecheader + eshstrndx);char* pshstrbuff = (char *)(pbuff + pshstr->sh_offset);//output infoprintf("共有 %d 节区表, 偏移位置开始于 0x%lx:rnrn",nNumSec, *(Elf64_Off*)(pbuff + 40));printf("节头:rn"); //打印标志位信息printf(" [Nr] %-16s %-16s %-16s %-16srn", "Name", "Type", "Address", "Offset");printf(" %-16s %-16s %-5s %-5s %-5s %-5srn", "Size", "EntSize", "Flags", "Link", "Info", "Align");//遍历每一个节表数量for(int i = 0;i<nNumSec;++i){printf(" [%2d] %-16s ", i, (char *)(psecheader[i].sh_name + pshstrbuff));//Typeswitch(psecheader[i].sh_type){case SHT_NULL:printf("%-16s ", "NULL");break;case SHT_PROGBITS:printf("%-16s ", "PROGBITS");break;case SHT_SYMTAB:printf("%-16s ", "SYMTAB");break;case SHT_STRTAB:printf("%-16s ", "STRTAB");break;case SHT_RELA:printf("%-16s ", "RELA");break;case SHT_HASH:printf("%-16s ", "GNU_HASH");break;case SHT_DYNAMIC:printf("%-16s ", "DYNAMIC");break;case SHT_NOTE:printf("%-16s ", "NOTE");break;case SHT_NOBITS:printf("%-16s ", "NOBITS");break;case SHT_REL:printf("%-16s ", "REL");break;case SHT_SHLIB:printf("%-16s ", "SHLIB");break;case SHT_DYNSYM:printf("%-16s ", "DYNSYM");break;case SHT_INIT_ARRAY:printf("%-16s ", "INIT_ARRY");break;case SHT_FINI_ARRAY:printf("%-16s ", "FINI_ARRY");break;case SHT_PREINIT_ARRAY:printf("%-16s ", "PREINIT_ARRAY");break;case SHT_GNU_HASH:printf("%-16s ", "GNU_HASH");break;case SHT_GNU_ATTRIBUTES:printf("%-16s ", "GNU_ATTRIBUTES");break;case SHT_GNU_LIBLIST:printf("%-16s ", "GNU_LIBLIST");break;case SHT_GNU_verdef:printf("%-16s ", "GNU_verdef");break;case SHT_GNU_verneed:printf("%-16s ", "GNU_verneed");break;case SHT_GNU_versym:printf("%-16s ", "GNU_versym");break;default:printf("%-16s ", "NONE");break;}printf("%016lX %08lXrn", psecheader[i].sh_addr, psecheader[i].sh_offset);printf(" %016lX %016lx ", psecheader[i].sh_size, psecheader[i].sh_entsize);switch (psecheader[i].sh_flags) {case 0:printf("%3s %4u %4u %4lurn","", psecheader[i].sh_link, psecheader[i].sh_info, psecheader[i].sh_addralign);break;case 1:printf("%3s %4u %4u %4lurn","W", psecheader[i].sh_link, psecheader[i].sh_info, psecheader[i].sh_addralign);break;case 2:printf("%3s %4u %4u %4lurn","A", psecheader[i].sh_link, psecheader[i].sh_info, psecheader[i].sh_addralign);break;case 4:printf("%3s %4u %4u %4lurn","X", psecheader[i].sh_link, psecheader[i].sh_info, psecheader[i].sh_addralign);break;case 3:printf("%3s %4u %4u %4lurn","WA", psecheader[i].sh_link, psecheader[i].sh_info, psecheader[i].sh_addralign);break;case 5://WXprintf("%3s %4u %4u %4lurn","WX", psecheader[i].sh_link, psecheader[i].sh_info, psecheader[i].sh_addralign);break;case 6://AXprintf("%3s %4u %4u %4lurn","AX", psecheader[i].sh_link, psecheader[i].sh_info, psecheader[i].sh_addralign);break;case 7://WAXprintf("%3s %4u %4u %4lurn","WAX", psecheader[i].sh_link, psecheader[i].sh_info, psecheader[i].sh_addralign);break;case SHF_MASKPROC://MSprintf("%3s %4u %4u %4lurn","MS", psecheader[i].sh_link, psecheader[i].sh_info, psecheader[i].sh_addralign);break;default:printf("NONErn");break;}}printf("Key to Flags:rn");printf(" W (write), A (alloc), X (execute), M (merge), S (strings), l (large)rn");printf(" I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)rn");printf(" O (extra OS processing required) o (OS specific), p (processor specific)rn");}

效果展示:

readelf 对比:

3. -s 打印符号表信息

目标文件的符号表中包含用来定位、重定位程序中符号定义和引用的信息。符号表 索引是对此数组的索引。索引 0 表示表中的第一表项,同时也作为 定义符号的索引。

符号是对某些类型的数据或者代码(如全局变量或函数)的符号引用。 例如,printf()函数会在动态符号表.dynsym 中存有一个指向该函数的 符号条目。在大多数共享库和动态链接可执行文件中,存在两个符号表。 如前面使用readelf –S命令输出的内容中,可以看到有两个节:.dynsym 和.symtab。

.dynsym保存了引用来自外部文件符号的全局符号,如printf这样的 库函数,.dynsym保存的符号是.symtab所保存符号的子集,.symtab中 还保存了可执行文件的本地符号,如全局变量,或者代码中定义的本地函数 等。因此,.symtab保存了所有的符号,而.dynsym只保存动态/全局符号。

因此,就存在这样一个问题:既然.symtab 中保存了.dynsym 中所有的 符号,那么为什么还需要两个符号表呢?使用readelf –S命令查看可执行文 件的输出,可以看到一部分节被标记为了 A(ALLOC) 、WA(WRITE/ALLOC) 或者 AX(ALLOC/EXEC) 。.dynsym 是被标记了 ALLOC 的,而.symtab 则没有标记。 ALLOC 表示有该标记的节会在运行时分配并装载进入内存,而.symtab 不是在运行时必需的,因此不会被装载到内存中。.dynsym保存的符号只能在运行时被解析,因此是运行时动态链接器所需要的唯一符号。.dynsym符号表 对于动态链接可执行文件的执行来说是必需的,而.symtab符号表只是用来进 行调试和链接的,有时候为了节省空间,会将.symtab符号表从生产二进制文 件中删掉。

符号表:.dynsym

符号表包含用来定位、重定位程序中符号定义和引用的信息,简单的理解就是符号表记录了该文件中的所有符号,所谓的符号就是经过修饰了的函数名或者变量名,不同的编译器有不同的修饰规则。例如符号_ZL15global_static_a,就是由global_static_a变量名经过修饰而来。

符号表格式如下:

typedef struct { Elf32_Word st_name; //符号表项名称。如果该值非0,则表示符号名的字//符串表索引(offset),否则符号表项没有名称。Elf32_Addr st_value; //符号的取值。依赖于具体的上下文,可能是一个绝对值、一个地址等等。Elf32_Word st_size; //符号的尺寸大小。例如一个数据对象的大小是对象中包含的字节数。unsigned char st_info; //符号的类型和绑定属性。unsigned char st_other; //未定义。Elf32_Half st_shndx; //每个符号表项都以和其他节区的关系的方式给出定义。//此成员给出相关的节区头部表索引。} Elf32_sym;

字符串表.dynstr 略

void tableheader(const char *pbuff){//从节区里面定位到偏移Elf64_Ehdr* pfilehead = (Elf64_Ehdr*)pbuff;Elf64_Half eshstrndx = pfilehead->e_shstrndx;Elf64_Shdr* psecheader = (Elf64_Shdr*)(pbuff + pfilehead->e_shoff);Elf64_Shdr* pshstr = (Elf64_Shdr*)(psecheader + eshstrndx);char* pshstrbuff = (char *)(pbuff + pshstr->sh_offset);for(int i = 0;i<pfilehead->e_shnum;++i){if(!strcmp(psecheader[i].sh_name + pshstrbuff, ".dynsym") || !strcmp(psecheader[i].sh_name + pshstrbuff, ".symtab")){Elf64_Sym* psym = (Elf64_Sym*)(pbuff + psecheader[i].sh_offset);int ncount = psecheader[i].sh_size / psecheader[i].sh_entsize;char* pbuffstr = (char*)((psecheader + psecheader[i].sh_link)->sh_offset + pbuff);printf("Symbol table '%s' contains %d entries:rn", psecheader[i].sh_name + pshstrbuff, ncount);outputsyminfo(psym, pbuffstr, ncount);continue;}}}

效果展示:

readelf 对比:

参数解释:

STT_NOTYPE:符号类型未定义。

STT_FUNC:表示该符号与函数或者其他可执行代码关联。

STT_OBJECT:表示该符号与数据目标文件关联。

符号绑定

STB_LOCAL:本地符号在目标文件之外是不可见的,目标文件包含 了符号的定义,如一个声明为 static 的函数。

STB_GLOBAL:全局符号对于所有要合并的目标文件来说都是可见 的。一个全局符号在一个文件中进行定义后,另外一个文件可以对这 个符号进行引用。

STB_WEAK:与全局绑定类似,不过比 STB_GLOBAL 的优先级低。 被标记为 STB_WEAK 的符号有可能会被同名的未被标记为 STB_WEAK的符号覆盖。

下面是对绑定和类型字段进行打包和解包的宏指令。

ELF32_ST_BIND(info)或者 ELF64_ST_BIND(info):从 st_info 值中提取出一个绑定。 ELF32_ST_TYPE(info)或者 ELF64_ST_TYPE(info):从 st_info 值中提取类型。 ELF32_ST_TYPE(bind,type)或者ELF64_ST_INFO(bind,type): 将一个绑定和类型转换成st_info值。

4. -l 指令 program头信息。

程序头表与段表互相独立,有ELF文件头同一管理。

结构信息:

typedef struct { Elf32_Word p_type; //此数组元素描述的段的类型,或者如何解释此数组元素的信息。 Elf32_Off p_offset; //此成员给出从文件头到该段第一个字节的偏移Elf32_Addr p_vaddr; //此成员给出段的第一个字节将被放到内存中的虚拟地址Elf32_Addr p_paddr; //此成员仅用于与物理地址相关的系统中。System V忽略所有应用程序的物理地址信息。Elf32_Word p_filesz; //此成员给出段在文件映像中所占的字节数。可以为0。Elf32_Word p_memsz; //此成员给出段在内存映像中占用的字节数。可以为0。Elf32_Word p_flags; //此成员给出与段相关的标志。Elf32_Word p_align; //此成员给出段在文件中和内存中如何对齐。} Elf32_phdr;

参数解释:

(1) p_type表示当前描述的段的种类。常见有以下常数。#define PT_NULL 0 //空段#define PT_LOAD 1 //可装载段#define PT_DYNAMIC 2 //表示该段包含了用于动态连接器的信息#define PT_INTERP 3 //表示当前段指定了用于动态连接的程序解释器,通常是ld-linux.so#define PT_NOTE 4 //该段包含有专有的编译器信息#define PT_SHLIB 5 //该段包含有共享库(2) p_offset给出了该段在二进制文件中的偏移量,单位为字节。(3) p_vaddr给出了该段需要映射到进程虚拟地址空间中的位置。(4) p_paddr在只支持物理寻址,不支持虚拟寻址的系统当中才使用。(5) p_filesz给出了该段在二进制文件当中的长度,单位为字节。(6) p_memsz给出了段在虚拟地址空间当中的长度,单位为字节。与p_filesz不等时会通过截断数据或者以0填充的方式处理。(7) p_flags保存了标志信息,定义了该段的访问权限。有如下值#define PF_R 0x4 //该段可读#define PF_W 0x2 //该段可写#define PF_X 0x1 //该段可执行(8) p_align指定了段在内存和二进制文件当中的对齐方式,即p_offset和p_vaddr必须是p_align的整数倍。

设计代码:

void programheader(const char *pbuff){Elf64_Ehdr* pfilehead = (Elf64_Ehdr*)pbuff;Elf64_Phdr* pproheader = (Elf64_Phdr*)(pbuff + pfilehead->e_phoff);Elf64_Shdr* psecheader = (Elf64_Shdr*)(pbuff + pfilehead->e_shoff);Elf64_Shdr* pshstr = (Elf64_Shdr*)(psecheader + pfilehead->e_shstrndx);char* pstrbuff = (char*)(pbuff + pshstr->sh_offset);printf("Elf 文件类型是");switch(pfilehead->e_type){case 0:printf(" No file typern");break;case 1:printf(" Relocatable filern");break;case 2:printf(" Executable filern");break;case 3:printf(" Shared object filern");break;case 4:printf(" Core filern");break;default:printf(" ERRORrn");break;}printf("入口点位置 0X%0lXrn", pfilehead->e_entry);printf("共有 %d 程序头, 偏移位置 %lurnrn", pfilehead->e_phnum, pfilehead->e_phoff);printf("Program Headers:rn");printf(" %-14s %-16s %-16s %-16srn", "Type", "Offset", "VirtAddr", "PhysAddr");printf(" %-14s %-16s %-16s %-6s %-6srn", "", "FileSiz", "MemSiz", "Flags", "Align");for(int i=0;i<pfilehead->e_phnum;++i){//typeswitch(pproheader[i].p_type){case PT_NULL:printf(" %-14s %016lX %016lX %016lXrn %-14s %016lX %016lX ", "", pproheader[i].p_offset, pproheader[i].p_vaddr,pproheader[i].p_paddr, "", pproheader[i].p_filesz, pproheader[i].p_memsz);break;case PT_LOAD:printf(" %-14s %016lX %016lX %016lXrn %-14s %016lX %016lX ", "LOAD", pproheader[i].p_offset, pproheader[i].p_vaddr,pproheader[i].p_paddr, "", pproheader[i].p_filesz, pproheader[i].p_memsz);break;case PT_DYNAMIC:printf(" %-14s %016lX %016lX %016lXrn %-14s %016lX %016lX ", "DYNAMIC", pproheader[i].p_offset, pproheader[i].p_vaddr,pproheader[i].p_paddr, "", pproheader[i].p_filesz, pproheader[i].p_memsz);break;case PT_INTERP:printf(" %-14s %016lX %016lX %016lXrn %-14s %016lX %016lX ", "INTERP", pproheader[i].p_offset, pproheader[i].p_vaddr,pproheader[i].p_paddr, "", pproheader[i].p_filesz, pproheader[i].p_memsz);break;case PT_NOTE:printf(" %-14s %016lX %016lX %016lXrn %-14s %016lX %016lX ", "NOTE", pproheader[i].p_offset, pproheader[i].p_vaddr,pproheader[i].p_paddr, "", pproheader[i].p_filesz, pproheader[i].p_memsz);break;case PT_SHLIB:printf(" %-14s %016lX %016lX %016lXrn %-14s %016lX %016lX ", "SHLIB", pproheader[i].p_offset, pproheader[i].p_vaddr,pproheader[i].p_paddr, "", pproheader[i].p_filesz, pproheader[i].p_memsz);break;case PT_PHDR:printf(" %-14s %016lX %016lX %016lXrn %-14s %016lX %016lX ", "PHDR", pproheader[i].p_offset, pproheader[i].p_vaddr,pproheader[i].p_paddr, "", pproheader[i].p_filesz, pproheader[i].p_memsz);break;case PT_TLS:printf(" %-14s %016lX %016lX %016lXrn %-14s %016lX %016lX ", "TLS", pproheader[i].p_offset, pproheader[i].p_vaddr,pproheader[i].p_paddr, "", pproheader[i].p_filesz, pproheader[i].p_memsz);break;case PT_NUM:printf(" %-14s %016lX %016lX %016lXrn %-14s %016lX %016lX ", "NUM", pproheader[i].p_offset, pproheader[i].p_vaddr,pproheader[i].p_paddr, "", pproheader[i].p_filesz, pproheader[i].p_memsz);break;case PT_GNU_EH_FRAME:printf(" %-14s %016lX %016lX %016lXrn %-14s %016lX %016lX ", "GNU_EH_FRAME", pproheader[i].p_offset, pproheader[i].p_vaddr,pproheader[i].p_paddr, "", pproheader[i].p_filesz, pproheader[i].p_memsz);break;case PT_GNU_RELRO:printf(" %-14s %016lX %016lX %016lXrn %-14s %016lX %016lX ", "GNU_RELRO", pproheader[i].p_offset, pproheader[i].p_vaddr,pproheader[i].p_paddr, "", pproheader[i].p_filesz, pproheader[i].p_memsz);break;case PT_GNU_STACK:printf(" %-14s %016lX %016lX %016lXrn %-14s %016lX %016lX ", "GNU_STACK", pproheader[i].p_offset, pproheader[i].p_vaddr,pproheader[i].p_paddr, "", pproheader[i].p_filesz, pproheader[i].p_memsz);break;default:break;}//switch(pproheader[i].p_flags){case PF_X:printf("%-6s %-lXrn", " E", pproheader[i].p_align);break;case PF_W:printf("%-6s %-lXrn", " W ", pproheader[i].p_align);break;case PF_R:printf("%-6s %-lXrn", "R ", pproheader[i].p_align);break;case PF_X|PF_W:printf("%-6s %-lXrn", " WE", pproheader[i].p_align);break;case PF_X|PF_R:printf("%-6s %-lXrn", "R E", pproheader[i].p_align);break;case PF_W|PF_R:printf("%-6s %-lXrn", "RW ", pproheader[i].p_align);break;case PF_X|PF_R|PF_W:printf("%-6s %-lXrn", "RWE", pproheader[i].p_align);break;default:printf("rn");break;}if(PT_INTERP == pproheader[i].p_type)printf(" [Requesting program interpreter: %s]rn", (char*)(pbuff + pproheader[i].p_offset));}printf("rn Section to Segment mapping:rn");printf(" 段节...rn");for(int i=0;i<pfilehead->e_phnum;++i){printf(" %-7d", i);for(int n = 0;n<pfilehead->e_shnum;++n){Elf64_Off temp = psecheader[n].sh_addr + psecheader[n].sh_size;if((psecheader[n].sh_addr>pproheader[i].p_vaddr && psecheader[n].sh_addr<pproheader[i].p_vaddr + pproheader[i].p_memsz) ||(temp > pproheader[i].p_vaddr && temp<=pproheader[i].p_vaddr + pproheader[i].p_memsz)){printf("%s ", (char*)(psecheader[n].sh_name + pstrbuff));}}printf("rn");}}

效果展示:

readelf对比:

最后还有一个重定位表的打印函数,暂时不介绍了。

gcc 删除elf_ELF文件格式解析器 原理 + 代码相关推荐

  1. 4符号代码_ELF文件格式解析器 原理 + 代码

    本文为看雪论坛精华文章 看雪论坛作者ID:菜鸟m号 写在前面 读<Linux 二进制>,发现作者对 ELF 文件格式部分并没有做详细介绍,为了加深对 ELF 文件格式理解,我自己着手写了个 ...

  2. wireshark协议解析器原理与插件编写

    工作原理 每个解析器解码自己的协议部分, 然后把封装协议的解码传递给后续协议. 因此它可能总是从一个Frame解析器开始, Frame解析器解析捕获文件自己的数据包细节(如:时间戳), 将数据交给一个 ...

  3. dom技术解析xml下jaxp解析器详细代码

    1.使用jaxp实现查询操作 person.xml <?xml version="1.0" encoding="UTF-8" standalone=&qu ...

  4. Python爬虫:URL管理器及其实现方式、网页下载器、网页解析器原理及其实现原理!

    Python爬虫之URL管理器: Python爬虫:URL管理器实现方式: Python爬虫之网页下载器: urllib2实现网页下载器的三种方法: 具体代码:                    ...

  5. python解析器原理_Python程序运行原理图文解析

    本文研究的主要是Python程序运行原理,具体介绍如下. 编译型语言(C语言为例) 动态型语言 一个程序是如何运行起来的?比如下面的代码 #othermodule.py def add(a, b): ...

  6. CedarX中代码技术的应用借鉴 (二)多态的方式创建格式解析器

    前言 CedarX是全志科技开源的多媒体SDK,其解码的调用基于自研的解码器接口,向上可对接MediaPlayer接口.本文记录与分析其源代码中对于C语言方面的代码技术的应用,仅作记录与借鉴. 源码参 ...

  7. SpringMVC源码之参数解析绑定原理

    摘要 本文从源码层面简单讲解SpringMVC的参数绑定原理 SpringMVC参数绑定相关组件的初始化过程 在理解初始化之前,先来认识一个接口 HandlerMethodArgumentResolv ...

  8. java会员卡的绑定和解绑_SpringMVC源码之参数解析绑定原理

    摘要 本文从源码层面简单讲解SpringMVC的参数绑定原理 SpringMVC参数绑定相关组件的初始化过程 在理解初始化之前,先来认识一个接口 HandlerMethodArgumentResolv ...

  9. 笨办法学 Python · 续 练习 33:解析器

    练习 33:解析器 原文:Exercise 33: Parsers 译者:飞龙 协议:CC BY-NC-SA 4.0 自豪地采用谷歌翻译 想象一下,你将获得一个巨大的数字列表,你必须将其输入到电子表格 ...

最新文章

  1. Android 在运行时请求权限
  2. 一文让你搞懂YOLO难关!
  3. frameset小结
  4. sqlserver 循环赋值变量
  5. 深度学习--Matlab使用LSTM长短期记忆网络对负荷进行分类
  6. 虚拟机的网络连接模式
  7. 基于zookeeper(集群)+LevelDB的ActiveMq高可用集群安装、配置、测试
  8. 32利用文件系统保存数据_网易技术实践|Docker文件系统实战
  9. 背景色透明,里面内容(图片、文字)不透明
  10. 【MySQL】外键踩坑集
  11. java编译提示错误信息_java常见编译错误信息
  12. JSONP原理及实现
  13. (小知识点) textview.setBackground不起作用
  14. STM32F103ZET6+TJA1050 HAL CAN通讯笔记
  15. 【论文阅读】Iterative Answer Prediction with Pointer-Augmented Multimodal Transformers for TextVQA
  16. 高德交通大数据为道路安全解法提供基础
  17. Android vivo手机无法调试安装Apk的解决办法
  18. 从 “搞不清楚” 到 “都明白了” 的费曼
  19. 爬虫模拟登录人人网的三种方法
  20. pip 批量安装和卸载package

热门文章

  1. Qt UDP 广播简单示例
  2. Java中在一个字符串的固定位置插入字符串
  3. 微信小程序自带地图_【小程序】微信小程序之地图功能
  4. 4.1.4 OS之文件的物理结构(连续分配、链接分配[隐式-显式]、索引分配[链接方案-多层索引-混合索引])
  5. 常用API-1(Object类、String类、StringBuffer类、StringBuilder类)
  6. Cpp 对象模型探索 / 系列文章的索引
  7. 乐鑫代理启明云端分享|基于ESP32-S2彩色触摸屏86面板方案
  8. mysql scope runtime_Maven依赖scope属性详解-一个报错引发的问题 - 老郭种树
  9. 转账 程序c语言,求C语言原创小游戏源代码(运行成功可支付宝转账)急!!!
  10. 怎么看到方法内引用方法的注释_网页内文字无法复制怎么办?一分钟看懂这些方法,让你随意复制...