我们提请您注意使用重叠指令创建汇编程序的技术-保护编译后的字节码不被反汇编。 此技术能够承受静态和动态字节码分析。 想法是选择一个字节流,当从两个不同的偏移量中分解出这些字节时,它们将导致两条不同的指令链,即,两种不同的执行程序的方式。 为此,我们采用多字节汇编器指令,并将受保护的代码隐藏在这些指令的字节码的可变部分中。 为了欺骗反汇编程序(将其放置在错误的路径上)(根据掩盖的指令链),并保护其隐藏的指令链,以防其目视。

创建有效“重叠”的三个先决条件
为了欺骗反汇编程序,重叠代码必须满足以下三个条件:1)来自屏蔽链和隐藏链的指令必须始终相互交叉,即 不应相对对齐(它们的第一个和最后一个字节不应重合)。 否则,部分隐藏代码将在屏蔽链中可见。 2)两个链条均应包含合理的组装说明。 否则,将在静态分析阶段已经检测到掩蔽(偶然发现不适合执行的代码,反汇编程序将更正命令指针并公开掩蔽)。 3)两个链的所有指令不仅应该合理,而且应该正确执行(为防止这种情况发生,当您尝试执行它们时,程序会崩溃)。 否则,在动态分析过程中,故障将引起反向注意,并显示出蒙版。
“重叠”汇编程序指令技术的描述
为了使创建重叠代码的过程尽可能灵活,仅需要选择这种多字节指令,对于这些指令,尽可能多的字节可以取任何值。 这些多字节指令将构成屏蔽指令链。
为了实现创建满足以上三个条件的重叠代码的目标,我们将每个屏蔽指令视为以下形式的字节序列:XX YY ZZ。
XX是指令前缀(指令代码和其他静态字节-不能更改)。
YY是可以任意更改的字节(通常,这些字节存储传递给指令的直接数值;或存储在内存中的操作数的地址)。 应该有尽可能多的YY字节,以便更多隐藏的指令适合它们。
ZZ-这些字节也是可以任意更改的字节,唯一的区别是ZZ字节与后续字节XX(ZZ XX)的组合应构成一条有效的指令,该指令应满足本文开头提出的三个条件。 理想情况下,ZZ应该只占用一个字节,以便在YY上(这实际上是最重要的部分-我们的隐藏代码位于此处)应该有尽可能多的字节。 最后一条隐藏指令应以ZZ结尾-为两条执行链创建一个收敛点。
上胶说明
组合ZZ XX-我们将调用粘合指令。 首先需要一个粘贴指令,以连接位于相邻屏蔽指令中的隐藏指令,其次,为了满足本文开头所述的第一个必要条件:两条链的指令应始终相互交叉(因此,粘贴指令始终位于两个遮罩指令的交集处)。
粘贴指令是在隐藏的命令链中执行的,因此必须以对隐藏代码施加尽可能少的限制的方式进行选择。 假设在执行时更改了通用寄存器和EFLAGS寄存器,则隐藏的代码将无法有效使用相应的寄存器和条件命令(例如,如果粘合指令前面有比较运算符,并且粘合指令本身更改了EFLAGS寄存器的值,则条件转换,粘贴说明后面的内容将无法正常工作)。
下图说明了重叠技术的上述说明。 如果执行从起始字节(XX)开始,则会激活屏蔽指令链。 如果从字节YY开始,则隐藏的指令链被激活。

适合“掩盖指令”作用的汇编器指令
最长的指令(乍看之下最适合我们)是10字节的MOV版本,其中寄存器和32位地址指定的偏移量作为第一个操作数传输,而32位数字作为第二个操作数传输。 该指令包含最多可以任意更改的最多字节(多达8个)。

但是,尽管该指令看起来合理(从理论上讲,它可以正确执行),但仍然不适合我们,因为它的第一个操作数通常会指示不可访问的地址,因此,在尝试执行此类MOV时,程序将崩溃。 T.O. 这个10字节的MOV不满足第三个必要条件:必须正确执行两条链的所有指令。
因此,对于掩盖说明的角色,我们将只选择那些不会造成程序崩溃的申请人。 这种情况极大地缩小了适合创建重叠代码的指令范围,但是仍然有合适的指令。 以下是其中的四个。 这四个指令中的每个指令都包含五个字节,可以任意更改它们,而不会导致程序崩溃。
- LEA。 该指令计算由表达式在第二个操作数中指定的内存地址,并将结果存储在第一个操作数中。 由于我们可以在不实际访问内存的情况下引用该内存(因此也没有程序崩溃的风险),因此该指令的最后五个字节可以采用任意值。

- CMOVcc。 如果满足“ cc”条件,则该指令将执行MOV操作。 为了使该指令满足第三个要求,必须选择条件,以便在任何情况下其值为FALSE。 否则,该指令可能会尝试访问不可访问的内存地址,依此类推。 调低程序。

- SETcc 它的操作原理与CMOVcc相同:如果满足条件“ cc”,则将字节设置为1。 该指令与CMOVcc具有相同的问题:访问无效地址将导致程序崩溃。 因此,必须非常谨慎地选择“ cc”条件。

- NOP。 NOP的长度可以不同(2到15个字节),具体取决于它们中指示的操作数。 在这种情况下,将不会有程序崩溃的风险(由于访问无效的内存地址)。 因为NOP唯一要做的就是增加指令计数器,(它们不对操作数执行任何操作)。 因此,指定操作数的NOP字节可以取任意值。 就我们的目的而言,最适合使用9字节的NOP。

