试验环境:archlinux 速龙3000+(即x86兼容32位处理器)

必须软件:gcc binutils

参考资料:

System V application binary interface

ELF Format (mirror txt format )

Hello,world in less than 20 bytes

Tutorial on creating teensy ELF file on linux (中文翻译版本 ,also see (smallest elf32 hello,world ))

Introduction to reverse engineering on linux   (also see crackz reverse engineering page(windows),resources )

Deconstructing an ELF file

The ELF virus writing howto

Playing with binary format

ELF hackery (many links)

ELF or assembly reference (on skyeye)

linkers and loaders

linkers(part 1 ,part 2 , part 3 , part 4 , part 5 , part 6 , part 7 , part 8 ,part 9 ,part 10

part 11 ,part 12 , part 13 , part 14 , part 15 , part 16 , part 17 , part 18 , part 19 , part 20 )

hacker's wisdom

还可用百度搜索"ELF site:ibm.com",能搜索到很多关于ELF中文翻译教程,其中文后的参考文献也很值得看。

pe(window下的库文件和可执行文件格式)相关链接可以在wikipedia上找到 。

ELF 文件分为三类:(1)可重定位文件(目标文件或者静态库文件,即linux通常后缀为.a和.o的文件) (2)可执行文件(即可以运行的二进制文件,例如bash,gcc等)(3)共享目标文件(即linux下后缀为so的文件)。Elf文件格式(参见System V Application Binary interface 第46页)的布局如下:

-----------------------------

ELF文件头(即ELF Header)

----------------------------

程序文件头表(Program header table)

-----------------------------------

Section 1

-----------------------------------

...

-----------------------------------

Section n

----------------------------------

...

----------------------------------

段表(Section header table)

-----------------------------------

其中程序文件头表对于可重定位文件是可选项(对另外两类文件是必需项),而段表对于可执行文件是可选项(对另外两类文件是必需项)。另外,Section可以是.text,.data,.bss(即代码段,数据段(用来存放已经初始化的全局变量和静态变量)和BSS段(用来存放未初始化的全局变量和静态变量))等

使用《程序员的自我修养--链接 装载和库》中第三章的例子来说明elf的具体格式

/** SimpleSection.c* * Linux:*   gcc -c SimpleSection.c* Windows:*   cl SimpleSection.c /c /Za*/
int printf(const char* format, ...);
int global_init_var = 84;
int global_uinit_var;void func1(int i)
{printf("%d\n", i);
}int main(void)
{static int static_var = 85;static int static_var2;int a = 1;int b;func1(static_var + static_var2 + a + b);return a;
}

使用下面命令来编译:

gcc -c SimpleSection.c

再使用下面命令来显示生成的目标文件(SimpleSection.c)的类型

file SimpleSection.o

输出下列内容:

SimpleSection.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped

说明SimpleSection.o是一个重定位文件

使用下面命令查看SimpleSection.o的大小:

ls -l SimpleSection.o

输出结果为:

-rw-r--r-- 1 host users 1092 11月  5 14:38 SimpleSection.o

根据输出结果可以知道,SimpleSection.o大小为1092字节。

使用下面命令用16进制的数字来显示SimpleSection.o的内容(也可以用od -x SimpleSection.o命令)

hexdump -x SimpleSection.o

输出结果为:

0000000    457f    464c    0101    0001    0000    0000    0000    0000
0000010    0001    0003    0001    0000    0000    0000    0000    0000
0000020    010c    0000    0000    0000    0034    0000    0000    0028
0000030    000b    0008    8955    83e5    18ec    458b    8908    2444
0000040    c704    2404    0000    0000    fce8    ffff    c9ff    55c3
0000050    e589    e483    83f0    20ec    44c7    1c24    0001    0000
0000060    158b    0004    0000    00a1    0000    8d00    0204    4403
0000070    1c24    4403    1824    0489    e824    fffc    ffff    448b
0000080    1c24    c3c9    0054    0000    0055    0000    6425    000a
0000090    4700    4343    203a    4728    554e    2029    2e34    2e35
00000a0    2032    3032    3131    3130    3732    2820    7270    7265
00000b0    6c65    6165    6573    0029    2e00    7973    746d    6261
00000c0    2e00    7473    7472    6261    2e00    6873    7473    7472
00000d0    6261    2e00    6572    2e6c    6574    7478    2e00    6164
00000e0    6174    2e00    7362    0073    722e    646f    7461    0061
00000f0    632e    6d6f    656d    746e    2e00    6f6e    6574    472e
0000100    554e    732d    6174    6b63    0000    0000    0000    0000
0000110    0000    0000    0000    0000    0000    0000    0000    0000
*
0000130    0000    0000    001f    0000    0001    0000    0006    0000
0000140    0000    0000    0034    0000    0050    0000    0000    0000
0000150    0000    0000    0004    0000    0000    0000    001b    0000
0000160    0009    0000    0000    0000    0000    0000    041c    0000
0000170    0028    0000    0009    0000    0001    0000    0004    0000
0000180    0008    0000    0025    0000    0001    0000    0003    0000
0000190    0000    0000    0084    0000    0008    0000    0000    0000
00001a0    0000    0000    0004    0000    0000    0000    002b    0000
00001b0    0008    0000    0003    0000    0000    0000    008c    0000
00001c0    0004    0000    0000    0000    0000    0000    0004    0000
00001d0    0000    0000    0030    0000    0001    0000    0002    0000
00001e0    0000    0000    008c    0000    0004    0000    0000    0000
00001f0    0000    0000    0001    0000    0000    0000    0038    0000
0000200    0001    0000    0030    0000    0000    0000    0090    0000
0000210    0028    0000    0000    0000    0000    0000    0001    0000
0000220    0001    0000    0041    0000    0001    0000    0000    0000
0000230    0000    0000    00b8    0000    0000    0000    0000    0000
0000240    0000    0000    0001    0000    0000    0000    0011    0000
0000250    0003    0000    0000    0000    0000    0000    00b8    0000
0000260    0051    0000    0000    0000    0000    0000    0001    0000
0000270    0000    0000    0001    0000    0002    0000    0000    0000
0000280    0000    0000    02c4    0000    00f0    0000    000a    0000
0000290    000a    0000    0004    0000    0010    0000    0009    0000
00002a0    0003    0000    0000    0000    0000    0000    03b4    0000
00002b0    0065    0000    0000    0000    0000    0000    0001    0000
00002c0    0000    0000    0000    0000    0000    0000    0000    0000
00002d0    0000    0000    0001    0000    0000    0000    0000    0000
00002e0    0004    fff1    0000    0000    0000    0000    0000    0000
00002f0    0003    0001    0000    0000    0000    0000    0000    0000
0000300    0003    0003    0000    0000    0000    0000    0000    0000
0000310    0003    0004    0000    0000    0000    0000    0000    0000
0000320    0003    0005    0011    0000    0004    0000    0004    0000
0000330    0001    0003    0021    0000    0000    0000    0004    0000
0000340    0001    0004    0000    0000    0000    0000    0000    0000
0000350    0003    0007    0000    0000    0000    0000    0000    0000
0000360    0003    0006    0032    0000    0000    0000    0004    0000
0000370    0011    0003    0042    0000    0004    0000    0004    0000
0000380    0011    fff2    0053    0000    0000    0000    001b    0000
0000390    0012    0001    0059    0000    0000    0000    0000    0000
00003a0    0010    0000    0060    0000    001b    0000    0035    0000
00003b0    0012    0001    5300    6d69    6c70    5365    6365    6974
00003c0    6e6f    632e    7300    6174    6974    5f63    6176    2e72
00003d0    3231    3232    7300    6174    6974    5f63    6176    3272
00003e0    312e    3232    0033    6c67    626f    6c61    695f    696e
00003f0    5f74    6176    0072    6c67    626f    6c61    755f    6e69
0000400    7469    765f    7261    6600    6e75    3163    7000    6972
0000410    746e    0066    616d    6e69    0000    0000    0010    0000
0000420    0501    0000    0015    0000    0d02    0000    002e    0000
0000430    0301    0000    0033    0000    0401    0000    0046    0000
0000440    0c02    0000
0000444

