一种对抗静态分析的方式

admin 2026-01-09 03:06:36 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 文档分析Floxif木马对抗静态分析的技术。该样本利用特定函数混淆控制流,通过栈操作将Call伪装为Jmp,阻碍反编译。作者通过汇编与调试分析还原逻辑,发现其本质是跳转至返回地址加2处。最终建议将此类Call替换为Jmp指令,以绕过对抗机制并还原真实代码。 综合评分: 91 文章分类: 恶意软件,逆向分析


cover_image

一种对抗静态分析的方式

原创

小和安全

小和安全

2026年1月8日 11:47 江苏

前言

样本信息:

| | | | — | — | | SHA256 | de055a89de246e629a8694bde18af2b1605e4b9b493c7e4aef669dd67acf5085 | | SHA1 | 14ba3fa927a06224dfe587014299e834def4644f | | MD5 | 7574cf2c64f35161ab1292e2f532aabf |

看一个Floxif样本,发现用了一些对抗静态分析的方式,点开一个函数看看:

再点进sub_10001678函数看看:

看出来了吗,sub_10001684这个函数最终就做了一个清理栈帧的操作。

但是我们通过交叉引用看一下这个函数出现了多少次:

这么一个简单逻辑的函数居然被调用了73次,很显然,并没有这么简单,木马的开发者是采用了一些手段让这个函数看起来没有威胁性。

分析

这个木马的脱壳、通信等等就不在这里分析了,就是一个很简单upx加壳,直接工具脱壳就行:

我们直接用IDA打开看一下刚刚的地方,这次不看伪代码,直接看汇编:

点进sub_10001678继续看:

直接把它们拼接起来看看呢,也就是把sub_10001684里面的这一步:

call    sub_10001678

替换为:

push  1000168Bjmp  10001678

那我们就得到了:

push    eaxpushapush    1000168Bjmp     10001678retn    4

现在从调用10001684函数开始分析。

假设我们执行call 10001684时,下一条指令地址为x,比如:

x-5      call  10001684x        下一条指令是什么不重要,知道地址为x就行

此时就相当于:

x-5      push  x         jmp  10001684x        下一条指令是什么不重要,知道地址为x就行

这时候当我们执行jmp 10001684时栈里面已经有一个x了,也就是:

| | | | — | — | | x | 栈底+栈顶esp |

然后我们jmp 10001684,继续执行指令:

push    eaxpushapush    1000168Bjmp     10001678retn    4

当我们即将执行jmp 10001678时,栈里面的内容如下(假设此时eax的值为y):

| | | | — | — | | x | | | y | | | all | | | 1000168B | 栈顶esp |

接下来jmp到10001678,我们直接跟过去看:

一步一步来,执行push eax后栈内容:

| | | | — | — | | x | | | y | | | all | | | 1000168B | | | eax | 栈顶esp |

执行mov eax,[esp+4]就是取esp+4位置指向的值,赋值给eax,此时eax寄存器值就变成了1000168B,然后add eax,4表示eax值加4,变为1000168F,再执行push eax后栈内容:

| | | | — | — | | x | | | eax | | | all | | | 1000168B | | | eax | | | 1000168F | 栈顶esp |

接下来retn 8表示栈顶地址出栈,跳转到这个地址也就是1000168F,然后清理8字节的内容,栈内容变为:

| | | | — | — | | x | | | eax | | | all | 栈顶esp |

并跳转到1000168F,在IDA里面看这个地址发现被识别错了:

1000168E后面就是10001690,没有我们要的1000168F.

我们需要手动重置这一块区域,让它变成原始数据,快捷键U即可:

然后选中1000168F,快捷键P重新识别为函数:

回到刚才我们的栈内容是:

| | | | — | — | | x | | | y | | | all | 栈顶esp |

现在jmp到了1000168F,执行popa后栈内容:

| | | | — | — | | x | | | y | 栈顶esp |

还剩下这些指令:

两个参数的值也有:

看上去很复杂,其实简单来说就是:

(1)sub     esp, 0FFFFFFF8h表示esp的值减-8,也就是加8,此时栈:

| | | | — | — | | | 栈顶esp | | x | | | y | |

(2)mov     eax, [esp-28h+arg_20]表示将esp-4的地址保存的值赋值给eax,很绕,其实就是栈里面的x,此时eax=x

(3)add     eax, 2就是将eax加2,此时eax=x+2

(4)mov     [esp-28h+arg_20], eax又把eax的值写回esp-4的地址,此时栈的内容:

| | | | — | — | | | 栈顶esp | | x+2 | | | y | |

(5)mov     eax, [esp-28h+arg_1C]表示取出esp-8地址保存的值,很显然就是y,那此时eax=y

(6)sub     esp, 4表示esp-4,栈顶地址减4,栈的内容变为:

| | | | — | — | | x+2 | 栈顶esp | | y | |

栈顶在x+2这里,那y其实就是没有用的数据了,最后,栈的内容就是:

| | | | — | — | | x+2 | 栈顶esp |

(7)retn表示将栈顶地址出栈,然后跳转到这个地址,这个时候栈清空了,然后跳转到x+2这个地址。

这下清楚了,我们调用10001684函数时,表面上执行的是:

x-5      call  10001684x        下一条指令是什么不重要,知道地址为x就行

但是我们执行前后,栈的内容没变,还跳转到了x+2地址,那call 10001684等效替换不就是jmp x+2吗?

验证

动态调试一下就可以验证我们的分析了:

首先在IDA找到一个调用了10001710函数的地方:

首先根据我们的分析,call sub_10001684会跳转到下一条地址加2的位置也就是10001715加2,也就是10001717。我们验证一下:

找到一个10001710函数,、然后打开dbg,在10001710和10001684下断点:

F7单步进入10001684,一直单步执行:

F7进入10001678,再一直单步执行:

一直执行到ret会根据我们的分析跳转到1000168F:

确实如此,根据我们的分析,ret后应该会跳转到10001717,我们单步执行到ret,看看是不是真的跳转到这里:

确实跳转到10001717:

总结

我们只需要将所有的call 10001684改为jmp $+7就可以绕过这个对抗机制了。

比如:

原先的10001710函数:

反编译后根本看不见真实的函数逻辑:

我们将它改为 jmp $+7再反编译:

成功还原真实的函数逻辑!


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:小和安全 小和安全《一种对抗静态分析的方式》

评论:0   参与:  0