导语
二进制安全
二进制安全是一盒比较偏向底层的方向,对计算机基础要求较高
- C/C++
- Python
- 汇编语言
- 计算机组成原理
- 操作系统
- 编译原理
二进制安全可以细分为逆向工程和漏洞挖掘与利用等方向
学习书籍推荐(中文):
- 程序员的自我修养–链接、装载与库
- 加密和解密(第四版)
学完之后,就可以进行软件漏洞的学习了,分析真实环境中的漏洞和恶意样本,推荐资料:
- 漏洞战争“软件漏洞分析精要
有了实践基础之后,可以学习一些理论分析:
- 数据流分析(Soot)
- 值集分析(BAP)
- 可满足性(Z3)
- 动态二进制插桩
- 符号执行
- 模糊测试
二进制文件
源文件–>可执行文件
一个C语言程序从源文件开始,这种高级语言的形式更容易被人理解。但是程序运行,每条C语句都必须翻译为一系列的低级机器语言指令。最后,这些指令按照可执行目标文件的格式打包,并以二进制的形式存放起来
编译原理
编程语言–>编译器—>目标语言
GCC编译过程
- 预处理(hello.c–>hello.i)
- 处理源代码中以“#”开始的预处理指令,如#include、#define,将其转换后直接插入程序文本中,得到另一个C程序hello.i
- 在命令中添加编译选-E 可以单独执行预处理
- 一些预处理规则
- #include:将对应文件的内容复制到该指令的位置(换行后的内容原封不动的出现在下面)
- #define 删除,并且在被引用的位置递归地扩展所有的宏定义(即原本文件中的数据使用的是宏,但之后会变成数字)
- 处理所有条件预处理指令:if、ifdef、elif、else、endif等
- 删除所有注释
- 添加行号和文件名标识
- 编译(hello.i–>hello.s)
- 此过程将预处理文件进行一系列的词法分析、语法分析、语义分析以及优化,最终生成汇编代码
- 在命令中添加编译选项-S,操作对象可以是源代码 hello.c,也可以是hello.i
- 注:生成的汇编代码中函数printf()被替换为puts(),因为当printf()只有单一参数时,与puts()功能类似
- 汇编(hello.s–>hello.o)
- 汇编器根据汇编指令与机器指令的对照表进行翻译,将hello.s汇编成目标文件hello.o
- hello.o是一个可重定位文件,可以使用objdump命令来查看其内容
- 链接(hello.o–>hello)
- 链接器collect2是对ld命令的封装,用于将C语言运行时库(CRT)中的目标文件(crt1.o,crti.o,crtbegin.o,crtend.o,crtn,o)以及所需的动态链接库链接到可执行hello
- 分类
- 静态链接— 添加编译选项-static即可指定使用静态链接
- 动态链接(默认)
- 此阶段将目标文件及其依赖库进行链接,生成可执行文件,主要包括地址和空间分配、符号绑定和重定位等操作
- 链接操作由链接器(ld.so)完成,得到hello文件,这是一个静态链接的可执行文件,包含大量的库文件
- 通过链接操作,对象文件中无法确定的符号地址已经被修正为实际的符号地址,程序就可以被加载到内存中正常执行了
ELF文件格式
ELF(Executable and Linkable Format):可执行可链接格式
ELF分类:
- 可执行文件(.exec)
- 经过链接的、可执行的目标文件,通常称为程序
- 可重定位文件(.rel)
- 由源文件编译而成尚未链接的目标文件,通常以“.o”作为扩展名。通常是一段位置独立的代码
- 共享目标文件(.dyn)
- 动态链接库文件
- 核心转储文件(core dump file)
- 作为进程意外终止时进程地址空间的转储,也是ELF文件的一种,使用gdb读取这类文件可以辅助调试和查找程序崩溃的原因(在Linux中遇到过核心转储问题)
注:ELF文件被统称为Object file,所以ELF可以认定为目标文件,而“.o”文件可以称为重定位文件
ELF文件的结构:
-
审视目标文件的两种视角
- 链接视角
-
通过节(Section)来进行划分
-
目标文件中包含的节
-
代码(.text):保存可执行的机器指令
- .text的数据是十六制形式,共0x4e个字节
- 偏移量(最左一列) 内容(中间四列) ASCII码(最右一列)
- .text的数据是十六制形式,共0x4e个字节
-
数据(.data):保存已初始化的全局变量和局部静态变量
- .data中是源代码中的变量
- .rodata 保存只读数据(只读变量和字符串常量)
-
BSS(.bss):保存未初始化的全局变量和局部静态变量
-
-
其他常见的节:
- .strtab(字符串表)
- 包含了以null结尾的字符序列,用来表示符合和节名,引用字符串时只需给出字符序列在表中的偏移即可
- symtab(符号表)
- 记录目标文件中所有用到的所有符号信息,通常分为.dynsym和.symtab,前者是后者的子集
- .dynsym:保存了引用自外部文件的符号,只能在运行时被解析
- .symtab:还保存了本地符号,用于调试和链接
- 目标文件通过一个符号在表中的索引值来使用该符号
- 记录目标文件中所有用到的所有符号信息,通常分为.dynsym和.symtab,前者是后者的子集
- (重定位):链接符号定义与符号引用的过程
- 可重定位文件在构建可执行文件或共享目标文件时,需要把节中的符号引用换成这些符号在进程空间中的虚拟地址
- .strtab(字符串表)
-
ELF文件头:位于目标文件最开始的位置,包含描述整个文件的一些基本信息
- ELF文件类型
- 版本/应用程序二进制接口ABI版本
- 目标机器
- 程序入口
- 段表和节表的位置和长度等
- 节头表:存储目标文件中各个节的信息
- 表的每一项都是一个Elf64_Shdr结构体(节描述符),记录节的名字、长度、偏移、读写权限等信息
- 节头表的位置记录在文件头的e_shoff域中
- 在运行时节头表不是必须的,所以常有程序去除节头表,以增加反编译器的分析难度
- 节头表:存储目标文件中各个节的信息
-
文件头存在魔术字符(7f 45 4c 46),即字符“\177ELF”,当文件被映射到内存时,可以通过搜索该字符确定映射地址,在dump内存时非常有用
指令和数据分别存放的好处:防止程序指令被改写和利用
- 运行视角
- 通过段(Segment)来进行划分
- 运行可执行文件时,首先需要将该文件和动态链接库装载到进程空间中,形成进程镜像
- 每个进程拥有独立的虚拟地址空间,这个空间布局由记录在段头表中的程序头决定的
- 段表头位置:ELF文件头的e_phoof域已给出
- 链接视角