上面的数据均为16进制数据(因为使用了-x选项),并且第一列为偏移地址。

使用下面命令来显示SimpleSection.o中各个段相关信息:

objdump -x SimpleSection.o

输出结果为:

SimpleSection.o:     file format elf32-i386
SimpleSection.o
architecture: i386, flags 0x00000011:
HAS_RELOC, HAS_SYMS
start address 0x00000000Sections:
Idx Name          Size      VMA       LMA       File off  Algn0 .text         00000050  00000000  00000000  00000034  2**2CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE1 .data         00000008  00000000  00000000  00000084  2**2CONTENTS, ALLOC, LOAD, DATA2 .bss          00000004  00000000  00000000  0000008c  2**2ALLOC3 .rodata       00000004  00000000  00000000  0000008c  2**0CONTENTS, ALLOC, LOAD, READONLY, DATA4 .comment      00000028  00000000  00000000  00000090  2**0CONTENTS, READONLY5 .note.GNU-stack 00000000  00000000  00000000  000000b8  2**0CONTENTS, READONLY
SYMBOL TABLE:
00000000 l    df *ABS*  00000000 SimpleSection.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .rodata    00000000 .rodata
00000004 l     O .data  00000004 static_var.1222
00000000 l     O .bss   00000004 static_var2.1223
00000000 l    d  .note.GNU-stack    00000000 .note.GNU-stack
00000000 l    d  .comment   00000000 .comment
00000000 g     O .data  00000004 global_init_var
00000004       O *COM*  00000004 global_uinit_var
00000000 g     F .text  0000001b func1
00000000         *UND*  00000000 printf
0000001b g     F .text  00000035 mainRELOCATION RECORDS FOR [.text]:
OFFSET   TYPE              VALUE
00000010 R_386_32          .rodata
00000015 R_386_PC32        printf
0000002e R_386_32          .data
00000033 R_386_32          .bss
00000046 R_386_PC32        func1

也可以用下面的命令来查看各个段信息:

readelf -a SimpleSection.o

输出结果为:

ELF Header:Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class:                             ELF32Data:                              2's complement, little endianVersion:                           1 (current)OS/ABI:                            UNIX - System VABI Version:                       0Type:                              REL (Relocatable file)Machine:                           Intel 80386Version:                           0x1Entry point address:               0x0Start of program headers:          0 (bytes into file)Start of section headers:          268 (bytes into file)Flags:                             0x0Size of this header:               52 (bytes)Size of program headers:           0 (bytes)Number of program headers:         0Size of section headers:           40 (bytes)Number of section headers:         11Section header string table index: 8Section Headers:[Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al[ 0]                   NULL            00000000 000000 000000 00      0   0  0[ 1] .text             PROGBITS        00000000 000034 000050 00  AX  0   0  4[ 2] .rel.text         REL             00000000 00041c 000028 08      9   1  4[ 3] .data             PROGBITS        00000000 000084 000008 00  WA  0   0  4[ 4] .bss              NOBITS          00000000 00008c 000004 00  WA  0   0  4[ 5] .rodata           PROGBITS        00000000 00008c 000004 00   A  0   0  1[ 6] .comment          PROGBITS        00000000 000090 000028 01  MS  0   0  1[ 7] .note.GNU-stack   PROGBITS        00000000 0000b8 000000 00      0   0  1[ 8] .shstrtab         STRTAB          00000000 0000b8 000051 00      0   0  1[ 9] .symtab           SYMTAB          00000000 0002c4 0000f0 10     10  10  4[10] .strtab           STRTAB          00000000 0003b4 000065 00      0   0  1
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings)I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)O (extra OS processing required) o (OS specific), p (processor specific)There are no section groups in this file.There are no program headers in this file.Relocation section '.rel.text' at offset 0x41c contains 5 entries:Offset     Info    Type            Sym.Value  Sym. Name
00000010  00000501 R_386_32          00000000   .rodata
00000015  00000d02 R_386_PC32        00000000   printf
0000002e  00000301 R_386_32          00000000   .data
00000033  00000401 R_386_32          00000000   .bss
00000046  00000c02 R_386_PC32        00000000   func1There are no unwind sections in this file.Symbol table '.symtab' contains 15 entries:Num:    Value  Size Type    Bind   Vis      Ndx Name0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 1: 00000000     0 FILE    LOCAL  DEFAULT  ABS SimpleSection.c2: 00000000     0 SECTION LOCAL  DEFAULT    1 3: 00000000     0 SECTION LOCAL  DEFAULT    3 4: 00000000     0 SECTION LOCAL  DEFAULT    4 5: 00000000     0 SECTION LOCAL  DEFAULT    5 6: 00000004     4 OBJECT  LOCAL  DEFAULT    3 static_var.12227: 00000000     4 OBJECT  LOCAL  DEFAULT    4 static_var2.12238: 00000000     0 SECTION LOCAL  DEFAULT    7 9: 00000000     0 SECTION LOCAL  DEFAULT    6 10: 00000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var11: 00000004     4 OBJECT  GLOBAL DEFAULT  COM global_uinit_var12: 00000000    27 FUNC    GLOBAL DEFAULT    1 func113: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND printf14: 0000001b    53 FUNC    GLOBAL DEFAULT    1 mainNo version information found in this file.

