Linux中常用的分析文件的小工具

Sunday, January 22, 2023
本文共3632字
8分钟阅读时长
re , pwn , tools

⚠️本文是作者P3troL1er原创,首发于https://peterliuzhi.top/tricks/linux%E4%B8%AD%E5%B8%B8%E7%94%A8%E7%9A%84%E5%88%86%E6%9E%90%E6%96%87%E4%BB%B6%E7%9A%84%E5%B0%8F%E5%B7%A5%E5%85%B7/。商业转载请联系作者获得授权,非商业转载请注明出处!

Action is the foundational key to all success. — Pablo Picasso

💡如需使用本文中介绍的大部分命令,请确保系统安装了binutils,如未安装,请使用如下命令安装:sudo apt-get install binutils

file

我们对一个文件使用该命令看一下:

file login

文内图片

我们能够看到,这个文件是一个64位的ELF文件,他运行在x86-64平台上,并且他的符号表没有被删除(not stripped)

文内图片

我们对一个文本文档试一下:

文内图片

我们发现file命令能识别出它是一个python脚本,同时识别出了它是一个UTF-8编码的文件,而且标出他是一个文本可执行文件(text executable),对应上面的LSB executable,Linux标准基础(Linux Standards Base,简称LSB)可执行文件

如果我们去除.py扩展名,它是否还能识别出来它是一个python脚本呢?

文内图片

仍然可以识别,说明file命令会根据文件内容进行判断

我们新建一个tmp文件,看看能识别出什么:

文内图片

得到结果是empty

如果我们加上一个shebang:

文内图片 文内图片

发现它此时不能判断它是一个python文件,但是通过shebang判断出了它的解释器

如果我们加上一句python语句:

文内图片

文内图片

如果我们加上C语言代码:

文内图片

文内图片

它仍然判断为python脚本,说明file给出的结果不一定是正确的,需要具体问题具体分析。file命令也可以根据幻数判断文件类型,然而这时候可能会导致file的判断出现差错

如果一个文件碰巧包含了某种文件格式的标记,file等工具很可能会错误地识别这个文件。你可以使用一个十六进制文件编辑器将任何文件的前 4 字节修改为 Java 的幻数序列 CA FE BA BE,自己证实一下上述情况。这时,file 会将这个新修改的文件错误地识别为已编译的 Java 类数据。同样,一个仅包含 MZ 这两个字符的文本文件会被误认为是一个 MS-DOS 可执行文件。

如果我们去掉shebang,就可以发现它是一个C语言源文件:

文内图片

文内图片

我们还可以通过通配符对特定一批文件进行分析:

文内图片

可以使用grep选择:

文内图片

file还有一系列选项,但一般来说这已经够了

如果我们希望在Windows上使用,可以安装Cygwin

文内图片

nm

虽然名字有些让人浮想联翩,但是它确实在某些情况挺好用的

nm是一个分析文件中的符号的命令,它可以将可执行文件中的函数符号分类列出:

文内图片

文内图片

我们发现它是一个C++可执行文件,并且它的符号都被分类列出了(以下翻译自man nm):

大写字母表示全局符号,小写字母则表示局部符号。但有一些小写字母表示特殊的含义(“u”, “v” , “w”)

类型标志 意义
A 符号的值是绝对的,不会因为进一步的链接而改变。
B 见下
b 符号在BSS数据部分。 这个部分通常包含零初始化或未初始化的数据。尽管具体行为取决于系统。
C 见下
c 符号是普通的。 通用符号是未初始化的数据。 在链接时,多个公共符号可能会出现相同的名称。 如果该符号在任何地方被定义,公共符号会被当作未定义的引用。当符号位于小公文的特殊部分时,小写的c字符被使用。
D 见下
d 符号在初始化数据部分。
G 见下
g 符号在小对象的初始化数据部分。一些对象文件格式允许更有效地访问小的数据对象,例如一个全局int变量,而不是一个大的全局数组。
i 对于PE格式的文件,这表示该符号在一个特定的DLLs实现部分。对于ELF格式文件,这表示该符号是一个间接函数。 这是一个GNU对符号类型的GNU扩展。 它表示一个符号,如果被重定位引用,不会评估到其地址,而是必须在运行时调用。 运行时的执行将返回用于重定位的值在重定位中使用的值。注意 - GNU间接符号的实际符号显示由 –ifunc-chars 命令行选项控制。如果提供了这个选项,那么字符串中的第一个字符将被用于全局间接函数符号。 如果字符串中包含第二个字符,那么它将被用于本地间接函数符号。
I 符号是对另一个符号的间接引用。
N 该符号是一个调试符号。
n 符号在只读数据部分。
p 该符号在堆栈解压部分。
R 见下
r 符号在只读数据部分。
S 见下
s 符号在未初始化或零初始化的小对象的数据段中。
T 见下
t 符号在文本(代码)部分。
U 符号是未定义的。
u 该符号是一个唯一的全局符号。 这是一个GNU对ELF符号绑定标准集的扩展。 对于符号,动态链接器将确保在整个过程中,只有一个具有此名称和类型的符号在使用。和类型的符号在使用。
V 见下
v 符号是一个弱对象。 当一个弱定义的符号与一个正常定义的符号连接时,正常定义的符号被使用,没有任何错误。 当一个弱的未定义的符号被链接并且该符号未被定义时,弱的符号的值会变成零,并且没有错误。符号的值变成了零,没有任何错误。 在某些系统中,大写字母表示默认值已被指定。
W 见下
w 该符号是一个弱符号,没有被特别标记为弱对象符号。 当一个弱定义的符号与正常定义的符号相连接时,正常定义的符号被使用,没有任何错误。 当一个弱的符号被链接,并且该符号没有被定义,那么该符号的值将以系统特定的方式确定,而不会出现错误。符号的值以系统特定的方式确定,没有错误。 在某些系统上,大写字母表示已经指定了一个默认值。
- 符号是a.out对象文件中的stabs符号。 在这种情况下,接下来打印的值是stabs other字段,stabs desc字段,以及stab类型。 stabs符号是用来保存调试信息的。
? 符号类型未知,或对象文件格式特定。

