我正在尝试检索ELF二进制文件中的其他部分的内容。此时,我使用以下代码检索每个部分的名称:
#include <stdio.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#pragma pack(push,1)
#pragma pack(pop)
#define EI_NIDENT 16
/* 32-bit ELF base types. */
typedef unsigned int Elf32_Addr;
typedef unsigned short Elf32_Half;
typedef unsigned int Elf32_Off;
typedef signed int Elf32_Sword;
typedef unsigned int Elf32_Word;
/* 64-bit ELF base types. */
typedef unsigned long long Elf64_Addr;
typedef unsigned short Elf64_Half;
typedef signed short Elf64_SHalf;
typedef unsigned long long Elf64_Off;
typedef signed int Elf64_Sword;
typedef unsigned int Elf64_Word;
typedef unsigned long long Elf64_Xword;
typedef signed long long Elf64_Sxword;
typedef struct elf32_hdr{
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry; /* Entry point */
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
} Elf32_Ehdr;
typedef struct elf32_shdr {
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr;
typedef struct elf64_hdr {
unsigned char e_ident[EI_NIDENT]; /* ELF "magic number" */
Elf64_Half e_type;
Elf64_Half e_machine;
Elf64_Word e_version;
Elf64_Addr e_entry; /* Entry point virtual address */
Elf64_Off e_phoff; /* Program header table file offset */
Elf64_Off e_shoff; /* Section header table file offset */
Elf64_Word e_flags;
Elf64_Half e_ehsize;
Elf64_Half e_phentsize;
Elf64_Half e_phnum;
Elf64_Half e_shentsize;
Elf64_Half e_shnum;
Elf64_Half e_shstrndx;
} Elf64_Ehdr;
typedef struct elf64_shdr {
Elf64_Word sh_name; /* Section name, index in string tbl */
Elf64_Word sh_type; /* Type of section */
Elf64_Xword sh_flags; /* Miscellaneous section attributes */
Elf64_Addr sh_addr; /* Section virtual addr at execution */
Elf64_Off sh_offset; /* Section file offset */
Elf64_Xword sh_size; /* Size of section in bytes */
Elf64_Word sh_link; /* Index of another section */
Elf64_Word sh_info; /* Additional section information */
Elf64_Xword sh_addralign; /* Section alignment */
Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;
int main(int argc, char **argv)
{
FILE* ElfFile = NULL;
char* SectNames = NULL;
Elf64_Ehdr elfHdr;
Elf64_Shdr sectHdr;
uint32_t idx;
if(argc != 2) {
printf("usage: %s <ELF_FILE>\n", argv[0]);
exit(1);
}
if((ElfFile = fopen(argv[1], "r")) == NULL) {
perror("[E] Error opening file:");
exit(1);
}
// read ELF header, first thing in the file
fread(&elfHdr, 1, sizeof(Elf64_Ehdr), ElfFile);
// read section name string table
// first, read its header.
/*
e_shoff This member holds the section header table's file offset
in bytes. If the file has no section header table, this
member holds zero.
e_shstrndx This member holds the section header table index of the
entry associated with the section name string table. If
the file has no section name string table, this member
holds the value SHN_UNDEF.
If the index of section name string table section is
larger than or equal to SHN_LORESERVE (0xff00), this
member holds SHN_XINDEX (0xffff) and the real index of
the section name string table section is held in the
sh_link member of the initial entry in section header
table. Otherwise, the sh_link member of the initial
entry in section header table contains the value zero.
SHN_UNDEF This value marks an undefined, missing,
irrelevant, or otherwise meaningless
section reference. For example, a symbol
"defined" relative to section number
SHN_UNDEF is an undefined symbol.
SHN_LORESERVE This value specifies the lower bound of the
range of reserved indices.
SHN_LOPROC Values greater than or equal to SHN_HIPROC
are reserved for processor-specific
semantics.
SHN_HIPROC Values less than or equal to SHN_LOPROC are
reserved for processor-specific semantics.
SHN_ABS This value specifies absolute values for
the corresponding reference. For example,
symbols defined relative to section number
SHN_ABS have absolute values and are not
affected by relocation.
SHN_COMMON Symbols defined relative to this section
are common symbols, such as Fortran COMMON
or unallocated C external variables.
SHN_HIRESERVE This value specifies the upper bound of the
range of reserved indices between
SHN_LORESERVE and SHN_HIRESERVE, inclusive;
the values do not reference the section
header table. That is, the section header
table does not contain entries for the
reserved indices.
*/
fseek(ElfFile, elfHdr.e_shoff + elfHdr.e_shstrndx * sizeof(sectHdr), SEEK_SET);
fread(§Hdr, 1, sizeof(sectHdr), ElfFile);
/*
sh_size This member holds the section's size in bytes. Unless the
section type is SHT_NOBITS, the section occupies sh_size
bytes in the file. A section of type SHT_NOBITS may have a
nonzero size, but it occupies no space in the file.
sh_offset This member's value holds the byte offset from the
beginning of the file to the first byte in the section.
One section type, SHT_NOBITS, occupies no space in the
file, and its sh_offset member locates the conceptual
placement in the file.
e_shnum This member holds the number of entries in the section
header table. Thus the product of e_shentsize and
e_shnum gives the section header table's size in bytes.
If a file has no section header table, e_shnum holds the
value of zero.
If the number of entries in the section header table is
larger than or equal to SHN_LORESERVE (0xff00), e_shnum
holds the value zero and the real number of entries in
the section header table is held in the sh_size member of
the initial entry in section header table. Otherwise,
the sh_size member of the initial entry in the section
header table holds the value zero.
sh_name This member specifies the name of the section. Its value
is an index into the section header string table section,
giving the location of a null-terminated string.
*/
// next, read the section, string data
// printf("sh_size = %llu\n", sectHdr.sh_size);
SectNames = malloc(sectHdr.sh_size);
fseek(ElfFile, sectHdr.sh_offset, SEEK_SET);
fread(SectNames, 1, sectHdr.sh_size, ElfFile);
// read all section headers
for (idx = 0; idx < elfHdr.e_shnum; idx++)
{
const char* name = "";
fseek(ElfFile, elfHdr.e_shoff + idx * sizeof(sectHdr), SEEK_SET);
fread(§Hdr, 1, sizeof(sectHdr), ElfFile);
// print section name
if (sectHdr.sh_name);
name = SectNames + sectHdr.sh_name;
printf("%2u %s\n", idx, name);
}
return 0;
}
在&#34; hello world&#34;上运行 readelf binary产生以下输出:
$ readelf -S helloworld
There are 30 section headers, starting at offset 0x1170:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 0000000000400238 00000238
000000000000001c 0000000000000000 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000400254 00000254
0000000000000020 0000000000000000 A 0 0 4
[ 3] .note.gnu.build-i NOTE 0000000000400274 00000274
0000000000000024 0000000000000000 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000400298 00000298
000000000000001c 0000000000000000 A 5 0 8
[ 5] .dynsym DYNSYM 00000000004002b8 000002b8
0000000000000060 0000000000000018 A 6 1 8
[ 6] .dynstr STRTAB 0000000000400318 00000318
000000000000003d 0000000000000000 A 0 0 1
[ 7] .gnu.version VERSYM 0000000000400356 00000356
0000000000000008 0000000000000002 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000400360 00000360
0000000000000020 0000000000000000 A 6 1 8
[ 9] .rela.dyn RELA 0000000000400380 00000380
0000000000000018 0000000000000018 A 5 0 8
[10] .rela.plt RELA 0000000000400398 00000398
0000000000000048 0000000000000018 A 5 12 8
[11] .init PROGBITS 00000000004003e0 000003e0
000000000000001a 0000000000000000 AX 0 0 4
[12] .plt PROGBITS 0000000000400400 00000400
0000000000000040 0000000000000010 AX 0 0 16
[13] .text PROGBITS 0000000000400440 00000440
0000000000000182 0000000000000000 AX 0 0 16
[14] .fini PROGBITS 00000000004005c4 000005c4
0000000000000009 0000000000000000 AX 0 0 4
[15] .rodata PROGBITS 00000000004005d0 000005d0
0000000000000013 0000000000000000 A 0 0 4
[16] .eh_frame_hdr PROGBITS 00000000004005e4 000005e4
0000000000000034 0000000000000000 A 0 0 4
[17] .eh_frame PROGBITS 0000000000400618 00000618
00000000000000f4 0000000000000000 A 0 0 8
[18] .init_array INIT_ARRAY 0000000000600e10 00000e10
0000000000000008 0000000000000000 WA 0 0 8
[19] .fini_array FINI_ARRAY 0000000000600e18 00000e18
0000000000000008 0000000000000000 WA 0 0 8
[20] .jcr PROGBITS 0000000000600e20 00000e20
0000000000000008 0000000000000000 WA 0 0 8
[21] .dynamic DYNAMIC 0000000000600e28 00000e28
00000000000001d0 0000000000000010 WA 6 0 8
[22] .got PROGBITS 0000000000600ff8 00000ff8
0000000000000008 0000000000000008 WA 0 0 8
[23] .got.plt PROGBITS 0000000000601000 00001000
0000000000000030 0000000000000008 WA 0 0 8
[24] .data PROGBITS 0000000000601030 00001030
0000000000000010 0000000000000000 WA 0 0 8
[25] .bss NOBITS 0000000000601040 00001040
0000000000000008 0000000000000000 WA 0 0 1
[26] .comment PROGBITS 0000000000000000 00001040
0000000000000024 0000000000000001 MS 0 0 1
[27] .shstrtab STRTAB 0000000000000000 00001064
0000000000000108 0000000000000000 0 0 1
[28] .symtab SYMTAB 0000000000000000 000018f0
0000000000000618 0000000000000018 29 45 8
[29] .strtab STRTAB 0000000000000000 00001f08
000000000000023c 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
O (extra OS processing required) o (OS specific), p (processor specific)
检查以下条目:
[13] .text PROGBITS 0000000000400440 00000440
0000000000000182 0000000000000000 AX 0 0 16
为了从二进制文件中完全检索 .text 部分,从文件的地址0x400440 + 0x440读取0x182字节就足够了吗?其中0x182是段大小,0x400440是地址,0x440是偏移量?此外,对齐(0x16)的作用是什么?
答案 0 :(得分:11)
要提取.text
部分,您需要从二进制文件中的0x440(Size
)地址开始复制0x182(Offset
)个字节。
忽略0x400440(Address
)值,它与文件地址无关,它与RAM内存中的地址.text
部分将被loader复制。来自ELF format specification:
sh_addr :如果该部分将出现在进程的内存映像中,则此成员将提供 该部分的第一个字节应该驻留的地址。否则,该成员包含0。
Align
值实际上是十进制,而不是十六进制。所以它是16,而不是0x16。对齐意味着节地址必须是16(字节)的倍数。
您可以自行验证所有这些,探索二进制文件。首先,观察二进制的反汇编:
$ objdump -D your-file | less
查找.text
开始的位置,然后查看.text
部分数据。现在只做一个愚蠢的hexdump
操作:
$ hexdump -C your-file | less
现在找到Offset
地址并查看从该地址开始的字节。您会发现它们与反汇编.text
部分的字节数相同。
结论:在处理文件时,您需要使用Offset
值(来自readelf
输出),而不是Address
值。