下面分析SimpleSection.o文件内容

首先是Elf文件头,其定义为(在/usr/include/elf.h中)

#define EI_NIDENT (16)
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;

大小为52个字节(16进制表示为0x34),因此SimpleSection.o前52个字节内容为ELF文件头,其二进制表示为:

0000000    457f    464c    0101    0001    0000    0000    0000    0000
0000010    0001    0003    0001    0000    0000    0000    0000    0000
0000020    010c    0000    0000    0000    0034    0000    0000    0028
0000030    000b    0008  

因为intel及其兼容处理器使用了小端法,此处的-x命令选项是一次性输出两个字节,所以457f实际表示了7f45,而464c实际上是4c46,依次类推。

其前16个字节(第一行,对应e_ident[EI_NIDENT])实际表示内容为7f454c46010101000000000000000000,前四个字节7f454c46(0x45,0x4c,0x46是'e','l','f'对应的ascii编码)是一个魔数(magic number),表示这是一个ELF对象。接下来的一个字节01表示是一个32位对象,接下来的一个字节01表示是小端法表示,再接下来的一个字节01表示文件头版本。剩下的默认都设置为0.

接下来(第二行)e_type(两个字节)值为0x0001,表示是一个重定位文件。e_machine(两个字节)值为0x0003,表示是intel80386处理器体系结构。e_version(四个字节)值为0x00000001,表示是当前版本。e_entry(四个字节)值为0x00000000,表示没有入口点。e_phoff(四个字节)值为0x00000000,表示没有程序头表。

