0%

CSapp-Link Lab

CMU无此实验,HIT增加

每个实验阶段考察 ELF 文件组成与程序链接过程的不同方面知识

  • 阶段1:全局变量 <=> 数据节
  • 阶段2:指令 <=> 代码节
  • 阶段3:符号解析
  • 阶段4:switch语句与重定位
  • 阶段5:重定位

实验文件

  • main.o:主程序的可重定位目标模块(实验中无需修改)
  • phase1.o …….. phase5.o:各阶段实验所针对的二进制可重定位目标模块,需在相应实验阶段中予以修改或补充
  • linkbombn:验证文件

在实验中的每一阶段,按照阶段的目标要求修改相应可重定位二进制目标模块 phasen.o 后,使用如下命令生成可执行程序 linkbomb:

1
2
linux > gcc -m32 -o linkbomb main.o phase[n].o 
./linkbomb

使用工具

hexedit

hexedit 不仅仅是十六进制编辑器,它提供了获取文件差异的信息,可用于比较二进制文件,它的用户界面基于 ncurses

我们可以像下面这样安装它:(可能需要 root 权限)

1
linux > apt install hexedit

打开终端输入以下指令即可运行:

1
linux > hexedit filename

用方向键移动,ctrl+w保存,ctrl+x退出,man hexedit

readelf

  • -a —all 等同于同时使用:-h -l -S -s -r -d -V -A -l
  • -h —file-header 显示ELF文件头
  • -l —program-headers 显示程序头
  • -S —section-headers 显示节头
  • -t —section-details 显示节详细信息
  • -s —syms 显示符号表(symbol table)
  • -r —relocs 显示重定位信息
  • -d —dynamic 显示动态节(dynamic section)
  • -x —hex-dump= 以字节形式显示输出指定节的内容
  • -p —string-dump= 以字符串形式显示输出指定节的内容
  • -R —relocated-dump= 以重定位后的字节形式显示输出指定节内容

符号相关知识

符号表

在计算机科学中,符号表是一种用于语言翻译器(例如:编译器和解释器)中的数据结构,在符号表中,程序源代码中的每个标识符都和它的声明或使用信息绑定在一起,比如其数据类型、作用域以及内存地址

1
2
3
4
5
6
7
8
9
typedef struct {
Elf32_Word st_name; /* 符号对应字符串在strtab节中的偏移量 */
Elf32_Word st_value; /* 在对应节中的偏移量,可执行文件中是虚拟地址 */
Elf32_Word st_size; /* 符号对应目标所占字节数 */
unsigned char type: 4, /* 符号对应目标的类型:数据、函数、源文件、节 */
binding: 4; /* 符号类别:全局符号、局部符号、弱符号 */
unsigned char st_other;
Elf32_Section st_shndx; /* 符号对应目标所在的节,或其他情况 */
}Elf32_Sym;

散列表

散列表是用来实现符号表的一种常用技术,编译器可能会使用一个很大的符号表来包含所有的符号,或是针对不同的作用域使用层次结构的多个独立的符号表

符号

  • Global symbols(模块内部定义的全局符号)
    • 由模块m定义并能被其他模块引用的符号(非static函数,非static全局变量)
  • External symbols(外部定义的全局符号)
    • 由其他模块定义并被模块m引用的全局符号
  • Local symbols(本模块的局部符号)
    • 仅由模块m定义和引用的本地符号(带有static的函数和全局变量)
    • 注意:局部变量不会在过程外被引用(分配在栈中),因此不是符号定义

符号变量

自动变量(动态局部变量):auto

  • 离开函数,值就消失
  • 不写 static 就默认是 auto

静态局部变量:static

  • 离开函数,值任然保留
  • 变量的值只在函数内部生效
  • 带有 static 的变量只会初始化一次(数据存储在 data 段)
  • 当上一级函数多次调用本函数时,带有 static 的变量数值不变(并且不会进行初始化)

寄存器变量:register

  • 离开函数,值就消失
  • 变量的值只在函数内部生效

全局变量:在 main 之外

  • 允许 main 中所有函数访问
  • 允许外部其他文件访问

静态全局变量:在 main 之外,static

  • 允许 main 中所有函数访问
  • 变量的值只在文件内部生效

节简析

使用目标文件的节头表,可以定位文件的所有节,节头表是 Elf32_ShdrElf64_Shdr 结构的数组

节头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
typedef struct {
elf32_Word sh_name; /* 节的名称,此成员值是节头字符串表节的索引,用于指定以空字符结尾的字符串的位置 */
Elf32_Word sh_type; /* 用于将节的内容和语义分类 */
Elf32_Word sh_flags; /* 节可支持用于说明杂项属性的1位标志 */
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_Word sh_name;
Elf64_Word sh_type;
Elf64_Xword sh_flags;
Elf64_Addr sh_addr;
Elf64_Off sh_offset;
Elf64_Xword sh_size;
Elf64_Word sh_link;
Elf64_Word sh_info;
Elf64_Xword sh_addralign;
Elf64_Xword sh_entsize;
} Elf64_Shdr;

参考:节 - 链接程序和库指南

节分配

节简述

  • ELF头:包括16字节的标识信息,文件类型(.o,exec,.so),机器类型(如Intel 80386),节头表的偏移,节头表的表项大小及表项个数
  • .text节:编译后的代码部分
  • .rodata节:只读数据,如 printf 用到的格式串,switch 跳转表等
  • .data节:已初始化的全局变量和静态变量
  • .bss节:未初始化全局变量和静态变量,仅是占位符,不占据任何磁盘空间,区分初始化和非初始化是为了空间效率
  • .symtab节:存放函数和全局变量(符号表)的信息,它不包括局部变量
  • .rel.text节:.text节的重定位信息,用于重新修改代码段的指令中的地址信息
  • .debug节:调试用的符号表(gcc -g)
  • .strtab节:包含 .symtab节和 .debug节 中的符号及节名

示例(可能会有不同,比如:在我的电脑上 .data 为第4节)

  • ABS:表示不该被重定位
  • UND:表示未定义
  • COM:表示未初始化数据(.bss)
  • value:表示对齐要求,size:给出最小大小

编译器驱动程序

编译器驱动程序的工作:调用语言预处理器,编译器,汇编器,链接器

详细过程:

  • 预处理阶段:预处理器(cpp)根据以字符 “#” 开头的命令,修改原始的 C 程序(比如 hello.c 中第 1 行的#include命令告诉预处理器读取系统头文件 stdio.h 的内容)并把它直接插入程序文本中,结果就得到了另一个 C 程序
    • 通常是以 .i 作为文件扩展名
    • 所谓的头文件,里面装的其实就是函数声明(libc库中的函数:scanf,printf 等)
  • 编译阶段:编译器(ccl)将文本文件 hello.i 翻译成文本文件 hello.s,它包含一个汇编语言程序
    • .s 文件其实就是装有汇编语言的文件
  • 汇编阶段:汇编器(as)将 hello.s 翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序(relocatable object program)的格式,并将结果保存在目标文件 hello.o 中
    • hello.o 文件是一个二进制文件,它包含的 17 个字节是函数 main 的指令编码
    • 如果我们在文本编辑器中打开 hello.o文件,将看到一堆乱码(二进制)
  • 链接阶段:链接器(ld)就负责处理合并各个 hello.o 文件,结果就得到 hello 文件,它是一个可执行目标文件(或者简称为可执行文件),可以被加载到内存中,由系统执行
    • 请注意,hello 程序调用了 printf 函数,它是每个 C 编译器都提供的标准 C 库中的一个函数
    • printf 函数存在于一个名为 printf.o 的单独的预编译好了的目标文件中,而这个文件必须以某种方式合并到我们的 hello.o 程序中
    • 通过修改 hello.o 可以影响最终文件 hello 的效果

强符号与弱符号

在C语言中:

  • 函数和初始化的全局变量(包括显示初始化为0)是强符号
  • 未初始化的全局变量是弱符号

对于它们,下列三条规则使用:

  • 同名的强符号只能有一个,否则编译器报”重复定义”错误
  • 允许一个强符号和多个弱符号,但定义会选择强符号的
  • 当有多个弱符号相同时,链接器选择最先出现那个,也就是与链接顺序有关

实验一

步骤一:使用 readelf 和 bjdump 工具,首先确定 printf(具体为 puts )输出函数的第2个调用参数对应的字符串地址(在 .data 节中)

步骤二:使用readelf 或 objdump 工具,查看 .data 节中的字符串内容并与未修改的 phase1.o 链接后程序输出的字符串比较,确定该字符串为修改的目标

步骤三:使用 hexedit 或自己写程序该字符串前若干字符替换为目标学号中的字符

实验一的程序框架:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include "config.h"
void (*phase)(); /*初始化为0*/
int main( int argc, const char* argv[] ) {
if ( phase )
(*phase)();
else
printf("To run lab, please link the relevant object module with the main module.\n");
return 0;
}

直接运行输出这个结果:

