![d2269d6432a7a4bb3d59fd8754ee0795.png](https://i-blog.csdnimg.cn/blog_migrate/03a5defd18d91a2b281905d7726a680e.jpeg)
本文为看雪论坛优秀文章
看雪论坛作者ID:三十二变
传送门:代码混淆之我见、代码混淆之我见(三) 我们以 @Wszzy 在《自写保护壳交流——指令的膨胀和花指令的插入》一文中留下的CrackMe为例,简单介绍一下静态分析在解混淆中的应用。 初探 经不完全统计,该程序供使用了如下四种混淆: 1. 花指令 2. 常量展开 3. 模式替换 4. 打乱代码顺序局部性 花指令可以被使用递归下降反汇编算法的IDA识别。如果要去除,则只需搜索E9 01 00 00 00 ??并替换为 90 90 90 90 90 90 即可。 打乱代码顺序局部性对分析的影响不大,解混淆时就不单独处理了。其实有许多解混淆插件对乱序的处理效果非常好。 模式替换和常量展开在该程序中被结合使用,以我随便选的一段代码为例。.ZP1:00423023 sub esp, 4
.ZP1:00423026 mov [esp+114h+var_114], esi
.ZP1:00423035 mov esi, 403539h
.ZP1:0042303A sub esi, offset loc_40363D
.ZP1:00423046 not esi
.ZP1:0042304E xchg esi, [esp+114h+var_114]
= >
push 0x103//这可以看成是模式替换与常量展开的嵌套使用
push imm
=> //模式匹配
sub esp,4
mov [esp],imm
=> //常量展开
sub esp,4
mov [esp],reg
mov reg,xxx
..... //对reg的解密
xchg [esp],reg
解除模式替换时,需要搜集程序使用的模式进行逆变换,对付常量展开则进行常量折叠优化。 对代码进行混淆时,一般都会涉及到一个不精确语义问题。 以 push imm32 为例, Wszzy 的混淆器对它混淆时,引入了sub指令,也就是说混淆前后代码的语义并不是完全等价的,因为混淆后的代码会影响标志位。 我在(一)中介绍常量展开时,为了偷懒一笔带过了这个问题(现在我仍然打算一笔带过)…… 该问题的解决办法是使用活跃分析,分析出每条指令上寄存器和标志位的活跃状态,死状态的寄存器和标志位可以随意使用。 当然,混淆前后语义不完全相等的混淆过程也可以是合法的,依据Collberg对混淆转换的定义,只要确保混淆后程序的可被用户观测到的行为相同即可。 解混淆 > > > >
1)花指令
void CodeObfs::CleanOPCode(void) {
PBYTE pBuf = (PBYTE)malloc(0x10000);
PBYTE p = pBuf;
BYTE b1[] = {
0xE9, 0x01, 0, 0, 0};
BYTE b2[] = {
0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
GetData(0x423000, pBuf, 0xF00);