返回
Featured image of post 前置知识整理(2)| 二进制

前置知识整理(2)| 二进制

导语

二进制安全

二进制安全是一盒比较偏向底层的方向,对计算机基础要求较高

  • 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码(最右一列)
        • 数据(.data):保存已初始化的全局变量和局部静态变量

          • .data中是源代码中的变量
          • .rodata 保存只读数据(只读变量和字符串常量)
        • BSS(.bss):保存未初始化的全局变量和局部静态变量

      • 其他常见的节:

        • .strtab(字符串表)
          • 包含了以null结尾的字符序列,用来表示符合和节名,引用字符串时只需给出字符序列在表中的偏移即可
        • symtab(符号表)
          • 记录目标文件中所有用到的所有符号信息,通常分为.dynsym和.symtab,前者是后者的子集
            • .dynsym:保存了引用自外部文件的符号,只能在运行时被解析
            • .symtab:还保存了本地符号,用于调试和链接
          • 目标文件通过一个符号在表中的索引值来使用该符号
        • (重定位):链接符号定义与符号引用的过程
          • 可重定位文件在构建可执行文件或共享目标文件时,需要把节中的符号引用换成这些符号在进程空间中的虚拟地址
      • ELF文件头:位于目标文件最开始的位置,包含描述整个文件的一些基本信息

        • ELF文件类型
        • 版本/应用程序二进制接口ABI版本
        • 目标机器
        • 程序入口
        • 段表和节表的位置和长度等
          • 节头表:存储目标文件中各个节的信息
            • 表的每一项都是一个Elf64_Shdr结构体(节描述符),记录节的名字、长度、偏移、读写权限等信息
            • 节头表的位置记录在文件头的e_shoff域中
            • 在运行时节头表不是必须的,所以常有程序去除节头表,以增加反编译器的分析难度

    文件头存在魔术字符(7f 45 4c 46),即字符“\177ELF”,当文件被映射到内存时,可以通过搜索该字符确定映射地址,在dump内存时非常有用

    指令和数据分别存放的好处:防止程序指令被改写和利用

    • 运行视角
      • 通过段(Segment)来进行划分
      • 运行可执行文件时,首先需要将该文件和动态链接库装载到进程空间中,形成进程镜像
      • 每个进程拥有独立的虚拟地址空间,这个空间布局由记录在段头表中的程序头决定的
        • 段表头位置:ELF文件头的e_phoof域已给出

静态链接

动态链接

Licensed under CC BY-NC-SA 4.0
/* */