反虚拟机技术
版权声明:本文为博主原创文章,未经博主允许不得转载。
1:浮点运算的花指令 如果不是花指令则更好了
2:借助EPO思想,解密函数远离入口点
3:多线程技术
4:SEH技术
5:元多形技术MetaPolymorphy
慢慢总结
6:同浮点运算一样 借用SSE指令 这些随时会被AV补上 但是缺提供给我们一种思路
- push eax
- comiss xmm0,xmm0 ;比较低位数并且设置标识位
- xor eax,eax
- cvtpi2ps xmm0,mm0 ;32位整数转变为浮点数
- pop eax
7:
- 00401716 |. 54 push esp ;esp->"google.com"
- 00401717 |. FFD7 call edi ;call - > gethostbyname("google.com")
- 00401719 |. 83C4 10 add esp,10
- 0040171C |. 64:A1 34000000 mov eax,dword ptr fs:[34] ; 从teb中读取GetLastError数值给eax
- 00401722 |. 69C0 01000100 imul eax,eax,10001
- 00401728 |. 35 58862413 xor eax,13248658
- 0040172D |. 3D 35A14934 cmp eax,3449A135 ; 校验返回值
- 00401732 |. 74 24 je short Anunnaki.00401758
- 00401734 |. 6A 00 push 0
- 00401736 |. 68 65737300 push 737365
- 0040173B |. 68 50726F63 push 636F7250
- 00401740 |. 68 45786974 push 74697845
- 00401745 |. 54 push esp ; esp->ExitProcess
- 00401746 |. E8 50FEFFFF call Anunnaki.0040159B
- 0040174B |. 50 push eax
- 0040174C |. E8 37FDFFFF call <Anunnaki.my_getprocaddress>
- 00401751 |. 83C4 0C add esp,0C
- 00401754 |. 6A 00 push 0
- 00401756 |. FFD0 call eax ;
- 00401758 |> 61 popad
- 00401759 /. C3 retn
这段代码的作用是:
通过没有 WSAStartup 前调用 gethostbyname 会返回10093错误来检测的:
0040171C |. 64:A1 34000000 mov eax,dword ptr fs:[34] ; 从teb中读取GetLastError数值给eax
00401722 |. 69C0 01000100 imul eax,eax,10001
00401728 |. 35 58862413 xor eax,13248658
0040172D |. 3D 35A14934 cmp eax,3449A135 ; 校验返回值
10093 这样乘出来正好是 0x3449A135。实际机器中直接gethostbyname会因为没有初始化而返回这个错误值,而虚拟环境中可能因为是个stub而GetLastError为0。
任何一个事物都不是尽善尽美,无懈可击的,虚拟机也不例外。由于反虚拟执行技术的出现,使得虚拟机查毒受到了一定的挑战。这里介绍几个比较典型的反虚拟执行技术:
首先是插入特殊指令技术,即在病毒的解密代码部分人为插入诸如浮点,3DNOW,MMX等特殊指令以达到反虚拟执行的目的。尽管虚拟机使用软件技术模拟真正CPU的工作过程,它毕竟不是真正的CPU,由于精力有限,虚拟机的编码者可能实现对整个Intel指令集的支持,因而当虚拟机遇到其不认识的指令时将会立刻停止工作。但通过对这类病毒代码的分析和统计,我们发现通常这些特殊指令对于病毒的解密本身没有发生任何影响,它们的插入仅仅是为了干扰虚拟机的工作,换句话说就是病毒根本不会利用这条随机的垃圾指令的运算结果。这样一来,我们可以仅构造一张所有特殊指令对应于不同寻址方式的指令长度表,而不必为每个特殊指令编写一个专用的模拟函数。有了这张表后,当虚拟机遇到不认识的指令时可以用指令的操作码索引表格以求得指令的长度,然后将当前模拟的指令指针(EIP)加上指令长度来跳过这条垃圾指令。当然,还有一个更为保险的办法那就是:得到指令长度后,可以将这条我们不认识的指令放到一个充满空操作指令(NOP)的缓冲区中,接着我们将跳到缓冲区中去执行,这等于让真正的CPU帮我们来执行这条指令,最后一步当然是将执行后真实寄存器中的结果放回我们的模拟寄存器中。这虚拟执行和真实执行参半方法的好处在于:即便在特殊指令对于病毒是有意义的,即病毒依赖其返回结果的情况下,虚拟机仍可保证虚拟执行结果的正确。
其次是结构化异常处理技术,即病毒的解密代码首先设置自己的异常处理函数,然后故意引发一个异常而使程序流程转向预先设立的异常处理函数。这种流程转移是 CPU和操作系统相互配合的结果,并且在很大程度上,操作系统在其中起了很大的作用。由于目前的虚拟机仅仅模拟了没有保护检查的CPU的工作过程,而对于系统机制没有进行处理。所以面对引发异常的指令会有两种结果:其一是某些设计有缺陷的虚拟机无法判断被模拟指令的合法性,所以模拟这样的指令将使虚拟机自身执行非法操作而退出;其二虚拟机判断出被模拟指令属于非法指令,如试图向只读页面写入的指令,则立刻停止虚拟执行。通常病毒使用该技术的目的在于将真正循环解密代码放到异常处理函数后,如此虚拟机将在进入异常处理函数前就停止了工作,从而使解密子有机会逃避虚拟执行。因而一个好的虚拟机应该具备发现和记录病毒安装异常过滤函数的操作并在其引发异常时自动将控制转向异常处理函数的能力。
再次是入口点模糊(EPO)技术,即病毒在不修改宿主原入口点的前提下,通过在宿主代码体内某处插入跳转指令来使病毒获得控制权。通过前面的分析,我们知道虚拟机扫描病毒时出于效率考虑不可能虚拟执行待查文件的所有代码,通常的做法是:扫描待查文件代码入口,假如在规定步数中没有发现解密循环,则由此判定该文件没有携带加密变形病毒。这种技术之所以能起到反虚拟执行的作用在于它正好利用了虚拟机的这个假设:由于病毒是从宿主执行到一半时获得控制权的,所以虚拟机首先解释执行的是宿主入口的正常程序,当然在规定步数中不可能发现解密循环,因而产生了漏报。如果虚拟机能增加规定步数的大小,则很有可能随着病毒插入的跳转指令跟踪进入病毒的解密子,但确定规定步数大小实在是件难事:太大则将无谓增加正常程序的检测时间;太小则容易产生漏报。但我们对此也不必过于担心,这类病毒由于其编写技术难度较大所以为数不多。在没有反汇编和虚拟执行引擎的帮助下,病毒很难在宿主体内定位一条完整指令的开始处来插入跳转,同时很难保证插入的跳转指令的深度大于虚拟机的规定步数,并且没有把握插入的跳转指令一定会被执行到。
另外还有多线程技术,即病毒在解密部分入口主线程中又启动了额外的工作线程,并且将真正的循环解密代码放置于工作线程中运行。由于多线程间切换调度由操作系统负责管理,所以我们的虚拟机只能在假定被执行线程独占处理器时间,即保证永远不被抢先,的前提下进行。如此一来,虚拟机对于模拟启用多线程工作的代码将很难做到与真实效果一致。多线程和结构化异常处理两种技术都利用了特定的操作系统机制来达到反虚拟执行的目的,所以在虚拟CPU中加入对特定操作系统机制的支持将是我们今后改进的目标。
最后是元多形技术(MetaPolymorphy),即病毒中并非是多形的解密子加加密的病毒体结构,而整体均采用变形技术。这种病毒整体都在变,没有所谓“病毒体明文”。当然,其编写难度是很大的。如果说前几种反虚拟机技术是利用了虚拟机设计上的缺陷,可以通过代码改进来弥补的话,那么这种元多形技术却使虚拟机配合的动态特征码扫描法彻底失效了,我们必须寻求如行为分析等更先进的方法来解决。