【PWN之路】二进制基础


前言

CTF越来越火,没想到我们单位也组织战队参赛,可惜的是PWN学习路径过于陡峭,短期又看不到成绩,此位空缺。想到大学时跟着台湾ROCK老师搞过一阵逆向,兴趣也有,决定从头复习一遍,谁叫我们大学那会没有CTF这种东西呢。以下内容为本人的学习笔记,边学边记录,不对的地方欢迎大家指正。

提示:本内容仅供技术交流,不可用于违法用途


一、编译原理

1.编译器

编译器简单理解就是将一种语言(源语言)转换为等价的另一种语言(目标语言)的程序。

编译器分为前端(Front end)和后端(Back end)两部分。

  • 前端:机器无关,把源程序分解成组成要素和相应的语法结构,通过这个结构创建源程序的中间表示,同时收集和程序相关的信息,存放到符号表中;
  • 后端:机器相关,根据中间表示和符号表构造目标程序。

编译过程大致为5个步骤:

  1. 词法分析(Lexical analysis)-》输出有意义的词素(Lexeme)
  2. 语法分析(Syntax analysis)-》产生语法树(Syntax tree)
  3. 语义分析(Semantic analysis)
  4. 中间代码生成和优化
  5. 代码生成和优化

编译过程

2.GCC编译过程

GCC编译主要有四个阶段:预处理(Preprocess)、编译(Compile)、汇编(Assemble)、链接(Link)。
GCC编译过程
三个工具组件:ccl(第一和第二阶段)、as(第三阶段)、collect2(对ld命令的封装,第四阶段)

1. 预处理阶段:处理以#开头的预处理指令,如#include、#define等,转换后直接插入程序文本中,通常以.i作为拓展名。

GCC  -E  hello.c -o hello.i #单独执行预处理

2. 编译阶段:执行词法分析、语法分析、语义分析以及优化,生成汇编代码。

#GCC中预处理已经和编译合并处理
GCC -S hello.c -o hello.s 
#masm指定为intel格式,fno-asynchronous-unwind-tables生成没有cfi宏的汇编指令,以提高可读性。
GCC -S hello.i -o -masm=intel -fno-asynchronous-unwind-tables 

3.汇编阶段:根据汇编指令和机器指令的对照表进行翻译,将.s翻译为目标文件.o。

GCC -c  hello.c -o hello.o
GCC -c hello.s -o hello.o

4.链接阶段:分为静态链接和动态链接两种方式,默认使用动态链接,使用-static指定使用静态链接。将目标文件和依赖库进行链接,主要包括地址和空间分配、符号绑定、重定位等。

GCC hello.o -o hello

两个常用命令:file和objdump
file 文件名:查看文件详细信息
objdump -sd hello -M intel:反汇编

二、ELF文件格式

ELF(Executable and Linkable Format):可执行可链接格式,在Linux上运行,在/usr/include/elf.h中定义。

1.ELF文件类型

1. 可执行文件(.exec)
2. 可重定位文件(.rel)
3. 共享目标文件(.dyn)

2.ELF文件结构

视角选择:
链接视角:通过节(Section)来进行划分,通常包含代码(.text)、数据(.data)、BSS(.bss)三种节;
运行视角:通过段(Segment)来进行划分。

(1)ELF文件头

ELF文件头(ELF header)位于目标文件最开始的位置,包含整个文件的基本信息。

readelf -h elfDemo.rel #查看elf文件头信息

注意:文件头中存在魔术字符(Magic):7f 45 4c 46 即字符串“\177ELF”,可以通过搜索该字符确定内存映射地址。

(2)节头表

节头表(Section header table)包含了目标文件中所有节的信息,记录了节的名字、长度、偏移、读写权限等信息。节头表的位置记录在文件头的e_shoff域中。

readelf -S elfdemo.rel#查看目标文件节头表

注意:节头表对于程序运行不是必须的,因为它与程序内存和布局无关,是程序头表的任务,所以常有程序去除节头表,以增加反编译器的分析难度。

(3)代码节(.text)

objdump -x -s -d elfDemo.rel #输出各节的内容

Contents of section .text部分是.text数据的十六进制形式,共0x4e个字节,最左边一列是偏移量,中间四列为内容,最右边一列为ASCII码形式。
Disassembly of section .text部分是反汇编结果。

(4)数据节(.data)

保存以及初始化的全局变量和局部静态变量。
.rodata 保存只读数据,包含只读变量和字符串常量

