目录
1. 任务描述
2. 实验阶段
2.1 反汇编+获取重定位记录
2.2 分析
2.3 查看节头表,确定偏移量
2.4 使用hexedit工具修改指定内容
1. 任务描述
修改二进制可重定位目标文件“phase1.o”的数据(.data)节内容(不允许修改其他节),使其与main.o模块如下链接后运行时输出目标字符串“123456789”。
注意:实验目录中的phase1-backup.o文件与未修改的phase1.o相同,供可用于重置/覆盖实验中被错误修改的phase1.o目标文件。
2. 实验阶段
① 使用objdump工具获得目标文件的汇编代码,使用readelf工具获得其重定位记录;
② 结合汇编代码和重定位信息,定位输出函数的调用参数在目标文件中的存储地址;
③使用hexedit工具,对phase1.o模块的数据节中相应字节进行修改。
2.1 反汇编+获取重定位记录
1. 先切换当前工作目录到目标关卡
cd /data/workspace/myshixun/step1
2. 使用实验平台中的objdump工具获得可重定位目标模块phase1.o的反汇编代码,分析程序执行逻辑
objdump -d phase1.o > phase1.s
cat phase1.s
注意:在可重定位的目标模块中,不管是要调用的函数还是其调用参数,它们的引用地址都不是以后链接后生成的可执行程序中的最终实际地址。
3. 使用readelf工具获得程序引用静态数据变量所对应的重定位记录,获得引用的变量名
readelf -r phase1.o
2.2 分析
readelf工具的输出是一条一条的重定位记录,每个重定位记录的内容如下面结构所示:
typedef struct {
int offset; //节内偏移
int symbol:24, //所绑定符号
type:8; //重定位类型
} Elf32_Rel;
do_phase函数中偏移量为0x10的call指令操作数对应了第二条重定位记录,也就是说call的实际目标函数是puts函数,用于输出一个字符串,并且链接器应该按照R_386_PC32重定位方式把puts函数的绝对地址相对于PC值的偏移量放到call指令中对应引用的4个字节的操作数中。
R_386_PC32 PC绝对地址重定位方式下:
重定位后的引用地址 = 符号定义地址 - 符号引用所在地址 + 重定位前引用处的初始值
既然call指令调用的是完成打印输出的puts函数,那么压入栈中,传递给它的唯一参数就是输出字符串的地址。
相应的可知:偏移量0x7开始的4个字节,在重定位后存放的是程序要输出的字符串的首地址,该引用地址在重定位时使用了R_386_32的重定位方法。
R_386_32 绝对地址重定位方式下:
重定位后的引用地址 = 符号定义地址 + 重定位前引用处的初始值
由此得出输出字符串的起始地址:在.data节中相对节起始、偏移量为0x11f的位置。接下来我们要设法获知.data节在目标模块phase1.o中的什么位置。
2.3 查看节头表,确定偏移量
节头表(Section Header Table)
• ELF可重定位目标文件中的重要组成部分;
• 描述每个节的节名、在文件中的偏移、大小、访问属性、对齐方式等。
IA32/Linux系统中ELF文件的节头数据结构:
typedef struct {
Elf32_Word sh_name; //节名
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Word sh_addr;
Elf32_Word sh_offset; //节在文件中的偏移地址
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
} Elf32_Shdr
1.获取节头表
readelf -S phase1.o
该命令打印输出了phase1.o文件中包含的所有节的信息:
2.确定偏移量
由图读出.data节在文件中的起始偏移量为0x60
结合前面的分析可知:输出字符串在文件中的起始地址为 0x60 + 0x11f = 0x17f
2.4 使用hexedit工具修改指定内容
1.检查确认原来的内容
readelf -x.data phase1.o
该命令输出打印了phase1.o模块中.data节的内容
2.使用hexedit工具修改二进制目标文件中的数据对象初始值
hexedit phase1.o
按Ctrl + G快速定位到0x17f的位置
从0x17f开始将输出字符串替换为123456789(1的ASCII码为31),最后不要忘记设置一个00字符作为字符串的结尾。
3.验证修改是否有效完成实验目标
gcc -no-pie -o linkbomb main.o phase1.o
./linkbomb
可以看到程序的输出就是我们所期望的“123456789”,至此,实验圆满结束。