1
2
➜  [/home/ywhkkx/ex1.linklab/l1] ./linkbomb1 
173000209UVv00d3vyFALuMYjxuPCr5m38uiiwLQwlzRxr80BxYnv7r6zz9aChMvWou2DL9e4tc6EidF1olhqrhH3gxYChAckMo7uMXJHwSDfiEmlYH hhJElIl RzCWKPcuNvdHAIt h7487zkd39QJEm4Rqwyer L 0gauz1N2w9g PyaHAl

现在先收集信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
➜  [/home/ywhkkx/ex1.linklab/l1] readelf -s phase1.o

Symbol table '.symtab' contains 17 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS phase1.c
2: 00000000 0 SECTION LOCAL DEFAULT 2
3: 00000000 0 SECTION LOCAL DEFAULT 4
4: 00000000 0 SECTION LOCAL DEFAULT 5
5: 00000000 200 OBJECT LOCAL DEFAULT 4 ALJsLxmF // target
6: 00000000 0 SECTION LOCAL DEFAULT 6
7: 00000000 0 SECTION LOCAL DEFAULT 8
8: 00000000 0 SECTION LOCAL DEFAULT 10
9: 00000000 0 SECTION LOCAL DEFAULT 11
10: 00000000 0 SECTION LOCAL DEFAULT 9
11: 00000000 0 SECTION LOCAL DEFAULT 1
12: 00000000 43 FUNC GLOBAL DEFAULT 2 do_phase
13: 00000000 0 FUNC GLOBAL HIDDEN 8 __x86.get_pc_thunk.ax
14: 00000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_ // GOT表
15: 00000000 0 NOTYPE GLOBAL DEFAULT UND puts
16: 00000000 4 OBJECT GLOBAL DEFAULT 6 phase

发现可疑目标:ALJsLxmF

  • Bind:LOCAL(局部符号)
  • Ndx:4(第4节 .data)
  • Value:0(节内偏移为0)
  • Type:OBJECT(全局变量)

局部全局变量,大小为 200,位于 .data 节,节内偏移为 0

接下来只需要查看 data 节在 .o 文件中的偏移是多少应该就可了:

1
2
3
4
5
6
7
8
9
10
11
12
➜  [/home/ywhkkx/ex1.linklab/l1] readelf -S phase1.o
There are 16 section headers, starting at offset 0x3f0:

节头:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .group GROUP 00000000 000034 000008 04 13 13 4
[ 2] .text PROGBITS 00000000 00003c 00002b 00 AX 0 0 1
[ 3] .rel.text REL 00000000 000328 000020 08 I 13 2 4
[ 4] .data PROGBITS 00000000 000080 0000c8 00 WA 0 0 32
[ 5] .bss NOBITS 00000000 000148 000000 00 WA 0 0 1
[ 6] .data.rel.local PROGBITS 00000000 000148 000004 00 WA 0 0 4

.data节 偏移为“0x80”,在 HexEdit 中定为偏移为“0x80+0x0”(节偏移+节内偏移)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
00000000   7F 45 4C 46  01 01 01 00  00 00 00 00  00 00 00 00  .ELF............
00000010 01 00 03 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
00000020 F0 03 00 00 00 00 00 00 34 00 00 00 00 00 28 00 ........4.....(.
00000030 10 00 0F 00 01 00 00 00 08 00 00 00 55 89 E5 53 ............U..S
00000040 83 EC 04 E8 FC FF FF FF 05 01 00 00 00 8D 90 11 ................
00000050 00 00 00 83 EC 0C 52 89 C3 E8 FC FF FF FF 83 C4 ......R.........
00000060 10 90 8B 5D FC C9 C3 00 00 00 00 00 00 00 00 00 ...]............
00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000080 37 30 6C 4C 39 46 39 09 39 68 6B 52 58 55 38 41 70lL9F9.9hkRXU8A
00000090 31 31 37 33 30 30 30 32 30 39 55 56 76 30 30 64 1173000209UVv00d
000000A0 33 76 79 46 41 4C 75 4D 59 6A 78 75 50 43 72 35 3vyFALuMYjxuPCr5
000000B0 6D 33 38 75 69 69 77 4C 51 77 6C 7A 52 78 72 38 m38uiiwLQwlzRxr8
000000C0 30 42 78 59 6E 76 37 72 36 7A 7A 39 61 43 68 4D 0BxYnv7r6zz9aChM
000000D0 76 57 6F 75 32 44 4C 39 65 34 74 63 36 45 69 64 vWou2DL9e4tc6Eid
000000E0 46 31 6F 6C 68 71 72 68 48 33 67 78 59 43 68 41 F1olhqrhH3gxYChA
000000F0 63 6B 4D 6F 37 75 4D 58 4A 48 77 53 44 66 69 45 ckMo7uMXJHwSDfiE
00000100 6D 6C 59 48 09 68 68 4A 45 6C 49 6C 20 52 7A 43 mlYH.hhJElIl RzC
00000110 57 4B 50 63 75 4E 76 64 48 41 49 74 09 68 37 34 WKPcuNvdHAIt.h74
00000120 38 37 7A 6B 64 33 39 51 4A 45 6D 34 52 71 77 79 87zkd39QJEm4Rqwy
00000130 65 72 09 4C 20 30 67 61 75 7A 31 4E 32 77 39 67 er.L 0gauz1N2w9g
00000140 20 50 79 61 48 41 6C 00 00 00 00 00 8B 04 24 C3 PyaHAl.......$.
00000150 00 47 43 43 3A 20 28 55 62 75 6E 74 75 20 37 2E .GCC: (Ubuntu 7.
00000160 33 2E 30 2D 31 36 75 62 75 6E 74 75 33 29 20 37 3.0-16ubuntu3) 7
00000170 2E 33 2E 30 00 00 00 00 14 00 00 00 00 00 00 00 .3.0............
--- phase1.o --0x0/0x670------------------------------------------------

可以发现 printf 并没有从变量 ALJsLxmF 的头部开始打印,对原字符串进行搜索,发现偏移多了“0x11”,利用 objdump 反汇编进行检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
➜  [/home/ywhkkx/ex1.linklab/l1] objdump -d phase1.o

phase1.o: 文件格式 elf32-i386


Disassembly of section .text:

00000000 <do_phase>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 53 push %ebx
4: 83 ec 04 sub $0x4,%esp
7: e8 fc ff ff ff call 8 <do_phase+0x8>
c: 05 01 00 00 00 add $0x1,%eax
11: 8d 90 11 00 00 00 lea 0x11(%eax),%edx
// lea 取源操作数地址的偏移量,并把它传送到目的操作数所在的单元
17: 83 ec 0c sub $0xc,%esp
1a: 52 push %edx
1b: 89 c3 mov %eax,%ebx
1d: e8 fc ff ff ff call 1e <do_phase+0x1e>
22: 83 c4 10 add $0x10,%esp
25: 90 nop
26: 8b 5d fc mov -0x4(%ebp),%ebx
29: c9 leave
2a: c3 ret

Disassembly of section .text.__x86.get_pc_thunk.ax:

00000000 <__x86.get_pc_thunk.ax>:
0: 8b 04 24 mov (%esp),%eax
3: c3 ret

现在可以用 HexEdit 修改二进制数据了,我将把它改为 “YHellow”

1
2
3
➜  [/home/ywhkkx/ex1.linklab/l1] gcc -m32 -o linkbomb main.o phase1.o 
➜ [/home/ywhkkx/ex1.linklab/l1] ./linkbomb
YHellow

实验二

修改二进制可重定位目标文件 phase2.o 的代码节内容,使其与 main.o 链接后能够运行输出(且仅输出)自己的学号

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
#include "config.h"
void (*phase)(); /*初始化为0*/
int main( int argc, const char* argv[] ) {
if ( phase )
(*phase)();
else
printf("To run lab, please link the relevant object module with the main module.\n");
return 0;
}
1
2
3
4
5
6
7
8
9
static void OUTPUT_FUNC_NAME(const char *id)     // 该函数名对每名学生均不同
{
if(strcmp(id,MYID) != 0 ) return;
printf("%s\n", id);
}
void do_phase() {
// 在代码节中预留存储位置供学生插入完成功能的必要指令
asm( “nop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop\n\t…” );
}

​ // 这个实验要求我们在 do_phase 中执行 OUTPUT_FUNC_NAME,打印 ID

步骤一:使用 objdump 工具,定位 phase2.o 代码节中包含对 printf(会被优化为 puts)输出函数调用的函数的偏移量地址

步骤二:使用 objdump 工具,分析 do_phase 函数的反汇编指令,确定加入对前述输出函数的调用指令的 .text 节中的偏移量位置

步骤三:构造调用输出函数(通过相对PC的偏移量)的机器指令,并替换 do_phase 函数中预留的 nop 指令偏移量

注:目标输出函数为 static 类型,可通过偏移量直接调用跳转(无需重定位)

直接执行程序:

1
➜  [/home/ywhkkx/ex1.linklab/l2] ./linkbomb2 // 啥也没有(本来也不该有)

信息收集:

1
2
➜  [/home/ywhkkx/ex1.linklab/l2] file linkbomb2 
linkbomb2: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=32fa54a562f55fda7e482d48180dc3e162fcea98, not stripped

