逆向工程方法论

什么是软件逆向工程?

逆向工程是揭示硬件或软件背后原理的过程,例如其体系结构和内部结构。推动逆向工程的发展是软件是如何工作的?

显然,如果你有文档,整个过程就会变得简单得多。但是经常发生的情况是没有文档,你需要找到另一种方法来了解软件的工作原理。

逆向工程在计算机科学领域有很多用途,包括:

  • 网络通信协议研究

  • 查找恶意软件中使用的算法,如计算机病毒、木马、勒索软件等

  • 研究用于存储任何类型信息的文件格式,例如电子邮件数据库和磁盘映像

  • 检验自己的软件反逆向工程的能力

  • 提高与平台和第三方软件的兼容性

  • 使用无文档记录的平台特性(像Windows里有很多未记录且功能强大的API)


逆向工程需要什么?

要启动逆向工程软件,你需要:

  1. 逆向工程领域的知识

  2. 应用你的知识反汇编软件。

让我们考虑一个与软件无关的通用示例。假设你有一块手表,你想知道它是机械表、石英表还是自动表。

拥有该领域的知识意味着你应该知道手表分为三种类型。此外,你应该知道,如果有电池,它位于手表内部,打开它就可以看到。你还应该对手表的内部结构、电池的外观以及打开手表外壳需要用什么工具有基本的了解。拥有应用知识的工具意味着你需要有一把螺丝刀或其他专用工具来让你有机会打开手表。

就像逆向工程一样,手表需要特定的技能和工具,逆向工程也需要它自己的领域特定的知识和工具。

理论知识—软件逆向过程

对于不同的逆向工作,你需要不同类型的知识。当然,有一些常识可以帮助你完成大多数逆向工作:常见应用程序结构、编程语言、编译器等方面知识。但是,如果没有专门的理论知识,就无法解决特定的逆向工作。

在逆向刚开始时,通常使用反汇编程序来找到合适的算法和程序逻辑。因为有许多不同的可执行文件格式、编译器(提供不同的输出)和操作系统。这种技术的多样性排除了使用单一技术来逆向所有类型软件的可能性。

要理解反汇编后的代码,你需要了解一些汇编语言、函数调用约定、栈结构、栈帧概念等知识。

了解不同的汇编器输出可以帮助我们更快的打破僵局。例如以下Windows x86平台的逆向工程示例:

假设我们有以下代码:

int count = 0;
for (int i = 0; i < 10; ++i)
{
count++;
}
std::cout << count;

如果我们将此代码编译为可执行文件,我们将在反汇编程序中看到:

004113DE loc_4113DE:
004113DE     mov     eax, [ebp-14h]
004113E1     add     eax, 1
004113E4     mov     [ebp-14h], eax
004113E7 loc_4113E7:
004113E7     cmp     [ebp-14h], 0Ah
004113EB     jge     short loc_4113F8
004113ED     mov     eax, [ebp-8]
004113F0     add     eax, 1
004113F3     mov     [ebp-8], eax
004113F6     jmp     short loc_4113DE
004113F8 loc_4113F8:
004113F8     mov     ecx, ds:?cout@std
004113FE     push   eax
00411400     call   ds:basic_ostream@operator<<(int)
00411404     xor     eax, eax
00411406     retn

正如我们所见,常规循环变成了带有比较和跳转的汇编代码。请注意,这里的常规的汇编循环代码没有使用常规的ECX寄存器来进行计数。另外,这里的局部变量被相应地称为[ebp-14h]和[ebp-8]。

让我们看看如果我们使用发布版本(release)编译这段代码会发生什么:

00401000 main     proc near
00401000     mov     ecx, ds:?cout@std
00401006     push   0Ah
00401008     call   ds:basic_ostream@operator<<(int)
0040100E     xor     eax, eax
00401010     retn
00401010 main     endp

