这个分析过程可以在windows中用010editor的elf模板打开一个elf文件对照分析。
elf文件大致结构:
| file header | readelf -h
|---------------------|
|program header table | readelf -l
|---------------------|
| data |
|---------------------|
| string "table" |
|---------------------|
|section header table | readelf -S
|---------------------|
0. 获取readelf源码
> which readelf
/usr/bin/readelf
> dpkg -S /usr/bin/readelf
binutils: /usr/bin/readelf
> apt-get source binutils
> cd binutils-2.24/binutils
可以用CLion导入binutils-2.24,但是慢。推荐ctags -R
.
结构体定义位于include/elf/ineternal.h
,主要函数位于binutils/readelf.c
,其中有一个static int process_file (char * file_name)
,里面又调用了ret = process_object (file_name, file);
,解析整个文件。
static int
process_object (char * file_name, FILE * file)
{
unsigned int i;
if (! get_file_header (file))
{
error (_("%s: Failed to read file header\n"), file_name);
return 1;
}
/* Initialise per file variables. */
for (i = ARRAY_SIZE (version_info); i--;)
version_info[i] = 0;
for (i = ARRAY_SIZE (dynamic_info); i--;)
dynamic_info[i] = 0;
dynamic_info_DT_GNU_HASH = 0;
/* Process the file. */
if (show_name)
printf (_("\nFile: %s\n"), file_name);
/* Initialise the dump_sects array from the cmdline_dump_sects array.
Note we do this even if cmdline_dump_sects is empty because we
must make sure that the dump_sets array is zeroed out before each
object file is processed. */
if (num_dump_sects > num_cmdline_dump_sects)
memset (dump_sects, 0, num_dump_sects * sizeof (* dump_sects));
if (num_cmdline_dump_sects > 0)
{
if (num_dump_sects == 0)
/* A sneaky way of allocating the dump_sects array. */
request_dump_bynumber (num_cmdline_dump_sects, 0);
assert (num_dump_sects >= num_cmdline_dump_sects);
memcpy (dump_sects, cmdline_dump_sects,
num_cmdline_dump_sects * sizeof (* dump_sects));
}
if (! process_file_header ())
return 1;
if (! process_section_headers (file))
{
/* Without loaded section headers we cannot process lots of
things. */
do_unwind = do_version = do_dump = do_arch = 0;
if (! do_using_dynamic)
do_syms = do_dyn_syms = do_reloc = 0;
}
if (! process_section_groups (file))
{
/* Without loaded section groups we cannot process unwind. */
do_unwind = 0;
}
if (process_program_headers (file))
process_dynamic_section (file);
process_relocs (file);
process_unwind (file);
process_symbol_table (file);
process_syminfo (file);
process_version_sections (file);
process_section_contents (file);
process_notes (file);
process_gnu_liblist (file);
process_arch_specific (file);
//free memory
/*
if (program_headers)
{
free (program_headers);
program_headers = NULL;
}
*/
free_debug_memory ();
return 0;
}
1. File Header
readelf.c的源码使用了BFD开发库,不方便阅读,所有转而查阅/usr/include/elf.h
。
/* Type for a 16-bit quantity. */
typedef uint16_t Elf32_Half;
typedef uint16_t Elf64_Half;
/* Types for signed and unsigned 32-bit quantities. */
typedef uint32_t Elf32_Word;
typedef int32_t Elf32_Sword;
typedef uint32_t Elf64_Word;
typedef int32_t Elf64_Sword;
下面是32位文件头定义,64位中把类型名的32改为64即可。
/* The ELF file header. This appears at the start of every ELF file. */
#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;
1.1 基本信息
e_ident
, 16字节magic number,
- [0-3], “elf” ascii码;
- [4], CLASS(类别), 0/1/2, 无效/32bit/64bit!!!!!!
- [5], 字节序, 0/1/2,无效/小段/大端
- [6], elf版本,默认1
- [7], 系统版本, 默认
ELFOSABI_NONE(0)
- [8], ABI版本, 默认0
- 剩余7字节都是0
/* Fields in the e_ident array. The EI_* macros are indices into the
array. The macros under each EI_* macro are the values the byte
may have. */
#define EI_MAG0 0 /* File identification byte 0 index */
#define ELFMAG0 0x7f /* Magic number byte 0 */
#define EI_MAG1 1 /* File identification byte 1 index */
#define ELFMAG1 'E' /* Magic number byte 1 */
#define EI_MAG2 2 /* File identification byte 2 index */
#define ELFMAG2 'L' /* Magic number byte 2 */
#define EI_MAG3 3 /* File identification byte 3 index */
#define ELFMAG3 'F' /* Magic number byte 3 */
/* Conglomeration of the identification bytes, for easy testing as a word. */
#define ELFMAG "\177ELF"
#define SELFMAG 4
#define EI_CLASS 4 /* File class byte index */
#define ELFCLASSNONE 0 /* Invalid class */
#define ELFCLASS32 1 /* 32-bit objects */
#define ELFCLASS64 2 /* 64-bit objects */
#define ELFCLASSNUM 3
#define EI_DATA 5 /* Data encoding byte index */
#define ELFDATANONE 0 /* Invalid data encoding */
#define ELFDATA2LSB 1 /* 2's complement, little endian */
#define ELFDATA2MSB 2 /* 2's complement, big endian */
#define ELFDATANUM 3
#define EI_VERSION 6 /* File version byte index */
/* Value must be EV_CURRENT */
#define EI_OSABI 7 /* OS ABI identification */
#define ELFOSABI_NONE 0 /* UNIX System V ABI */
#define ELFOSABI_SYSV 0 /* Alias. */
#define ELFOSABI_HPUX 1 /* HP-UX */
#define ELFOSABI_NETBSD 2 /* NetBSD. */
#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */
#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */
#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */
#define ELFOSABI_AIX 7 /* IBM AIX. */
#define ELFOSABI_IRIX 8 /* SGI Irix. */
#define ELFOSABI_FREEBSD 9 /* FreeBSD. */
#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */
#define ELFOSABI_MODESTO 11 /* Novell Modesto. */
#define ELFOSABI_OPENBSD 12 /* OpenBSD. */
#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */
#define ELFOSABI_ARM 97 /* ARM */
#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */
#define EI_ABIVERSION 8 /* ABI version */
e_type
/* Legal values for e_type (object file type). */
#define ET_NONE 0 /* No file type */
#define ET_REL 1 /* Relocatable file */
#define ET_EXEC 2 /* Executable file */
#define ET_DYN 3 /* Shared object file */
#define ET_CORE 4 /* Core file */
#define ET_NUM 5 /* Number of defined types */
#define ET_LOOS 0xfe00 /* OS-specific range start */
#define ET_HIOS 0xfeff /* OS-specific range end */
#define ET_LOPROC 0xff00 /* Processor-specific range start */
#define ET_HIPROC 0xffff /* Processor-specific range end */
汇编器输出的是可重定位文件1,静态链接器输出的是可执行文件2 和 共享目标文件3.
-
e_machine
:如EM_386
(3, Intel 80386) -
e_version
:elf文件版本,默认1; -
e_entry
:程序入口点线性地址,一般用于可执行文件;重定位文件中则为0; -
e_flags
:平台属性,默认0 -
e_ehsize
:文件头大小 -
e_shstrndx
:.shstrtab
在段表中的下标。
1.2 程序头表
e_phoff
:程序头表在elf文件中的偏移e_phentsize
:程序头表大小e_phnum
:程序头表数量
1.3 段表
e_shoff
:段表在elf文件中的偏移e_shentsize
:段表大小e_shnum
:段表数量
程序头表和段表是两个很重要的表。
1.4 readelf中的代码
/* Decode the data held in 'elf_header'. */
static int
process_file_header (void)
{
if ( elf_header.e_ident[EI_MAG0] != ELFMAG0
|| elf_header.e_ident[EI_MAG1] != ELFMAG1
|| elf_header.e_ident[EI_MAG2] != ELFMAG2
|| elf_header.e_ident[EI_MAG3] != ELFMAG3)
{
error
(_("Not an ELF file - it has the wrong magic bytes at the start\n"));
return 0;
}
init_dwarf_regnames (elf_header.e_machine);
if (do_header)
{
int i;
printf (_("ELF Header:\n"));
printf (_(" Magic: "));
for (i = 0; i < EI_NIDENT; i++)
printf ("%2.2x ", elf_header.e_ident[i]);
printf ("\n");
printf (_(" Class: %s\n"),
get_elf_class (elf_header.e_ident[EI_CLASS]));
printf (_(" Data: %s\n"),
get_data_encoding (elf_header.e_ident[EI_DATA]));
printf (_(" Version: %d %s\n"),
elf_header.e_ident[EI_VERSION],
(elf_header.e_ident[EI_VERSION] == EV_CURRENT
? "(current)"
: (elf_header.e_ident[EI_VERSION] != EV_NONE
? _("<unknown: %lx>")
: "")));
printf (_(" OS/ABI: %s\n"),
get_osabi_name (elf_header.e_ident[EI_OSABI]));
printf (_(" ABI Version: %d\n"),
elf_header.e_ident[EI_ABIVERSION]);
printf (_(" Type: %s\n"),
get_file_type (elf_header.e_type));
printf (_(" Machine: %s\n"),
get_machine_name (elf_header.e_machine));
printf (_(" Version: 0x%lx\n"),
(unsigned long) elf_header.e_version);
printf (_(" Entry point address: "));
print_vma ((bfd_vma) elf_header.e_entry, PREFIX_HEX);
printf (_("\n Start of program headers: "));
print_vma ((bfd_vma) elf_header.e_phoff, DEC);
printf (_(" (bytes into file)\n Start of section headers: "));
print_vma ((bfd_vma) elf_header.e_shoff, DEC);
printf (_(" (bytes into file)\n"));
printf (_(" Flags: 0x%lx%s\n"),
(unsigned long) elf_header.e_flags,
get_machine_flags (elf_header.e_flags, elf_header.e_machine));
printf (_(" Size of this header: %ld (bytes)\n"),
(long) elf_header.e_ehsize);
printf (_(" Size of program headers: %ld (bytes)\n"),
(long) elf_header.e_phentsize);
printf (_(" Number of program headers: %ld"),
(long) elf_header.e_phnum);
if (section_headers != NULL
&& elf_header.e_phnum == PN_XNUM
&& section_headers[0].sh_info != 0)
printf (" (%ld)", (long) section_headers[0].sh_info);
putc ('\n', stdout);
printf (_(" Size of section headers: %ld (bytes)\n"),
(long) elf_header.e_shentsize);
printf (_(" Number of section headers: %ld"),
(long) elf_header.e_shnum);
if (section_headers != NULL && elf_header.e_shnum == SHN_UNDEF)
printf (" (%ld)", (long) section_headers[0].sh_size);
putc ('\n', stdout);
printf (_(" Section header string table index: %ld"),
(long) elf_header.e_shstrndx);
if (section_headers != NULL
&& elf_header.e_shstrndx == (SHN_XINDEX & 0xffff))
printf (" (%u)", section_headers[0].sh_link);
else if (elf_header.e_shstrndx != SHN_UNDEF
&& elf_header.e_shstrndx >= elf_header.e_shnum)
printf (_(" <corrupt: out of range>"));
putc ('\n', stdout);
}
if (section_headers != NULL)
{
if (elf_header.e_phnum == PN_XNUM
&& section_headers[0].sh_info != 0)
elf_header.e_phnum = section_headers[0].sh_info;
if (elf_header.e_shnum == SHN_UNDEF)
elf_header.e_shnum = section_headers[0].sh_size;
if (elf_header.e_shstrndx == (SHN_XINDEX & 0xffff))
elf_header.e_shstrndx = section_headers[0].sh_link;
else if (elf_header.e_shstrndx >= elf_header.e_shnum)
elf_header.e_shstrndx = SHN_UNDEF;
free (section_headers);
section_headers = NULL;
}
return 1;
}