ELF 就是 Executable and Linkable Format,它定义了可重定位文件、可执行文件和 共享目标文件的结构。这种格式能让操作系统正确解释文件中的机器指令。
理解ELF文件(目标文件)格式主要三种结构
- 可重定向文件(Relocatable file)
文件保存着代码和适当的数据,用来和其他的目标文件一起来创建一个可执行文件或者是一个共享目标文件。(目标文件或者静态库文件,即linux通常后缀为.a和.o的文件)这是由汇编器汇编生成的 .o 文件。
- 可执行文件(Executable file)
文件保存着一个用来执行的程序。(例如bash,gcc等)
- 共享目标文件
一种是可用于静态链接文件,另一种是程序运行中被动态链接文件,如Linux下的".a"和".so"文件,Windows的dll文件。
ELF文件中三个重要的索引表
-
ELF header:在文件的开始,描述整个文件的组织。
-
Program header table:告诉系统如何创建进程映像。用来构造进程映像的目标文件必须具有程序头部表,可重定位文件不需要这个表。
-
Section header table :包含了描述文件节区的信息,每个节区在表中都有一项,每一项给出诸如节区名称、节区大小这类信息。用于链接的目标文件必须包含节区头部表,其他目标文件可以有,也可以没有这个表。
-
sections 或者 segments:segments是从运行的角度来描述elf文件,sections是从链接的角度来描述elf文件,也就是说,在链接阶段,我们可以忽略program header table来处理此文件,在运行阶段可以忽略section header table来处理此程序(所以很多加固手段删除了section header table)。注意:segments与sections是包含的关系,一个segment包含若干个section
目标文件ELF结构
下面我们分析可执行文件的ELF结构。看看下面的例子:
my_sleep.c
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void sig_alarm(int signo)
{
//Do Nothing
}
unsigned int my_sleep(unsigned int nsec)
{
struct sigaction new_act;
struct sigaction old_act;
sigset_t newmask,oldmask,suspmask;
unsigned int unslept=0;
new_act.sa_handler=sig_alarm;
sigemptyset(&new_act.sa_mask);
new_act.sa_flags=0;
sigaction(SIGALRM,&new_act,&old_act);//注册信号处理函数
sigemptyset(&newmask);
sigaddset(&newmask,SIGALRM);
sigprocmask(SIG_BLOCK,&newmask,&oldmask);
alarm(nsec);//设定闹钟
suspmask=oldmask;
sigdelset(&suspmask,SIGALRM);
sigsuspend(&suspmask);
unslept=alarm(0);//取消闹钟
sigaction(SIGALRM,&old_act,NULL);//恢复默认信号处理处理动作
sigprocmask(SIG_SETMASK,&oldmask,NULL);
return unslept;
}
int main(int argc,char **argv)
{
while(1)
{
my_sleep(5);
printf("5 seconds!\n");
break;
}
return 0;
}
通常我们会误认为只有二进制或可执行的文件是 ELF 格式。其实目标文件也是 ELF 格式的,还有动态库、核转储文件,甚至内核和内核模块上也是。
接着使用命令readelf -S my_sleep得到Section Table。如下:
在程序中,段(segment)并不等于节(section)。段是程序执行的必要组成部分,在每个段中,会有代码或者数据被划分为不同的节。节头表是对这些节的位置和大小描述,主要用于链接和调试。节头表对于程序的执行来说不是必需的,没有节头表,程序仍然可以正常执行,因为节头表没有对程序的内存布局进行描述,对程序内存布局描述是程序头表的任务。
section类型:
1、.text节
.text节是保留了程序代码指令的代码节。一段可执行程序,如果存在Phdr,.text节就会存放在text段中。由于.text节保存了程序代码,因此节的类型为SHT_PROGBITS。
2、.rodata节
.rodata节保存了只读数据,因此只能存放于一个可执行文件的只读段中。也因此,只能在text段(不是data段)中找到.rodata节。由于.rodata节是只读的,因此节类型为SHT_PROGBITS。
3、.plt节
过程链接表(Procedure Linkage Table,PLT),.plt节中包含了动态链接器调用从共享库导入的函数所必需的相关代码。由于其存在于text段中,同样保存了代码,因此节类型为SHT_PROGBITS。
4、.data节
.data节存在于data段中,保存了初始化的全局变量等数据。由于其保存了程序的变量数据,因此节类型被标记为SHT_PROGBITS。
5、.bss节
.bss节保存了未进行初始化的全局数据,是data段的一部分,占用空间不超过4字节,仅表示这个节本身的空间。程序加载时数据被初始化为0,在程序执行期间可以进行赋值。由于.bss节未保存实际的数据,因此节类型为SHT_PROGBITS。
6、.got.plt节
.got节保存了全局偏移表。.got节和.plt节一起提供了对导入的共享库函数的访问入口,由动态链接器在运行时进行修改。如果攻击者获得了堆或者.bss漏洞的一个指针大小的写原语,就可以对该节任意进行修改。.got.plt节跟程序执行有关,因此节类型被标记为SHT_PROGBITS。
7、.dynsym节
.dynsym节保存了从共享库导入的动态符号信息,该节保存在text段中,节类型被标记为SHT_PROGBITS。
8、.dynstr节
.dynstr节保存了动态符号字符串表,表中存放了一系列字符串,这些字符串代表了符号的名称,以空字符作为终止符。
9、.rel.*节
重定位节保存了重定位相关的信息,这些信息描述;了在链接或者运行时,对ELF目标文件的某部分内容或者进程镜像进行补充或者修改。重定位节保存了重定位相关的数据,因此节类型被标记为SHT_REL。
10、.hash节
.hash节有时也称为.gnu.hahs,保存了一个用于查找符号的散列表。
11、.symtab节
.symtab节保存了ElfN_Sym类型的符号信息,因此节类型被标记为SHT_SYMTAB。
12、.strtab节
.strtab节保存的是符号字符串表,表中的内容会被.symtab的ElfN_Sym结构中的st_name条目引用。由于其保存了字符串表,因此节类型被标记为SHT_STRTAB。
13、.shstrtab节
.shstrtab节保存节头字符串表,该表是一个以空字符终止的字符串的集合,字符串保存了每个节的节名,如.text、.data等。有一个名为e_shsrndx的ELF文件头条目会指向.shstrtab节,e_shstrndx中保存了.shstrtab的偏移量。由于其保存了字符串表,因此节类型被标记为SHT_STRTAB。
14、.ctors和.dtors节
.ctors(构造器)和.dtors(析构器)这两个节保存了指向析构函数和析构函数的指针,构造函数是在main函数执行之前需要执行的代码,析构函数是在main函数之后需要执行的代码。
C/C++实现 从进程映像重建一个可执行的ELF文件
...
static struct string_table shstrtab[] = {
{ SH_NULL, "", 0 },
{ SH_INTERP, ".interp", 7 },
{ SH_TEXT, ".text", 5 },
{ SH_DYNSTR, ".dynstr", 7 },
{ SH_DYNAMIC, ".dynamic", 8 },
{ SH_RELA_DYN, ".rela.dyn", 9 },
{ SH_REL_DYN, ".rel.dyn", 8 },
{ SH_RELA_PLT, ".rela.plt", 9 },
{ SH_REL_PLT, ".rel.plt", 8 },
{ SH_INIT, ".init", 5 },
{ SH_GOT_PLT, ".got.plt", 8 },
{ SH_DATA, ".data", 5 },
{ SH_DYNSYM, ".dynsym", 7 },
{ SH_HASH, ".hash", 5 },
{ SH_GNU_HASH, ".gnu.hash", 9 },
{ SH_VERNEED, ".gnu.version_r", 14 },
{ SH_VERSYM, ".gnu.version", 12 },
{ SH_FINI, ".fini", 5 },
{ SH_SHSTRTAB, ".shstrtab", 9 },
{ SH_PLT_GOT, ".plt.got", 8 },
{ SH_NOTE, ".note", 5 },
{ SH_EH_FRAME_HDR, ".eh_frame_hdr", 13 },
{ SH_EH_FRAME, ".eh_frame", 9 },
{ SH_RODATA, ".rodata", 7 },
{ SH_INIT_ARRAY, ".init_array", 11 },
{ SH_FINI_ARRAY, ".fini_array", 11 },
{ SH_BSS, ".bss", 4 },
};
...
static bool parse_elf(struct elf *elf, pid_t pid)
{
int nsym;
elf->ehdr = (elf_ehdr *) elf->buf;
elf->phdr = (elf_phdr *) (elf->buf + elf->ehdr->e_phoff);
elf->shdr = xcalloc(NUM_SECTIONS, sizeof(*elf->shdr));
elf->section_list = xcalloc(1, sizeof(*elf->section_list));
elf->section_list->shdr = &elf->shdr[SH_NULL];
elf->section_list->id = SH_NULL;
if (elf->ehdr->e_type != ET_EXEC && elf->ehdr->e_type != ET_DYN) {
err_msg("ELF type not supported: %d", elf->ehdr->e_type);
return false;
}
for (int i = 0; i < elf->ehdr->e_phnum; i++) {
switch (elf->phdr[i].p_type) {
case PT_LOAD:
if (elf->phdr[i].p_offset && elf->phdr[i].p_flags == (PF_R | PF_W)) {
printf(" Data segment: " XFMT " - " XFMT " (offset: " XFMT ", size: " UFMT " bytes)\n",
elf->phdr[i].p_vaddr, elf->phdr[i].p_vaddr + elf->phdr[i].p_filesz,
elf->phdr[i].p_offset, elf->phdr[i].p_filesz);
ph_data = i;
elf->shdr[SH_DATA].sh_name = get_strtbl_idx(shstrtab, ARRAY_SIZE(shstrtab), SH_DATA);
elf->shdr[SH_DATA].sh_type = SHT_PROGBITS;
elf->shdr[SH_DATA].sh_flags = SHF_WRITE | SHF_ALLOC;
elf->shdr[SH_DATA].sh_link = SHN_UNDEF;
elf->shdr[SH_DATA].sh_info = 0;
elf->shdr[SH_DATA].sh_addralign = sizeof(elf_addr);
elf->shdr[SH_DATA].sh_entsize = 0;
} else if (elf->phdr[i].p_offset && elf->phdr[i].p_flags == (PF_R | PF_X)) {
printf(" Text segment: " XFMT " - " XFMT " (offset: " XFMT ", size: " UFMT " bytes)\n",
elf->phdr[i].p_vaddr, elf->phdr[i].p_vaddr + elf->phdr[i].p_filesz,
elf->phdr[i].p_offset, elf->phdr[i].p_filesz);
ph_text = i;
elf->shdr[SH_TEXT].sh_name = get_strtbl_idx(shstrtab, ARRAY_SIZE(shstrtab), SH_TEXT);
elf->shdr[SH_TEXT].sh_type = SHT_PROGBITS;
elf->shdr[SH_TEXT].sh_flags = SHF_EXECINSTR | SHF_ALLOC;
elf->shdr[SH_TEXT].sh_link = SHN_UNDEF;
elf->shdr[SH_TEXT].sh_info = 0;
elf->shdr[SH_TEXT].sh_addralign = 16;
elf->shdr[SH_TEXT].sh_entsize = 0;
} else if (elf->phdr[i].p_offset && elf->phdr[i].p_flags == PF_R) {
ph_rodata = i;
elf->shdr[SH_RODATA].sh_name = get_strtbl_idx(shstrtab, ARRAY_SIZE(shstrtab), SH_RODATA);
elf->shdr[SH_RODATA].sh_type = SHT_PROGBITS;
elf->shdr[SH_RODATA].sh_flags = SHF_ALLOC;
elf->shdr[SH_RODATA].sh_addr = elf->phdr[i].p_vaddr;
elf->shdr[SH_RODATA].sh_offset = elf->phdr[i].p_offset;
elf->shdr[SH_RODATA].sh_link = SHN_UNDEF;
elf->shdr[SH_RODATA].sh_info = 0;
elf->shdr[SH_RODATA].sh_addralign = sizeof(elf_addr);
elf->shdr[SH_RODATA].sh_entsize = 0;
section_list_add(elf, SH_RODATA);
}
break;
case PT_INTERP:
elf->shdr[SH_INTERP].sh_name = get_strtbl_idx(shstrtab, ARRAY_SIZE(shstrtab), SH_INTERP);
elf->shdr[SH_INTERP].sh_type = SHT_PROGBITS;
elf->shdr[SH_INTERP].sh_flags = SHF_ALLOC;
elf->shdr[SH_INTERP].sh_addr = elf->phdr[i].p_vaddr;
elf->shdr[SH_INTERP].sh_offset = elf->phdr[i].p_offset;
elf->shdr[SH_INTERP].sh_size = elf->phdr[i].p_filesz;
elf->shdr[SH_INTERP].sh_link = SHN_UNDEF;
elf->shdr[SH_INTERP].sh_info = 0;
elf->shdr[SH_INTERP].sh_addralign = elf->phdr[i].p_align;
elf->shdr[SH_INTERP].sh_entsize = 0;
section_list_add(elf, SH_INTERP);
break;
case PT_DYNAMIC:
printf(" Dynamic segment: " XFMT " - " XFMT " (size: " UFMT " bytes)\n",
elf->phdr[i].p_vaddr, elf->phdr[i].p_vaddr + elf->phdr[i].p_memsz,
elf->phdr[i].p_memsz);
elf->dyn = (elf_dyn *) (elf->buf + elf->phdr[i].p_offset);
elf->shdr[SH_DYNAMIC].sh_name = get_strtbl_idx(shstrtab, ARRAY_SIZE(shstrtab), SH_DYNAMIC);
elf->shdr[SH_DYNAMIC].sh_type = SHT_DYNAMIC;
/* 如果SHF_WRITE位被设置为特定于处理器,则检查p_flags */
elf->shdr[SH_DYNAMIC].sh_flags = SHF_ALLOC;
elf->shdr[SH_DYNAMIC].sh_addr = elf->phdr[i].p_vaddr;
elf->shdr[SH_DYNAMIC].sh_offset = elf->phdr[i].p_offset;
elf->shdr[SH_DYNAMIC].sh_size = elf->phdr[i].p_filesz;
elf->shdr[SH_DYNAMIC].sh_info = 0;
elf->shdr[SH_DYNAMIC].sh_addralign = elf->phdr[i].p_align;
elf->shdr[SH_DYNAMIC].sh_entsize = 0;
section_list_add(elf, SH_DYNAMIC);
read_dynamic_segment(elf);
nsym = get_nsymbols(elf);
elf->shdr[SH_DYNSYM].sh_size = nsym * elf->shdr[SH_DYNSYM].sh_entsize;
elf->shdr[SH_VERSYM].sh_size = nsym * sizeof(elf_half);
if (elf->reldyn_type == DT_RELA)
elf->shdr[SH_VERNEED].sh_size = elf->shdr[SH_RELA_DYN].sh_addr - elf->shdr[SH_VERNEED].sh_addr;
else
elf->shdr[SH_VERNEED].sh_size = elf->shdr[SH_REL_DYN].sh_addr - elf->shdr[SH_VERNEED].sh_addr;
break;
case PT_NOTE:
elf->shdr[SH_NOTE].sh_name = get_strtbl_idx(shstrtab, ARRAY_SIZE(shstrtab), SH_NOTE);
elf->shdr[SH_NOTE].sh_type = SHT_NOTE;
elf->shdr[SH_NOTE].sh_flags = SHF_ALLOC;
elf->shdr[SH_NOTE].sh_addr = elf->phdr[i].p_vaddr;
elf->shdr[SH_NOTE].sh_offset = elf->phdr[i].p_offset;
elf->shdr[SH_NOTE].sh_size = elf->phdr[i].p_filesz;
elf->shdr[SH_NOTE].sh_link = SHN_UNDEF;
elf->shdr[SH_NOTE].sh_info = 0;
elf->shdr[SH_NOTE].sh_addralign = 4;
elf->shdr[SH_NOTE].sh_entsize = 0;
section_list_add(elf, SH_NOTE);
break;
case PT_GNU_EH_FRAME:
elf->shdr[SH_EH_FRAME_HDR].sh_name = get_strtbl_idx(shstrtab, ARRAY_SIZE(shstrtab), SH_EH_FRAME_HDR);
elf->shdr[SH_EH_FRAME_HDR].sh_type = SHT_PROGBITS;
elf->shdr[SH_EH_FRAME_HDR].sh_flags = SHF_ALLOC;
elf->shdr[SH_EH_FRAME_HDR].sh_addr = elf->phdr[i].p_vaddr;
elf->shdr[SH_EH_FRAME_HDR].sh_offset = elf->phdr[i].p_offset;
elf->shdr[SH_EH_FRAME_HDR].sh_size = elf->phdr[i].p_filesz;
elf->shdr[SH_EH_FRAME_HDR].sh_link = SHN_UNDEF;
elf->shdr[SH_EH_FRAME_HDR].sh_info = 0;
elf->shdr[SH_EH_FRAME_HDR].sh_addralign = 4;
elf->shdr[SH_EH_FRAME_HDR].sh_entsize = 0;
section_list_add(elf, SH_EH_FRAME_HDR);
elf->shdr[SH_EH_FRAME].sh_name = get_strtbl_idx(shstrtab, ARRAY_SIZE(shstrtab), SH_EH_FRAME);
elf->shdr[SH_EH_FRAME].sh_type = SHT_PROGBITS;
elf->shdr[SH_EH_FRAME].sh_flags = SHF_ALLOC;
elf->shdr[SH_EH_FRAME].sh_addr = ALIGN(elf->shdr[SH_EH_FRAME_HDR].sh_addr
+ elf->shdr[SH_EH_FRAME_HDR].sh_size, 8);
elf->shdr[SH_EH_FRAME].sh_offset = ALIGN(elf->shdr[SH_EH_FRAME_HDR].sh_offset
+ elf->shdr[SH_EH_FRAME_HDR].sh_size, 8);
elf->shdr[SH_EH_FRAME].sh_link = SHN_UNDEF;
elf->shdr[SH_EH_FRAME].sh_info = 0;
elf->shdr[SH_EH_FRAME].sh_addralign = sizeof(elf_addr);
elf->shdr[SH_EH_FRAME].sh_entsize = 0;
section_list_add(elf, SH_EH_FRAME);
break;
default:
break;
}
}
if (!elf->dyn) {
err_msg("Cannot find dynamic segment");
return false;
}
if (elf->rela_plt && !patch_got(elf))
return false;
/* Update .text and .fini */
if (elf->rela_plt)
elf->shdr[SH_TEXT].sh_addr = ALIGN(elf->shdr[SH_PLT_GOT].sh_addr + elf->shdr[SH_PLT_GOT].sh_size,
elf->shdr[SH_TEXT].sh_addralign);
else
elf->shdr[SH_TEXT].sh_addr = ALIGN(elf->shdr[SH_INIT].sh_addr + elf->shdr[SH_INIT].sh_size,
elf->shdr[SH_TEXT].sh_addralign);
elf->shdr[SH_TEXT].sh_offset = get_offset(elf, elf->shdr[SH_TEXT].sh_addr);
elf->shdr[SH_TEXT].sh_size = elf->shdr[SH_INIT].sh_addr + elf->phdr[ph_text].p_filesz
- elf->shdr[SH_TEXT].sh_addr;
section_list_add(elf, SH_TEXT);
elf->shdr[SH_FINI].sh_size = elf->shdr[SH_TEXT].sh_addr + elf->shdr[SH_TEXT].sh_size
- elf->shdr[SH_FINI].sh_addr;
/* update .eh_frame */
if (get_section_idx(elf, SH_RODATA) != -1) {
elf->shdr[SH_EH_FRAME].sh_size = elf->shdr[SH_RODATA].sh_addr + elf->phdr[ph_rodata].p_filesz -
elf->shdr[SH_EH_FRAME].sh_addr;
/* update .rodata size */
elf->shdr[SH_RODATA].sh_size = ALIGN(elf->shdr[SH_EH_FRAME_HDR].sh_addr - elf->shdr[SH_RODATA].sh_addr,
elf->shdr[SH_RODATA].sh_addralign);
} else {
elf->shdr[SH_EH_FRAME].sh_size = elf->shdr[SH_TEXT].sh_addr + elf->shdr[SH_TEXT].sh_size -
elf->shdr[SH_EH_FRAME].sh_addr;
}
/* add .got */
elf->shdr[SH_GOT].sh_addr = get_got_addr(elf);
elf->shdr[SH_GOT].sh_offset = get_offset(elf, elf->shdr[SH_GOT].sh_addr);
elf->shdr[SH_GOT].sh_name = get_strtbl_idx(shstrtab, ARRAY_SIZE(shstrtab), SH_PLT_GOT) + 4;
elf->shdr[SH_GOT].sh_type = SHT_PROGBITS;
elf->shdr[SH_GOT].sh_flags = SHF_WRITE | SHF_ALLOC;
elf->shdr[SH_GOT].sh_link = SHN_UNDEF;
elf->shdr[SH_GOT].sh_info = 0;
elf->shdr[SH_GOT].sh_addralign = sizeof(elf_addr);
elf->shdr[SH_GOT].sh_entsize = 8;
elf->shdr[SH_GOT].sh_size = GOT_SIZE(elf);
section_list_add(elf, SH_GOT);
/* update .data */
if (get_section_idx(elf, SH_GOT_PLT) != -1) {
elf->shdr[SH_DATA].sh_addr = elf->shdr[SH_GOT_PLT].sh_addr + elf->shdr[SH_GOT_PLT].sh_size;
elf->shdr[SH_DATA].sh_offset = get_offset(elf, elf->shdr[SH_DATA].sh_addr);
elf->shdr[SH_DATA].sh_size = ALIGN(elf->phdr[ph_data].p_vaddr + elf->phdr[ph_data].p_filesz -
elf->shdr[SH_DATA].sh_addr, elf->shdr[SH_DATA].sh_addralign);
section_list_add(elf, SH_DATA);
} else {
elf->shdr[SH_DATA].sh_addr = elf->shdr[SH_GOT].sh_addr + elf->shdr[SH_GOT].sh_size;
elf->shdr[SH_DATA].sh_offset = get_offset(elf, elf->shdr[SH_DATA].sh_addr);
elf->shdr[SH_DATA].sh_size = ALIGN(elf->phdr[ph_data].p_vaddr + elf->phdr[ph_data].p_filesz -
elf->shdr[SH_DATA].sh_addr, elf->shdr[SH_DATA].sh_addralign);
section_list_add(elf, SH_DATA);
}
/* add .bss */
elf->shdr[SH_BSS].sh_addr = ALIGN(elf->shdr[SH_DATA].sh_addr + elf->shdr[SH_DATA].sh_size, 4);
elf->shdr[SH_BSS].sh_offset = ALIGN(elf->shdr[SH_DATA].sh_offset + elf->shdr[SH_DATA].sh_size, 4);
elf->shdr[SH_BSS].sh_name = get_strtbl_idx(shstrtab, ARRAY_SIZE(shstrtab), SH_BSS);
elf->shdr[SH_BSS].sh_type = SHT_NOBITS;
elf->shdr[SH_BSS].sh_flags = SHF_WRITE | SHF_ALLOC;
elf->shdr[SH_BSS].sh_link = SHN_UNDEF;
elf->shdr[SH_BSS].sh_info = 0;
elf->shdr[SH_BSS].sh_addralign = 4;
elf->shdr[SH_BSS].sh_entsize = 0;
elf->shdr[SH_BSS].sh_size = ALIGN(elf->phdr[ph_data].p_vaddr + elf->phdr[ph_data].p_memsz -
elf->shdr[SH_BSS].sh_addr, 4);
section_list_add(elf, SH_BSS);
/* 更新sh_link和sh_info */
elf->shdr[SH_DYNAMIC].sh_link = get_section_idx(elf, SH_DYNSTR);
elf->shdr[SH_DYNSYM].sh_link = get_section_idx(elf, SH_DYNSTR);
elf->shdr[SH_DYNSYM].sh_info = get_idx_last_local_sym(elf);
if (elf->rela_plt) {
if (elf->relplt_type == DT_RELA) {
elf->shdr[SH_RELA_PLT].sh_link = get_section_idx(elf, SH_DYNSYM);
elf->shdr[SH_RELA_PLT].sh_info = get_section_idx(elf, SH_GOT_PLT);
} else {
elf->shdr[SH_REL_PLT].sh_link = get_section_idx(elf, SH_DYNSYM);
elf->shdr[SH_REL_PLT].sh_info = get_section_idx(elf, SH_GOT_PLT);
}
}
elf->shdr[SH_HASH].sh_link = get_section_idx(elf, SH_DYNSYM);
elf->shdr[SH_GNU_HASH].sh_link = get_section_idx(elf, SH_DYNSYM);
if (elf->reldyn_type == DT_RELA)
elf->shdr[SH_RELA_DYN].sh_link = get_section_idx(elf, SH_DYNSYM);
else
elf->shdr[SH_REL_DYN].sh_link = get_section_idx(elf, SH_DYNSYM);
elf->shdr[SH_VERSYM].sh_link = get_section_idx(elf, SH_DYNSYM);
elf->shdr[SH_VERNEED].sh_link = get_section_idx(elf, SH_DYNSTR);
return true;
}
int main(int argc, char **argv)
{
...
while ((opt = getopt(argc, argv, "p:r:hv")) != -1)
{
switch (opt)
{
case 'p':
if (strlen(optarg) > 10)
err_quit("Invalid process id: %s", optarg);
pid = atoi(optarg);
break;
case 'r':
core = optarg;
break;
case 'v':
verbose = true;
break;
case 'h':
default:
usage(argv[0]);
exit(EXIT_SUCCESS);
}
}
if (argc > optind)
output_file = argv[optind];
if (pid == 0 && !core)
{
usage(argv[0]);
exit(EXIT_FAILURE);
}
memset(&elf, 0, sizeof(elf));
if (pid != 0)
{
FILE *fp;
if ((procname = get_procname(pid)) == NULL)
err_quit("Error getting process name");
if (!output_file)
output_file = procname;
printf("[+] Reading process: %s\n", procname);
if (snprintf(path, 32, "/proc/%d/maps", pid) < 0)
{
free(procname);
err_sys("snprintf error");
}
if (!(fp = fopen(path, "r")))
{
free(procname);
err_sys("fopen error");
}
ptrace(PTRACE_ATTACH, pid, NULL, NULL);
waitpid(pid, NULL, 0);
if (!read_process(&elf, fp, procname, pid))
{
free(procname);
fclose(fp);
err_quit("Error reading process");
}
fclose(fp);
}
else if (core)
{
int fd;
unsigned char *buf;
struct stat st;
if (!output_file)
output_file = DEFAULT_FILE;
if ((fd = open(core, O_RDONLY)) == -1)
err_sys("open error");
if (fstat(fd, &st) == -1)
err_sys("fstat error");
if ((buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED)
err_sys("mmap error");
close(fd);
}
...
done:
if (pid)
ptrace(PTRACE_DETACH, pid, NULL, NULL);
if (procname)
free(procname);
if (elf.buf)
free(elf.buf);
if (elf.sections.buf)
free(elf.sections.buf);
if (elf.shdr)
free(elf.shdr);
section_list_free(&elf);
...
}
If you need the complete source code, please add the WeChat number (c17865354792)
运行结果:
总结
ELF 文件是可重定位文件、可执行文件和 共享目标文件的结构,取决于最初的目的,它包含需要的 segments 或 sections. Segments 被内核用于映射到内存。Sections 被链接器用来创建可执行文件或共享目标文件,最后ELF暂时介绍到这了。
Welcome to follow WeChat official account【程序猿编码】
参考:1.https://linux-audit.com/elf-binaries-on-linux-understanding-and-analysis/
2.Linux二进制分析
3.linux /usr/include/elf.h