在这一章中,我们将关注 Linux 环境,因为它与本书的重点相关。 由于本书关注的是 Linux 二进制分析,所以利用 Linux 自带的每个人都可以访问的本地环境工具是有意义的。 Linux 自带了已经安装的无处不在的二进制文件,但是可以在http://www.gnu.org/software/binutils/找到它们。 它们包含大量可供二进制分析和黑客使用的工具。 这不是另一本关于使用 IDA Pro 的书。 IDA 无疑是二进制文件逆向工程的最佳通用软件,我鼓励在需要时使用它,但在本书中我们不会使用它。 相反,您将获得能够跳到任何 Linux 系统上的技能,并了解如何在一个已经可以访问的环境中开始破解二进制文件。 因此,您可以学会欣赏 Linux 作为一个真正的黑客环境的美妙之处,这里有许多可用的免费工具。 在整本书中,我们将演示各种工具的使用,并在每一章中概述如何使用它们。 与此同时,让本章作为 Linux 环境中这些工具和技巧的入门或参考。 如果您已经非常熟悉 Linux 环境及其用于反汇编、调试和解析 ELF 文件的工具,那么您可以跳过本章。
在本书中,我们将使用各种任何人都可以访问的免费工具。 本节将简要介绍其中一些工具。
GNU 调试器(GDB)不仅可以调试有 bug 的应用。 它还可以用于了解程序的控制流、更改程序的控制流以及修改代码、寄存器和数据结构。 这些任务对于正在利用软件漏洞或正在阐明复杂病毒的内部工作原理的黑客来说是常见的。 GDB 适用于 ELF 二进制文件和 Linux 进程。 它是 Linux 黑客的基本工具,在本书的各个例子中都会用到。
对象转储(objdump)是简单而干净的解决方案,用于代码的快速反汇编。 它可以很好地分解简单且未被篡改的二进制文件,但当试图将其用于任何真正具有挑战性的逆向工程任务时,尤其是针对敌对软件时,它将很快显示出其局限性。 它的主要缺点是它依赖于ELF
节报头,并且不执行控制流分析,这两个限制极大地降低了它的鲁棒性。 这将导致无法正确地反汇编二进制文件中的代码,甚至在没有段头的情况下根本无法打开二进制文件。 然而,对于许多常规任务来说,它应该足够了,比如在分解未进行任何强化、剥离或模糊处理的通用二进制文件时。 它可以读取所有常见的ELF
类型。 下面是一些如何使用objdump
的常见例子:
-
查看
ELF
文件中每个部分的所有数据/代码:objdump -D <elf_object>
-
只查看
ELF
文件中的程序代码:objdump -d <elf_object>
-
查看所有符号:
objdump -tT <elf_object>
在第二章、ELF 二进制格式介绍ELF
格式期间,我们将深入探讨objdump
和其他工具。
Object copy(Objcopy)是一个非常强大的小工具,我们无法用一个简单的概要来概括。 我建议您阅读手册页以获得完整的描述。 Objcopy
可以用来分析和修改任何类型的ELF
对象,尽管它的一些特征是特定于ELF
对象的某些类型的。 Objcopy
常用于将ELF
节修改或从ELF
二进制文件中复制。
要将.data
节从ELF
对象复制到文件中,使用下面这行:
objcopy –only-section=.data <infile> <outfile>
objcopy
工具将在本书的其余部分根据需要进行演示。 请记住,它是存在的,对于 Linux 二进制代码黑客来说,它是一个非常有用的工具。
系统调用跟踪(strace)是一个工具【显示】,ptrace(2)
是基于系统调用,它利用PTRACE_SYSCALL
请求在一个循环中显示的信息系统调用(也称为syscalls
)活动在一个运行的程序以及执行期间捕获的信号。 这个程序对于调试非常有用,或者只是用来收集运行时调用的syscalls
的信息。
这是用来跟踪一个基本程序的strace
命令:
strace /bin/ls -o ls.out
用于附加到现有进程的strace
命令如下:
strace -p <pid> -o daemon.out
初始输出将显示每个以文件描述符作为参数的系统调用的文件描述符编号,例如:
SYS_read(3, buf, sizeof(buf));
如果你想看到所有被读入文件描述符 3 的数据,你可以运行以下命令:
strace -e read=3 /bin/ls
您也可以使用-e write=fd
来查看写入的数据。 strace
工具是一个伟大的小工具,毫无疑问,你会发现许多理由使用它。
库跟踪(ltrace)是另一个简洁的小工具,它与strace
非常相似。 它的工作原理与此类似,但实际上它解析程序的共享库链接信息,并打印正在使用的库函数。
您可能会看到带有-S
标志的库函数调用除了之外还有系统调用。 ltrace
命令的目的是提供更细粒度的信息,因为它解析可执行文件的动态段,并从共享库和静态库打印实际的符号/函数:
ltrace <program> -o program.out
功能跟踪(ftrace)是我设计的一个工具。 它类似于ltrace
,但它也显示了对二进制文件本身中的函数的调用。 在 Linux 中,我找不到其他公开可用的工具可以做到这一点,所以我决定编写一个。 这个工具可以在https://github.com/elfmaster/ftrace找到。 下一章将给出这个工具的演示。
readelf
命令是用于解析ELF
二进制文件最有用的工具之一。 它提供了特定于ELF
的每一位数据,这些数据是在逆向工程之前收集关于一个对象的信息所必需的。 本书将经常使用这个工具来收集关于符号、段、节、重定位条目、数据的动态链接等等的信息。 readelf
命令是ELF
的瑞士军刀。 我们将在第二章、ELF 二进制格式中根据需要深入讨论它,但这里有一些它最常用的标志:
-
检索 section 头表:
readelf -S <object>
-
检索程序头表:
readelf -l <object>
-
检索一个符号表:
readelf -s <object>
-
检索
ELF
文件头数据:readelf -e <object>
-
检索重定位条目:
readelf -r <object>
-
检索动态段:
readelf -d <object>
ERESI 项目(http://www.eresi-project.org)包含一套包含许多工具的工具,这是 Linux 二进制黑客的梦想。 不幸的是,它们中的许多都不是最新的,也不能与 64 位 Linux 完全兼容。 然而,它们确实存在于各种架构中,并且无疑是用于破解当今存在的ELF
二进制文件的最具创新性的工具集合。 因为我个人并不熟悉使用 ERESI 项目的工具,而且它们不再是最新的,所以我不会在本书中探讨它们的功能。 但是,请注意,有两篇 Phrack 文章展示了 ERESI 工具的创新和强大特性:
- 地狱犬 ELF 接口(http://www.phrack.org/archives/issues/61/8.txt)
- 嵌入式 ELF 调试(http://www.phrack.org/archives/issues/63/9.txt)
Linux 有许多文件、设备和/proc
条目,这些对热心的黑客和反向工程师非常有帮助。 在本书中,我们将展示这些文件中的许多有用之处。 下面是本书中常用的一些短语的描述。
/proc/<pid>/maps
文件包含进程映像的布局,通过显示每个内存映射。 这包括可执行文件、共享库、堆栈、堆、VDSO 等等。 该文件对于快速解析进程地址空间的布局至关重要,在本书中多次使用该文件。
/proc/kcore
是proc
文件系统中的一个条目,它充当 Linux 内核的一个动态核心文件。 也就是说,它是一个以ELF
核心文件的形式呈现的原始内存转储,GDB 可以使用它来调试和分析内核。 我们将在第九章,Linux /proc/kcore Analysis中深入探讨/proc/kcore
。
这个文件在几乎所有的 Linux 发行版中都可用,对内核黑客非常有用。 它包含了整个内核的所有符号。
kallsyms
与非常相似,只是它是一个/proc
条目,这意味着它由内核维护并动态更新。 因此,如果安装了任何新的 lkm,符号将被动态地添加到/proc/kallsyms
中。 /proc/kallsyms
至少包含内核中的大部分符号,如果在CONFIG_KALLSYMS_ALL
内核配置中指定,则将包含所有符号。
iomem
是一个有用的 proc 条目,因为它与/proc/<pid>/maps
非常相似,但是对于所有的系统内存来说。 例如,如果你想知道内核的文本段在物理内存中映射到哪里,你可以搜索Kernel
字符串,你会看到code/text
段、数据段和bss
段:
$ grep Kernel /proc/iomem
01000000-016d9b27 : Kernel code
016d9b28-01ceeebf : Kernel data
01df0000-01f26fff : Kernel bss
扩展核心文件快照(ECFS)是一种特殊的核心转储技术,专为进程映像的高级取证分析而设计。 这个软件的代码可以在https://github.com/elfmaster/ecfs找到。 此外,第八章,ECFS -扩展核心文件快照技术,专门解释了什么是 ECFS 以及如何使用它。 对于那些对高级记忆取证感兴趣的人来说,你会想要密切关注这个。
动态加载器/链接器和链接概念是程序链接和执行过程中不可避免的组成部分。 在本书中,你会学到很多关于这些主题的知识。 在 Linux 中,有相当多的方法可以改变动态链接器的行为,以多种方式为二进制黑客服务。 随着本书的深入,您将开始理解链接、重定位和动态加载(程序解释器)的过程。 下面是一些与链接器相关的属性,它们将在本书中使用。
可以设置LD_PRELOAD
环境变量来指定一个库路径,该库路径应该在任何其他库之前被动态链接。 这样做的效果是允许来自预加载库的函数和符号覆盖随后链接的其他库中的函数和符号。 这实际上允许您通过重定向共享库函数来执行运行时补丁。 正如我们将在后面的章节中看到的,这种技术可以用于绕过反调试代码和用户的 rootkit。
这个环境变量告诉程序装入器在运行时显示程序的辅助向量。 辅助向量是放置在程序堆栈(由内核的ELF
加载例程)上的信息,这些信息与程序的某些信息一起传递给动态连接器。 我们将在第三章,Linux 进程跟踪中更仔细地研究这个问题,但是这些信息可能对反转和调试有用。 例如,如果您想在进程映像中获得 VDSO 页面的内存地址(也可以从maps
文件中获得,如前面所示),您必须查找AT_SYSINFO
。
下面是一个带有LD_SHOW_AUXV
的辅助向量的例子:
$ LD_SHOW_AUXV=1 whoami
AT_SYSINFO: 0xb7779414
AT_SYSINFO_EHDR: 0xb7779000
AT_HWCAP: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2
AT_PAGESZ: 4096
AT_CLKTCK: 100
AT_PHDR: 0x8048034
AT_PHENT: 32
AT_PHNUM: 9
AT_BASE: 0xb777a000
AT_FLAGS: 0x0
AT_ENTRY: 0x8048eb8
AT_UID: 1000
AT_EUID: 1000
AT_GID: 1000
AT_EGID: 1000
AT_SECURE: 0
AT_RANDOM: 0xbfb4ca2b
AT_EXECFN: /usr/bin/whoami
AT_PLATFORM: i686
elfmaster
辅助向量将在第二章、ELF 二进制格式中更深入地介绍中的。
链接器脚本是我们感兴趣的一点,因为它们由链接器解释,并帮助塑造关于片段、内存和符号的程序布局。 默认的链接器脚本可以用ld -verbose
查看。
ld
链接器程序有一个完整的语言,当它获取输入文件(如可重定位的目标文件、共享库和头文件)时,它将解释该语言,并且它使用该语言来确定输出文件(如可执行程序)的组织方式。 例如,如果输出是一个ELF
可执行文件,链接器脚本将帮助确定布局是什么,以及哪些段将存在于哪些段中。 下面是另一个实例:.bss
段总是在数据段的末尾; 这是由链接器脚本决定的。 你可能想知道我们对这有什么兴趣。 好! 首先,在编译时了解链接过程是很重要的。 gcc
依赖于链接器和其他程序来执行这项任务,在某些情况下,能够控制可执行文件的布局是很重要的。 命令语言是一种非常深入的语言,超出了本书的范围,但是值得一试。 当反向工程可执行文件时,请记住,公共段地址有时可能被修改,布局的其他部分也可能被修改。 这表明涉及自定义链接器脚本。 可以使用-T
标志用gcc
指定链接器脚本。 我们将在第 5 章,Linux 二进制保护中看一个使用链接器脚本的具体例子。
我们只是触及了 Linux 环境的一些基本方面,以及在每一章的演示中最常用的工具。 二进制分析主要是关于了解可用的工具和资源以及它们是如何组合在一起的。 我们只是简单地介绍了这些工具,但是在接下来的章节中,当我们探索 Linux 二进制代码破解的广阔世界时,我们将有机会强调每种工具的功能。 在下一章中,我们将深入研究 ELF 二进制格式的内部结构,并讨论许多有趣的主题,例如动态链接、重定位、符号、节等等。