修复/重新编译严重控制流混淆的函数

逆向工程 艾达 视窗 混淆 x86-64 控制流图
2021-06-12 05:19:42

我最近遇到了一个二进制文件,它被一个非常模糊的保护混淆了。

混淆

据我了解,混淆器在几个方面都很有效。

  • 多个 JMP 通过相对寻址 (0xE9),JMP 到堆栈中的地址 ( e.g jmp [rsp-0x8]),以及较少使用的 JMP 使用寄存器 ( jmp reg)。这些对于使 IDA Pro 中的 CFG 变得无用非常有效。
  • 常量堆栈算法。混淆器不断地使用堆栈内存来保存信息的重要部分,例如变量和 JMP 到下一个块(如前所述)。更重要的是,指令sub rsp, #andadd rsp, #和 取代了通过lea rsp, [rsp+-#]. IDA 的分析器也无法确认指令中堆栈指针的变化lea rsp, [rsp+8]
  • 垃圾代码。不完全有任何效果或与原始函数的目标相关的指令。
  • 轻微使用不透明谓词(但我没有发现太多)。
  • 通过cmov指令进行条件分支,例如
test rax, rax
... ; junk
mov rdx, sub_not_equal
mov rcx, [rsp-0x8]
cmovz rcx, rdx
... ; stack stuff
-> jmp to position in stack containing rcx

例子

.text:00007FF639A15EA9                                                     loc_7FF639A15EA9:                       
.text:00007FF639A15EA9 048 48 89 04 24                                                     mov     [rsp+48h+var_48], rax
.text:00007FF639A15EAD 048 48 8D 64 24 08                                                  lea     rsp, [rsp+8]
.text:00007FF639A15EB2 040 48 8B 74 24 F8                                                  mov     rsi, [rsp+40h+var_48]
.text:00007FF639A15EB7 040 48 85 C0                                                        test    rax, rax
.text:00007FF639A15EBA 040 E9 F8 10 44 FF                                                  jmp     loc_7FF638E56FB7
.text:00007FF638E56FB7                                                     loc_7FF638E56FB7:                       
.text:00007FF638E56FB7 040 0F 85 F4 C2 B9 FB                                               jnz     near ptr sub_7FF6349F32B1
.text:00007FF638E56FBD 040 E9 33 EE 9F 02                                                  jmp     loc_7FF63B855DF5
.text:00007FF63B855DF5                                                     loc_7FF63B855DF5:                       
.text:00007FF63B855DF5 040 4C 89 74 24 F8                                                  mov     [rsp+40h+var_48], r14
.text:00007FF63B855DFA 040 48 8D 64 24 F8                                                  lea     rsp, [rsp-8]
.text:00007FF63B855DFF 048 48 8B 34 24                                                     mov     rsi, [rsp+48h+var_48]
.text:00007FF63B855E03 048 48 8D 64 24 08                                                  lea     rsp, [rsp+8]
.text:00007FF63B855E08 040 E9 86 B1 74 FD                                                  jmp     loc_7FF638FA0F93
.text:00007FF6392FCD6C                                                     loc_7FF6392FCD6C:                       
.text:00007FF6392FCD6C 048 48 8D 2D 42 65 6F FB                                            lea     rbp, loc_7FF6349F32B5
.text:00007FF6392FCD73 048 48 87 2C 24                                                     xchg    rbp, [rsp+48h+var_48]
.text:00007FF6392FCD77 048 48 8D 64 24 08                                                  lea     rsp, [rsp+8]
.text:00007FF6392FCD7C 040 FF 64 24 F8                                                     jmp     [rsp+40h+var_48]

等等等等..

努力

我的目标是创建一个工具,该工具将采用混淆例程,跟踪每次跳转,然后创建一个窥视孔优化器,以完全删除指令序列,例如lea rsp, [rsp+-]xhng reg, [rsp+x]和其他人显示并用他们的“反混淆”变体替换它们。所以我开始研究一种工具,它可以模拟某些指令(尤其是与堆栈有关的任何指令),然后创建我自己的“虚拟堆栈”,其中包含与原始程序相同的信息。这实际上有效,除了我没有考虑很多事情,例如 JCC。在我的工具中显示的最终结果是最终组合转储中有超过 60 个 JMP 和超过 200 条指令(这甚至不包括多个 JCC 分支,这些分支很容易具有相同或更多的 CF 混淆)。我觉得我的尝试指向了错误的方向。

指导

好。使用我的工具,我可能只是浏览整个程序集并弄清楚添加条件分支支持后发生了什么,但我仍然感到不满意。我最初的目标是生成一个完全优化的、可反编译的转储,但是我觉得由于我的设计缺陷,我已经走到了尽头。

我想问所有有耐心阅读这篇文章的人,他们的建议是否是图书馆推荐,或者如何重新处理我在这里的去混淆过程。

对于上下文,我正在分析一个进程的解包转储,并使用我的 C++ 编码工具使用 Zydis 反汇编器来解码和手动模拟一些指令。

1个回答

由于以静态方式或手动方式以动态方式执行这显然很痛苦,因此我建议尝试另一种方法。

我个人会尝试通过编写一个小型模拟器来做到这一点。

由于它主要是“堆栈算术”和垃圾代码,这将是完美的。看看名为“Unicorn”的仿真框架。

使用此选项,您无需重新实现任何内容,并且它将支持比简单的“虚拟堆栈”(如您所称)更多的情况。

您基本上必须在内存中映射整个二进制文件,设置初始堆栈帧,为每条指令创建一个钩子,并且可能创建一个用于调试的断点系统。然后,您可以启动仿真,并检查程序的行为和所采用的 jmp。

如果您想让代码更具可读性,您的钩子程序可能会扫描当前指令是否包含对任何堆栈指针的引用。如果,将其替换为相关变量的内容。

如果你只想关注一小部分代码,这也是可以的。但是您必须仔细设置初始堆栈/寄存器状态并从您的自定义入口点开始