gcc 删除elf_ELF文件格式解析器 原理 + 代码
本文為看雪論壇精華文章
看雪論壇作者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文件格式解析器 原理 + 代码的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 洗洁精洗车有危害吗?
- 下一篇: 本田鹰仔100前刹车油怎么换?