发现 linkbomb2 是 dynamically,函数采用懒加载(利用PLT/GOT进行加载)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
➜  [/home/ywhkkx/ex1.linklab/l2] readelf -s phase2.o  

Symbol table '.symtab' contains 22 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS phase2.c
2: 00000000 0 SECTION LOCAL DEFAULT 3
3: 00000000 0 SECTION LOCAL DEFAULT 5
4: 00000000 0 SECTION LOCAL DEFAULT 6
5: 00000000 0 SECTION LOCAL DEFAULT 7
6: 00000000 65 FUNC LOCAL DEFAULT 3 yeDfwUkv
7: 00000000 0 SECTION LOCAL DEFAULT 8
8: 00000000 0 SECTION LOCAL DEFAULT 10
9: 00000000 0 SECTION LOCAL DEFAULT 11
10: 00000000 0 SECTION LOCAL DEFAULT 13
11: 00000000 0 SECTION LOCAL DEFAULT 14
12: 00000000 0 SECTION LOCAL DEFAULT 12
13: 00000000 0 SECTION LOCAL DEFAULT 1
14: 00000000 0 SECTION LOCAL DEFAULT 2
15: 00000000 0 FUNC GLOBAL HIDDEN 11 __x86.get_pc_thunk.bx
16: 00000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
17: 00000000 0 NOTYPE GLOBAL DEFAULT UND strcmp
18: 00000000 0 NOTYPE GLOBAL DEFAULT UND puts
19: 00000041 48 FUNC GLOBAL DEFAULT 3 do_phase // target
20: 00000000 0 FUNC GLOBAL HIDDEN 10 __x86.get_pc_thunk.ax
21: 00000000 4 OBJECT GLOBAL DEFAULT 8 phase

目标在:第3节,节内偏移为“0x41”的地方

1
2
3
4
5
6
7
8
9
➜  [/home/ywhkkx/ex1.linklab/l2] readelf -S phase2.o
There are 19 section headers, starting at offset 0x458:

节头:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .group GROUP 00000000 000034 000008 04 16 20 4
[ 2] .group GROUP 00000000 00003c 000008 04 16 15 4
[ 3] .text PROGBITS 00000000 000044 000071 00 AX 0 0 1

在 .text 节中找到指定的 do_phase 位置(需要修改的函数),偏移为“0x44+0x41”(注意,puts,strcmp等函数是要在链接重定向完成之后才能确定地址,在反汇编代码中显示出来)

1
2
3
4
5
6
7
8
9
10
11
00000000   7F 45 4C 46  01 01 01 00  00 00 00 00  00 00 00 00  .ELF............
00000010 01 00 03 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
00000020 58 04 00 00 00 00 00 00 34 00 00 00 00 00 28 00 X.......4.....(.
00000030 13 00 12 00 01 00 00 00 0A 00 00 00 01 00 00 00 ................
00000040 0B 00 00 00 55 89 E5 53 83 EC 04 E8 FC FF FF FF ....U..S........
00000050 81 C3 02 00 00 00 83 EC 08 8D 83 00 00 00 00 50 ...............P
00000060 FF 75 08 E8 FC FF FF FF 83 C4 10 85 C0 75 10 83 .u...........u..
00000070 EC 0C FF 75 08 E8 FC FF FF FF 83 C4 10 EB 01 90 ...u............
00000080 8B 5D FC C9 C3 55 89 E5 E8 FC FF FF FF 05 01 00 .]...U..........
00000090 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
000000A0 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 ................
1
2
3
4
5
6
7
8
9
00000041 <do_phase>:
41: 55 push %ebp
42: 89 e5 mov %esp,%ebp
44: e8 fc ff ff ff call 45 <do_phase+0x4>
49: 05 01 00 00 00 add $0x1,%eax
4a: 90 nop
4b: 90 nop
4c: 90 nop
4d: 90 nop

在“0x86”处发现 do_phase (55 89 e5 e8 …….. )

先输入:gcc -m32 -o linkbomb2 main.o phase2.o 进行链接重定向(phase2.o 没有变)

然后用 objdump 进行打印:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
➜  [/home/ywhkkx/ex1.linklab/l2] objdump -d linkbomb2 

....................

00001215 <yeDfwUkv>:
1215: 55 push %ebp
1216: 89 e5 mov %esp,%ebp
1218: 53 push %ebx
1219: 83 ec 04 sub $0x4,%esp
121c: e8 9f fe ff ff call 10c0 <__x86.get_pc_thunk.bx>
1221: 81 c3 b3 2d 00 00 add $0x2db3,%ebx
1227: 83 ec 08 sub $0x8,%esp
122a: 8d 83 a8 e0 ff ff lea -0x1f58(%ebx),%eax
1230: 50 push %eax
1231: ff 75 08 pushl 0x8(%ebp)
1234: e8 07 fe ff ff call 1040 <strcmp@plt> // strcmp
1239: 83 c4 10 add $0x10,%esp
123c: 85 c0 test %eax,%eax
123e: 75 10 jne 1250 <yeDfwUkv+0x3b> // do_phase
1240: 83 ec 0c sub $0xc,%esp
1243: ff 75 08 pushl 0x8(%ebp)
1246: e8 05 fe ff ff call 1050 <puts@plt> // printf的优化
124b: 83 c4 10 add $0x10,%esp
124e: eb 01 jmp 1251 <yeDfwUkv+0x3c>
1250: 90 nop
1251: 8b 5d fc mov -0x4(%ebp),%ebx
1254: c9 leave
1255: c3 ret

想要在 do_phase 中执行 OUTPUT_FUNC_NAME,打印 ID,必须先传入参数 ID,因为“strcmp(id,MYID)”,参数 ID 在 MYID 中,所以改为传入 MYID 的地址

但是 MYID 的地址是变化的(PIE保护),不能直接获取其地址

但是在动态链接中,地址可以通过GOT表来获取,确定MYID在GOT中的偏移

1
2
3
4
5
6
7
121c:	e8 9f fe ff ff       	call   10c0 <__x86.get_pc_thunk.bx> 
1221: 81 c3 b3 2d 00 00 add $0x2db3,%ebx
............
122a: 8d 83 a8 e0 ff ff lea -0x1f58(%ebx),%eax
1230: 50 push %eax
1231: ff 75 08 pushl 0x8(%ebp)
1234: e8 07 fe ff ff call 1040 <strcmp@plt> // strcmp

“121c”和“1221”这两步就是为了获取GOT表,装入ebx

“122a”这一步取出了位于偏移为“-0x1f58”的数组,装入eax

“1230”和“1231”这两步显然是把“strcmp”的参数压栈(MYID 和 id,从右往左依次入栈)

而“strcmp”的参数就是 MYID ,反推得“-0x1f58”为MYID在GOT表中的偏移,MYID的地址有了

可以在构思注入 do_phase 的汇编了:

1
2
3
4
5
.code32 // 不添加就会报错
lea -0x1f58(%eax), %eax
push %eax
call -86
pop %eax

查看二进制代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  [/home/ywhkkx/ex1.linklab/l2] gcc -c 2.s -o 2.o  
➜ [/home/ywhkkx/ex1.linklab/l2] objdump -d 2.o

2.o: 文件格式 elf64-x86-64


Disassembly of section .text:

0000000000000000 <.text>:
0: 8d 80 a8 e0 ff ff lea -0x1f58(%rax),%eax
6: 50 push %rax
7: e8 00 00 00 00 callq 0xc
c: 58 pop %rax
1
2
3
➜  [/home/ywhkkx/ex1.linklab/l2] gcc -m32 -o linkbomb2 main.o phase2.o 
➜ [/home/ywhkkx/ex1.linklab/l2] ./linkbomb2
1180300330

成功了,最后再看一眼链接后的 linkbomb2

1
2
3
4
5
6
7
8
9
10
00001256 <do_phase>:
1256: 55 push %ebp
1257: 89 e5 mov %esp,%ebp
1259: e8 b3 ff ff ff call 1211 <__x86.get_pc_thunk.ax>
125e: 05 76 2d 00 00 add $0x2d76,%eax /* 因为链接,变具体了 */
/* 新添 */
1263: 8d 80 a8 e0 ff ff lea -0x1f58(%eax),%eax
1269: 50 push %eax
126a: e8 a6 ff ff ff call 1215 <yeDfwUkv>
126f: 58 pop %eax

实验三

创建生成一个名为 “phase3_patch.o” 的二进制可重定位目标文件,使其与 main.o phase3.o 链接后能够运行和输出(且仅输出)自己的学号

模块入口函数 do_phase() 依次遍历一个 COOKIE 字符串(由一组互不相同的英文字母组成,且总长度与学号字符串相同)中的每一字符,并通过一个映射数组将该字符的不同可能 ASCII 编码取值映射为输出字符

