<一>ELF简介:
可执行链接格式(Executable and Linking
Format)最初是由UNIX系统实验室(UNIX System
Laboratories,USL)开发并发布的,作为应用程序二进制接口(Application Binary
Interface,ABI)的一部分。工具接口标准(Tool Interface
Standards,TIS)委员会将还在发展的ELF标准选作为一种可移植的目标文件格式,可以在32位Intel体系结构上的很多操作系统中使用。
ELF标准的目的是为软件开发人员提供一组二进制接口定义,这些接口可以延伸到多种操作环境,从而减少重新编码、重新编译程序的需要。接口的内容包括目标模块格式、可执行文件格式以及调试记录信息与格式等。
目标文件有三种类型:
可重定位文件(Relocatable File) 包含适合于与其他目标文件链接来创建可执行文件或者共享目标文件的代码和数据。
可执行文件(Executable File) 包含适合于执行的一个程序,此文件规定了 exec() 如何创建一个程序的进程映像。
共享目标文件(Shared Object File)
包含可在两种上下文中链接的代码和数据。首先链接编辑器可以将它和其它可重定位文件和共享目标文件一起处理,生成另外一个目标文件。其次,动态链接器
(Dynamic Linker)可能将它与某个可执行文件以及其它共享目标一起组合,创建进程映像。
<二>Linux下的ELF文件
我们在linux下如何识别elf文件?
这要用到file这个linux下识别文件类型的工具了。
例如:
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$ ll
总用量 10
-rwxrwx--- 1 root plugdev 592 2008-08-31 08:19 e_aout
-rwxrwx--- 1 root plugdev 768 2008-08-31 08:09 hello
-rwxrwx--- 1 root plugdev 704 2008-08-31 11:41 hello.o
-rwxrwx--- 1 root plugdev 195 2008-08-31 08:08 hello.s
-rwxrwx--- 1 root plugdev 36 2008-08-31 15:11 h.sh
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$ file *
e_aout: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped
hello: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped
hello.o: ELF 32-bit LSB relocatable, Intel 80386, version 1 (SYSV), not stripped
hello.s: ASCII assembler program text
h.sh: Bourne-Again shell script text executable
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$
标注为ELF的就是ELF文件,并可以看到后面有executable字样的为可执行文件,而后面有relocatable字样的为可重定位文件。
<三>开始动手分析
关于分析的工具,我的上一篇日志已经提到了,敬请参考。
首先声明一下——如果你对ELF文件的格式还不太清楚,建议你先读一下下面的文章:
但有时你会打不开,我已经把它转载到了我的上一篇日志中了。
好了,开始:
首先我们知道ELF文件都有一个固定大小为52字节的文件头ELF32_Ehdr,该结构定义在/usr/include/elf.h中
。我们就先从文件头开始:
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$ hexdump -s 0 -n 52 -C hello
00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 02 00 03 00 01 00 00 00 80 80 04 08 34 00 00 00 |............4...|
00000020 08 01 00 00 00 00 00 00 34 00 20 00 02 00 28 00 |........4. ...(.|
00000030 07 00 04 00 |....|
00000034
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$
我们来对应ELF32_Ehdr结构来分析:
e_ident[EI_NIDENT]: 16字节(在/usr/include/elf.h 中有 #define EI_NIDENT 16)
VmGLinux联盟 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
e_ident[0] 0x7fVmGLinux联盟
e_ident[1] 'E'VmGLinux联盟
e_ident[2] 'L'VmGLinux联盟
e_ident[3] 'F'VmGLinux联盟
e_ident[4]VmGLinux联盟 1
e_ident[5] 1VmGLinux联盟
e_ident[6] 1VmGLinux联盟
剩下的全为 0VmGLinux联盟
e_type: 2字节: 0x0002 表示可执行文件(ET_EXEC 2 Executable file)VmGLinux联盟
e_machine: 2字节: 0x0003 表示386体系文件(EM_386 3 Intel 80386)VmGLinux联盟
e_version: 4字节: 0x00000001 和e_ident里面的EI_VERSION含义一样.VmGLinux联盟
e_entry: 4字节: 0x04088080 表示程序入口地址0x04088080VmGLinux联盟
e_phoff: 4字节: 0x00000034 表示program head table在文件中的偏移量(开始位置)VmGLinux联盟
e_shoff: 4字节: 0x00000108 表示section head table在文件中的偏移量(开始位置)VmGLinux联盟
e_flags: 4字节: 0x00000000VmGLinux联盟
e_ehsize: 2字节: 0x0034 表示elf header大小, 其实就是sizeof(Elf32_Ehdr)VmGLinux联盟
e_phentsize: 2字节: 0x0020 表示每个program header 的大小(0x20)VmGLinux联盟
e_phnum: 2字节: 0x0002 表示一共多少个program header(0x02)VmGLinux联盟
e_shentsize: 2字节: 0x0028 表示每个section header的大小(0x28)VmGLinux联盟
e_shnum: 2字节: 0x0007 表示有多少个section headerVmGLinux联盟
e_shstrndx:
2字节: 0x0004 表示section string table在section header
table中的索引值.(即第几个section描述了section string table的位置和大小)
这样就把这种十六进制和ELF格式对应了起来。
此外还有一个分析ELF的强大的工具readelf
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$ readelf -h hello
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x8048080
Start of program headers: 52 (bytes into file)
Start of section headers: 264 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 2
Size of section headers: 40 (bytes)
Number of section headers: 7
Section header string table index: 4
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$
看readelf的读取是否于咱们分析一一对应上了
在每个可执行文件中都必有程序头部表,每个程序头部表都对应这一个程序段,有readelf读出的内容可知,该程序的第一个程序头部表相对文件的偏移地址为52bytes(Start of program headers:52 (bytes into file)),而该头部表的大小为32bytes(Size of program headers: 32 (bytes)),由hexdump我们来查看程序头部表的内容:
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$ hexdump -s 52 -n 32 -C hello
00000034 01 00 00 00 00 00 00 00 00 80 04 08 00 80 04 08 |................|
00000044 9f 00 00 00 9f 00 00 00 05 00 00 00 00 10 00 00 |................|
00000054
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$
对应于程序头部表的数据结构来分析:
/* Program segment header. */
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;
p_type: 4字节: 0x00000001 表示该段是PT_PHDR类型(自己的入口)VmGLinux联盟
p_offset: 4字节: 0x00000000 在文件中的偏移量.VmGLinux联盟
p_vaddr: 4字节: 0x08048000 虚拟地址:0x08048000VmGLinux联盟
p_paddr: 4字节: 0x08048000 物理地址: 0x08048000VmGLinux联盟
p_filesz: 4字节: 0x0000009f 段的大小:0x9fVmGLinux联盟
p_memsz: 4字节: 0x0000009f 在内存中的大小: 0x9fVmGLinux联盟
p_flags: 4字节: 0x00000005 段标记0x5VmGLinux联盟
p_align: 4字节: 0x00001000 VmGLinux联盟 段对齐
有 Number of program headers:2我们知道该程序有两个程序段,并且每个程序头部表的大小都一样,则查看第二个程序头部表:
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$ hexdump -s 84 -n 32 -C hello
00000054 01 00 00 00 a0 00 00 00 a0 90 04 08 a0 90 04 08 |................|
00000064 0d 00 00 00 0d 00 00 00 06 00 00 00 00 10 00 00 |................|
00000074
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$
有其中的p_offset 为0x000000a0可知该程序在整个可执行文件中的位置,起大小为0x0000000d即14,进而查看该段的内容:
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$ hexdump -s 0xa0 -n 14 -C hello
000000a0 68 65 6c 6c 6f 2c 20 77 6f 72 6c 64 0a 00 |hello, world..|
000000ae
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$
由此可以断定此程序段为程序的数据段,我们可以用readelf来确认一下:
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$ readelf -l hello
Elf file type is EXEC (Executable file)
Entry point 0x8048080
There are 2 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x08048000 0x08048000 0x0009f 0x0009f R E 0x1000
LOAD 0x0000a0 0x080490a0 0x080490a0 0x0000d 0x0000d RW 0x1000
Section to Segment mapping:
Segment Sections...
00 .text
01 .data
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$
可以看到第二个段的offset 为0x0000a0 ;MenSiz 为0x0000d Flg(权限)为RW 可读可写,为数据段。
如果还存在其他的程序段则,则可以继续增加程序头表的偏移(加程序头表的大小32字节),进而找到该程序段和其大小来查看该程序段的内容。
注:
该程序是我的一个用nasm语法格式写的一个简单的hello程序:
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$ cat hello.s
section .data
msg db "hello, world",0xA
len equ $-msg
section .text
global _start
_start:
mov eax,4
mov ebx,1
mov ecx,msg
mov edx,len
int 80h
mov eax,1
xor ebx,ebx
int 80h
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$
编译:
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$ nasm -f elf hello.s
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$
此时生成hello.o可重定位文件。
连接:
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$ ld -o hello hello.o
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$
这样生成hello可重执行文件。
执行:
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$ ./hello
hello, world
lzel@lzel-laptop:~/works/ASM/3/elftoaout/nasm$
浅显分析到此,肯定有不少缺失和错误,还请路过不吝赐教!
阅读(2577) | 评论(0) | 转发(0) |