查看原文
其他

对一篇反沙箱文章的分析学习小记

dayday向上8 看雪学苑 2022-07-01


本文为看雪论坛优秀文章

看雪论坛作者ID:dayday向上8





前言


前段时间看到一篇McAfee关于反沙箱的文章,文中总结了一些病毒所用手段以及手段的进化史。链接如下:
https://www.mcafee.com/blogs/other-blogs/other-blogs/mcafee-labs/evolution-of-malware-sandbox-evasion-tactics-a-retrospective-study/

看完之后总觉得应该亲自去实践一下,一方面更深的了解其中的具体细节部分,另外也想锻炼下自己,所以才有了这篇小记。

本次样本没有找全,主要涉及Delaying Execution(从两个方面,时间复杂度和API调用)两个样本,GetTickCount一个样本,API Flooding一个样本,CPU Temperature Check(没找到合适样本,有验证代码)。

本次分析只涉及到了关键的反沙箱部分(包括定位到这部分代码和代码分析),能力有限,没有全面分析,算是浅析,大佬见谅。





延迟执行-时间复杂度


SHA-1:00111b6d9552986d75607968268c22518948ac8eMicrosoft:Trojan:Win32/Ursnif.SM!MTB


1. 入手点


对于这个样本,我的想法是通过导入函数VirtualAlloc/VirtualProtect入手试试,猜测有可能是解密病毒代码的时候造成的延时。


2. 具体思路


通过IDA对函数VirtualAlloc进行交叉引用,发现都是库代码进行了引用,所以放弃。再次尝试对函数VirtualProtect进行交叉引用,发现此处存在调用,紧接着再次对lpAddress进行交叉引用。


很幸运的是只有一处对lpAddress进行了赋值操作,不难发现此处是从地址0x4514e8向地址0x45B5F8进行了内存拷贝,字节数是0x491f。



到了这步,怎么办?我的想法是进行动态调试,通过在0x45b5f8下硬件写入断点找到对其进行更改的位置,因为我觉着程序应该会对这块内存进行重写。通过下断,找到了如下位置。



接下来就是需要好好分析这段代码了,那我自己是通过最终的赋值位置(mov [esi], eax)向上回溯两个寄存器值的来源,这样相比顺序分析快很多而且可以避免垃圾代码的干扰。

3. 分析


通过分析,这段代码是存在很多的垃圾代码的,其真正有用的代码只有短短的十行,如下:

//找到数据修改地方进行分析,在进行62((0BCAh-0AD2)/4)次循环对248((0BCAh-0AD2))个字节修改之后推出循环//头部text:004349FB BD D2 0A 00 00 mov ebp, 0AD2h
//中部text:00434AAE A1 44 96 46 00 mov eax, lpAddress //(unk_45B5F8)text:00434AB3 49 dec ecxtext:00434AB4 8D B4 28 2E F5 FF FF lea esi, [eax+ebp-0AD2h]text:00434ABB 8B 06 mov eax, [esi]
//尾部text:00434B06 05 C0 24 60 01 add eax, 16024C0htext:00434B0B A3 A0 9F 46 00 mov dword_469FA0, eaxtext:00434B10 89 06 mov [esi], eax ; 修改数据
text:00434B33 83 C5 04 add ebp, 4text:00434B42 81 FD CA 0B 00 00 cmp ebp, 0BCAh

紧接着就是通过引用查看这个函数在何处被引用,结果发现有两处对此进行了调用,而且两处又都是一个大循环。


第一处引用:

//循环代码行数00434C2B-00434D2A = 0xFF(255)行//循环跳出点text:00434C49 83 7C 24 10 01 cmp dword ptr [esp+10h], 1text:00434C4E 0F 82 DB 00 00 00 jb loc_434D2F
text:00434D0E FF 4C 24 10 dec dword ptr [esp+10h]

第二处引用:

//循环代码行数00434D6A-00435341 = 0x5D7(1495)//循环跳出点text:00435075 83 7C 24 10 00 cmp dword ptr [esp+10h], 0text:0043507A 89 0D 30 90 45 00 mov dword_459030, ecx
text:004351E4 FF 4C 24 10 dec dword ptr [esp+10h]

其实不难发现,循环跳出的关键就在于[esp+0x10]中存的数是多少?通过动态调试可知其值为固定的0x911FB2(IDA中也有体现)。其实到这里,几乎可以确定这段就是超时代码了。


总结


共同的内循环次数为0x3e次,两次不同的外循环次数相同,0x911FB2次,所以直到解出来最终代码共运行了0x911fb2 0x3e 2 = 0x464B5A38‬(1,179,343,416‬)次循环。


按照代码中的计算方式来说,一层循环0x3e次就已经完全可以了,将要解密的248字节数据按照四字节取出,分别加上(0x16024C0*2)之后在放回去即可,那这个病毒代码硬生生的将其嵌套了两层循环,实打实的增加了时间的复杂度完成延时操作。





延迟执行-API调用


SHA-1:bbca30fc40784ef52ec352c5d9bbadd7a95b2f14Microsoft:Trojan:Win32/Qakbot.AR!MTB

1. 入手点


这个样本前期的操作和上个样本类似,所以本次分析重点并不是他,而是他在内存中解出来的另外一个pe文件,我直接dump下来之后对其进行分析,样本会放到压缩包里。

这次我换了个思路,就是通过火绒剑先大概定位到超时的位置,之后再进行具体分析。

2. 具体思路