1
2
3
4
5
6
7
char PHASE3_CODEBOOK[256];
void do_phase(){
const char char cookie[] = PHASE3_COOKIE;
for( int i=0; i<sizeof(cookie)-1; i++ )
printf( "%c", PHASE3_CODEBOOK[ (unsigned char)(cookie[i]) ] );
printf( "\n" );
}
  • 分析 do_phase 函数反汇编指令,获知 COOKIE 字符串(保存于栈帧中的局部字符数组中)的组成内容和起始地址
  • 定位循环结构,根据 cookie 中字符的使用,定位映射数组的引用位置,结合重定位记录,确定映射数组的变量名
  • 通过符号表,发现该数组为一未初始化变量(类型为COM,长度为256字节)
  • 要改变程序输出(为学号),必须改变该映射数组的内容,因此,可利用强弱全局符号的解析规则,在 patch 模块中定义同名且按输出要求正确初始化映射关系的数组变量——从而在链接时替换对原数组的引用

先执行文件:

1
➜  [/home/ywhkkx/ex1.linklab/l3] ./linkbomb3 // 打印了个寂寞

PHASE3_CODEBOOK 未知,没有进行初始化,如果我们想要让 do_phase 打印我们需要的数据,就需要另写一个重定位文件对 PHASE3_CODEBOOK 进行初始化

反汇编 linkbomb3 寻找 COOKIE :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
00001225 <do_phase>:
1225: 55 push %ebp
1226: 89 e5 mov %esp,%ebp
1228: 53 push %ebx
1229: 83 ec 24 sub $0x24,%esp
122c: e8 9f fe ff ff call 10d0 <__x86.get_pc_thunk.bx>
1231: 81 c3 9f 2d 00 00 add $0x2d9f,%ebx
1237: 65 a1 14 00 00 00 mov %gs:0x14,%eax
123d: 89 45 f4 mov %eax,-0xc(%ebp)
1240: 31 c0 xor %eax,%eax
1242: c7 45 e9 61 73 74 70 movl $0x70747361,-0x17(%ebp) // target
1249: c7 45 ed 71 72 77 68 movl $0x68777271,-0x13(%ebp) // target
1250: 66 c7 45 f1 62 66 movw $0x6662,-0xf(%ebp) // target
1256: c6 45 f3 00 movb $0x0,-0xd(%ebp)
125a: c7 45 e4 00 00 00 00 movl $0x0,-0x1c(%ebp)
1261: eb 2b jmp 128e <do_phase+0x69>
1263: 8d 55 e9 lea -0x17(%ebp),%edx
1266: 8b 45 e4 mov -0x1c(%ebp),%eax
1269: 01 d0 add %edx,%eax
126b: 0f b6 00 movzbl (%eax),%eax
126e: 0f b6 c0 movzbl %al,%eax
1271: 8d 93 70 00 00 00 lea 0x70(%ebx),%edx
1277: 0f b6 04 02 movzbl (%edx,%eax,1),%eax
127b: 0f be c0 movsbl %al,%eax
127e: 83 ec 0c sub $0xc,%esp
1281: 50 push %eax
1282: e8 e9 fd ff ff call 1070 <putchar@plt>
1287: 83 c4 10 add $0x10,%esp
128a: 83 45 e4 01 addl $0x1,-0x1c(%ebp)
128e: 8b 45 e4 mov -0x1c(%ebp),%eax
1291: 83 f8 09 cmp $0x9,%eax
1294: 76 cd jbe 1263 <do_phase+0x3e>
1296: 83 ec 0c sub $0xc,%esp
1299: 6a 0a push $0xa
129b: e8 d0 fd ff ff call 1070 <putchar@plt>
12a0: 83 c4 10 add $0x10,%esp
12a3: 90 nop
12a4: 8b 45 f4 mov -0xc(%ebp),%eax
12a7: 65 33 05 14 00 00 00 xor %gs:0x14,%eax
12ae: 74 05 je 12b5 <do_phase+0x90>
12b0: e8 8b 00 00 00 call 1340 <__stack_chk_fail_local>
12b5: 8b 5d fc mov -0x4(%ebp),%ebx
12b8: c9 leave
12b9: c3 ret
12ba: 66 90 xchg %ax,%ax
12bc: 66 90 xchg %ax,%ax
12be: 66 90 xchg %ax,%ax

COOKIE 在栈中分配(看到与ebp有关的操作,和密之数字,基本猜到了)

1
2
3
1242:	c7 45 e9 61 73 74 70 	movl   $0x70747361,-0x17(%ebp) // target
1249: c7 45 ed 71 72 77 68 movl $0x68777271,-0x13(%ebp) // target
1250: 66 c7 45 f1 62 66 movw $0x6662,-0xf(%ebp) // target
1
2
3
4
5
6
In [5]: a=[0x61,0x73,0x74,0x70,0x71,0x72,0x77,0x68,0x62,0x66]

In [6]: data=''

In [7]: for i in range(len(a)):
...: data+=chr(a[i])
1
2
In [9]: data
Out[9]: 'astpqrwhbf'

再在 GDB 中看看:

1
2
3
04:00100xffffcf00 ◂— 0x747361fc /* 'astp' >> 由于GDB被0xfc干扰,没有打印出来 */
05:00140xffffcf04 ◂— 'pqrwhbf'
06:00180xffffcf08 ◂— 0x666268 /* 'hbf' */
1
2
pwndbg> x/s 0xffffcf00+1
0xffffcf01: "astpqrwhbf"

文本中的 PHASE3_CODEBOOK 是弱符号(未初始化的全局变量),我们就在 phase3_patch.o 中定义一个强符号来替换它(强符号会优先被定义)

假设我们想让程序输出:YHelow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
➜  [/home/ywhkkx/ex1.linklab/l3] readelf -s phase3.o 

Symbol table '.symtab' contains 18 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FILE LOCAL DEFAULT ABS phase3.c
2: 00000000 0 SECTION LOCAL DEFAULT 2
3: 00000000 0 SECTION LOCAL DEFAULT 4
4: 00000000 0 SECTION LOCAL DEFAULT 5
5: 00000000 0 SECTION LOCAL DEFAULT 6
6: 00000000 0 SECTION LOCAL DEFAULT 8
7: 00000000 0 SECTION LOCAL DEFAULT 10
8: 00000000 0 SECTION LOCAL DEFAULT 11
9: 00000000 0 SECTION LOCAL DEFAULT 9
10: 00000000 0 SECTION LOCAL DEFAULT 1
11: 00000020 256 OBJECT GLOBAL DEFAULT COM cDBDohBAOo // 获取字符串名称
12: 00000000 149 FUNC GLOBAL DEFAULT 2 do_phase
13: 00000000 0 FUNC GLOBAL HIDDEN 8 __x86.get_pc_thunk.bx
14: 00000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
15: 00000000 0 NOTYPE GLOBAL DEFAULT UND putchar
16: 00000000 0 NOTYPE GLOBAL HIDDEN UND __stack_chk_fail_local
17: 00000000 4 OBJECT GLOBAL DEFAULT 6 phase
1
2
char  cDBDohBAOo[256] =   "1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111Y01119121111111lloHe11w";

1
2
3
4
➜  [/home/ywhkkx/ex1.linklab/l3] gcc -m32 -c phase3_patch.c -o phase3_patch.o
➜ [/home/ywhkkx/ex1.linklab/l3] gcc -m32 -o linkbomb3 main.o phase3.o phase3_patch.o
➜ [/home/ywhkkx/ex1.linklab/l3] ./linkbomb3
YHellow209

实验四

修改二进制可重定位目标文件 “phase4.o” 中相应节中的数据内容(注意不允许修改 .text 节的内容),使其与 main.o 链接后能够运行输出(且仅输出)自己的学号

本阶段学生所拿到的.o文件中的“重定位位置”信息已经被抹除,学生需要根据实际情况确认冲重定位的发生位置,并根据重定位类型对位置信息进行恢复

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void do_phase()
{
const char cookie[] = PHASE4_COOKIE;
char c;
for ( int i = 0; i < sizeof(cookie)-1; i++ )
{
c = cookie[i];
switch (c)
{
// 每个学生的映射关系和case顺序建议不一样
case ‘A’: { c = 48; break; }
case ‘B’: { c = 121; break; }

case ‘Z’: { c = 93; break; }
}
printf("%c", c);
}
}
  • 通过分析 do_phase 函数的反汇编程序获知 COOKIE 字符串(保存于栈帧中的局部字符数组中)的组成内容
  • 确定 switch 跳转表在 .rodata 节中的偏移量(“A”,“B”,“C” …… 这些字符都在 .rodata 节中)
  • 定位 COOKIE 中每一字符’c’在switch跳转表中的对应表项(索引为’c’-0x41),将其值设为输出目标学号中对应字符的 case 首指令的偏移量

直接运行:

1
2
➜  [/home/ywhkkx/ex1.linklab/l4] ./linkbomb4                          
wl0_TZb3vJ

实验三 一样的套路,只不过这个在外面包装了 switch-case

