如何混淆x86汇编代码?

逆向工程 部件 x86 混淆 安全
2021-07-10 08:43:34

对于我的项目,我正在对部分代码执行一种校验和操作以保护它,因此不希望其模板容易可见,因此需要混淆。

我在网上搜索了很多,并阅读了描述混淆定义、类型等的论文。但似乎没有关于混淆 x86 汇编代码的教程。有人可以建议一个简单的算法/工具吗?

我已经阅读了关于插入虚拟代码、更改指令的顺序和其他技术的内容,但它们似乎是完全随机的,即插入多少虚拟代码是无止境的,等等。

有人至少可以指导我采取正确的方法吗?

3个回答

您不应该混淆“代码保护”和“代码混淆”。“代码保护”技术的目标是识别代码修改(您提到了校验和),并在识别出篡改代码时采取适当的方法,例如崩溃或提供错误的结果。反调试措施也属于这一类。

二进制中的“代码混淆”旨在用笨拙且通常无用的代码(从简单的 jmp 链到更复杂的结构)使调查者(和反汇编者)蒙蔽。大多数代码混淆技术会导致一些代码膨胀,必须考虑混淆和性能之间的权衡。这里有两个“在野外”发现的混淆示例。

  • 虚拟代码:有很多方法可以在二进制文件中插入 nops。示例范围从简单的语句如 shr eax, 0 或(在 32 位世界中)shl bx, 20h 到更复杂的结构,如以下示例。jz 和 jnz 的组合后跟垃圾语句(cpuid、ret),避免使用 jmp 通常会混淆反汇编程序显示逻辑汇编块的能力。

mov si, si mov esp, ebp jnz loc_abcd xchg edi, esi mov cl, cl xchg esi, edi mov di, di jz loc_dcba ; followed by a jmp to loc_abcd cpuid ret 在这个例子中,唯一的“真实”语句是 mov esp, ebp。

  • 使简单的装配结构复杂化。您可以编写一个简单的 jmp 作为 push 和 ret 的组合。或者,如果您不喜欢“ret”语句,您可以将其替换为(在 64 位代码中): lea rsp, [rsp+8] jmp qword ptr[rsp-8]

可以找到更多、更复杂的例子。如果你想深入研究这个问题,你需要练习,就像所有的 SW 倒车一样。获取受保护和/或混淆的二进制文件,如游戏或加密狗保护器和火车。在文献中(例如 Chris Eagle 出色的 IdaPro 书),您也可能会发现一些混淆结构。

玩得开心!

正如评论中提到的,查看第 5 章以PDF Practical Reverse Engineering获得一些想法。

垃圾代码插入:

jmp label
<junk> 
label:
<real code>

基于操作系统的控制间接:

push addr_seh_handler
push fs:[0]
mov fs:[0], esp
xor eax, eax
mov [eax], 1234h
<junk code>
addr_seh_handler:
<continue execution here>
pop fs:[0]
add esp, 4

基于处理器的控制间接:

call target_addr
<junk code>
target_addr:
add esp, 4

通过身份的算术替换:

-x = ~x + 1 (by definition of two's complement) 
rotate left(x,y) = (x << y) | (x >> (bits(x)-y))
rotate right(x,y) = (x >> y) | (x << (bits(x)-y))
x-1 = ~-x
x+1 = - x

基于模式的混淆:

push reg32

变成

push imm32
mov dword ptr [esp], reg32

更多基于模式的例子在这里

Nop 代码插入

如果您不想手动混淆代码,这是“成熟”的方法:

  • 源代码 -> 编译器 -> IR aka bitcode
  • IR -> 混淆器 -> 混淆 IR
  • 混淆的 IR -> LLVM 静态编译器 -> 最终可执行文件

在哪里:

操作 IR 代码比操作本机代码容易得多。然而,学习 llvm 的工作原理以及如何使用其类进行更改并非易事。