ELF 文档中代码、连接信息和注释是以节(section)为单位存放的,并存有一
个节头表(section header)。对每一节,在节头表中都有一个表项(节头表项)
和之对应,表项记录了该节的一些信息,例如该节在文档中的位置信息和该节
的字节长度信息。

一、ELF 文档和有关术语

Unix 系统的可执行文档和动态库文档是以 ELF 格式存放的。为使下面的叙述
清楚而没有伎义,先简要介绍一下 ELF 文档格式,并约定一些术语。关于ELF
文档格式的周详情况请参看有关文献。

ELF 文档中代码、连接信息和注释是以节(section)为单位存放的,并存有一
个节头表(section header)。对每一节,在节头表中都有一个表项(节头表项)
和之对应,表项记录了该节的一些信息,例如该节在文档中的位置信息和该节
的字节长度信息。

程式运行读入内存时,是以程式段(program segment)为单位读入的。在 ELF
文档中有一个程式头表(program header table),每个程式段在程式头表中有
一个表项(程式头表项)和之对应,表项记录了该程式段的有关信息,例如该程
序段在文档中的位置信息和该程式段的字节长度信息。程式段的内容由若干节
组成,节的内容组合在一起连成一片构成程式段的内容。

在任何这些节中,有一节的内容由字符串构成,这些字符串是各节的名称,叫
节名。下面称这一节为节名表。另有一节,节名为".dynsym",他的内容为符
号表,符号表的每一表项记录了一个符号的有关信息,例如该符号对应的代码
的地址值。更有一节,节名为".dynstr",他的内容由字符串构成。大多数符
号在该节中有一个字符串和之对应,这个字符串是该符号的符号名。而每一函
数对应一个符号,函数名即为符号名。下面称被某一函数对应的符号为函数符
号。

ELF 文档开始处的一段叫 ELF 文档头。他记录了程式头表在文档中的偏移、
程式头表的表项数目、程式头表每一表项的字节长度、节头表在文档中的偏移、
节头表的表项数目、节头表每个表项的字节长度。他还记录了节名表所在的节
的索引序号。

二、动态库符号表修改方法

修改变态库符号表的方法和步骤如下:

第一步:

读取 ELF 文档头,取出

(1) 程式头表在文档中的偏移,获取程式头表在文档中的位置;
(2) 程式头表的表项数目和程式头表每一表项的字节长度;
(3) 节头表在文档中的偏移,获取节头表在文档中的位置;
(4) 节头表的表项数目和节头表每个表项的字节长度;
(3) 节名表所在的节的索引序号。

ELF 文档头在文档中的偏移为零,即起始于 ELF 文档开头的第一字节,他的
数据结构为:

#define EI_NIDENT (16)

typedef uint16_t Elf32_Half;
typedef uint32_t Elf32_Word;
typedef uint32_t Elf32_Addr;
typedef uint32_t Elf32_Off;
typedef uint16_t Elf32_Section;

typedef struct
{
unsigned char e_ident[EI_NIDENT]; /* Magic number及其他信息 */
Elf32_Half e_type; /* ELF 文档类型 */
Elf32_Half e_machine; /* 机器型号 */
Elf32_Word e_version; /* 版本 */
Elf32_Addr e_entry; /* 程式入口虚地址 */
Elf32_Off e_phoff; /* 程式头表在文档中的偏移 */
Elf32_Off e_shoff; /* 节头表在文档中的偏移 */
Elf32_Word e_flags; /* 处理器标志 */
Elf32_Half e_ehsize; /* ELF 文档头长度 */
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_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; /* alignment */
Elf32_Word sh_entsize; /* 如内容为表,每个表项的长度 */
} Elf32_Shdr;

第三步:

按节名表在文档中的偏移和该节的长度读取节名表,并缓存在一个缓冲区中。

第四步:

依据节头表在文档中的偏移、节头表的表项总数、连同节头表每个表项长度搜
索节头表。对每个节头表项,读出节名索引号,由节名索引号从上面缓存在缓
冲区中节名表得出该节头表项对应的节的名字。假如名字为".dynsym",记录
该节的在文档中的偏移和字节长度。名字为 ".dynsym" 的节的内容是符号表,
除了记录他在文档中的偏移和字节长度外,还要记下他的每个表项的长度。每
个表项即是对一个符号所记录的信息,表项的数据结构为:

typedef struct
{
Elf32_Word st_name; /* 符号名索引号 */
Elf32_Addr st_value; /* 符号地址值 */
Elf32_Word st_size; /* 符号对应的代码长度 */

文章整理:西部数码--专业提供域名注册虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!