故技重施,先反汇编 linkbomb4 寻找 COOKIE :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
00001225 <do_phase>:
1225: 55 push %ebp
1226: 89 e5 mov %esp,%ebp
1228: 53 push %ebx
1229: 83 ec 24 sub $0x24,%esp
122c: e8 9f fe ff ff call 10d0 <__x86.get_pc_thunk.bx>
1231: 81 c3 9f 2d 00 00 add $0x2d9f,%ebx
1237: 65 a1 14 00 00 00 mov %gs:0x14,%eax
123d: 89 45 f4 mov %eax,-0xc(%ebp)
1240: 31 c0 xor %eax,%eax
1242: c7 45 e9 42 47 4f 4d movl $0x4d4f4742,-0x17(%ebp) // target
1249: c7 45 ed 45 49 55 46 movl $0x46554945,-0x13(%ebp) // target
1250: 66 c7 45 f1 51 4a movw $0x4a51,-0xf(%ebp) // target
1256: c6 45 f3 00 movb $0x0,-0xd(%ebp)
125a: c7 45 e4 00 00 00 00 movl $0x0,-0x1c(%ebp)
1261: e9 e7 00 00 00 jmp 134d <.L30+0x19>
1266: 8d 55 e9 lea -0x17(%ebp),%edx
1269: 8b 45 e4 mov -0x1c(%ebp),%eax
126c: 01 d0 add %edx,%eax
126e: 0f b6 00 movzbl (%eax),%eax
1271: 88 45 e3 mov %al,-0x1d(%ebp)
1274: 0f be 45 e3 movsbl -0x1d(%ebp),%eax
1278: 83 e8 41 sub $0x41,%eax
127b: 83 f8 19 cmp $0x19,%eax
127e: 0f 87 b5 00 00 00 ja 1339 <.L30+0x5>
1284: c1 e0 02 shl $0x2,%eax
1287: 8b 84 18 ac e0 ff ff mov -0x1f54(%eax,%ebx,1),%eax
128e: 01 d8 add %ebx,%eax
1290: ff e0 jmp *%eax

这次直接在GDB中获取 COOKIE 了:

1
2
pwndbg> x/xs 0xffffcf00+1
0xffffcf01: "BGOMEIUFQJ"

在 Switch-Case 中:“BGOMEIUFQJ” 对应“跳转表”表项的顺序为:2 7 15 13 5 9 21 6 17 10

先看下汇编:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
00001225 <do_phase>:
1225: 55 push %ebp
1226: 89 e5 mov %esp,%ebp
1228: 53 push %ebx
1229: 83 ec 24 sub $0x24,%esp
122c: e8 9f fe ff ff call 10d0 <__x86.get_pc_thunk.bx>
1231: 81 c3 9f 2d 00 00 add $0x2d9f,%ebx
1237: 65 a1 14 00 00 00 mov %gs:0x14,%eax
123d: 89 45 f4 mov %eax,-0xc(%ebp)
1240: 31 c0 xor %eax,%eax
1242: c7 45 e9 42 47 4f 4d movl $0x4d4f4742,-0x17(%ebp)
1249: c7 45 ed 45 49 55 46 movl $0x46554945,-0x13(%ebp)
1250: 66 c7 45 f1 51 4a movw $0x4a51,-0xf(%ebp)
1256: c6 45 f3 00 movb $0x0,-0xd(%ebp)
125a: c7 45 e4 00 00 00 00 movl $0x0,-0x1c(%ebp)
1261: e9 e7 00 00 00 jmp 134d <.L30+0x19>
1266: 8d 55 e9 lea -0x17(%ebp),%edx
1269: 8b 45 e4 mov -0x1c(%ebp),%eax
126c: 01 d0 add %edx,%eax
126e: 0f b6 00 movzbl (%eax),%eax
1271: 88 45 e3 mov %al,-0x1d(%ebp)
1274: 0f be 45 e3 movsbl -0x1d(%ebp),%eax
1278: 83 e8 41 sub $0x41,%eax
127b: 83 f8 19 cmp $0x19,%eax
127e: 0f 87 b5 00 00 00 ja 1339 <.L30+0x5>
1284: c1 e0 02 shl $0x2,%eax
1287: 8b 84 18 ac e0 ff ff mov -0x1f54(%eax,%ebx,1),%eax
128e: 01 d8 add %ebx,%eax
1290: ff e0 jmp *%eax

00001292 <.L4>:
1292: c6 45 e3 54 movb $0x54,-0x1d(%ebp) // A >> T
1296: e9 9e 00 00 00 jmp 1339 <.L30+0x5>

0000129b <.L6>:
129b: c6 45 e3 77 movb $0x77,-0x1d(%ebp) // B >> w
129f: e9 95 00 00 00 jmp 1339 <.L30+0x5>

000012a4 <.L7>:
12a4: c6 45 e3 70 movb $0x70,-0x1d(%ebp) // C >> p
12a8: e9 8c 00 00 00 jmp 1339 <.L30+0x5>

000012ad <.L8>:
12ad: c6 45 e3 36 movb $0x36,-0x1d(%ebp) // D >> 6
12b1: e9 83 00 00 00 jmp 1339 <.L30+0x5>

000012b6 <.L9>:
12b6: c6 45 e3 54 movb $0x54,-0x1d(%ebp) // E >> T
12ba: eb 7d jmp 1339 <.L30+0x5>

000012bc <.L10>:
12bc: c6 45 e3 33 movb $0x33,-0x1d(%ebp) // F >> 3
12c0: eb 77 jmp 1339 <.L30+0x5>

000012c2 <.L11>:
12c2: c6 45 e3 6c movb $0x6c,-0x1d(%ebp) // G >> l
12c6: eb 71 jmp 1339 <.L30+0x5>

000012c8 <.L12>:
12c8: c6 45 e3 34 movb $0x34,-0x1d(%ebp) // H >> 4
12cc: eb 6b jmp 1339 <.L30+0x5>

000012ce <.L13>:
12ce: c6 45 e3 5a movb $0x5a,-0x1d(%ebp) // I >> Z
12d2: eb 65 jmp 1339 <.L30+0x5>

000012d4 <.L14>:
12d4: c6 45 e3 4a movb $0x4a,-0x1d(%ebp) // J >> J
12d8: eb 5f jmp 1339 <.L30+0x5>

000012da <.L15>:
12da: c6 45 e3 51 movb $0x51,-0x1d(%ebp) // K >> Q
12de: eb 59 jmp 1339 <.L30+0x5>

000012e0 <.L16>:
12e0: c6 45 e3 4d movb $0x4d,-0x1d(%ebp) // L >> M
12e4: eb 53 jmp 1339 <.L30+0x5>

000012e6 <.L17>:
12e6: c6 45 e3 5f movb $0x5f,-0x1d(%ebp) // M >> -
12ea: eb 4d jmp 1339 <.L30+0x5>

000012ec <.L18>:
12ec: c6 45 e3 38 movb $0x38,-0x1d(%ebp) // N >> 8
12f0: eb 47 jmp 1339 <.L30+0x5>

000012f2 <.L19>:
12f2: c6 45 e3 30 movb $0x30,-0x1d(%ebp) // O >> 0
12f6: eb 41 jmp 1339 <.L30+0x5>

000012f8 <.L20>:
12f8: c6 45 e3 32 movb $0x32,-0x1d(%ebp) // P >> 2
12fc: eb 3b jmp 1339 <.L30+0x5>

000012fe <.L21>:
12fe: c6 45 e3 76 movb $0x76,-0x1d(%ebp) // Q >> v
1302: eb 35 jmp 1339 <.L30+0x5>

00001304 <.L22>:
1304: c6 45 e3 35 movb $0x35,-0x1d(%ebp) // R >> 5
1308: eb 2f jmp 1339 <.L30+0x5>

0000130a <.L23>:
130a: c6 45 e3 3c movb $0x3c,-0x1d(%ebp) // S >> <
130e: eb 29 jmp 1339 <.L30+0x5>

00001310 <.L24>:
1310: c6 45 e3 39 movb $0x39,-0x1d(%ebp) // J >> 9
1314: eb 23 jmp 1339 <.L30+0x5>

00001316 <.L25>:
1316: c6 45 e3 62 movb $0x62,-0x1d(%ebp) // U >> b
131a: eb 1d jmp 1339 <.L30+0x5>

0000131c <.L26>:
131c: c6 45 e3 57 movb $0x57,-0x1d(%ebp) // V >> W
1320: eb 17 jmp 1339 <.L30+0x5>

00001322 <.L27>:
1322: c6 45 e3 60 movb $0x60,-0x1d(%ebp) // W >> ~
1326: eb 11 jmp 1339 <.L30+0x5>

00001328 <.L28>:
1328: c6 45 e3 4d movb $0x4d,-0x1d(%ebp) // X >> M
132c: eb 0b jmp 1339 <.L30+0x5>

0000132e <.L29>:
132e: c6 45 e3 31 movb $0x31,-0x1d(%ebp) // Y >> 1
1332: eb 05 jmp 1339 <.L30+0x5>