由于定位在未知模块,多半又是堆空间,结合导入函数VirtualAlloc的存在,就不多赘述了,在得到堆空间的地址之后,在超时偏移处先下个断点之后运行,在断下之后我并没有什么思路,毕竟完全不知道什么套路,所以我选择了单步步过,很幸运在单步几步之后看到了令我激动的函数WaitForMultipleObjects,本能觉得此处是关键处。



之后我的思路是,找到这个内核对象创建的地方,有两种方法,一是在这个栈地址下个硬件写入,二是直接在附近的函数调用中找一找,应该很快就可以找到,此处我选择第二种方法。



由图可知,进程创建了一个计时器对象,在经过一定时间的时候才能出发,这样就可以达到超时的目的。

至此,等待的第一个对象解决了,那么等待的第二个内核对象是啥呢?通过一顿猛如虎的操作,最终我找到了其实是个事件对象。



从参数可以发现,这个事件对象在创建的时候被设置为手动激活,而且初始信号为无信号,但是通过在SetEvent函数下断点无果之后,到底有没有激活,再让我们回头看下函数WaitForMultipleObjects发现其中第三个参数为false,也就是等到其中任意一个对象即可。

之后通过手动更改让其等待所有对象,结果……没有结果,果然没有激活事件对象。





GetTickCount


SHA-1:c3f3ecf24b6d39b0e4ff51af31002f3d37677476Microsoft:Trojan:Win32/Kovter


1. 入手点


没啥说的直接火绒剑跑一下行为,结果就悲剧了:



打开并读取文件是在有明显超时时间之后执行的动作,所以红色框中的地址就是我准备入手的地方,但是突然就崩了是什么操作?先不管了,先找超时点吧。


2. 具体思路


通过之前几个样本的分析,所以我就直接顺着入手点所在的函数一路交叉带闪电向上回溯,此时我判断的标准也很简单,就是找有循环或者敏感api的调用,最后找到了几段类似如下的代码,此处只放一张作为实例。


3. 分析


可以看到这段代码的巧妙之处就在于,只有当GetTickCount返回指定值的时候才能够继续,这样在某种程度上说就达到了超时效果,而且这样的片段有好几个,有意思的是指定的返回值在经过简单的计算之后会被用来拼装成要获取的函数名称字串。

cmp eax, 7jz short loc_401A62call ds:GetTickCountmov byte_465290, aljmp short loc_401A49----------------------------movzx ecx, byte_465290xor ecx, 4Ehxor ecx, 7mov ProcName, cl ;只有上面al正确,此处函数名的第一个字节才正确

剩下的问题就是程序怎么崩了!?


由于和本次的分析主题不是太相关就简单记录下了,通过从崩溃点入手,动态调试几次之后发现是在进行内存拷贝时候有个地址不存在导致的。



仔细看这个源地址是不是有点感觉像是申请出来的对空间,但是地址老感觉又不太对。

之后通过向上查找代码,发现确实是使用VirtualAllocEx申请得到,然后向这块内存拷贝了一段内嵌字节码用来解密,解密之后在把这段代码拷贝到内存0x46fa20处,从后面代码对这个解密之后的代码头部验证是否是0x5a4d就可以知道应该解出来的是个pe文件。那就看看他是如何解密的吧。



从上图可知,这个程序必须运行在2016年7月份才能解密正确,生而为人,我很抱歉!手动更改年份和月份之后运行正确。





API Flooding


SHA-1:6ca2bbcecc76d9c14e0373c919d67729311430a6Microsoft:TrojanDropper:Win32/Cutwail.gen!K

1. 入手点


这个样本并不是像文章中描述的那样重复调用系统的API,所以不能算是真正意义上的API Flooding,而是递归调用自己写的垃圾代码。

用火绒剑,找到超时之后执行的能看到第一个位置,开始栈回溯分析。

我开始以为又是什么循环之类的,所以尝试搜索loop结果失败,开始动态调试在没有执行到超时位置按下暂停,如此往复几次之后就可以发现有这么一个函数,递归调用自身。


2. 分析








CPU Temperature Check


这个我找了半天的样本未果,幸运的是网上有哥们写过一个ps脚本通过wmi对象调用获取CPU温度的验证代码:

function Get-AntiVMwithTemperature { $t = Get-WmiObject MSAcpi_ThermalZoneTemperature -Namespace "root/wmi" $valorTempKelvin = $t.CurrentTemperature / 10 $valorTempCelsius = $valorTempKelvin - 273.15 $valorTempFahrenheit = (9/5) * $valorTempCelsius + 32 return $valorTempCelsius.ToString() + " C : " + $valorTempFahrenheit.ToString() + " F : " + $valorTempKelvin + "K" }

通过实践验证和一篇文章中片段,大概说明了通过这种方法也并不完全可行,因为可能会放过一些不支持这种方式获取CPU温度的物理机。


这是我在物理机中运行得到的结果,虚拟机肯定也就不行了。
https://blog.talosintelligence.com/2018/04/gravityrat-two-year-evolution-of-apt.html





最后


很遗憾没找到所有匹配的样本,希望有样本提供的小伙伴可以留个hash,那就先这样吧,对了密码是kanxue。



- End -



看雪ID:dayday向上8

https://bbs.pediy.com/user-868245.htm

  *本文由看雪论坛 dayday向上8 原创,转载请注明来自看雪社区。



推荐文章++++

* 探究 Process Explorer 进程树选项灰色问题

* Linux Kernel Pwn 学习笔记(栈溢出)

* ARM栈回溯——从理论到实践,开发IDA-arm-unwind-plugin

* CVE-2020-1048、CVE-2020-1337漏洞原理及利用

* Windows不太常见的进程注入学习小记(二)








公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com


求分享

求点赞

求在看


“阅读原文”一起来充电吧!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存