4

我目前正在尝试用 C++ 编写一个多态引擎来玩弄我有一个简洁的反黑客存活检查想法。然而,编写多态引擎被证明是相当困难的——我什至还没有确定我应该如何去做。这个想法是将可执行代码流式传输给用户(即我正在保护的应用程序),并偶尔向他们发送一些代码,这些代码在内存映像上运行一些校验和并将其返回给服务器。问题是我不希望有人简单地劫持或以编程方式破解存活检查;相反,每一个都将在服务器上生成一个简单的代码存根,并通过多态引擎运行它。每个存活检查都将返回一个值,该值取决于数据的校验和以及潜入存活检查内部的随机算法。

我必须处理的内容:

*可执行图像 PDB 文件。*汇编器和反汇编器引擎,我在它们之间实现了一个接口,允许重新定位代码等......

以下是我正在考虑执行的步骤以及如何执行这些步骤。我在 Windows PE 可执行文件上使用 x86 指令集

我计划采取的步骤(我的问题是第 2 步):

  1. 展开说明

    • 找到像 mov 这样的简单指令,或者将它们推送并用几个指令替换它们,尽管有更多指令,但它们可以达到相同的目的。在这一步中,我还将添加大量垃圾代码。
    • 我计划通过在数据库中使用一系列翻译表来做到这一点。这应该不是很难做到的。
  2. 洗牌

    • 这是我最麻烦的部分。我需要将代码隔离到函数中。然后我需要建立一系列指令依赖树,然后我需要根据一个依赖另一个来重新定位它们。我可以通过解析 pdb 文件找到函数,但是创建指令依赖树是我完全迷失的棘手部分。
  3. 压缩指令

    • 压缩指令并在过程中执行一系列不常见和晦涩的指令。并且,像第一步一样,通过使用代码签名数据库来做到这一点。

再次澄清一下,我需要帮助执行第 2 步,我什至不确定我应该如何开始。我曾尝试制作一些图表,但它们变得非常难以理解。

OH:很明显,受保护的代码不会是最佳的——但这只是我想在学校玩的一个安全项目。

4

3 回答 3

2

我认为您对“指令依赖树”的追求是数据流分析。这是经典的编译器技术,它为每个代码元素(编程语言中的原始操作)确定从其他代码元素传递给它的信息。完成后,您最终会得到一个图形,其中节点是代码元素(在您的情况下是单独的指令),它们之间的有向弧线显示了必须流动的信息,以便后面的元素可以执行“较早”的元素。

您可以在我的网站上看到此类流分析的一些示例(专注于进行程序分析的工具;该工具可能不适合二进制分析,但这些示例应该会有所帮助)。

编译器书籍中有大量关于进行数据流分析的文献。请参阅任何编译器教科书。

您需要处理许多问题:

  • 解析代码以提取代码元素。您听起来好像已经可以访问所有说明。

  • 确定每个代码元素所需的操作数和产生的值。这对于“ADD register,register”来说非常简单,但是对于具有惊人的大而疯狂的指令集的生产 x86 CPU,您可能会发现这令人生畏。您必须为 CPU 可能执行的每条指令收集这些信息,这意味着几乎所有指令。尽管如此,这只是汗水和花费大量时间查看说明参考手册。

  • 循环。值可以从一条指令通过其他指令流回同一条指令,因此数据流可以形成循环(复杂循环的大量循环)。数据流文献将告诉您如何根据计算图中的数据流弧来处理这个问题。我不知道这些对您的保护计划意味着什么。

  • 保守分析:你无法得到理想的数据流,因为实际上你在分析任意算法(例如,图灵机);指针非常严重地加剧了这个问题,并且机器代码中充满了指针。因此,数据流分析引擎在无法确定“x 馈送 y”时经常做的事情就是简单地假设“x(可能)馈送 y”。数据流图在概念上从“x(必须)馈送 y”转变为实用的“x(可能)馈送 y”类型弧;因此,文献中实际上充满了“必须”和“可能”的算法。再一次,文献告诉你很多方法来做 [保守的] 流分析(大多有不同程度的保守主义;事实上,最保守的数据流分析只是说“每个 x 馈送每个 y”!)。

有很多人对二进制代码分析感兴趣(例如,NSA),他们对机器指令进行数据流分析,并完成指针分析。您可能会发现此演示文稿很有趣: http ://research.cs.wisc.edu/wisa/presentations/2002/0114/gogul/gogul.1.14.02.pdf

于 2012-05-25T13:49:07.587 回答
1

我不确定您尝试的操作是否有助于防止篡改流程。如果有人附加调试器(进程)并中断发送/接收功能,则内存的校验和保持不变,所有改组将保持原样,即使客户端无效,客户端也会被视为有效。当您询问进程使用哪些页面时,此调试器或注入代码能够操纵您(因此您不会看到注入代码,因为它不会告诉您它所在的页面)。

对于您的实际问题:

不能通过重新链接可执行文件来实现改组。链接器跟踪 .o 文件导出和导入的所有符号。当读取所有 .o 文件时,函数的实际地址将放在导入的占位符中。如果将每个函数放在单独的 cpp 文件中并将它们编译为 .o 文件。在链接器调用中重新排序 .o 文件时,所有函数都将位于不同的地址上,并且可执行文件仍然可以正常运行。

我在 Windows 上用 gcc 测试了这个 - 它有效。通过在链接时重新排序 .o 文件,所有函数都被放置到不同的地址。

于 2012-05-20T06:09:35.493 回答
1

我可以通过解析 pdb 文件找到函数,但是创建指令依赖树是我完全迷失的棘手部分。

不可能的。欢迎来到停机问题。

于 2012-05-25T10:04:47.330 回答