00001334 <.L30>:
1334: c6 45 e3 37 movb $0x37,-0x1d(%ebp) // Z >> 7
1338: 90 nop
1339: 0f be 45 e3 movsbl -0x1d(%ebp),%eax
133d: 83 ec 0c sub $0xc,%esp
1340: 50 push %eax
1341: e8 2a fd ff ff call 1070 <putchar@plt>
1346: 83 c4 10 add $0x10,%esp
1349: 83 45 e4 01 addl $0x1,-0x1c(%ebp)
134d: 8b 45 e4 mov -0x1c(%ebp),%eax
1350: 83 f8 09 cmp $0x9,%eax
1353: 0f 86 0d ff ff ff jbe 1266 <do_phase+0x41>
1359: 83 ec 0c sub $0xc,%esp
135c: 6a 0a push $0xa
135e: e8 0d fd ff ff call 1070 <putchar@plt>
1363: 83 c4 10 add $0x10,%esp
1366: 90 nop
1367: 8b 45 f4 mov -0xc(%ebp),%eax
136a: 65 33 05 14 00 00 00 xor %gs:0x14,%eax
1371: 74 05 je 1378 <.L30+0x44>
1373: e8 88 00 00 00 call 1400 <__stack_chk_fail_local>
1378: 8b 5d fc mov -0x4(%ebp),%ebx
137b: c9 leave
137c: c3 ret
137d: 66 90 xchg %ax,%ax
137f: 90 nop

这些奇怪的 L4 L6 L7 就是字符了(主要是因为它们有26个)

“jmp 1339 <.L30+0x5>”应该就是“break”,可以通过 movb 来获取对应的打印数据(已标记)

实验四想要我们修改“phase4.o”,但不让我们修改 .text 节的内容(可执行指令的集合),所以我们只能改 .rodata 节中的“跳转表”

​ // 可采用以下代码证明:L4 L6 L7 就是 A,B,C,D ……

1
2
➜  [/home/ywhkkx/ex1.linklab/l4] readelf -s phase4.o
➜ [/home/ywhkkx/ex1.linklab/l4] readelf -S phase4.o

用 readelf 打印重定位节:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
重定位节 '.rel.rodata' at offset 0x648 contains 26 entries:
偏移量 信息 类型 符号值 符号名称
00000000 00000a09 R_386_GOTOFF 0000006d .L4
00000004 00000b09 R_386_GOTOFF 00000076 .L6
00000008 00000c09 R_386_GOTOFF 0000007f .L7
0000000c 00000d09 R_386_GOTOFF 00000088 .L8
00000010 00000e09 R_386_GOTOFF 00000091 .L9
00000014 00000f09 R_386_GOTOFF 00000097 .L10
00000018 00001009 R_386_GOTOFF 0000009d .L11
0000001c 00001109 R_386_GOTOFF 000000a3 .L12
00000020 00001209 R_386_GOTOFF 000000a9 .L13
00000024 00001309 R_386_GOTOFF 000000af .L14
00000028 00001409 R_386_GOTOFF 000000b5 .L15
0000002c 00001509 R_386_GOTOFF 000000bb .L16
00000030 00001609 R_386_GOTOFF 000000c1 .L17
00000034 00001709 R_386_GOTOFF 000000c7 .L18
00000038 00001809 R_386_GOTOFF 000000cd .L19
0000003c 00001909 R_386_GOTOFF 000000d3 .L20
00000040 00001a09 R_386_GOTOFF 000000d9 .L21
00000044 00001b09 R_386_GOTOFF 000000df .L22
00000048 00001c09 R_386_GOTOFF 000000e5 .L23
0000004c 00001d09 R_386_GOTOFF 000000eb .L24
00000050 00001e09 R_386_GOTOFF 000000f1 .L25
00000054 00001f09 R_386_GOTOFF 000000f7 .L26
00000058 00002009 R_386_GOTOFF 000000fd .L27
0000005c 00002109 R_386_GOTOFF 00000103 .L28
00000060 00002209 R_386_GOTOFF 00000109 .L29
00000064 00002309 R_386_GOTOFF 0000010f .L30

获取各个元素的节内偏移

1
2
3
4
5
6
7
8
9
10
11
12
13
➜  [/home/ywhkkx/ex1.linklab/l4] readelf -S phase4.o
There are 18 section headers, starting at offset 0x7cc:

节头:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .group GROUP 00000000 000034 000008 04 15 39 4
[ 2] .text PROGBITS 00000000 00003c 000158 00 AX 0 0 1
[ 3] .rel.text REL 00000000 000618 000030 08 I 15 2 4
[ 4] .data PROGBITS 00000000 000194 000000 00 WA 0 0 1
[ 5] .bss NOBITS 00000000 000194 000000 00 WA 0 0 1
[ 6] .rodata PROGBITS 00000000 000194 000068 00 A 0 0 4
[ 7] .rel.rodata REL 00000000 000648 0000d0 08 I 15 6 4

“.rel.rodata ”的偏移为“000648”,

现在可以用 hexedit 进行修改了,主要关注偏移为“000648”的地方,目的是交换’.rel.rodata’的信息,以改变重定位值,进而改变输出值:

1
2
3
4
5
00000640   4F 01 00 00  02 2A 00 00  00 00 00 00  09 0A 00 00  O....*..........
00000650 04 00 00 00 09 0B 00 00 08 00 00 00 09 0C 00 00 ................
00000660 0C 00 00 00 09 0D 00 00 10 00 00 00 09 0E 00 00 ................
00000670 14 00 00 00 09 0F 00 00 18 00 00 00 09 10 00 00 ................
00000680 1C 00 00 00 09 11 00 00 20 00 00 00 09 12 00 00 ........ .......
  • cookie:BGOMEIUFQJ
  • 对应符号名称:L6 L11 L19 L17 L9 L13 L25 L10 L21 L14
  • 原本的输出:wl0_TZb3vJ

假设我们想输出:0123456789

  • 对应符号名称:L19 L29 L20 L10 L12 L22 L8 L30 L18 L24

然后用 hexedit 做出调换:L6->L19,L11->L29,L19->L20 ……….

