reverse engineering: 综述

2024-09-01

1. 什么是逆向工程

在讲逆向工程前,先说说正向工程,正向工程指的是你编写一个程序的过程,包括确定要写什么程序,开始写程序,编译程序,执行程序
在这个过程中存在信息损失

在design和code之间,你或许会忘记是谁写的程序

在compile和assemble之间,会丢失代码注释、变量名称、函数名称、结构体数据、有时还会丢失这个算法(optimization)

把正向工程中丢失的信息,通过人的一些努力工作,还原信息,这个过程就叫做逆向工程
这项技术的核心是:how do we reverse the design from the binary(我们如何从二进制逆向出设计思想)

2. 函数和栈帧

2.1 什么是程序

程序可以按照如下方式进行结构:

  • 1.一个程序(program)由很多个模块(module)组成
  • 2.一个模块(mudole)由很多个函数(functionality)组成
  • 3.一个函数(functionality)包含了很多块(block)
  • 4.块中有很多条指令(instruction)
  • 5.指令(instruction)主要用来操作变量(variables)和数据结构(data structures)

在逆向过程中,module通常是以lib的形式呈现,即动态库和静态库,开发人员极大程度的依靠库函数。
在逆向时需要关注库的精细手册,在逆向时把库函数排除在外,专心逆向主要逻辑


函数就是指实现好的某种功能,通常每个函数的实现都有明确的目的
例如获取一些数据,分发功能,etc.
一开始,函数可以单独逆向,之后就可以了解函数们是如何被组合的


函数可以通过graph(图)表示,graph由很多block(块)和很多条edge(边)构成
block就是计算机执行的指令(instruction)的集合,block由edge连接

2.2 栈帧

每个function序言和结语的指令都很类似:
Set up the stack frame.
Tear down the stack frame
开辟栈帧的主要目的是在栈中给每个函数开辟一段空间用于存放局部变量!(但不是必须的)
在一个elf文件中,有3个段被用来存放数据:

.data: 
used for pre-initialized global writable data (such as global arrays with initial values)

.rodata: 
used for global read-only data (such as string constants)

.bss: 
used for uninitialized global writable data (such as global arrays without initial values)

但是这3个段都被用来存放全局变量,对于局部变量,一般都放在栈中,栈是一段用来存储局部变量和函数调用上下文的空间,采用后进先出的方式(即后向增长)
顺道一提,程序的环境变量也是存放在栈中的

gcc编译时,可以通过-fomit-frame-pointer选项取消栈帧的使用

2.3 数据

总结一下,一个程序(program)中用来存放数据的空间有5处:

.data: 
used for pre-initialized global writable data (such as global arrays with initial values)

.rodata: 
used for global read-only data (such as string constants)

.bss: 
used for uninitialized global writable data (such as global arrays without initial values)

stack: 
used for statically-allocated local variables

heap: used for dynamically-allocated (malloc()ed) variables. 

3. 静态分析工具

Static (as opposed to dynamic) reverse engineering: analyzing a program at rest
静态分析工具里有很多好用东西:

kaitai struct (https://ide.kaitai.io/): file format parser and explorer

nm: lists symbols used/provided by ELF files

strings: dumps ASCII (and other format) strings found in a file

objdump: simple disassembler

checksec (https://github.com/slimm609/checksec.sh): analyzes security features used by an executable

当然,上述只能看到二进制文件的某些内容,要想分析程序逻辑,还需要反汇编器!

Commercial:
IDA Pro: the "gold standard" of disassemblers (https://www.hex-rays.com/products/ida/)
Binary Ninja: IDA's main commercial competitor (https://binary.ninja/)

Free:
Binary Ninja Cloud: a version of Binary Ninja that runs in your browser! (https://cloud.binary.ninja/) 

Open source:
angr management: an academic binary analysis framework! (https://github.com/angr/angr-management/releases) 
ghidra: a reversing tool created by the National Security Agency (https://ghidra-sre.org/) 
cutter: a reversing tool created by the radare2 open source project (https://cutter.re/) 

4. 动态分析工具

Dynamic (as opposed to static) reverse engineering: analyzing a program at runtime.
动态分析工具就是在程序运行时分析它的逻辑。
常见的动态分析工具有

ltrace :traces library calls
strace :traces system calls

Running the program with multiple different inputs might get you farther.
ltrace the program with input A
ltrace the program again with input B
see if you can reverse the algorithm from looking at input and output
still does not scale to complex algorithms

前面两个用来分析简单的算法还是没问题的,复杂的就难了,这时候就要请出我们的高手,gdb!

gdb: 调试工具!

另外还有一个概念叫做Timeless Debugging

Timeless debugging frees you from having to think of breakpoints ahead of time.

1. record execution
2. rewind execution
3. replay execution

相关工具如下:

  1. gdb has built-in record-replay functionality 其网址为https://sourceware.org/gdb/current/onlinedocs/gdb/Process-Record-and-Replay.html
  2. rr is a highly-performant record-replay engine https://github.com/mozilla/rr
  3. qira is a timeless debugger made for reverse engineering https://qira.me/

5. 逆向工程的实际应用

逆向工程在现实中比较多的应用有两个方面:keygen和modding

5.1 keygen

  1. 上古时期,所有的应用都必须能不通过互联网进行安装,这就表明所有运行程序所需要的组件都被安装在本地
    软件开发商为了保证交过钱的用户才能使用它们的软件,通常组件里都有一个叫做licence checker的东西用来校验用户是否交过钱
    但是由于licence checker的逻辑都在本地,所以能够通过逆向其逻辑还原校验逻辑,从而破解软件,导致不交钱也能用!

  2. 进入石器时代,自从有了哈希算法之后,Licence checker中通常比对的都是哈希值,但是哈希是不可逆的,怎么办呢?修改二进制程序变成了好的选择,直接patch哈希校验逻辑,让验证通过,还不用还原整个licence checker的逻辑,赢!

  3. 进入青铜时代,为了抵抗二进制patch,出现了代码混淆obfuscated code,核心思想是让你看不懂代码,以至于不知道patch哪里,经典的混淆方式就是VM(virtual machine)了.虚拟机核心的思想就是模拟cpu的指令执行步骤,要想破译需要花心思看懂其含义,要想逆好VM,只能靠耐心不断练习和熟悉了

  4. 现代,软件开发商通常通过互联网来进行校验(即licence checker的逻辑在服务器,正常情况无法还原其逻辑)
    现在的解决办法如下:
1. 打穿服务器
2. patch本地程序,移除所有的check逻辑并保证程序正常运行
3. 花钱

对于方法二而言,软件开发商为了抵抗,研究了很多保护机制:

1. Anti-debugging: 
techniques to fight dynamic analysis

2. VMing: 
wrapping the DRM(digital rights management) code in a heavily obfuscated, protected emulator for a custom architecture, with the DRM logic inside that architecture.

3. Trusted Execution Environments: 
moving the DRM outside of the CPU into protected hardware.

这些保护措施真的带来了很大的逆向难度!

5.2 modding

玩过steam上的游戏的人都知道,游戏有很多mod可以用,这也是逆向工程的功劳。
最开始大家都通过ce engine来改游戏里的数据(比如等级,攻击力,etc),来增强实力。
后来游戏产商有意识的保护了这些关键数据,大家就开始打mod,即修改/添加游戏的代码!更有甚者,有人在网络游戏了也进行作弊!
这里可能有人好奇,对抗keygen的方法不能应用在游戏里吗?
答案是可以的,代价是惨烈的:那些方法会导致程序的性能开销显著提高(可能是原本应用的好几倍),对于普通应用倒也罢了,对于游戏而言,就是明显提高游戏延迟,造成游戏卡顿!
而且现在大型游戏往往本来所需资源就很多,再加个几倍还让不让人玩了😀所以游戏的保护往往会比较弱,如何用较少的资源来有效率得提高游戏安全性就是当下的艺术
想要做好逆向不容易,需要稳扎稳打的实战!(倒不如说,做安全还是实战更重要)