• Linux 二进制分析-Linux环境和工具(chapter 1)


    目录

    1. Linux工具

    1.1 gdb调试器

    1.2 binutils工具箱

    1.3 hexdump

    1.4 strace

    1.5 ltrace

    2. 设备和文件

    2.1 /proc/${pid}/maps

    2.2 /proc/kcore

    2.3 /boot/System.map

    2.4 /boot/kallsyms

    2.5 /proc/iomem

    2.6 ECFS(extended core file snapshot,扩展核心文件快照)

    3. 链接器

    3.1 LD_PRELOAD环境变量

    3.2 LD_SHOW_AUXV环境变量

    3.3 链接器脚本


    Linux下二进制文件遵循一个统一的格式布局规范,这个二进制规范使得二进制在不同系统中可以拷贝、共享和运行,以及支持跨平台的交叉编译。二进制文件规范的制定,考虑了不同的硬件平台、各类操作系统和编译器的支持。实际上,有了该规范,才会产生操作系统内核的实现支持,才会有编译器的编译、链接器的链接、动态加载的支持。

    二进制分析,是信息安全行业逆向工程的技术。在本系列文章中,我将根据参考一系列教材,介绍Linux下二进制相关的知识、实践相关的二进制分析工具。同时,从安全的角度,给出各类针对二进制进行漏洞、病毒的恶意攻击方法和防御方法,包括但不限于:二进制保护、进程追踪方法、病毒技术、二进制取证分析等等。

    本系列文章将耗费接近半年的时间来完成,并在随后的时间里,继续添加一系列实践类的篇章。技术文章创作不易,希望对自己和读者都有所收益。欢迎关注和收藏,记得一键四连(关注、评论、点赞、收藏)

    1. Linux工具

    1.1 gdb调试

    调试工具,不详述。做C/C++开发的小伙伴,都比较熟悉它。基于ptrace实现,实际有更多使用功能。后文会介绍到。

    1.2 binutils工具箱

    • objdump

    objdump可以对ELF二进制进行解析(反编译)的工具,对未被篡改过的二进制文件能进行信息dump。显示二进制中的相关信息。objdump依赖于ELF的Section Header,因此无法进行控制流分析,且如果没有正确的ELF Section Header,objdump无法正常工作。

    ELF二进制中的Section,将在后面的章节根据设计规范详细介绍。

    常使用的功能如下:

    1. #include
    2. #include
    3. int main()
    4. {
    5. printf("hello binar\n");
    6. }
    # gcc -o a a.c

    1) 查看二进制中的所有符号(静态和动态符号表)

    # objdump -tT a

    2) 显示二进制中的Sections(header)

    # objdump -h a

     

    3)显示二进制中特定section的信息

    配合-S可以尽可能显示源代码。

    # objdump -j .data -s a

    4) 显示二进制中所有section的数据或代码

    # objdump -D a

    5) 显示二进制中可执行代码section的部分

    # objdump -d a
    • objcopy

    objcopy是公认的正统分析和修改ELF二进制的工具,支持将.elf格式文件转换为.hex文件,支持将二进制转换成目标文件(.o文件)。用来分析和修改任意类型的ELF目标文件,可修改,也可将ELF Section复制到ELF二进制中。例如:将一个ELF目标文件复制到另一个文件中使用如下命令:

    objcopy --only-section=.data  

    1) 生成ELF二进制

    1. # dd if=/dev/urandom of=foo.bin bs=1K count=1 oflag=direct
    2. # objcopy -I binary -O elf64-x86-64 foo.bin foo.o
    3. # objdump -h foo.o
    4. foo.o: file format elf64-little
    5. Sections:
    6. Idx Name Size VMA LMA File off Algn
    7. 0 .data 00000400 0000000000000000 0000000000000000 00000040 2**0
    8. CONTENTS, ALLOC, LOAD, DATA
    9. # objdump -j .data -s foo.o > foo2.bin

    Usage: objcopy [option(s)] in-file [out-file]

    支持的目标有: elf64-x86-64 elf32-i386 elf32-iamcu elf32-x86-64 a.out-i386-linux pei-i386 pei-x86-64 elf64-l1om elf64-k1om elf64-little elf64-big elf32-little elf32-big pe-x86-64 pe-bigobj-x86-64 pe-i386 plugin srec symbolsrec verilog tekhex binary ihex

    选项:

    -I --input-target Assume input file is in format

    -O --output-target Create an output file in format

     

    上述命令将二进制文件foo.bin,转换为foo.o的目标文件。可以看到foo.bin内容与foo.o的.data section的内容是一致的。 默认情况下,二进制数据放在目标文件的.data Section下。 可以通过--rename-section选项,更改到其它Section。如下:

    1. # objcopy -I binary -O elf64-x86-64 \
    2. --rename-section .data=.rodata,alloc,load,readonly foo.bin foo2.o
    3. # objdump -h foo2.o
    4. foo2.o: file format elf64-little
    5. Sections:
    6. Idx Name Size VMA LMA File off Algn
    7. 0 .rodata 00000400 0000000000000000 0000000000000000 00000040 2**0
    8. CONTENTS, ALLOC, LOAD, READONLY, DATA

    2)通过添加Section嵌入数据到二进制对象中

    例如,有如下的目标对象:

    # echo 'int main() { puts("Hello world\n"); }' | gcc -x c - -c -o hello.o

    通过objdump把foo.bin的二进制添加到.mydata的Section中,并编译成可执行文件:

    1. # objcopy --add-section .mydata=foo.bin \
    2. --set-section-flags .mydata=noload,readonly hello.o hello2.o
    3. # gcc hello2.o -o hello
    4. # ./hello
    5. Hello world
    6. # objdump -h hello | grep data
    7. 15 .rodata 00000011 00000000000006e0 00000000000006e0 000006e0 2**2
    8. 22 .data 00000010 0000000000201000 0000000000201000 00001000 2**3
    9. 25 .mydata 00000400 0000000000000000 0000000000000000 00001039 2**0

    通过 objdump -sj .mydata hello命令可以看到二进制内容与foo.bin完全一致。

    QA: objcopy --only-section=.mydata -O binary hello2.o hello.bin

    • readelf

    readelf是用来解析ELF二进制文件的强大工具。在进行二进制分析(黑客攻击)时,与objcopy一样,是一个利器,可以收集目标文件相关的所有特定于ELF的数据,为后续的反编译准备信息。常用如下:

    1)查询ELF文件头

    1. # readelf -h hello
    2. ELF Header:
    3. Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
    4. Class: ELF64
    5. Data: 2's complement, little endian
    6. Version: 1 (current)
    7. OS/ABI: UNIX - System V
    8. ABI Version: 0
    9. Type: DYN (Shared object file)
    10. Machine: Advanced Micro Devices X86-64
    11. Version: 0x1
    12. Entry point address: 0x530
    13. Start of program headers: 64 (bytes into file)
    14. Start of section headers: 7496 (bytes into file)
    15. Flags: 0x0
    16. Size of this header: 64 (bytes)
    17. Size of program headers: 56 (bytes)
    18. Number of program headers: 9
    19. Size of section headers: 64 (bytes)
    20. Number of section headers: 30
    21. Section header string table index: 29

    2)查询ELF符号表

     3) 查询ELF二进制程序头

    1. # readelf -l hello
    2. Elf file type is DYN (Shared object file)
    3. Entry point 0x530
    4. There are 9 program headers, starting at offset 64
    5. Program Headers:
    6. Type Offset VirtAddr PhysAddr
    7. FileSiz MemSiz Flags Align
    8. PHDR 0x0000000000000040 0x0000000000000040 0x0000000000000040
    9. 0x00000000000001f8 0x00000000000001f8 R 0x8
    10. INTERP 0x0000000000000238 0x0000000000000238 0x0000000000000238
    11. 0x000000000000001c 0x000000000000001c R 0x1
    12. [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
    13. LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
    14. 0x0000000000000838 0x0000000000000838 R E 0x200000
    15. LOAD 0x0000000000000db8 0x0000000000200db8 0x0000000000200db8
    16. 0x0000000000000258 0x0000000000000260 RW 0x200000
    17. DYNAMIC 0x0000000000000dc8 0x0000000000200dc8 0x0000000000200dc8
    18. 0x00000000000001f0 0x00000000000001f0 RW 0x8
    19. NOTE 0x0000000000000254 0x0000000000000254 0x0000000000000254
    20. 0x0000000000000044 0x0000000000000044 R 0x4
    21. GNU_EH_FRAME 0x00000000000006f4 0x00000000000006f4 0x00000000000006f4
    22. 0x000000000000003c 0x000000000000003c R 0x4
    23. GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
    24. 0x0000000000000000 0x0000000000000000 RW 0x10
    25. GNU_RELRO 0x0000000000000db8 0x0000000000200db8 0x0000000000200db8
    26. 0x0000000000000248 0x0000000000000248 R 0x1
    27. Section to Segment mapping:
    28. Segment Sections...
    29. 00
    30. 01 .interp
    31. 02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame
    32. 03 .init_array .fini_array .dynamic .got .data .bss
    33. 04 .dynamic
    34. 05 .note.ABI-tag .note.gnu.build-id
    35. 06 .eh_frame_hdr
    36. 07
    37. 08 .init_array .fini_array .dynamic .got

    4) -S查询Section headers表

    5)-d查询动态Section部分

    6)-r查询可重定位入口

    7) -e查询ELF文件Header数据(等同于-l -h -S)

    1.3 hexdump

    hexdump将文件看作二进制,并以十六进制dump显示。

    该工具主要能调整dump的位置,以及显示的格式。

    1. # hexdump -h
    2. hexdump: invalid option -- 'h'
    3. usage: hexdump [-bcCdovx] [-e fmt] [-f fmt_file] [-n length]
    4. [-s skip] [file ...]
    5. hd [-bcdovx] [-e fmt] [-f fmt_file] [-n length]
    6. [-s skip] [file ...]

    1.4 strace

    strace是系统调用的追踪工具,基于ptrace实现。strace在一个循环中使用PTRACE_SYSCALL来显示运行程序中所使用到的系统调用。在调试二进制运行,可以收集二进制运行机制。

    1)追踪命令执行过程中的系统调用

    例如,追踪ls命令:

     2) 追踪特定进程系统调用

    # strace -p 12100 -o 12100.strace

    1.5 ltrace

    ltrace(library trace)是一个库追踪的工具,比较简洁。可以用来解析共享库,即一个程序的链接信息,并打印出用到的库函数。

     

    2. 设备和文件

    2.1 /proc/${pid}/maps

    该文件可以输出进程的映像,展现每个内存映射。内容包括:可执行文件、共享库、栈、堆、VDSO等信息。可以用这些信息快速解析一个进程的地址空间的分布情况。后文详细介绍。

    2.2 /proc/kcore

    Linux内核的动态核心文件,是以ELF核心文件的形式展示的原生内存转储,gdb可以对/proc/kcore来对内核进行调试和分析。第9章节将详细介绍。

    2.3 /boot/System.map

    该文件存在于几乎所有的Linux发行版中,包含了整个内核的所有符号。对内核黑客来说,非常重要。

    2.4 /boot/kallsyms

    与System.map类似,但区别是kallsyms是内核所属的/proc的一个入口,可动态更新。安装了新的LKM(Linux Kernel Module)之后,符号会自动添加到kallsyms中。其中包含了内核中的绝大多数的符号,通过配置内核编译选项CONFIG_KALLSYMS_ALL甚至可以包含内核中的全部符号。

    2.5 /proc/iomem

    Linux系统中,固定分配给设备以及内核代码段、数据段等的系统物理内存 地址。可以查看内核的code/text段、data段和bss段的地址范围如下:

    1. # cat /proc/iomem | grep Kernel
    2. ea8400000-ea9200e30 : Kernel code
    3. ea9200e31-eaa051cff : Kernel data
    4. eaa321000-eaa7fffff : Kernel bss

    2.6 ECFS(extended core file snapshot,扩展核心文件快照)

    Extended Core File Snapshot(ECFS),是一项特殊的核心转储技术,专门为进程映像设计的高级取证分析。该软件代码参考:https://github.com/elfmaster/ecf

    第8章节将详细介绍ECFS及其使用方法,可以用它进行高级内存取证分析。

    3. 链接器

    动态链接/加载,是在程序链接、运行过程中的基本功能。在Linux中,有许多可以代替动态链接的方法,可以被黑客利用,从而攻击或分析运行中的应用程序。下面给出两种方法,后文会详细给出操作实践:

    3.1 LD_PRELOAD环境变量

    该环境变量可以设置一个指定库的路径,运行时动态链接时可以比其它库有更高的链接优先级。这就允许预加载库中的函数和符号,能够覆盖掉后续链接库中的函数和符号。利用它的技术,可以通过重定向共享库函数来进行运行时修复,或者绕过反调试代码,以及用作用户级的rootkit。

    3.2 LD_SHOW_AUXV环境变量

    该环境变量能够通知程序加载器来展示程序运行时的辅助向量。辅助向量是放在程序栈(通过内核的ELF常规加载方式)上的信息,其附带了传递给动态链接器的程序相关的特定信息。这些信息对于反编译和调试来说非常有用。例如,想要获取进程映像VDSO页的内存地址,就需要查询AT_SYSINFO。

    第2章节将介绍辅助向量,如下带有LD_SHOW_AUXV辅助向量的例子:

    1. # LD_SHOW_AUXV=1 whoami
    2. AT_SYSINFO_EHDR: 0x7ffd7af17000
    3. AT_HWCAP: bfebfbff
    4. AT_PAGESZ: 4096
    5. AT_CLKTCK: 100
    6. AT_PHDR: 0x5615bf740040
    7. AT_PHENT: 56
    8. AT_PHNUM: 9
    9. AT_BASE: 0x7f4338e2a000
    10. AT_FLAGS: 0x0
    11. AT_ENTRY: 0x5615bf741a10
    12. AT_UID: 0
    13. AT_EUID: 0
    14. AT_GID: 0
    15. AT_EGID: 0
    16. AT_SECURE: 0
    17. AT_RANDOM: 0x7ffd7ae83239
    18. AT_HWCAP2: 0x0
    19. AT_EXECFN: /usr/bin/whoami
    20. AT_PLATFORM: x86_64
    21. root

    3.3 链接器脚本

    链接器脚本的使用,可以极大提高分析的能力。**链接器脚本是由链接器linker进行解释,把程序划分成相应的节、内存和符号。默认的连机器脚本可以使用ld --verbose查看

    ld链接程序有自己解释的一套语言,当有文件输入时,如可重定位的目标文件、共享库或头文件,ld链接器会用自己的语言来决定输出文件(如可执行文件)的组织方式。

    例如:如果输出的是一个ELF可执行文件,链接器脚本能够决定该输出文件的布局,以及每个段中包含哪些Sections。

    再如:.bss Section总是存放在data段的尾部,这也是由链接器脚本决定的。

    对于编译链接过程的深入了解是很重要的,gcc依赖于链接器和其它程序来完成编译任务,在某些情况下,能够控制可执行文件的布局相当重要(制作病毒)。ld命令语言是一门非常深入的语言,非常值得学习和深究。

    同时,在对可执行文件进行反编译时,普通段地址或者文件的其它部分有时会被修改,这就表明引入了一个自定义的链接器脚本。gcc通过使用-T选项来指定链接器脚本。 第5章节将详细介绍相应的链接器脚本的使用实践。

    关于作者:

    犇叔,浙江大学计算机科学与技术专业,研究生毕业,而立有余。先后在华为、阿里巴巴和字节跳动,从事技术研发工作,资深研发专家。主要研究领域包括虚拟化、分布式技术和存储系统(包括CPU与计算、GPU异构计算、分布式块存储、分布式数据库等领域)、高性能RDMA网络协议和数据中心应用、Linux内核等方向。

    专业方向爱好:数学、科学技术应用

    关注犇叔,期望为您带来更多科研领域的知识和产业应用。

    内容坚持原创,坚持干货有料。坚持长期创作,关注犇叔不迷路

  • 相关阅读:
    【C++】string类的模拟实现
    技能大赛训练题:ftp 服务攻防与加固
    【打卡】【sysfs相关API详解】21天学习挑战赛—RK3399平台开发入门到精通-Day21
    《计算机视觉中的多视图几何》笔记(0)
    Anchor-free目标检测综述 -- Dense Prediction篇
    Linux系统编程·进程地址空间
    vue3+elementUiPlus表格导出功能
    JAVA泡泡堂网络游戏的设计与实现免费源代码+LW
    [dp]洛谷P1990 覆盖墙壁 / Leecode790. 多米诺和托米诺平铺
    股票价格预测 | Python基于RNN及股票预测实战
  • 原文地址:https://blog.csdn.net/landy_john/article/details/128124744