这段代码与前面的代码完全不同。这是因为代码的优化方式。从技术上讲,这个循环被删除了,因为它除了将count变量加1到10之外,没有做任何有价值的事情。因此优化器决定只保留count变量的最终值,并将该值直接作为count输出操作符的参数。

我们现在使用的编译器非常擅长优化代码。这就是为什么在进行逆向工程时,最好理解代码背后的思想(代码的生成原理),而不是尝试获取原始代码本身。


对 Windows 软件进行逆向的有用工具

反汇编程序

反汇编程序是将可执行文件翻译成汇编语言的程序。毫无疑问,最流行的是IDA Pro

IDA Pro 是一款方便而强大的反汇编工具。它有大量的组件可以让你快速反汇编一个软件。它可以显示函数调用树,解析可执行文件的导入导出,并显示有关它们的信息。它甚至可以用c语言显示代码。此外,它支持多种 CPU 架构,因此可以使用 IDA Pro 对 ARM、AVR、M68k 和许多其他架构的代码进行逆向工程。

Windows Sysinternals

Windows Sysinternals实用程序通常用于管理、诊断、故障排除和监视 Microsoft Windows 环境。但它们也适用于对 Windows 软件进行逆向工程。

TCPView 是一个网络嗅探器,它显示有关来自所有进程的 TCP/UDP 数据包的所有信息。该工具可用于逆向网络协议。

PortMon 是一个物理系统端口监视器。它监视串行和并行端口以及通过它们的所有流量。

WinObj 以层次结构显示系统中的所有全局对象。在逆向使用同步原语(如互斥锁和信号量)的应用程序以及逆向内核模式的驱动程序时,此工具非常有用。

网络监控工具

Wireshark

Wireshark是最强大的网络嗅探器之一。它不仅允许你捕获网络流量,而且还包含各种网络协议的解析器,从非常底层的以太网、TCP和IP到特定于应用程序的协议,如WebSockets和XMPP

Fiddler

Fiddler是一个 Web 代理,它记录来自浏览器的流量并允许你分析 HTTP/HTTPS 请求。与 Wireshark 不同,它显示 HTTP 会话而不是单独的网络数据包。Fiddler 还允许你分析通过 HTTP 发送的压缩数据,并在监视 SOAP、REST 和 AJAX 请求时分析 JSON 和 XML 数据。

API监控器

API Monitor

API Monitor是一个很棒的工具,用于发现应用程序调用了哪些 API 以及应用程序期望从这些 API 获得什么行为。这个工具有一个强大的数据库,可以让你看到对大量 API 函数的调用,不仅是kernel32ntdll,还有COM托管环境和其他API函数。此外,API Monitor 还提供了方便的过滤机制。

调试器

对于任何开发人员来说,调试器都是了解程序当前正在做什么的宝贵工具。在对应用程序进行逆向调试时,你可以从调试实时应用程序中获得在静态分析时看不见的东西。

最流行的调试器是 OllyDbg/x64dbg、WinDbg 和 Windbg Preview。、

OllyDbg/x64dbg:

OllyDbg及其继任者x64dbg可能是软件逆向工程中最好的调试器。它们是专门为逆向的需要而开发的,并具有实现该目的所需的所有工具组件,更重要的优势是这两款调试器有非常多的人开发插件,具备一定的反反调试能力,而一般的调试器要显得无力些

WinDbg:

尽管界面简单,但WinDbg具有强大的调试工具。它有一个内置的反汇编程序、各种命令,可以让你了解有关正在调试的进程/系统的几乎所有信息,以及进行内核模式调试的能力(这可能是最有价值的功能)。对于逆向驱动程序,尤其是内核模式驱动程序来说这是一个很大的优势。

Windbg Preview:

Windbg Preview是 Microsoft 开发的新版本的 Windbg。它仅通过 Windows 应用商店下载。它具有经典 Windbg 的所有功能以及新的 UI 和几个新功能。其中一项新功能是 时间旅行调试(Time Travel Debugging),它允许你记录程序执行的某些时期,然后根据需要多次重播。这样,你可以通过单步执行代码中有趣的部分,而不必担心不小心运行某些代码而丢失上下文或所有数据。


现实生活中的软件逆向工程示例

假设你有一个可疑的可执行文件。你需要了解该程序的作用以及它对用户是否安全。

考虑到这种情况,最好不要在工作计算机上运行此可执行文件,而是使用虚拟机。让我们在虚拟机中启动应用程序。

可以看到,这个文件创建了一个名为TestDriver的Windows服务。它的类型是kernel,所以我们知道它是一个驱动程序。但是它从哪里获取驱动文件来运行呢?我们可以使用Sysinternals套件中的ProcessMonitor来找到答案。当我们打开ProcessMonitor时,我们可以设置过滤器,只向我们显示我们感兴趣的进程的文件活动。它的活动日志如下所示:

驱动文件是由我们正在逆向的进程创建的,这个进程将这个文件放在用户的临时目录中。无需在临时文件夹中查找该文件,因为我们看到该进程会在使用后立即将其删除。那么进程对这个文件做了什么?如果它是解压文件,我们可以尝试在进程的资源部分找到它,因为这是存储此类数据的常见位置。让我们看看,我们使用另一种工具Resource Hacker来检查资源:

很好!从找到的资源内容可以看出,这很可能是Windows可执行文件,因为它以MZ签名开头,并带有字符串“This program cannot be run in DOS mode”。让我们检查它是否是我们的驱动程序文件。为此,我们使用 Resource Hacker 提取资源并在反汇编程序中打开它。

OK,毋庸置疑,DriverEntry是 Windows 系统中驱动程序的入口点。我们可以继续往前推进。

开始对驱动程序进行逆向工程,我们逐个检查从 DriverEntry 调用的函数

为了简单起见,这里省略了其他一些行。

可以看到:

在第一个代码片段中,创建了一个 unicode 字符串,该字符串指向路径 C:hello.txt。之后,结构体OBJECT_ATTRIBUTES被填充为常规值;我们知道在调用像ZwCreateFile这样的函数时经常需要这个结构。

在第二个代码片段中,我们看到ZwCreateFile确实被调用了,这使我们非常确定驱动程序创建了该文件——并且我们知道该文件创建后的位置。

从第三个和第四个代码片段中,我们可以看到驱动程序获取 unicode 字符串并将其写入缓冲区(这发生在 sub_11150 函数中),之后再使用ZwWriteFile函数写入文件。最后,驱动程序使用ZwClose API 关闭文件。

让我们总结一下。我们发现原始程序从其资源中提取驱动程序文件,将其放在当前用户的temp文件夹中,为该驱动程序创建Windows服务,并运行它。之后,程序停止并从临时目录中删除服务和原始驱动文件。从这种行为和对反汇编的分析来看,驱动程序除了在C驱动器上创建一个名为Hello .txt的文件并写入字符串" Hello From driver "之外,似乎什么也没做。

现在我们需要检查我们的推理是否正确。让我们运行程序并检查C盘:

漂亮! 我们对这个简单的计算机程序进行了逆向工程,现在我们知道它是良民,可以安全使用。

我们还可以用许多不同的方法来实现这个结果——使用调试器或API Mon等等。你可以找到更适合自己的方法来逆向。

最后

Windows软件逆向工程需要扎实的编程知识和逆向经验。为了进行逆向工程,你需要结合反汇编、网络监控、调试器、API集成、多种编程语言、编译器等方面的技能。此外,在逆向软件你要意识到自己正在做一件什么样的事,不要让版权法爆锤你的狗头!不要以为版权法开玩笑的,最好在得到授权的情况下,不要有事没事去逆向别人的加密的产品,或者翻录贩卖别人的视频,真的可耻



请使用浏览器的分享功能分享到微信等