我最近遇到了一个二进制文件,它被一个非常模糊的保护混淆了。
混淆
据我了解,混淆器在几个方面都很有效。
- 多个 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 反汇编器来解码和手动模拟一些指令。