(5)BSS节(.bss)

用于保存未初始化的全局变量和局部静态变量,没有Contents属性,只是为变量预留了位置,因此该节的sh_offset域也就没有意义。

(6)其他节

字符串表(.strtab)

用来表示符号名和节名,引用字符串表时只需给出字符徐磊在表中的偏移量即可

字符串表的第一个和最后一个字符均为null字符,以确保所有字符串的开始和终止。

符号表(.dynsym和.symtab)

.dynsym保存引用外部文件的符号,只在运行时被解析;
.symtab还保存了本地符号,用于调试和链接。
目标文件通过在该表中的索引值来使用该符号,索引值从0开始,但0的表项不具备实际意义,他表示未定义的符号。

3.可执行文件的装载

当运行一个可执行文件时,首先要将该文件和动态链接库装载到 进程空间中,形成一个进程镜像。
每个进程都有独立的虚拟地址空间,这个空间如何布局是由记录在 段头表 中的程序头决定的。
ELF文件头的e_phoff域给出了段头表的位置。
通过

readelf - l elefDemo.exec

可以看出一个段包含了一个或多个节,相当于对这些节按不同权限进行分组,使之可以同时装载多个节,从而节省资源。

常见的段:
PT_LOAD类型段:用于描述可装载的节(动态链接的可执行文件则包含两个–将.data和.text分开存放)
PT_DYNAMIC动态段:包含动态链接库所必须的信息,如共享库列表、GOT表和重定位表等。
PT_NOTE类型段:保存了系统相关的附加信息,运行并不需要
PT_INTERP段:将位置和大小信息存放在一个字符串中,是对程序解释器的位置的描述。
PT_PHDR段;保存了程序头表本身的位置和大小。

三、静态链接

1.地址空间分配

将两个目标文件链接为一个可执行文件,有两种方式:按序叠加相似节合并

  • 按序叠加:最简单的方式,如果参与链接的目标文件过多,输出的可执行文件可能会非常零散,不足一页的代码节或数据节也要占一页,浪费内存空间。
  • 相似节合并:对应的节进行合并成新的节,当前的连接器采用。

2.详细过程

链接器的两项重要工作:符号解析(Symbol resolution)和重定位(relocation)。

  • 符号解析是将每个符号(函数、全局变量、静态变量)的引用与其定义进行关联。

  • 重定位是将每个符号的定义与一个内存地址进行关联,然后修改这些符号的引用,使其指向这个内存地址。

使用

objdump -h xxxx.o#VMA是虚拟地址,LMA是加载地址

objdump -h  xxxx.ELF#VMA是虚拟地址,LMA是加载地址

对比可以发现链接前目标文件.o的虚拟地址VMA均为0,链接后的ELF文件中相似节被合并,且完成了虚拟地址的分配。
符号地址在链接前的.o文件中均为临时地址,不是实际地址,链接后才填入真实地址。

可重定位文件中包含重定位表,用于告诉链接器如何修改节的内容。

3.静态链接库

使用ar工具将一组不同的目标文件进行压缩编号索引形成.a的静态链接库文件。

四、动态链接

由于静态链接浪费空间,且对标准函数做一点改动都要重新编译整个源文件,将系统库和开发的代码分割成两个独立的模块,运行程序时再进行链接,节省硬盘空间,同时系统库在内存中可以被多个程序共同使用,节省物理内存空间,就叫动态链接。

可以加载而无需重定位的代码称为位置无关代码(PIC),是共享库必须具有的属性,通过GCC传递-fpic参数可以生成PIC,通过PIC,一个共享库可以被无限多个进程所共享,从而节约内存资源。

指令和变量之间的距离是一个运行时常量,保持不变,与绝对内存地址无关。
全局偏移量表(GOT),位于段开头用于保存全局变量和库函数的引用,每个条目占8个字节,在加载时进行重定位并填入符号的绝对地址。

为了引入RELRO保护机制,GOT被拆分为.got节和.got.plt节两个部分。
.got节不需要延迟绑定,用于全局变量的引用,加载到内存后被标记为只读。
.got.plt需要延迟绑定用于保存函数的引用,具有读写权限。

延迟绑定:基本思想是当函数第一次被调用时,动态链接器才进行符号查找、重定位等操作,如果未被调用则不进行绑定。
ELF文件通过过程链接表(PLT)和GOT配合实现延迟绑定。


  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值