我们也可以用nm -D login只打印出动态链接的符号:

文内图片

我们也可以向使用file一样使用通配符批量解析文件

更多选项见nm --helpman nm

c++filt

这个命令可以协同nm命令使用。因为C++中有模板、重载等,因为要唯一标志一个同名函数,所以编译出来的函数名会非常奇怪,难以阅读

我们可以使用c++filt来重命名这些函数

注意,C++filt的重命名有可能因程序使用的编译器不同而失败,也可能丢失一些信息

nm login | c++filt

文内图片

我们可以看到,这时候函数名就类似于编写C++程序时使用的函数名了

我们还可以更改这个呈现函数的形式:

文内图片

nm login | c++filt -s java

文内图片

ldd

这个命令很常用,是用来查看动态链接程序所使用的动态库的:

文内图片

可以加上-v来查看更多信息

ldd命令可以在我们对一个程序使用patchelf命令更换动态库的时候提供向导

objdump

使用线性反汇编对程序进行静态分析

我们可以查看程序的头部(起到和file命令类似的作用):

查看file header的总览:

文内图片

查看file header的简化版本:

文内图片

显示特定对象格式的文件头内容:

文内图片

也可以单独查看节头:

文内图片

可以直接查看所有header:

文内图片

可以查看程序的调试信息:

文内图片

可以不加分析地查看所有节头的二进制信息:

文内图片

可以只查看可执行节的反汇编结果:

这样的文本文件叫做反汇编死代码清单(dead listing),尽管这些文件可用于实施逆向工程,但它们很难有效导航,也无法以一致且无错的方式被修改。

文内图片

但是它的反汇编结果是AT&T风格的,我们可以更换为Intel风格:

文内图片

还有更多用法可以查看objdump --helpman objdump

strings

strings可以搜索至少包含 4 个连续可打印 ASCII 字符的字符串,并将结果在控制台打印出来(程序虽然包含这样的字符串,但是并不代表程序会使用),有一些字符串是程序实际使用的,有一些是程序的函数名等

文内图片

我们可以只打印.data节中的字符串:

文内图片

我们可以指定包含多少个连续可打印字符的字符串会被输出:

文内图片

readelf

readelf与objdump大体相同,主要区别在于 readelf 并不依赖 libbfd

readelf -S  查询节头表
        -l  查询程序头表
        -s  查询符号表
        -e  查询ELF文件头数据
        -r  查询重定位入口
        -d  查询动态段

对ELF的具体解读请阅读我写的ELF详解

ndisasm

ndisasm是Netwide Assembler(NASM)提供的流式反汇编器

我们选择架构为64位intel指令集,并将反汇编结果输入到一个文件中:

文内图片

文内图片

由于流式反汇编非常灵活,因此它的用途相当广泛。例如,在分析网络数据包中可能包含shellcode 的计算机网络攻击时,就可以采用流式反汇编器来反汇编数据包中包含 shellcode 的部分,以分析恶意负载的行为。另外一种情况是分析那些不包含布局参考的 ROM 镜像。ROM 中有些部分是数据,其他部分则为代码,可以使用流式反汇编器来反汇编镜像中的代码

objcopy

objcopy用于将object的部分获全部内容拷贝到另一个object,这里的object通常是可执行文件

我们可以将调试信息分离:

文内图片

可以剥离调试信息:

文内图片

注意,这里只是剥离了调试信息,并不代表符号表被剥离了:

文内图片

文内图片

可以将调试信息加回去:

文内图片

文内图片

更多阅读:

strip

我们在做ctf题目的过程中,常常会有一些善良的出题者将符号表删除,导致ida解析出来的函数列表没有名字,极其难懂

那么,我们要如何做到这一点呢?答案是使用strip命令:

strip <程序名>

文内图片

然后就只剩下.dynsym了:

文内图片

这样也不影响程序运行,达到了给程序瘦身的目的,只是不利于反编译🤭

size

用于列出目标文件或库文件的section大小

文内图片