Linux中常用的分析文件的小工具
Sunday, January 22, 2023
本文共3632字
8分钟阅读时长
⚠️本文是作者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
我们对一个文件使用该命令看一下:
我们能够看到,这个文件是一个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 --help
或man nm
c++filt
这个命令可以协同nm命令使用。因为C++中有模板、重载等,因为要唯一标志一个同名函数,所以编译出来的函数名会非常奇怪,难以阅读
我们可以使用c++filt来重命名这些函数
注意,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 --help
或man 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命令:
然后就只剩下.dynsym了:
这样也不影响程序运行,达到了给程序瘦身的目的,只是不利于反编译🤭
size
用于列出目标文件或库文件的section大小
扫码阅读此文章
点击按钮复制分享信息
点击订阅