前言
样本分析过程思路来源:套娃式的FormBook家族样本对抗部分分析
本文仅扩充一些分析过程中省略的技术细节,适合分析小白食用
使用到的工具
dnspy-release-win32
de4dot
包括中间分析文件
分析
根据dnspy反编译的结果,可以看到具有大量的混淆:
将样本使用de4dot解混淆:
PE1
入口点和绝大数使用C#编写的PE Loader一样,为运行一个窗口,加载类0中的KeysOrderedAcrossPartitions string对象将该string对象进行url解码,该对象是保存在资源之中,将解码后的资源进行load,调用其Buta导出方法
编辑类方法获取程序集(记得保存后退出重新打开程序集,如果不这样的话,调试的箭头实际上是指向原来的代码的,代码实际上没有变化)
对应的C#代码:将URL解码的字节流输出到文件
public void method_1()
{
try
{
Stream stream = new FileStream("C:\\Users\\ysnb\\Desktop\\test.dll", FileMode.OpenOrCreate, FileAccess.Write);
byte[] array = HttpServerUtility.UrlTokenDecode(Class0.KeysOrderedAcrossPartitions);
stream.Write(array, 0, array.Length);
}
catch
{
Thread.GetDomain().Load(HttpServerUtility.UrlTokenDecode(Class0.KeysOrderedAcrossPartitions)).GetTypes()[0].GetMethod("Buta").Invoke(null, this.qqqqqqqqqqqqqqqqqqqqqqqqqqqqq);
}
}
PE2
同样使用de4dot解混淆,找到buta方法,可以看到是一个死循环,整体case的顺序为default、6、4;然后退出进程
为了能够跟踪dll调用,我们需要将dll程序集合并到PE1的程序集,右键域合并程序集选择de4dot处理过的PE2即可(记得保存文件后退出,在ns0中可以看到Jarico类),使用相同的参数调用Buta方法
Buta中有一个sleep,可以注释掉跳过
会将资源中的cXRsF进行读取后解密调用dll导出方法Kuchi解压缩,得到一个PE文件
还是同样的方法,编辑类将字节流输出到文件
之后会无参加载这个PE文件,第二阶段结束
PE3
使用de4dot去混淆。在main函数之前会先初始化一些环境、初始化一些静态类的静态变量。其中第二个类会解密获取一些配置字符串和解密资源得到一个PE文件,资源名叫做”UxllYqyI”。
解密函数1:先将“NAzjGQJblQU”转为unicode字符串数组bytes,后取出资源文件中最后一个字符亦或112赋值为num2,然后将资源文件中每个字符亦或上num2再亦或之前的bytes中的对应字符bytes[num],num<= bytes的长度
解密函数2:一个简单的循环亦或,解密函数2完成后才能得到真正的PE文件
右键保存至文件即可
process hollowing修改.text段,将刚刚解密的PE文件的Text段对重启的进程替换空间中的.text段
PE4
接下来对拿到的PE样本进行分析,首先找到main
因为我们主要关注的是这个进程的逃逸手段,所以我们主要关注函数Sub_409D20_Init_ControlBlock_And_AdjustToken
重点关注初始化控制块的部分,可以看到整个判断环境是否为虚拟环境和分析环境的代码如图所示
这个代码运用了很多反检测的手段,例如
-
检测rdtsc的运行时间是否和真实物理机一致
-
判断是否存在位于黑名单内的进程或者用户名或者模块名。比较特别的是,这里采取的是通过hash比较的方法
-
判断是否处于调试状态,包括ProcessDebugerport和SystemKernelDebuggerInformation两种,采用的函数都是NtQuerySystemInformation
-
重载两份ntdll并自行构建部分ntdll敏感函数调用表,类似于增量链接。重载ntdll,是为了防止某些edr,主防工具在3环的ntdll上下钩子做检测
-
里面的字符串比较都是通过取hash后与hash比较。在检测通过后都会恢复部分函数hash。这样可以防止基于敏感函数的静态检测
在通过这些检查之后,就会进行提权,这里不做重点关注(其实是不会)
对解密后的PE进行重映射,准备注入到explorer中
接着就是经典的那套:找到explorer进程名->NtOpenProcess打开进程->NtQueryInformationProcess获得进程信息->NtSuspendThread挂起线程->NtGetContextThread获得线程上下文->NtMapviewOfSection映射一个节->NtSetContextThread设置上下文到映射的节->NtResumeThread恢复线程
后续等之后再看吧。。