记录1: 绕过iOS某音最新版反调试
2020-08-23 15:54:17 Author: bbs.pediy.com(查看原文) 阅读量:475 收藏

前言

前段时间看到一篇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:00111b6d9552986d75607968268c22518948ac8e
Microsoft: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     ecx
text: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, 16024C0h
text:00434B0B A3 A0 9F 46 00                                mov     dword_469FA0, eax
text:00434B10 89 06                                         mov     [esi], eax      ; 修改数据

text:00434B33 83 C5 04                                      add     ebp, 4
text: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], 1
text: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], 0
text: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:bbca30fc40784ef52ec352c5d9bbadd7a95b2f14
Microsoft:Trojan:Win32/Qakbot.AR!MTB

1:入手点

这个样本前期的操作和上个样本类似,所以本次分析重点并不是他,而是他在内存中解出来的另外一个pe文件,我直接dump下来之后对其进行分析,样本会放到压缩包里。
这次我换了个思路,就是通过火绒剑先大概定位到超时的位置,之后再进行具体分析。

2:具体思路

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

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

由图可知,进程创建了一个计时器对象,在经过一定时间的时候才能出发,这样就可以达到超时的目的。至此,等待的第一个对象解决了,那么等待的第二个内核对象是啥呢?通过一顿猛如虎的操作,最终我找到了其实是个事件对象,

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

GetTickCount

SHA-1:c3f3ecf24b6d39b0e4ff51af31002f3d37677476
Microsoft:Trojan:Win32/Kovter

1:入手点

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

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

2:具体思路

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

3:分析

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

cmp     eax, 7
jz      short loc_401A62
call    ds:GetTickCount
mov     byte_465290, al
jmp     short loc_401A49
----------------------------
movzx   ecx, byte_465290
xor     ecx, 4Eh
xor     ecx, 7
mov     ProcName, cl       ;只有上面al正确,此处函数名的第一个字节才正确

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

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

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

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

API Flooding

SHA-1:6ca2bbcecc76d9c14e0373c919d67729311430a6
Microsoft: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.

看雪论坛2020激励机制:能力值、活跃值和雪币体系!会员积分、权限和会员发帖、回帖活跃程度关联!


文章来源: https://bbs.pediy.com/thread-261592.htm
如有侵权请联系:admin#unsafe.sh