原文:http://www.ouah.org/RevEng/
Linux中逆向工程软件简介
(本文为以上链接的前4个章节的自动翻译,只有获取基本信息的工具介绍,详细及更多内容请参看原文)
本文档试图介绍Linux中的逆向工程软件. 由于逆向工程正在迅速受到法律攻击, 因此作者认为最好的反应是使知识广泛传播.
我们的想法是, 由于讨论特定的逆向工程专长是非法的, 我们应该讨论一般方法, 以便不是下载裂缝或描述程序的弱点 (是的, 两者现在都是非法的), 每个Linux用户都有能力制作它们. 此外, 封闭源程序让我感到沮丧. 抵抗是徒劳的. 你将是开源的.
目录
1.介绍
1.1. 什么是逆向工程?
1.2. 为什么逆向工程?
2. Linux编译过程
2.1. 介绍
2.2. GCC
2.3. gcc -E (预处理器阶段)
2.4. gcc -S (解析+翻译阶段)
2.5. 作为 (装配阶段)
2.6. ld / collect2 (链接阶段)
3.收集信息工具
3.1 ldd
3.2 nm
3.3 /proc
3.4 netstat, 已被ss取代
3.5 lsof
3.6 fuser
4.确定程序行为
4.1. strace/truss (Solaris)
4.2. ltrace
4.3. LD_PRELOAD
4.4. GDB
5.原文章节链接
1.介绍
1.1. 什么是逆向工程?
反向工程, 因为本文档将讨论它只是确定你没有源代码的软件的行为.
1.2. 为什么逆向工程?
答:因为你可以.
您系统上没有源代码的软件通常是最有趣的软件.
有时您可能正在寻找安全漏洞,
有时候您很好奇复制保护是如何工作的等等.
我不了解您,
但对我而言,
我没有源代码的软件只会让我生气.
所以我想:拧紧它,
让我们做一些伤害.
此外,
它使你成为一个更好的程序员.
本书将教你大量关于你的计算机如何在低级别工作,
并且你对它的理解越好,
你可以编写的效率更高的程序.
如果您不懂汇编语言,
那么在本书的最后,
您将从内到外逐字地了解它.
虽然大多数关于汇编语言的第一期课程和书籍都教会您如何将它用作编程语言,
但您将了解如何使用C作为汇编语言生成工具,
以及如何将汇编视为C程序.
这不仅在编程能力方面,
而且在你弄清楚黑盒子如何工作的能力方面,
使你在同行方面具有巨大的优势.
简而言之,
通过这种方式学习将使您成为更好的逆向工程师.
另外,
你会有一个很好的区别,
能够回答“谁教你汇编语言?”的问题.
用“为什么,
我的C编译器,
当然!”
2. Linux编译过程
2.1. 介绍
编译一般分为大约5个阶段:预处理, 解析, 翻译, 汇编和链接. 所有5个阶段都由UNIX中的一个程序实现, 即cc, 或者在我们的例子中是gcc. 事情的一般顺序是gcc -> gcc -E -> gcc -S -> as -> ld.
2.2. GCC
gcc是大多数UNIX的首选C编译器. 程序gcc本身实际上只是一个前端, 它执行与编译过程中每个阶段相对应的各种其他程序. 要让它打印出每步执行的命令, 请使用gcc -v.
2.3. gcc -E (预处理器阶段)
gcc -E只运行预处理器阶段. 这会将所有包含文件放入.c文件中, 并将所有宏转换为内联C代码.
2.4. gcc -S (解析+翻译阶段)
gcc
-S将.c文件作为AT&T语法的输入和输出.s汇编文件.
可以使用各种优化选项调用gcc,
这些选项可以对输出的汇编代码执行有趣的操作.
可以使用-ON指定4到7个通用优化类,
其中0
<= N <= 6. 0不是优化
(默认),
6是最大值.
还有一些使用-f标志指定的细粒度程序集选项.
最有趣的是-funroll-loops,
-finline-functions和-fomit-frame-pointer.
循环展开意味着扩展循环,
以便循环的n次迭代有n个代码副本
(即没有循环顶部的jmp语句).
在现代处理器上,
这种优化可以忽略不计.
内联函数意味着有效地将文件中的所有函数转换为宏,
并将其代码的副本直接放在调用函数中
(如C
++内联关键字).
这仅适用于与其定义在同一C文件中调用的函数.
这也是一个相对较小的优化.
省略帧指针
(也称为基指针)可以释放一个额外的寄存器,
以便在程序中使用.
如果你有超过4个使用频繁的局部变量,
这可能是相当大的优势,
否则它只是一个麻烦
(并使调试更加困难).
由于其中一些在默认情况下在较高优化级中处于启用状态,
因此尽管手册页没有明确提及它,
但是所有-f选项都具有-fno等价物,
这一点很有用.
因此,
无论-O选项如何,
-fnoinline函数都会阻止函数内联.
(我认为它默认发生在-O3).
2.5. 作为 (装配阶段)
就像GNU汇编程序一样. 它将输入作为AT&T语法asm文件并生成.o对象文件.
2.6. ld / collect2 (链接阶段)
ld是GNU链接器. 它将生成一个有效的可执行文件. 如果链接到共享库, 则需要实际使用gcc调用的内容, 即collect2. 观看gcc -v标志
3.收集信息工具
3.1 ldd
ldd是一个基本实用程序, 它向我们展示程序链接的库, 或者它是静态链接的. 它还为我们提供了这些库映射到程序执行空间的地址, 这对于在反汇编输出中跟随函数调用很方便 (我们将在稍后介绍).
3.2 nm
nm列出了二进制文件中的所有本地和库函数, 全局变量及其地址. 但是, 它不适用于已剥离条带的二进制文件.
3.3 /proc
Linux
/proc文件系统包含各种有趣的信息,
从代码库和代码的其他部分映射到哪里,
文件和套接字在哪里打开.
/proc文件系统包含每个当前正在运行的进程的目录.
因此,
如果您启动了一个pid为3137的进程,
您可以进入目录/proc/3137/以查找当前正在运行的进程的几乎所有内容.
您只能查看您拥有的进程的进程信息.
此目录中的文件随每个操作系统而变化.
Linux中有趣的是:cmdline
- 列出传递给进程cwd的命令行参数
-
指向进程环境的当前工作目录的链接
-
进程exe的环境变量列表
-
链接到进程可执行文件fd
- 进程映射使用的文件描述符列表
-
非常有用.
列出此过程使用的内存位置.
这些可以直接用gdb查看,
找出各种有用的东西.
此目录中的文件随每个操作系统而变化.
Linux中有趣的是:
cmdline
-- lists the command line parameters passed to the process
cwd
-- a link to the current working directory of the process
environ
-- a list of the environment variables for the process
exe
-- the link to the process executable
fd -- a list of the
file descriptors being used by the process
maps -- VERY
USEFUL. Lists the memory locations in use by this process.
These
can be viewed directly with gdb to find out various useful things.
3.4 netstat, 已被ss取代
它用于显示网络连接, 路由表, 接口统计信息等.
3.5 lsof
lsof是一个程序, 它列出系统上运行的进程的所有打开文件.
3.6 fuser
与lsof密切相关的命令是fuser. fuser接受文件或套接字的名称作为命令行参数. 它将返回访问该文件或套接字的进程的pid.
4.确定程序行为
有一些工具可以让我们在更接近的层面上研究程序行为. 让我们看看其中的一些:
4.1. strace/truss (Solaris)
这些程序跟踪程序在制作程序时所进行的系统调用.
有用的选择:
-f
(跟叉)
-ffo
filename (输出跟踪到filename.pid
forforking)
-i (每个系统调用的打印指令指针)
4.2. ltrace
该实用程序非常有用.
它跟踪程序产生的所有库调用.
有用的选择:
-S
(显示系统调用)
-f
(跟叉)
-o
filename (输出跟踪到文件名)
-C
(demangle C ++函数调用名称)
-n
2 (缩进每个嵌套调用2个空格)
-i
(打印调用者的指令指针)
-p
pid (附加到指定的pid)
4.3. LD_PRELOAD
这是一个环境变量,
允许我们将库添加到特定程序的执行中.
此库中的任何函数都会自动覆盖标准库函数.
对不起,
你不能在suid程序中使用它.
例:
%
gcc -o preload.so -shared preload.c -ldl
% LD_PRELOAD=preload.so
ssh students.uiuc.edu
4.4. GDB
gdb是GNU调试器.
对大多数人来说这是非常令人生畏的,
但实际上没有理由这样做.
对于命令行调试器来说,
它做得非常好.
有一个很好的GUI前端称为
DDD,
但我们的目的是需要与命令行建立更密切的关系.
gdb有一个很好的内置帮助系统按主题组织.
打字帮助会告诉你catagories.
我们感兴趣的主要命令是run,
break, cont, stepi, finish, disassemble, bt, info [registers /
frame]和x.
gdb中的每个命令后跟一个数字N,
表示重复N次.
例如,
stepi 1000将跨越1000个组装指令.
-
>使用gdb在带有和不带调试符号的函数中设置断点的示例.
5.原文章节链接
Table of Contents1. Introdution
2. The Linux Compilation Process
- 2.1. Intro
- 2.2. gcc
- 2.3. gcc -E (Preprocessor Stage)
- 2.4. gcc -S (Parsing+Translation Stages)
- 2.5. as (Assembly Stage)
- 2.6. ld/collect2 (Linking Stage)
4. Determining Program Behavior
- 4.1. strace/truss(Solaris)
- 4.2. ltrace
- 4.3. LD_PRELOAD
- 4.4. gdb
5. Determining Interesting Functions
- 5.1. Reconstructing function & control information
- 5.2. Consider the objective
- 5.3. Finding key functions
- 5.4. Plotting out program flow
- 6.1. Registers
- 6.2. The stack
- 6.3. Two's complement
- 6.4. Reading Assembly
- 6.5. Know Your Compiler
7. Writing Standalone Assembly
- 7.1. Instructions with side-effects
- 7.2. Opcode Tables
- 7.3. Using GNU as
- 7.4. Conventions on saving registers
- 7.5. Using Library Functions
8. Working with the ELF Program Format
- 8.1. ELF Layout
- 8.2. Editing ELF
9. Understanding Copy Protection10. Code Modification
- 10.1. Reasons for Code Modification
- 10.2. Instruction Modification
- 10.3. Single Instruction Insertion
- 10.4. Single Function Insertion
- 10.5. Multiple Function Insertion
- 10.6. Attacking copy protection
11. Buffer Overflows
- 11.1. Stack Overflows
- 11.2. 1-Byte Overflows
- 11.3. Returning to Libc
- 11.4. Attacking Countermeasures
- 11.5. Heap Overflows
- 11.6. Attacking hard copy protection
- 12.1. Write assembly tutorial section
- 12.2. Create Diagrams & example outputs
- 12.3. More detail
- 12.4. Update disasm.pl
- 12.5. Do this for windows
- 12.6. Do this for protocols
- 12.7. Do this for hardware
13. Extra Resources