1
2
3
4
5
6
7
8
9
10
11
12
13
14
00000640   4F 01 00 00  02 2A 00 00  00 00 00 00  09 0A 00 00  O....*..........
00000650 04 00 00 00 09 18 00 00 08 00 00 00 09 0C 00 00 ................
00000660 0C 00 00 00 09 0D 00 00 10 00 00 00 09 11 00 00 ................
00000670 14 00 00 00 09 23 00 00 18 00 00 00 09 22 00 00 .....#......."..
00000680 1C 00 00 00 09 11 00 00 20 00 00 00 09 1B 00 00 ........ .......
00000690 24 00 00 00 09 1D 00 00 28 00 00 00 09 14 00 00 $.......(.......
000006A0 2C 00 00 00 09 15 00 00 30 00 00 00 09 23 00 00 ,.......0....#..
000006B0 34 00 00 00 09 17 00 00 38 00 00 00 09 19 00 00 4.......8.......
000006C0 3C 00 00 00 09 19 00 00 40 00 00 00 09 17 00 00 <.......@.......
000006D0 44 00 00 00 09 1B 00 00 48 00 00 00 09 1C 00 00 D.......H.......
000006E0 4C 00 00 00 09 1D 00 00 50 00 00 00 09 0D 00 00 L.......P.......
000006F0 54 00 00 00 09 1F 00 00 58 00 00 00 09 20 00 00 T.......X.... ..
00000700 5C 00 00 00 09 21 00 00 60 00 00 00 09 22 00 00 \....!..`...."..
00000710 64 00 00 00 09 23 00 00 00 00 00 00 01 26 00 00 d....#.......&..
1
2
3
➜  [/home/ywhkkx/ex1.linklab/l4] gcc -m32 -o linkbomb4 main.o phase4.o
➜ [/home/ywhkkx/ex1.linklab/l4] ./linkbomb4
0127456789

实验五

修改二进制可重定位目标文件“phase5.o”的重定位节中的数据内容(不允许修改.text节的内容),补充完成其中被清零的一些重定位记录(分别对应于本模块中需要重定位的符号引用),使其与 main.o 链接后能够正确输出(且仅输出)自己学号

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const int TRAN_ARRAY[] = {… …};
const char FDICT[] = FDICTDAT;
char BUF[] = MYID;
char CODE = PHASE5_COOKIE;

int transform_code( int code, int mode ) {
switch( TRAN_ARRAY [mode] & 0x00000007 ) {
case 0:
code = code & (~ TRAN_ARRAY[mode]);
break;
case 1:
code = code ^ TRAN_ARRAY[mode];
break;
… …
}
return code;
}

void generate_code( int cookie ) {
int i;
CODE = cookie;
for( i=0; i<sizeof(TRAN_ARRAY)/sizeof(int); i++ )
CODE = transform_code( CODE, i );
}

int encode( char* str ) {
int i, n = strlen(str);
for( i=0; i<n; i++ ) {
str[i] = (FDICT[str[i]] ^ CODE) & 0x7F;
if( str[i]<0x20 || str[i]>0x7E ) str[i] = ' ';
}
return n;
}

void do_phase() {
generate_code(PHASE5_COOKIE);
encode(BUF);
printf("%s\n", BUF);
}
  • 对照 phase5.o 的反汇编程序及已有重定位记录,定位每一空重定位记录可能对应的符号引用
  • 对每一待处理的符号引用,按照下列重定位记录结构,构造其二进制表示(8字节块)
  • 使用 hexedit 或编程将生成的重定位记录写入到相应被清空的记录位置中

直接运行程序:(报错了)

1
2
3
➜  [/home/ywhkkx/ex1.linklab/l5] gcc -m32 -o linkbomb5 main.o phase5.o 
➜ [/home/ywhkkx/ex1.linklab/l5] ./linkbomb5
[1] 4179 illegal hardware instruction (core dumped) ./linkbomb5

可能 实验五 就是为了让我们修复程序,一般段错误有以下原因:

  • 未对相关引用进行必要的重定位(实验PTT给出了这个)

用GDB单步排查:

1
Invalid instructions at 0x56556399

“do_phase”出问题了,但不知道是什么问题(其实大概率是重定位的问题,实验给了提示)

看一下重定位表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
➜  [/home/ywhkkx/ex1.linklab/l5] readelf -r phase5.o

重定位节 '.rel.text' at offset 0x6e8 contains 27 entries:
偏移量 信息 类型 符号值 符号名称
00000000 00000000 R_386_NONE
00000009 00001b0a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_
00000013 00001509 R_386_GOTOFF 00000000 CmhNnM
00000000 00000000 R_386_NONE
00000000 00000000 R_386_NONE
0000004c 00001509 R_386_GOTOFF 00000000 CmhNnM
0000005d 00001509 R_386_GOTOFF 00000000 CmhNnM
00000000 00000000 R_386_NONE
0000007e 00001509 R_386_GOTOFF 00000000 CmhNnM
00000000 00000000 R_386_NONE
0000009e 00001b0a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_
000000a7 00001809 R_386_GOTOFF 0000000b CODE
00000000 00000000 R_386_NONE
00000000 00000000 R_386_NONE
000000cc 00001809 R_386_GOTOFF 0000000b CODE
000000ea 00001d02 R_386_PC32 00000000 __x86.get_pc_thunk.bx
00000000 00000000 R_386_NONE
000000fb 00001f04 R_386_PLT32 00000000 strlen
00000120 00001609 R_386_GOTOFF 00000040 cRvFVR
00000127 00001809 R_386_GOTOFF 0000000b CODE
00000000 00000000 R_386_NONE
00000189 00001b0a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_
00000193 00001c02 R_386_PC32 00000090 generate_code
0000019f 00001709 R_386_GOTOFF 00000000 BUF
000001a5 00001e02 R_386_PC32 000000e2 encode
000001b1 00001709 R_386_GOTOFF 00000000 BUF
000001b7 00002104 R_386_PLT32 00000000 puts

重定位节 '.rel.rodata' at offset 0x7c0 contains 8 entries:
偏移量 信息 类型 符号值 符号名称
000000c0 00000c09 R_386_GOTOFF 0000002d .L3
000000c4 00000d09 R_386_GOTOFF 00000032 .L5
000000c8 00000a09 R_386_GOTOFF 00000087 .L2
000000cc 00000e09 R_386_GOTOFF 00000046 .L6
000000d0 00000f09 R_386_GOTOFF 00000057 .L7
000000d4 00001009 R_386_GOTOFF 00000069 .L8
000000d8 00000a09 R_386_GOTOFF 00000087 .L2
000000dc 00001109 R_386_GOTOFF 00000078 .L9

重定位节 '.rel.data.rel.local' at offset 0x800 contains 1 entry:
偏移量 信息 类型 符号值 符号名称
00000000 00002001 R_386_32 0000017b do_phase

重定位节 '.rel.eh_frame' at offset 0x808 contains 6 entries:
偏移量 信息 类型 符号值 符号名称
00000020 00000202 R_386_PC32 00000000 .text
00000040 00000202 R_386_PC32 00000000 .text
00000064 00000202 R_386_PC32 00000000 .text
00000088 00000202 R_386_PC32 00000000 .text
000000ac 00000702 R_386_PC32 00000000 .text.__x86.get_pc_thu
000000c0 00000802 R_386_PC32 00000000 .text.__x86.get_pc_thu

发现很多条目空着的,需要补全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
➜  [/home/ywhkkx/ex1.linklab/l5] readelf -S phase5.o
There are 20 section headers, starting at offset 0x8f0:

节头:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .group GROUP 00000000 000034 000008 04 17 26 4
[ 2] .group GROUP 00000000 00003c 000008 04 17 29 4
[ 3] .text PROGBITS 00000000 000044 0001c4 00 AX 0 0 1
[ 4] .rel.text REL 00000000 0006e8 0000d8 08 I 17 3 4
[ 5] .data PROGBITS 00000000 000208 00000c 00 WA 0 0 4
[ 6] .bss NOBITS 00000000 000214 000000 00 WA 0 0 1
[ 7] .rodata PROGBITS 00000000 000220 0000e0 00 A 0 0 32
[ 8] .rel.rodata REL 00000000 0007c0 000040 08 I 17 7 4

“.rel.rodata”偏移为“0x7c0”,“.rel.text”偏移为“0x6e8”

在 hexedit 中看看:

1
2
3
4
5
6
000006D0   64 65 00 73  74 72 6C 65  6E 00 64 6F  5F 70 68 61  de.strlen.do_pha
000006E0 73 65 00 70 75 74 73 00 00 00 00 00 00 00 00 00 se.puts.........
000006F0 09 00 00 00 0A 1B 00 00 13 00 00 00 09 15 00 00 ................
00000700 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000710 4C 00 00 00 09 15 00 00 5D 00 00 00 09 15 00 00 L.......].......
00000720 00 00 00 00 00 00 00 00 7E 00 00 00 09 15 00 00 ........~.......

可以发现一个规律,对应 ‘.rel.text’ 表中:

  • 第一个4字节空间是偏移量
  • 第二个4字节空间是信息(注意小端字节序)

在“0x6e8”和“0x6e8+0x4”的位置是空的,’.rel.text’ 表中空着的地方都没有数据,通过前面几个实验的观察,’.rel.text’ 表是不应该存在空数据的,所以实验五的核心就是为了把 ‘.rel.text’ 表补全

那么填入什么数据呢,需要在汇编中查看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
➜  [/home/ywhkkx/ex1.linklab/l5] objdump -d phase5.o

phase5.o: 文件格式 elf32-i386


Disassembly of section .text:

00000000 <transform_code>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: e8 fc ff ff ff call 4 <transform_code+0x4> // target 0x4
8: 05 01 00 00 00 add $0x1,%eax // _GLOBAL_OFFSET_TABLE_
d: 8b 55 0c mov 0xc(%ebp),%edx
10: 8b 94 90 00 00 00 00 mov 0x0(%eax,%edx,4),%edx // CmhNnM
17: 83 e2 07 and $0x7,%edx
1a: 83 fa 07 cmp $0x7,%edx
1d: 77 68 ja 87 <.L2>
1f: c1 e2 02 shl $0x2,%edx
22: 8b 94 02 c0 00 00 00 mov 0xc0(%edx,%eax,1),%edx // target 0x25
29: 01 c2 add %eax,%edx
2b: ff e2 jmp *%edx

0000002d <.L3>:
2d: f7 55 08 notl 0x8(%ebp)
30: eb 59 jmp 8b <.L2+0x4>

00000032 <.L5>:
32: 8b 55 0c mov 0xc(%ebp),%edx
35: 8b 84 90 00 00 00 00 mov 0x0(%eax,%edx,4),%eax // target 0x38
3c: 83 e0 03 and $0x3,%eax
3f: 89 c1 mov %eax,%ecx
41: d3 7d 08 sarl %cl,0x8(%ebp)
44: eb 45 jmp 8b <.L2+0x4>

00000046 <.L6>:
46: 8b 55 0c mov 0xc(%ebp),%edx
49: 8b 84 90 00 00 00 00 mov 0x0(%eax,%edx,4),%eax // CmhNnM
50: f7 d0 not %eax
52: 21 45 08 and %eax,0x8(%ebp)
55: eb 34 jmp 8b <.L2+0x4>

00000057 <.L7>:
57: 8b 55 0c mov 0xc(%ebp),%edx
5a: 8b 84 90 00 00 00 00 mov 0x0(%eax,%edx,4),%eax // CmhNnM
61: c1 e0 08 shl $0x8,%eax
64: 09 45 08 or %eax,0x8(%ebp)
67: eb 22 jmp 8b <.L2+0x4>

00000069 <.L8>:
69: 8b 55 0c mov 0xc(%ebp),%edx
6c: 8b 84 90 00 00 00 00 mov 0x0(%eax,%edx,4),%eax // target 0x6f
73: 31 45 08 xor %eax,0x8(%ebp)
76: eb 13 jmp 8b <.L2+0x4>

00000078 <.L9>:
78: 8b 55 0c mov 0xc(%ebp),%edx
7b: 8b 84 90 00 00 00 00 mov 0x0(%eax,%edx,4),%eax // CmhNnM
82: 89 45 08 mov %eax,0x8(%ebp)
85: eb 04 jmp 8b <.L2+0x4>

00000087 <.L2>:
87: f7 5d 08 negl 0x8(%ebp)
8a: 90 nop
8b: 8b 45 08 mov 0x8(%ebp),%eax
8e: 5d pop %ebp
8f: c3 ret

00000090 <generate_code>:
90: 55 push %ebp
91: 89 e5 mov %esp,%ebp
93: 53 push %ebx
94: 83 ec 10 sub $0x10,%esp
97: e8 fc ff ff ff call 98 <generate_code+0x8> // target 0x98
9c: 81 c3 02 00 00 00 add $0x2,%ebx // _GLOBAL_OFFSET_TABLE_
a2: 8b 45 08 mov 0x8(%ebp),%eax
a5: 88 83 00 00 00 00 mov %al,0x0(%ebx) // CODE
ab: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%ebp) // target 0xae
b2: eb 20 jmp d4 <generate_code+0x44>
b4: 0f b6 83 00 00 00 00 movzbl 0x0(%ebx),%eax // target 0xb7
bb: 0f be c0 movsbl %al,%eax
be: ff 75 f8 pushl -0x8(%ebp)
c1: 50 push %eax
c2: e8 fc ff ff ff call c3 <generate_code+0x33>
c7: 83 c4 08 add $0x8,%esp
ca: 88 83 00 00 00 00 mov %al,0x0(%ebx) // CODE
d0: 83 45 f8 01 addl $0x1,-0x8(%ebp)
d4: 8b 45 f8 mov -0x8(%ebp),%eax
d7: 83 f8 08 cmp $0x8,%eax
da: 76 d8 jbe b4 <generate_code+0x24>
dc: 90 nop
dd: 8b 5d fc mov -0x4(%ebp),%ebx
e0: c9 leave
e1: c3 ret

000000e2 <encode>:
e2: 55 push %ebp
e3: 89 e5 mov %esp,%ebp
e5: 53 push %ebx
e6: 83 ec 14 sub $0x14,%esp
e9: e8 fc ff ff ff call ea <encode+0x8> // __x86.get_pc_thunk.bx
ee: 81 c3 02 00 00 00 add $0x2,%ebx // target 0xf0
f4: 83 ec 0c sub $0xc,%esp
f7: ff 75 08 pushl 0x8(%ebp)
fa: e8 fc ff ff ff call fb <encode+0x19> // strlen
ff: 83 c4 10 add $0x10,%esp
102: 89 45 f4 mov %eax,-0xc(%ebp)
105: c7 45 f0 00 00 00 00 movl $0x0,-0x10(%ebp)
10c: eb 5d jmp 16b <encode+0x89>
10e: 8b 55 f0 mov -0x10(%ebp),%edx
111: 8b 45 08 mov 0x8(%ebp),%eax
114: 01 d0 add %edx,%eax
116: 0f b6 00 movzbl (%eax),%eax
119: 0f be c0 movsbl %al,%eax
11c: 0f b6 94 03 00 00 00 movzbl 0x0(%ebx,%eax,1),%edx // cRvFVR
123: 00
124: 0f b6 83 00 00 00 00 movzbl 0x0(%ebx),%eax // CODE
12b: 89 d1 mov %edx,%ecx
12d: 31 c1 xor %eax,%ecx
12f: 8b 55 f0 mov -0x10(%ebp),%edx
132: 8b 45 08 mov 0x8(%ebp),%eax
135: 01 d0 add %edx,%eax
137: 83 e1 7f and $0x7f,%ecx
13a: 89 ca mov %ecx,%edx
13c: 88 10 mov %dl,(%eax)
13e: 8b 55 f0 mov -0x10(%ebp),%edx
141: 8b 45 08 mov 0x8(%ebp),%eax
144: 01 d0 add %edx,%eax
146: 0f b6 00 movzbl (%eax),%eax
149: 3c 1f cmp $0x1f,%al
14b: 7e 0f jle 15c <encode+0x7a>
14d: 8b 55 f0 mov -0x10(%ebp),%edx
150: 8b 45 08 mov 0x8(%ebp),%eax
153: 01 d0 add %edx,%eax
155: 0f b6 00 movzbl (%eax),%eax
158: 3c 7f cmp $0x7f,%al
15a: 75 0b jne 167 <encode+0x85>
15c: 8b 55 f0 mov -0x10(%ebp),%edx
15f: 8b 45 08 mov 0x8(%ebp),%eax
162: 01 d0 add %edx,%eax
164: c6 00 20 movb $0x20,(%eax)
167: 83 45 f0 01 addl $0x1,-0x10(%ebp)
16b: 8b 45 f0 mov -0x10(%ebp),%eax
16e: 3b 45 f4 cmp -0xc(%ebp),%eax
171: 7c 9b jl 10e <encode+0x2c>
173: 8b 45 f4 mov -0xc(%ebp),%eax
176: 8b 5d fc mov -0x4(%ebp),%ebx
179: c9 leave
17a: c3 ret

0000017b <do_phase>:
17b: 55 push %ebp
17c: 89 e5 mov %esp,%ebp
17e: 53 push %ebx
17f: 83 ec 04 sub $0x4,%esp
182: e8 fc ff ff ff call 183 <do_phase+0x8> // target 0x183
187: 81 c3 02 00 00 00 add $0x2,%ebx // _GLOBAL_OFFSET_TABLE_
18d: 68 87 00 00 00 push $0x87
192: e8 fc ff ff ff call 193 <do_phase+0x18> // generate_code
197: 83 c4 04 add $0x4,%esp
19a: 83 ec 0c sub $0xc,%esp
19d: 8d 83 00 00 00 00 lea 0x0(%ebx),%eax // BUF
1a3: 50 push %eax
1a4: e8 fc ff ff ff call 1a5 <do_phase+0x2a> // encode
1a9: 83 c4 10 add $0x10,%esp
1ac: 83 ec 0c sub $0xc,%esp
1af: 8d 83 00 00 00 00 lea 0x0(%ebx),%eax // BUF
1b5: 50 push %eax
1b6: e8 fc ff ff ff call 1b7 <do_phase+0x3c> // puts
1bb: 83 c4 10 add $0x10,%esp
1be: 90 nop
1bf: 8b 5d fc mov -0x4(%ebp),%ebx
1c2: c9 leave
1c3: c3 ret

Disassembly of section .text.__x86.get_pc_thunk.ax:

00000000 <__x86.get_pc_thunk.ax>:
0: 8b 04 24 mov (%esp),%eax
3: c3 ret

Disassembly of section .text.__x86.get_pc_thunk.bx:

00000000 <__x86.get_pc_thunk.bx>:
0: 8b 1c 24 mov (%esp),%ebx
3: c3 ret

主要看 “00 00 00” 和 “ff ff ff” 有没有在 ‘.rel.text’ 表中出现,把出现的标记,没有的可能就是需要修补的,最后进行修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
➜  [/home/ywhkkx/ex1.linklab/l5] readelf -r phase5.o             

重定位节 '.rel.text' at offset 0x6e8 contains 27 entries:
偏移量 信息 类型 符号值 符号名称
00000004 00001d02 R_386_PC32 00000000 __x86.get_pc_thunk.bx
00000009 00001b0a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_
00000013 00001d02 R_386_PC32 00000000 __x86.get_pc_thunk.bx
00000025 00001b0a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_
00000038 00001509 R_386_GOTOFF 00000000 CmhNnM
0000004c 00001509 R_386_GOTOFF 00000000 CmhNnM
0000005d 00001509 R_386_GOTOFF 00000000 CmhNnM
0000006f 00001509 R_386_GOTOFF 00000000 CmhNnM
0000007e 00001509 R_386_GOTOFF 00000000 CmhNnM
00000098 00001d02 R_386_PC32 00000000 __x86.get_pc_thunk.bx
0000009e 00001b0a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_
000000a7 00001809 R_386_GOTOFF 0000000b CODE
000000ae 00001809 R_386_GOTOFF 0000000b CODE
000000b7 00001809 R_386_GOTOFF 0000000b CODE
000000cc 00001809 R_386_GOTOFF 0000000b CODE
000000ea 00001d02 R_386_PC32 00000000 __x86.get_pc_thunk.bx
000000f0 00001b0a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_
000000fb 00001f04 R_386_PLT32 00000000 strlen
00000120 00001609 R_386_GOTOFF 00000040 cRvFVR
00000127 00001809 R_386_GOTOFF 0000000b CODE
00000183 00001d02 R_386_PC32 00000000 __x86.get_pc_thunk.bx
00000189 00001b0a R_386_GOTPC 00000000 _GLOBAL_OFFSET_TABLE_
00000193 00001c02 R_386_PC32 00000090 generate_code
0000019f 00001709 R_386_GOTOFF 00000000 BUF
000001a5 00001e02 R_386_PC32 000000e2 encode
000001b1 00001709 R_386_GOTOFF 00000000 BUF
000001b7 00002104 R_386_PLT32 00000000 puts

“偏移量”很好处理,就是“信息”不好选择,我是根据“汇编代码的相似性”进行选择的,最后总算可以输出了(想要完美完成这个实验,必须掌握汇编)

1
2
➜  [/home/ywhkkx/ex1.linklab/l5] ./linkbomb5 
PP$;`;;``;

没有段错误了,但是输出很奇怪(有些地方没有改好)


收获

  • 以前就对 link 有困惑,做了这个实验感受了一下 link 的过程后,发现自己对 link 的理解更清晰了
  • 学习到了 objdump,readelf,hexedit 等工具的用法
  • 看到了各个节在二进制文件的分布,学会了用“节偏移”和“节内偏移”来寻找需要的数据