作为参考,这是其他一些NOP选项。

适合“粘合说明”角色的汇编程序说明
对于每个特定的屏蔽指令,适合于粘合指令作用的指令列表是唯一的。 下面是一个列表(由下图所示的算法生成),以9字节NOP为例。

在形成此列表时,我们仅考虑了ZZ占用1个字节的那些选项(否则,隐藏的代码将剩下很少的空间)。 这是一个适合9字节NOP的便利说明的列表。

在这一系列的说明中,没有一个没有副作用。 它们每个都更改EFLAGS或通用寄存器,或同时更改两者。 根据指令的副作用,此列表分为4类。
第一类包括更改EFLAGS寄存器但不更改通用寄存器的指令。 当没有条件跳转或基于EFLAGS寄存器的评估信息的隐藏指令链中的任何指令时,可以使用该类别的指令。 在这种情况下(对于9字节的NOP),只有两条指令:TEST和CMP。

以下是使用TEST作为粘合指令的隐藏代码的简单示例。 此示例进行了退出系统调用,该调用针对任何版本的Linux返回1,为了正确形成满足我们需求的TEST指令,我们需要将第一个NOP的最后一个字节设置为0xA9。 该字节与下一个NOP的前四个字节(66 0F 1F 84)耦合时,将变为TEST EAX指令0x841F0F66。 下两个图显示了相应的汇编代码(用于屏蔽链和隐藏链)。 当控制权转移到第一个NOP的第4个字节时,隐藏链被激活。


第二类包括更改通用寄存器或可用存储器(例如堆栈)的值,但不更改EFLAGS寄存器的指令。 当执行PUSH指令或任何MOV变体时,将立即值指定为第二个操作数,则EFLAGS寄存器保持不变。 T.O. 第二类粘合指令甚至可以放在比较指令(例如TEST)和评估EFLAGS寄存器的指令之间。 但是,此类别中的指令限制了出现在相应胶合指令中的寄存器的使用。 例如,如果将MOV EBP 0x841F0F66用作粘合指令,则使用EBP寄存器(从其余隐藏代码开始)的可能性将受到极大限制。
第三类包括更改EFLAGS寄存器和更改通用寄存器(或存储器)的指令。 与前两类指令相比,这些指令没有明显的优势。 但是,也可以使用它们,因为它们不与本文开头提出的三个条件相矛盾。 第四类包括指令,这些指令的执行不能保证程序不会崩溃-存在对存储器进行非法访问的风险。 极不希望使用它们,因为 他们不满足第三个条件。
可以在隐藏链中使用的汇编程序指令
在我们的情况下(当9字节NOP被用作屏蔽指令时),隐藏链中每条指令的长度不应超过4字节(此限制不适用于占用5字节的粘性指令)。 但是,这不是一个非常关键的限制,因为长于四个字节的大多数指令都可以分解为几个较短的指令。 下面是一个5字节MOV的示例,该MOV太大而无法放入隐藏链中。

但是,此五字节的MOV可以分解为三个指令,其长度不超过四个字节。

通过在整个程序中分散掩盖NOP来增强掩盖
从相反的角度来看,大量连续的NOP看起来非常可疑。 将他的兴趣集中在这些可疑的NOP上,有经验的反向器可以深入了解隐藏在其中的代码。 为了避免这种情况,可以将掩盖的NOP分散在整个程序中。
在这种情况下,隐藏代码的正确执行链可以通过无条件跳转的双字节指令来支持。 在这种情况下,每个NOP的最后两个字节将占用2字节的JMP。
此技巧可让您将一个长的NOP序列拆分为几个短的NOP(甚至每个使用一个NOP)。 在这样短序列的最后一个NOP中,只能分配有效负载的3个字节(第4个字节将由无条件跳转指令获取)。 T.O. 这对有效指令的大小有附加的限制。 但是,如上所述,长指令可以放在较短的指令链上。 下面是同一个5字节MOV的示例,我们已经对其进行了布局以适合4字节限制。 但是,现在我们以适合3字节限制的方式分解此MOV。

根据相同的原理将所有长指令分解为较短的指令后,为了掩盖更多指令,我们通常可以仅使用分散在整个程序中的单个NOP。 两字节的JMP指令可以向前和向后跳转127个字节,这意味着两个连续的NOP(连续,就隐藏指令的链而言)必须在127个字节之内。
这个技巧还有另一个重要的优点(除了增强的屏蔽功能):在它的帮助下,您可以将隐藏的代码放置在已编译的二进制文件的现有NOP中(即,在将二进制文件编译后将有效载荷插入二进制文件中)。 在这种情况下,这些孤立的NOP不必为9字节。 例如,如果二进制文件中的一行中有多个单字节NOP,则可以将它们转换为多字节NOP,而不会破坏程序的功能。 下面是分散NOP的技术示例(此代码在功能上等同于上面讨论的示例)。

隐藏在整个程序中的,隐藏在NOP中的这种隐藏代码已经很难检测到。
细心的读者必须注意到第一个NOP没有最后一个字节。 但是,没有什么可担心的。 因为此无人认领的字节之前是无条件跳转。 T.O. 控制权永远不会转移给他。 所以一切都井井有条。
这是一种用于创建重叠代码的技术。 用于健康。 隐藏您的宝贵代码,以免被撬开。 但是,只需采用其他一些指令,而不是9字节的NOP。 因为反向者也可能会读这篇文章。