轉(zhuǎn)載自:https://blog.csdn.net/yyttiao/article/details/7358578
今天主要講述的是ELF 文件的另一個(gè)大塊叫節(jié)頭(Section Headers),那么什么叫節(jié)頭呢.節(jié)頭里面分散著不同的段,有.bss .text .init .data .got .shstrtab 等等.節(jié)頭表主要是在連接的角度看待ELF的.每一個(gè)節(jié)都保存著該節(jié)特有的數(shù)據(jù),由于節(jié)中數(shù)據(jù)的用途不同,節(jié)被分為不同的類(lèi)型,每種類(lèi)型都又自己組織數(shù)據(jù)的方式.有的節(jié)儲(chǔ)存著一些字符串,例如前面提過(guò)的.shstrtab 這個(gè)節(jié),就專(zhuān)門(mén)儲(chǔ)存節(jié)對(duì)應(yīng)名詞的字符串.今天這一節(jié)內(nèi)容,我們就用使用這個(gè)節(jié),獲取節(jié)的名字.
下面按照我們每一次的慣例,先看一下結(jié)構(gòu)體說(shuō)明,然后再逐一介紹每一個(gè)成員,在前面幾節(jié)中,我們主要是對(duì)ELF進(jìn)行一個(gè)全面的了解,為以后進(jìn)行HOOK做充分的準(zhǔn)備.因?yàn)槟遣糠值迷贓LF文件的基礎(chǔ)上去實(shí)施的,好了.加油吧~~~
詳細(xì)說(shuō)明請(qǐng)參閱 elf.h
/* Section header. */
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;
sh_name: 該成員是字符串表中的一個(gè)索引
通過(guò)該索引就可以獲取這個(gè)節(jié)所對(duì)應(yīng)的節(jié)的名字.通常同節(jié)的類(lèi)型(sh_type)來(lái)判斷當(dāng)前是屬于什么節(jié).
sh_type: 節(jié)的類(lèi)型
這個(gè)成員可以告訴我們這個(gè)節(jié)里面存放的到底是什么數(shù)據(jù),對(duì)于不同的數(shù)據(jù),我們?cè)偻ㄟ^(guò)不同的數(shù)據(jù)結(jié)構(gòu)去獲取數(shù)據(jù)
sh_flags: 節(jié)的屬性
這個(gè)成員指定該節(jié)的內(nèi)容是否可讀可寫(xiě)等屬性,具體定義如下:
#define SHF_WRITE (1 << 0)
/* Writable */
#define SHF_ALLOC (1 << 1)
/* Occupies memory during execution */
#define SHF_EXECINSTR (1 << 2)
/* Executable */
#define SHF_MERGE (1 << 4)
/* Might be merged */
#define SHF_STRINGS (1 << 5)
/* Contains nul-terminated strings */
#define SHF_INFO_LINK (1 << 6)
/* `sh_info' contains SHT index */
#define SHF_LINK_ORDER(1 << 7)
/* Preserve order after combining */
#define SHF_OS_NONCONFORMING (1 << 8)
/* Non-standard OS specific handling required */
sh_addr: 指向進(jìn)程空間內(nèi)的虛擬地址
如果這個(gè)節(jié)需要被加載到進(jìn)程中,那么該字段就指向內(nèi)存中的虛擬地址
sh_offset: 指向文件空間內(nèi)的絕對(duì)偏移(相對(duì)整個(gè)文件的起始地址的)
該地址可以獲取到很多關(guān)于節(jié)內(nèi)容的信息.
sh_size: 節(jié)的內(nèi)容大小
如果此節(jié)在文件中占用一定的字節(jié),那么這個(gè)字段給出了該節(jié)所占用的字節(jié)大小,
如果此節(jié)不存在于文件卻在內(nèi)存中,那么這個(gè)字段給出了該節(jié)在內(nèi)存中的字節(jié)大小
sh_link: 如果這個(gè)節(jié)于別的節(jié)相連,那么這個(gè)字段給出了相關(guān)的節(jié)在節(jié)頭中的索引
sh_info: 額外的信息
對(duì)于部分節(jié)來(lái)說(shuō),還有一些額外的信息.
sh_addralign: 地址對(duì)齊
對(duì)于雙字節(jié)等數(shù)據(jù)格式,很多時(shí)候必須對(duì)數(shù)據(jù)進(jìn)行必要的對(duì)齊,來(lái)保證數(shù)據(jù)的準(zhǔn)確存儲(chǔ),這個(gè)數(shù)是2的整數(shù)次冪,對(duì)齊只能有2字節(jié)對(duì)齊,4字節(jié)對(duì)齊,8字節(jié)對(duì)齊等,如果是0或者1表示這個(gè)節(jié)不用對(duì)齊.
sh_entsize: 指定節(jié)內(nèi)部數(shù)據(jù)的大小
這個(gè)字段代表字節(jié)大小的數(shù),對(duì)于某些字節(jié)才有意義,例如動(dòng)態(tài)符號(hào)節(jié)來(lái)說(shuō),這個(gè)字段就給出動(dòng)態(tài)符號(hào)表中每一個(gè)符號(hào)結(jié)構(gòu)的字節(jié)大小.
上面說(shuō)到,要獲取節(jié)的名字要怎么做呢.sh_name字段只是保存了一個(gè)值,并不是字符串地址,那要怎么才能獲取字符串呢.在前面講述的第一個(gè)ELF Head的時(shí)候有這樣一個(gè)字段e_shstrndx這個(gè)成員就是指向字符串表的索引,就可以知道找到節(jié)點(diǎn)中的字符串節(jié)表了.
同樣在遍歷節(jié)頭的時(shí)候,如果字段類(lèi)型等于SHT_STRTAB,那么對(duì)應(yīng)的也就是字符串表.但是為了方便,在ELF Head中已經(jīng)指出了他所對(duì)應(yīng)的索引了.在字符串表中是以一系列的'\0'結(jié)尾的字符串,在這個(gè)節(jié)的第一個(gè)字節(jié)也是0,為什么第一個(gè)字節(jié)會(huì)是0呢,其實(shí)這就是空字符串.在這個(gè)節(jié)的最后一個(gè)字節(jié)也是0. 因?yàn)樽址淖詈笠粋€(gè)都市'\0'.
既然找到了字符串表,那怎么獲取他里面的名字呢,其實(shí)可以把字符串表當(dāng)成一個(gè)很大的char型數(shù)組sh_name就是所需要的字符串的首字符,通過(guò)字符串表首地址加上相對(duì)的偏移量就是對(duì)應(yīng)的字符串的名字了.
下面我們就來(lái)通過(guò)代碼來(lái)加深印象..
示例代碼:
#include "readShdr.h"
SectionType secTyoe[] = {
{0, "NULL"},
{1, "SHT_PROGBITS"},
{2, "SHT_SYMTAB"},
{3, "SHT_STRTAB"},
{4, "SHT_RELA"},
{5, "SHT_HASH"},
{6, "SHT_DYNAMIC"},
{7, "SHT_NOTE"},
{8, "SHT_NOBITS"},
{9, "SHT_REL"},
{10, "SHT_SHLIB"},
{11, "SHT_DYNSYM"},
{14, "SHT_INIT_ARRAY"},
{15, "SHT_FINI_ARRAY"},
{16, "SHT_PREINIT_ARRAY"},
{17, "SHT_GROUP"},
{18, "SHT_SYMTAB_SHNDX"},
{19, "SHT_NUM"},
{0x60000000, "SHT_LOOS"},
{0x6ffffff6, "SHT_GNU_HASH"},
{0x6ffffff7, "SHT_GNU_LIBLIST"},
{0x6ffffff8, "SHT_CHECKSUM"},
{0x6ffffffa, "SHT_LOSUNW"},
{0x6ffffffb, "SHT_SUNW_move"},
{0x6ffffffc, "SHT_SUNW_COMDAT"},
{0x6ffffffd, "SHT_SUNW_syminfo"},
{0x6ffffffe, "SHT_GNU_verdef"},
{0x6fffffff, "SHT_GNU_verneed"},
{0x70000000, "SHT_LOPROC"},
{0x7fffffff, "SHT_HIPROC"},
{0x80000000, "SHT_LOUSER"},
{0x8fffffff, "SHT_HIUSER"},
};
char *findSecTypeName(unsigned int type)
{
int i = 0;
for (i = 0; i < sizeof(secTyoe) / sizeof(SectionType); i++)
{
if (secTyoe[i].type == type)
{
return secTyoe[i].typeName;
break;
}
}
return secTyoe[0].typeName;
}
void displayShdr(Elf32_Ehdr *ehdr, Elf32_Shdr *shdr)
{
int py = ehdr->e_shstrndx * sizeof(Elf32_Shdr);
Elf32_Shdr *symtab = (Elf32_Shdr *)((char *)shdr + py);
printf("symtab 0x%x\n", symtab);
char *szShdrName = (char *)(symtab->sh_offset + (char *)ehdr);
printf("e_shstrndx=%dsizeof(Elf32_Shdr)=%dshdr=x%x\n", ehdr->e_shstrndx, sizeof(Elf32_Shdr), shdr);
printf("Section Headers: 0x%x\n", szShdrName);
int i = 0;
printf("[Nr] %-20s%-20s%-5s%-8s%-6s%-6s%-6s%-4s%-4s%-2s\n",
"Name", "Type",
"Flg", "Addr", "Off", "Size", "Lk", "Inf", "Al", "ES");
for (i = 0; i < ehdr->e_shnum; i++)
{
printf("[%-2d] %-20s", i, szShdrName + shdr->sh_name);
printf("%-20s", findSecTypeName(shdr->sh_type));
printf("%-5x", shdr->sh_flags);
printf("%-08x", shdr->sh_addr);
printf("%-06x", shdr->sh_offset);
printf("%-06x", shdr->sh_size);
printf("%-4x", shdr->sh_link);
printf("%-4x", shdr->sh_info);
printf("%-4x", shdr->sh_addralign);
printf("%-02x\n", shdr->sh_entsize);
shdr++;
}
}