接下来(第三行)e_shoff(四个字节)值为0x0000010c,表示段表的偏移地址。e_flags(四个字节)值为0x00000000,表示未知处理器特定标志(#define EF_SH_UNKNOWN        0x0)。e_ehsize(两个字节)值为0034,表示elf文件头大小(正好是52个字节)。e_phentsize(两个字节)和e_phnum(两个字节)的值均为0x0000,因为重定位文件没有程序头表。e_ehentsize(两个字节)值为0x0028表示段头大小为40个字节。

接下来(第四行)e_shnum(两个字节)值为0x000b,表示段表入口有11个。e_shstrndx(两个字节)值为0x0008,表示段名串表的在段表中的索引号。

SimpleSection.o中紧接着ELF头的部分是代码段(.text)。使用下面命令对SimpleSection.o的文本段进行反汇编:

objdump -d SimpleSection.o

输出结果为:

00000000 <func1>:0:  55                      push   %ebp1:   89 e5                   mov    %esp,%ebp3:  83 ec 18                sub    $0x18,%esp6: 8b 45 08                mov    0x8(%ebp),%eax9: 89 44 24 04             mov    %eax,0x4(%esp)d: c7 04 24 00 00 00 00    movl   $0x0,(%esp)14:   e8 fc ff ff ff          call   15 <func1+0x15>19:    c9                      leave  1a:  c3                      ret    0000001b <main>:1b:    55                      push   %ebp1c:  89 e5                   mov    %esp,%ebp1e: 83 e4 f0                and    $0xfffffff0,%esp21:  83 ec 20                sub    $0x20,%esp24:    c7 44 24 1c 01 00 00    movl   $0x1,0x1c(%esp)2b:   00 2c:  8b 15 04 00 00 00       mov    0x4,%edx32:  a1 00 00 00 00          mov    0x0,%eax37:  8d 04 02                lea    (%edx,%eax,1),%eax3a:    03 44 24 1c             add    0x1c(%esp),%eax3e:   03 44 24 18             add    0x18(%esp),%eax42:   89 04 24                mov    %eax,(%esp)45:   e8 fc ff ff ff          call   46 <main+0x2b>4a: 8b 44 24 1c             mov    0x1c(%esp),%eax4e:   c9                      leave  4f:  c3                      ret

代码段刚好对应SimpleSection.o紧接着ELF头的80(0x50)个字节代码。即

                           8955    83e5    18ec    458b    8908    2444
0000040    c704    2404    0000    0000    fce8    ffff    c9ff    55c3
0000050    e589    e483    83f0    20ec    44c7    1c24    0001    0000
0000060    158b    0004    0000    00a1    0000    8d00    0204    4403
0000070    1c24    4403    1824    0489    e824    fffc    ffff    448b
0000080    1c24    c3c9

这段代码是func1和main函数对应的汇编代码

紧接着代码段是数据段(.data)的内容(8个字节,16进制表示为0x08),即0x00000084地址开始8个字节内容:

 0054    0000    0055    0000

数据段是全局和静态变量初始化数据的存放地。其中global_init_var(int类型,四个字节) 值为84(16进制表示为0x00000054),对应0054 0000.而static_var(static int类型,四个字节)值为85(16进制表示为0x00000055),对应0055 0000.

紧接着数据段的是.bss和.rodata(只读数据段,用于存放常数串等,此处与.bss段重叠)段,大小为4个字节对应0x0000008c地址开始四个字节内容:

 6425    000a

恰好是字符串"%d\n"的二进制表示(0x64对应字符'd',0x25对应字符'%',0x0a对应字符LF(换行符号,unix/linux下'\n'用与LF相对应,而在为window则需要用CR(回车)和LF两个符号来对应与'\n'),0x00对应字符'\0'来作为串的终止符号)。

紧接着.rodata段的是.comment段,它用来存放编译器版本信息等,此处对应0x00000090地址开始的40个字节(16进制下为0x28):

0000090    4700    4343    203a    4728    554e    2029    2e34    2e35
00000a0    2032    3032    3131    3130    3732    2820    7270    7265
00000b0    6c65    6165    6573    0029

实际对应于一个字符串"\0GCC: (GNU) 4.5.2 20110127 (prerelease)\0"(感兴趣的话,可以自己将上面的16进制ascii值转换成字符试试,刚好与使用gcc -v命令的得到的结果一致(这个结果是使用od -c SimpleSection.o和上面使用-x选项的结果对比得到的,该命令在我的电脑上输出结果中最后一行为:gcc 版本 4.5.2 20110127 (prerelease) (GCC) )

紧接着.note.GNU-Stack段(该段大小为0,所以没有对应的实质性内容)

紧接着从0x000000b8地址开始81(0x51)个字节是.shstrtab段,用来存放段的名称。对应内容为:

                                           2e00    7973    746d    6261
00000c0    2e00    7473    7472    6261    2e00    6873    7473    7472
00000d0    6261    2e00    6572    2e6c    6574    7478    2e00    6164
00000e0    6174    2e00    7362    0073    722e    646f    7461    0061
00000f0    632e    6d6f    656d    746e    2e00    6f6e    6574    472e
0000100    554e    732d    6174    6b63    00 

其对应字符串为"\0.symtab\0.strtab\0.shstrtab\0.rel.text\0.data\0.bss\0.rodata\0.comment\0.note.GNU-Stack\0"(双引号是我手动添加的,为了看起来更好看,这个字符串是我用od -c SimpleSection.o得到的结果与前面使用-x选项得到结果对比得到的。)

紧接着.shstrtab段的是段表(Section table),从前面对ELF头的解析可以知道段表的地址是从0x0000010c(e_shoff项)开始,而上面的.shstrtab的开始地址为0xb8,大小为0x51,所以此处段表应该是0x109开始,但是从前面使用readelf得出结果可以知道,段表对齐要求是4个字节对齐,所以地址最后两位必须是00,这就导致是从0x10c开始,留下了三个字节的空洞(英文为hole)。再根据e_shnum=11和ehentsize=40可知,有11个段,每个段占40个字节大小,总共占据440个字节(16进制为0x1b8)。段入口的类型定义如下(/usr/include/elf.h):

typedef struct
{Elf32_Word sh_name;        /* Section name (string tbl index) */Elf32_Word sh_type;        /* Section type */Elf32_Word    sh_flags;       /* Section flags */Elf32_Addr   sh_addr;        /* Section virtual addr at execution */Elf32_Off    sh_offset;      /* Section file offset */Elf32_Word sh_size;        /* Section size in bytes */Elf32_Word   sh_link;        /* Link to another section */Elf32_Word sh_info;        /* Additional section information */Elf32_Word  sh_addralign;       /* Section alignment */Elf32_Word   sh_entsize;     /* Entry size if section holds table */
} Elf32_Shdr;

恰好占据了40个字节。

从0x0000010c开始有11个段,每个段占40个字节大小。

第一个段为0x0000010c-0x00000133,其中内容全部为0,所以不表示任何段。

第二个段为0x00000134-0x0000015b,对应内容为:

                           001f    0000    0001    0000    0006    0000
0000140    0000    0000    0034    0000    0050    0000    0000    0000
0000150    0000    0000    0004    0000    0000    0000   

段中每个成员均为4个字节,所以分析起来相对简单一些。

sh_name值为0x0000001f,它表示该段名称在.shstrtab中偏移量,通过计算可知该名称为.text。sh_type值为0x00000001(对应SHT_PROGBITS),表示这个段拥有程序所定义的信息,其格式和含义完全有该程序确定。sh_flags值为0x00000006(对应于SHF_ALLOC和SHF_EXECINSTR)。sh_addr值为0x00000000,表示这个段不会出现在进程的地址镜像中。

sh_offset值为0x00000034(偏移地址),sh_size值为0x00000050,表示代码段大小为80(0x50)个字节。sh_link值为0x00000000,表示没有链接信息。sh_info值为0x00000000,表示没有辅助信息。sh_addalign值为0x00000004,表示4个字节对齐,sh_entsize值为0x00000000,表示没有入口。

第三个段为0x0000015c-0x00000184,对应内容为:

                                                           001b    0000
0000160    0009    0000    0000    0000    0000    0000    041c    0000
0000170    0028    0000    0009    0000    0001    0000    0004    0000
0000180    0008    0000 

该段为.rel.text段(偏移量为0x0000001b),sh_type为0x00000009(对应SHT_REL),sh_offset为0x0000041c,sh_size为0x00000028(40个字节)。sh_link和sh_info分别为9和1,分别表示相关符号表索引和重定位应用段的段头索引。其余段的内容不再详细分析,可以自己分析并于前面readelf输出结果相对照。

第四个段为0x00000184-0x000001ac(0x25,即.shstrtab第37个字节偏移处,即.data段),对应内容:

                           0025    0000    0001    0000    0003    0000
0000190    0000    0000    0084    0000    0008    0000    0000    0000
00001a0    0000    0000    0004    0000    0000    0000 

第五个段为0x000001ac-0x00001d4(0x2b,即第43个字节偏移处,即.bss段),对应内容为:

                                                           002b    0000
00001b0    0008    0000    0003    0000    0000    0000    008c    0000
00001c0    0004    0000    0000    0000    0000    0000    0004    0000
00001d0    0000    0000

第六个段为0x000001d4-0x000001fc(0x30,即第48个字节偏移处,即.rodata段),对应内容为:

                           0030    0000    0001    0000    0002    0000
00001e0    0000    0000    008c    0000    0004    0000    0000    0000
00001f0    0000    0000    0001    0000    0000    0000 

第七个段为0x000001fc-0x00000224(0x38,即第56个字节偏移处,即.comment段),对应内容为:

                                                           0038    0000
0000200    0001    0000    0030    0000    0000    0000    0090    0000
0000210    0028    0000    0000    0000    0000    0000    0001    0000
0000220    0001    0000   

第八个段为0x00000224-0x0000024c(0x41,即第65个字节偏移处,即.note.GNU-Stack段),对应内容为:

                           0041    0000    0001    0000    0000    0000
0000230    0000    0000    00b8    0000    0000    0000    0000    0000
0000240    0000    0000    0001    0000    0000    0000  

第九个段为0x0000024c-0x00000274(0x11,即第17个字节偏移处,即.shstrtab段),对应内容为:

                                                           0011    0000
0000250    0003    0000    0000    0000    0000    0000    00b8    0000
0000260    0051    0000    0000    0000    0000    0000    0001    0000
0000270    0000    0000

第十个段为0x00000274-0x0000029c(0x01,即第1个字节偏移处,即.symtab段),对应内容为:

                           0001    0000    0002    0000    0000    0000
0000280    0000    0000    02c4    0000    00f0    0000    000a    0000
0000290    000a    0000    0004    0000    0010    0000   

第十一个段为0x0000029c-0x000002c4(0x9,即第9个字节偏移处,即.strtab段),对应内容为:

                                                           0009    0000
00002a0    0003    0000    0000    0000    0000    0000    03b4    0000
00002b0    0065    0000    0000    0000    0000    0000    0001    0000
00002c0    0000    0000    

所以段表中表示的段从偏移地址1开始(1-10,因为第一个段全为空)依次为.text, .rel.text, .data, .bss, .rodata, .comment, .note.GNU-Stack, .shstrtab, .symtab, .strtab)

从0x000002c4开始为.symtab(符号表)段,大小为0xf0(240个字节),对应内容为:

                           0000    0000    0000    0000    0000    0000
00002d0    0000    0000    0001    0000    0000    0000    0000    0000
00002e0    0004    fff1    0000    0000    0000    0000    0000    0000
00002f0    0003    0001    0000    0000    0000    0000    0000    0000
0000300    0003    0003    0000    0000    0000    0000    0000    0000
0000310    0003    0004    0000    0000    0000    0000    0000    0000
0000320    0003    0005    0011    0000    0004    0000    0004    0000
0000330    0001    0003    0021    0000    0000    0000    0004    0000
0000340    0001    0004    0000    0000    0000    0000    0000    0000
0000350    0003    0007    0000    0000    0000    0000    0000    0000
0000360    0003    0006    0032    0000    0000    0000    0004    0000
0000370    0011    0003    0042    0000    0004    0000    0004    0000
0000380    0011    fff2    0053    0000    0000    0000    001b    0000
0000390    0012    0001    0059    0000    0000    0000    0000    0000
00003a0    0010    0000    0060    0000    001b    0000    0035    0000
00003b0    0012    0001   

符号表结构定义(/usr/include/elf.h):

typedef struct
{Elf32_Word st_name;        /* Symbol name (string tbl index) */Elf32_Addr  st_value;       /* Symbol value */Elf32_Word    st_size;        /* Symbol size */unsigned char  st_info;        /* Symbol type and binding */unsigned char  st_other;       /* Symbol visibility */Elf32_Section    st_shndx;       /* Section index */
} Elf32_Sym;

该结构大小为16个字节,而整个符号表段大小为240个字节,所以总共可以分成15个符号(有些符号未使用)。这15个符号刚好和前面objdump输出的符号表(14个符号,第一个符号为空,所以被忽略)结果相对应:

SYMBOL TABLE:
00000000 l    df *ABS*  00000000 SimpleSection.c
00000000 l    d  .text  00000000 .text
00000000 l    d  .data  00000000 .data
00000000 l    d  .bss   00000000 .bss
00000000 l    d  .rodata    00000000 .rodata
00000004 l     O .data  00000004 static_var.1222
00000000 l     O .bss   00000004 static_var2.1223
00000000 l    d  .note.GNU-stack    00000000 .note.GNU-stack
00000000 l    d  .comment   00000000 .comment
00000000 g     O .data  00000004 global_init_var
00000004       O *COM*  00000004 global_uinit_var
00000000 g     F .text  0000001b func1
00000000         *UND*  00000000 printf
0000001b g     F .text  00000035 main  

也和使用readelf中符号表相关内容对应(readelf命令输出结果比objdump输出结果看起来更好看一些):

Symbol table '.symtab' contains 15 entries:Num:    Value  Size Type    Bind   Vis      Ndx Name0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 1: 00000000     0 FILE    LOCAL  DEFAULT  ABS SimpleSection.c2: 00000000     0 SECTION LOCAL  DEFAULT    1 3: 00000000     0 SECTION LOCAL  DEFAULT    3 4: 00000000     0 SECTION LOCAL  DEFAULT    4 5: 00000000     0 SECTION LOCAL  DEFAULT    5 6: 00000004     4 OBJECT  LOCAL  DEFAULT    3 static_var.12227: 00000000     4 OBJECT  LOCAL  DEFAULT    4 static_var2.12238: 00000000     0 SECTION LOCAL  DEFAULT    7 9: 00000000     0 SECTION LOCAL  DEFAULT    6 10: 00000000     4 OBJECT  GLOBAL DEFAULT    3 global_init_var11: 00000004     4 OBJECT  GLOBAL DEFAULT  COM global_uinit_var12: 00000000    27 FUNC    GLOBAL DEFAULT    1 func113: 00000000     0 NOTYPE  GLOBAL DEFAULT  UND printf14: 0000001b    53 FUNC    GLOBAL DEFAULT    1 main

15个符号中分析比较重要的符号,其他部分可以自己分析。

首先是0x000002d4-0x000002e3,对应内容:

                        0001    0000    0000    0000    0000    0000
00002e0    0004    fff1 

该部分内容中st_name值为0x00000001,表示在常数串表中偏移量为1,即SimpleSection.c的首地址。st_info值为0x04,表示是文件名和局部符号。st_shndx值为0xfff1(即SHN_ABS),宝石该付好包含了一个绝对值。其他成员值为0.

接下来64个字节(地址0x000002e4-0x00000323,每个符号占16个字节,4个符号)对应内容为:

                           0000    0000    0000    0000    0000    0000
00002f0    0003    0001    0000    0000    0000    0000    0000    0000
0000300    0003    0003    0000    0000    0000    0000    0000    0000
0000310    0003    0004    0000    0000    0000    0000    0000    0000
0000320    0003    0005  

这四个符号除了值有st_shndx成员的值不同(分别为1,3,4,5),其他成员均相同。而其他成员值有st_info的值有实际含义,均为0x03,表示该符号是一个段,所以1,3,4,5分别对应段的偏移,分别表示.text,.data,.bss,.rodata(可以查看readelf -a输出结果,其中段表部分第一列即为偏移量)。

接下来比较重要的符号是0x00000324-0x00000333,对应内容:

                        0011    0000    0004    0000    0004    0000
0000330    0001    0003  

这部分内容st_name值为0x00000011,表示在常数串表中偏移量为0x00000011(17),即static_var.1222的首地址。st_size的值均为0x00000004(int类型),分别表示符号值和符号大小。st_info值为0x01,表示是一个局部变量。st_value值为0x00000004,st_shndx值为0x0003,表示偏移为4的段(即.data段)中偏移地址为3的位置。

接下来的重要符号地址为0x00000334-0x00000343,对应内容:

                        0021    0000    0000    0000    0004    0000
0000340    0001    0004 

该部分内容st_name值为0x00000021,表示常数串中偏移量为0x00000021(33),即static_var2.1223的首地址。st_size值为0x00000004(int类型)。st_info值为0x01,表示是一个局部变量.st_shndx值为0x0003,st_value值为0x00000000,表示偏移为0的段中偏移地址为4的未知。

另一个重要符号地址为0x00000364-0x00000373,对应内容:

                        0032    0000    0000    0000    0004    0000
0000370    0011    0003   

该部分st_name值为0x00000032,表示常数串中偏移量为0x00000032(50),即global_init_var的首地址。st_info值为0x11,表示全局变量。其他成员含义与上一个符号类似。

还有一个重要符号地址为0x00000374-0x00000383,对应内容:

                        0042    0000    0004    0000    0004    0000
0000380    0011    fff2   

该部分st_name值为0x00000042,表示常数串中偏移量为0x0000042(66),即global_uint_var的首地址。st_shndx值为0xfff2,表示该符号是common类型符号。st_value值为0x00000004,表示是4个字节对齐。st_size值为0x00000004,表示大小为4(int类型)。

还有重要符号地址为0x00000384-0x00000393,对应内容:

                       0053    0000    0000    0000    001b    0000
0000390    0012    0001   

该部分st_name值为0x00000053,表示对应func1首地址。st_info值为0x12,表示是全局函数。st_value值为0x0000000,st_size值为0x0000001b,故表示偏移为0(.text)的段中28个字节。

从 0x000003b4开始为.strtab(字符串表)段,大小为0x65(101个字节),对应内容为:

                           5300    6d69    6c70    5365    6365    6974
00003c0    6e6f    632e    7300    6174    6974    5f63    6176    2e72
00003d0    3231    3232    7300    6174    6974    5f63    6176    3272
00003e0    312e    3232    0033    6c67    626f    6c61    695f    696e
00003f0    5f74    6176    0072    6c67    626f    6c61    755f    6e69
0000400    7469    765f    7261    6600    6e75    3163    7000    6972
0000410    746e    0066    616d    6e69    00

对应字符串为"\0SimpleSection.c\0static_var.1222\0static_var2.1223\0global_init_var\0global_uint_var\0func1\0printf\0main\0"(使用od -c SimpleSection.o与-x选项得到结果对比即可)

从0x0000041c开始为.rel.text段(重定位表),大小为0x28(40个字节),对应内容为:

                                                           0010    0000
0000420    0501    0000    0015    0000    0d02    0000    002e    0000
0000430    0301    0000    0033    0000    0401    0000    0046    0000
0000440    0c02    0000

重定位表数据结构(/usr/include/elf.h):

typedef struct
{Elf32_Addr    r_offset;               /* Address */Elf32_Word    r_info;                 /* Relocation type and symbol index */
} Elf32_Rel;

该数据结构大小为8个字节,所以40个字节应该包含5个这样的结构。

而需要重定位的符号可以使用下面的命令列出:

objdump -r SimpleSection.o

输出结果:

SimpleSection.o:     file format elf32-i386RELOCATION RECORDS FOR [.text]:
OFFSET   TYPE              VALUE
00000010 R_386_32          .rodata
00000015 R_386_PC32        printf
0000002e R_386_32          .data
00000033 R_386_32          .bss
00000046 R_386_PC32        func1

刚好是5个符号。

根据上面内容,SimpleSection.o的结构形式如下:

------------------------------------- 0x00000000

|   ELF Header(e_shoff=0x10c)

------------------------------------- 0x00000034

0x50  | .text

------------------------------------- 0x00000084

0x08  | .data

------------------------------------- 0x0000008c

0x04  | .rodata

------------------------------------- 0x00000090

0x28  | .comment

------------------------------------- 0x000000b8

0x51  | .shsttab

------------------------------------- 0x00000109

--------------------------------------0x0000010c

0x1b8 | Section Table

-------------------------------------- 0x000002c4

0xf0   |  .symtab

-------------------------------------- 0x000003b4

0x65  |   .strtab

-------------------------------------- 0x0000041c

0x28  |   .rel.text

--------------------------------------- 0x00000444

根据上面的实例观察可知,分析一个elf文件的内部构造,可以先找到elf头,并找到其中几个关键成员e_shoff(段表偏移地址), e_shentnum(段的个数), e_shsize(每个段的大小)。这样可以到段表中再分析每个段相关信息。另外,这个例子中elf文件是重定位文件,它缺乏elf中的一个重要段(程序文件头表),这个表只存在于可执行文件或动态链接库文件中。它可以通过elf头的e_phoff(文件头表偏移地址),e_phnum(文件头表个数)和e_phsize(每个表大小)来确定该文件头表的位置及大小。分析方式和elf头类似。文件头表类型定义(/usr/include/elf.h):

typedef struct
{Elf32_Word    p_type;                 /* Segment type */Elf32_Off     p_offset;               /* Segment file offset */Elf32_Addr    p_vaddr;                /* Segment virtual address */Elf32_Addr    p_paddr;                /* Segment physical address */Elf32_Word    p_filesz;               /* Segment size in file */Elf32_Word    p_memsz;                /* Segment size in memory */Elf32_Word    p_flags;                /* Segment flags */Elf32_Word    p_align;                /* Segment alignment */
} Elf32_Phdr;/* Special value for e_phnum.  This indicates that the real number ofprogram headers is too large to fit into e_phnum.  Instead the realvalue is in the field sh_info of section 0.  */#define PN_XNUM         0xffff/* Legal values for p_type (segment type).  */#define PT_NULL         0               /* Program header table entry unused */
#define PT_LOAD         1               /* Loadable program segment */
#define PT_DYNAMIC      2               /* Dynamic linking information */
#define PT_INTERP       3               /* Program interpreter */
#define PT_NOTE         4               /* Auxiliary information */
#define PT_SHLIB        5               /* Reserved */
#define PT_PHDR         6               /* Entry for header table itself */
#define PT_TLS          7               /* Thread-local storage segment */
#define PT_NUM          8               /* Number of defined types */
#define PT_LOOS         0x60000000      /* Start of OS-specific */
#define PT_GNU_EH_FRAME 0x6474e550      /* GCC .eh_frame_hdr segment */
#define PT_GNU_STACK    0x6474e551      /* Indicates stack executability */
#define PT_GNU_RELRO    0x6474e552      /* Read-only after relocation */
#define PT_LOSUNW       0x6ffffffa
#define PT_SUNWBSS      0x6ffffffa      /* Sun Specific segment */
#define PT_SUNWSTACK    0x6ffffffb      /* Stack segment */
#define PT_HISUNW       0x6fffffff
#define PT_HIOS         0x6fffffff      /* End of OS-specific */
#define PT_LOPROC       0x70000000      /* Start of processor-specific */
#define PT_HIPROC       0x7fffffff      /* End of processor-specific *//* Legal values for p_flags (segment flags).  */#define PF_X            (1 << 0)        /* Segment is executable */
#define PF_W            (1 << 1)        /* Segment is writable */
#define PF_R            (1 << 2)        /* Segment is readable */
#define PF_MASKOS       0x0ff00000      /* OS-specific */
#define PF_MASKPROC     0xf0000000      /* Processor-specific */

感兴趣的可以自己分析,并和readelf -a得到结果对比来加深理解。

例如我写的一个小测试程序:

#include <stdio.h>
#include <elf.h>
int main(void)
{printf("%d\n",sizeof(Elf32_Sym));printf("Hello, World\n");return 0;
}

使用下面命令编译:

gcc -o test test.c

得到一个名为test的可执行文件。

使用下面命令得到test文件文件头表相关部分的结构:

readelf -l test

该命令输出:

Elf file type is EXEC (Executable file)
Entry point 0x8048340
There are 8 program headers, starting at offset 52Program Headers:Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg AlignPHDR           0x000034 0x08048034 0x08048034 0x00100 0x00100 R E 0x4INTERP         0x000134 0x08048134 0x08048134 0x00013 0x00013 R   0x1[Requesting program interpreter: /lib/ld-linux.so.2]LOAD           0x000000 0x08048000 0x08048000 0x005b8 0x005b8 R E 0x1000LOAD           0x0005b8 0x080495b8 0x080495b8 0x0010c 0x00114 RW  0x1000DYNAMIC        0x0005cc 0x080495cc 0x080495cc 0x000d0 0x000d0 RW  0x4NOTE           0x000148 0x08048148 0x08048148 0x00020 0x00020 R   0x4GNU_EH_FRAME   0x000514 0x08048514 0x08048514 0x00024 0x00024 R   0x4GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x4Section to Segment mapping:Segment Sections...00     01     .interp 02     .interp .note.ABI-tag .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame 03     .ctors .dtors .jcr .dynamic .got .got.plt .data .bss 04     .dynamic 05     .note.ABI-tag 06     .eh_frame_hdr 07

使用hexdump得到test文件前308个字节(前52个字节为ELF头内容,后面256个字节(这部分是文件头表,可以从0000001c地址开始四个字节值0x00000034(e_phoff)知道文件头表偏移地址是从第52个字节开始,每个表32(e_phentsize,0x0000002a地址开始的2个字节,0x0020)个字节,共8(e_phnum,0x0000002c地址开始的2个字节,对应0x0008)个表)内容:

hexdump -x test -n 308

输出结果:

0000000    457f    464c    0101    0001    0000    0000    0000    0000
0000010    0002    0003    0001    0000    8340    0804    0034    0000
0000020    07e8    0000    0000    0000    0034    0020    0008    0028
0000030    001e    001b    0006    0000    0034    0000    8034    0804
0000040    8034    0804    0100    0000    0100    0000    0005    0000
0000050    0004    0000    0003    0000    0134    0000    8134    0804
0000060    8134    0804    0013    0000    0013    0000    0004    0000
0000070    0001    0000    0001    0000    0000    0000    8000    0804
0000080    8000    0804    05b8    0000    05b8    0000    0005    0000
0000090    1000    0000    0001    0000    05b8    0000    95b8    0804
00000a0    95b8    0804    010c    0000    0114    0000    0006    0000
00000b0    1000    0000    0002    0000    05cc    0000    95cc    0804
00000c0    95cc    0804    00d0    0000    00d0    0000    0006    0000
00000d0    0004    0000    0004    0000    0148    0000    8148    0804
00000e0    8148    0804    0020    0000    0020    0000    0004    0000
00000f0    0004    0000    e550    6474    0514    0000    8514    0804
0000100    8514    0804    0024    0000    0024    0000    0004    0000
0000110    0004    0000    e551    6474    0000    0000    0000    0000
0000120    0000    0000    0000    0000    0000    0000    0006    0000
0000130    0004    0000
0000134

文件头段第一个表(0x00000034-0x00000053)内容:

                           0006    0000    0034    0000    8034    0804
0000040    8034    0804    0100    0000    0100    0000    0005    0000
0000050    0004    0000    

刚好有8个成员,每个成员4个字节。

p_type值为0x00000006,表示文件头表入口。p_offset值为0x00000034,表示其偏移地址。p_vaddr值为0x08048034,表示虚拟地址。p_paddr值为0x08048034,表示物理地址。p_filesz值为0x00000100,表示文件中段的大小256个字节。p_memsz值为0x00000100,表示内存中段大小为256个字节。p_flags值为0x00000005,表示该段可读并且可执行。p_align值为0x00000004表示该段是4字节对齐。这个段表刚好对应文件头段的256个字节。

其他内容不再详细分析,感兴趣的可以自己探索研究

elf文件格式实例解析相关推荐

  1. linux 可执行文件_linux中ELF二进制程序解析

    0. 简介 在Linux系统的可执行文件(ELF文件)中,开头是一个文件头,用来描述程序的布局,整个文件的属性等信息,包括文件是否可执行.静态还是动态链接及入口地址等信息:如下图所示: 程序文件中包含 ...

  2. ELF文件格式的详解

    1.说明 2.elf文件的基本格式 3.elf文件的头部信息 4.elf文件的节区(Section) 4.1 节区的作用 4.2 节区的组成 5.elf文件的段(Segment) 6.用python解 ...

  3. cfile清空文件内容_编译-链接-加载 :ELF文件格式解析

    摘要:对于C++的初学者,经常在程序的编译或者加载过程中遇到很多错误,类似undefined reference to ... 和 GLIBCXX_3.4.20 not found 等.这些错误都涉及 ...

  4. elf文件格式_Android so(ELF) 文件解析

    Android so(ELF) 文件解析 Android so(ELF) 文件解析 前言 生成 so 文件 相关工具 objdump readelf 整体结构图 头部结构 段表结构 字符串表结构 程序 ...

  5. ELF文件格式与进程地址空间的联系

    http://blog.csdn.net/q_l_s/article/details/52597330 三.分析在fork产生新进程中ELF文件格式与进程地址空间的联系 1.进程的虚拟地址空间 每个程 ...

  6. 【Android 逆向】ELF 文件格式 ( 程序头数据 | 节区头数据 | 动态符号表 )

    文章目录 一.程序头数据 二.节区头数据 三.动态符号表 一.程序头数据 在上一篇博客 [Android 逆向]ELF 文件格式 ( ELF 程序头入口大小 | ELF 程序头入口个数 | ELF 文 ...

  7. 【Android 逆向】ELF 文件格式 ( ELF 文件头 | ELF 文件头标志 | ELF 文件位数 | ELF 文件大小端格式 )

    文章目录 一.ELF 文件简介 二.ELF 文件头 三.ELF 文件头标志 四.ELF 文件位数 五.ELF 文件大小端格式 一.ELF 文件简介 在上一篇博客 [Android 逆向]ELF 文件格 ...

  8. ELF文件格式详解-请查收

    上一篇文章中主要介绍了ELF文件的基本定义和目标文件的分类,这篇文章中主要介绍下ELF文件格式 ELF文件主要提供了两个视图:链接视图及执行视图,分别针对程序运行过程的链接过程和执行过程.如下图所示: ...

  9. linux的静态编译elf无法调试,[翻译]自己动手编写一个Linux调试器系列之4 ELF文件格式与DWARF调试格式 by lantie@15PB...

    自己动手编写一个Linux调试器系列之4 ELF文件格式与DWARF调试格式 by lantie@15PB 在上一节中,你已经听说了DWARF调试格式,它是程序的调试信息,是一种可以更好理解源码的方式 ...

最新文章

  1. 通俗易懂详解Java代理及代码实战
  2. pwn入门-PLT表与GOT表、libc入门
  3. [USACO1.3]混合牛奶 Mixing Milk
  4. 执行SQL-MapperMethod.execute()
  5. IOS学习之UIDatePicker控件使用
  6. 为MFC中的ListBox添加水平滚动条
  7. 动态二维数组外圈元素值的和_C语言 | 用指向元素的指针变量输出二维数组元素的值...
  8. 荷兰商业银行使用精益领导力推行改进
  9. “Hello World!”团队召开的第十二次会议
  10. 44膜型键盘_小学四年级数学上册考试必考题型:判断题练习题
  11. 黑马程序员——C语言基础教程笔记
  12. 使用最小二乘法拟合曲线
  13. Nature Aging | 复旦大学冯建峰/程炜揭示每晚睡7小时刚刚好,睡过多或过少都有损大脑和心理健康...
  14. 郭靖大侠的IT为学之路
  15. 一个WEB应用的开发流程
  16. DeepHPV:一个用于预测HPV整合人类基因位点的深度学习模型
  17. 面试问题——英语26 改善环境 愿望
  18. 二阶无源低通滤波器的截止频率与RC关系
  19. linux shell脚本 nohup,linux真正使用shell脚本做定时任务 关键的Nohup
  20. 4.5 银行间拆借利率爬取(switch_to.frame()函数切换到子网页,浏览器同级页面切换--switch.window()函数)

热门文章

  1. ubuntu好文收集
  2. bootstrap-select动态生成数据,设置默认选项(默认值)
  3. console 速查手册
  4. 8.4. su - root
  5. [QGLViewer]3D场景鼠标点击位置
  6. 在PHP中利用wsdl创建标准webservice
  7. Using JSON for data transfer
  8. php上传文件简单类
  9. hadoop-0.21.0-eclipse-plugin无法在eclipse中运行解决方案
  10. PL/SQL配置文件解析