0x01 动态段
类型:PT_DYNAMIC
包括但不限于:
1) 运行时需要链接的共享库列表
2) 全局偏移表GOT地址
3) 重定位条目的相关信息
ELF 动态数组标记 可以参考: https://docs.oracle.com/cd/E24847_01/html/E22196/chapter6-14428.html#scrolltoc
一些入口可以参考:
https://refspecs.linuxfoundation.org/LSB_1.2.0/gLSB/dynamicsection.html
32位ELF文件动态段的结构体如下:
typedef struct {
Elf32_Sword d_tag;
union {
Elf32_Word d_val;
Elf32_Addr d_ptr;
} d_un;
} Elf32_Dyn;
extern Elf32_Dyn _DYNAMIC[]
其中d_tag决定了d_un的含义,d_val成员保存了一个整型值,d_ptr成员保存了一个内存虚址。
一些和re2diresolve相关的 类型/入口/地址
DT_SYMTAB –> 动态符号表的地址,对应的节名.dynsym
其中
struct Elf32_Sym
{
Elf32_Word st_name; /* Symbol name (string tbl index) */
Elf32_Addr st_value; /* Symbol value */
Elf32_Word st_size; /* Symbol size */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf32_Section st_shndx; /* Section index */
};
struct Elf64_Sym
{
Elf64_Word st_name; /* Symbol name (string tbl index) */
unsigned char st_info; /* Symbol type and binding */
unsigned char st_other; /* Symbol visibility */
Elf64_Section st_shndx; /* Section index */
Elf64_Addr st_value; /* Symbol value */
Elf64_Xword st_size; /* Symbol size */
};
如: Elf32_Sym <offset aRead - offset byte_804822C, 0, 0, 12h, 0, 0> ; "read"
其中的offset aRead - offset byte_804822C
,即该符号的名字,是下面提到的DT_STRTAB
中的一个字符串,
DT_STRTAB –> 符号字符串表的地址,对应的节名.dynstr
DT_JMPREL –>Address of PLT relocs,与过程链接表单独关联的重定位项的地址。
typedef struct elf32_rel {
Elf32_Addr r_offset;
Elf32_Word r_info;
} Elf32_Rel;
其中r_offset为应用重定位操作的位置,r_info可以认为是ELF32_R_SYM||ELF32_R_TYPE
(#define ELF32_R_INFO(s, t) (((s)«8)+(unsigned char)(t)))
可参考:
https://docs.oracle.com/cd/E19957-01/806-0641/chapter6-54839/index.html
DT_VERSYM –>Address of the table provided by the .gnu.version section.This section contains the Symbol Version Table. 如:
再用我手上的程序对着四个类型举个例子
参考:
还是《Linux二进制分析》
还是angelboy的slide
http://pwn4.fun/2016/11/09/Return-to-dl-resolve/
https://ctf-wiki.github.io/ctf-wiki/pwn/stackoverflow/advanced_rop/
《程序员的自我修养》
彩蛋:
32位linux的内存地址分布:
64位
继续彩蛋
free 命令显示系统使用和空闲的内存情况,包括物理内存、交互区内存(swap)和内核缓冲区内存。
还是彩蛋:
如果ELF文件是以PIC模式编译的(动态链接的可执行文件一般是PIC的),调用的外部函数会出现在.rel.plt
中,不使用的话会出现在.rel.dyn
中。
.rel.dyn
实际上是对数据引用的修正,所修正的位置位于.got
及其数据段;
.rel.plt
是对数据引用的修正,修正的位置位于.got.plt
彩蛋+1:
进程的内存布局。
随手举个例子。
gdb-peda$ vmmap
Start End Perm Name
0x08048000 0x08049000 r-xp /root/Desktop/king/12lev/level12
0x08049000 0x0804a000 r--p /root/Desktop/king/12lev/level12
0x0804a000 0x0804b000 rw-p /root/Desktop/king/12lev/level12
0xf7dc6000 0xf7ddf000 r--p /lib32/libc-2.27.so
0xf7ddf000 0xf7f2b000 r-xp /lib32/libc-2.27.so
0xf7f2b000 0xf7f99000 r--p /lib32/libc-2.27.so
0xf7f99000 0xf7f9a000 ---p /lib32/libc-2.27.so
0xf7f9a000 0xf7f9c000 r--p /lib32/libc-2.27.so
0xf7f9c000 0xf7f9d000 rw-p /lib32/libc-2.27.so
0xf7f9d000 0xf7fa0000 rw-p mapped
0xf7fce000 0xf7fd0000 rw-p mapped
0xf7fd0000 0xf7fd3000 r--p [vvar]
0xf7fd3000 0xf7fd5000 r-xp [vdso]
0xf7fd5000 0xf7fd6000 r--p /lib32/ld-2.27.so
0xf7fd6000 0xf7ff1000 r-xp /lib32/ld-2.27.so
0xf7ff1000 0xf7ffb000 r--p /lib32/ld-2.27.so
0xf7ffc000 0xf7ffd000 r--p /lib32/ld-2.27.so
0xf7ffd000 0xf7ffe000 rw-p /lib32/ld-2.27.so
0xfffdd000 0xffffe000 rw-p [stack]
↓
text段 可读可执行
data段 part one -->开了只读重定位RELRO // -->.got
data段剩余部分 //.got.plt -->
然后是libc的内存映射,如下图
其中0xf7f99000 0xf7f9a000 ---p /lib32/libc-2.27.so
的存在是为了占用text段和data段之间的内存空间,从而无法创建任意的内存映射。
PS:堆通常位于data段之后(maps文件中)(这里没有),而当调用malloc()请求的内存块大小超过MMAP_THRESHOLD时,会创建匿名内存段,这种类型的匿名内存段不会被标上[heap]标签。
0xf7f9d000 0xf7fa0000 rw-p mapped
0xf7fce000 0xf7fd0000 rw-p mapped
可参考:
然后是用于内核数据交互的vvar。内核通过映射到用户空间的一段只读区域来返回内核数据,这就是内存中的[vvar]区域。 + 虚拟动态共享目标文件(Virtual Dynamic Shared Object),被glibc用于调用一些常用的系统调用。
0xf7fd0000 0xf7fd3000 r--p [vvar]
0xf7fd3000 0xf7fd5000 r-xp [vdso]
然后是一大段动态连接器的内存映射。从技术上来讲,动态连接器也是一个共享库。 最后是栈。