元始天尊 发表于 2015-2-8 13:18:52

虚拟机检测程序的分析

工作了就忙了,抽空研究写个文章:

起因:前几个礼拜在网上下了个视频exe(5.exe),win7下运行直接异常,因此想放到xp虚拟机里看看,结果直接检测出虚拟机,弹窗后直接关闭,很好奇想知道怎么检测的。
用windbg加载该exe,运行到弹窗后暂停,“Play请不要在虚拟机中播放!!”。查看调用栈,发现空空如也,感觉不太妙,试着移动窗体,发现居然可以移动!那么这一定是另一个进程的,也就是说5.exe启动了另一个进程。用process explorer查看进程,发现进程树中5.exe居然开启了C:\WINDOWS\system32\winver.exe,命令行参数传的是F238FF10FB00F72CD46CD727B402EC259151C620E00AEC26C25EE230F90AEC28C276D135E00CF01D66C260B2C817E732C55E967AF11BE7,之后我在真正的(非虚拟机)win7系统里运行了一下,这个串变成了F238FF01E706F032ED6ECA37FC02ED1DF567D03FE00CF21D842CC62CF1,现在奇怪的事情来了,自己手动执行winver.exe,参数是后面那个长串,预期会弹上面那个窗口,结果却只是winver自己的界面。后来搞了很长时间没有什么进展,无意间我看到5.exe资源(DEL_FD1)里有个pe文件(记为DEL_FD1.exe),把它dump出来,加上那个参数运行了一下,居然弹窗了,也就说5.exe吧资源在内存dump下来直接运行了,没有通过生成文件这一步。。。而虚拟机检测和参数解析都是DEL_FD1.exe做的,5.exe仅仅用于产生另一个进程。详细看了字符串引用还会有更多发现,原来5.exe用到了BoxedApp SDK来实现资源直接生成进程这一步。如果将资源DEL_FD1替换成其他exe,会发现运行的恰好是替换的这个文件!!!现在目标明确了,用Windbg分析该del_fd1.exe,当然要加上会弹窗的那个参数,再弹窗时,暂停下来看调用栈:
0012F67C77D19418   包含ntdll.KiFastSystemCallRet         USER32.77D19416               0012F6AC
0012F68000469032<JMP.&user32.WaitMessage>             DEL_FD1.0046902D            0012F6AC
0012F6B0004684AC   ? DEL_FD1.00468F00                  DEL_FD1.004684A7            0012F6AC
0012F6D400464C28   DEL_FD1.00468490                      DEL_FD1.00464C23            0012F730
这次仍然不是很明显(调Dephi程序好难受,各种奇怪的库函数),后几个函数跟进去以后可能是窗口回调之类的。没办法,搜字符串吧,0x4a9eb0处可以搜到结果,之后在这里下访问断点,再看调用栈:
0012F61C77D1AE26ntdll.RtlMultiByteToUnicodeN         USER32.77D1AE20            0012F618
0012F64477D3C76E   USER32.MBToWCSEx                      USER32.77D3C769               0012F640
0012F67477D3C730   USER32.DrawTextExA                  USER32.77D3C72B               0012F670
0012F67801010054   hDC = 01010054
0012F67C004A9EB0   Text = "请不要在虚拟机中播放!!"
0012F68000000017   Count = 17 (23.)
0012F6840012F6E8   pRect = 0012F6E8{0.,0.,960.,0.}
0012F68800000450   Flags =DT_LEFT|DT_TOP|DT_WORDBREA
0012F68C00000000   pDrawTextParams =NULL
0012F6A80043B898<JMP.&user32.DrawTextA>               DEL_FD1.0043B893            0012F6A4
0012F6AC01010054   hDC = 01010054
0012F6B0004A9EB0   Text = "请不要在虚拟机中播放!!"
0012F6B400000017   Count = 17 (23.)
0012F6B80012F6E8   pRect = 0012F6E8{0.,0.,960.,0.}
0012F6BC00000450   Flags =DT_LEFT|DT_TOP|DT_WORDBREA
0012F74C0043BC52   ? DEL_FD1.0043B68C                  DEL_FD1.0043BC4D            0012F748
0012F7580043BD9B   ? DEL_FD1.0043BC2C                  DEL_FD1.0043BD96
0012F7780043BC93   ? DEL_FD1.0043BD34                  DEL_FD1.0043BC8E
0012F77C00000000   Arg1 = 00000000
0012F780FFFFFFFF   Arg2 = FFFFFFFF
0012F784FFFFFFFF   Arg3 = FFFFFFFF
0012F78800000000   Arg4 = 00000000
0012F7940043BDCE   DEL_FD1.0043BC74                      DEL_FD1.0043BDC9            0012F790
0012F798FFFFFFFF   Arg1 = FFFFFFFF
0012F79CFFFFFFFF   Arg2 = FFFFFFFF
0012F7A000000000   Arg3 = 00000000
0012F7A40043BDBB   DEL_FD1.0043BDBC                      DEL_FD1.0043BDB6            0012FB78
0012F7A8004A94F7   DEL_FD1.0043BDB0                      DEL_FD1.004A94F2            0012FB78
可以发现窗口是DrawTextA产生的,怪不得MessageBox断不下呢!输入at 004A94F7转到反汇编,跟到最后终于快得到结果了:
。。。。。。。。。。。。前面一堆代码。。。。。。。。。。。
.text:004A94CE loc_4A94CE:                           ; CODE XREF:sub_4A8A1C+AAAj
.text:004A94CE                                       ; DATA XREF: sub_4A8A1C+A95o
.text:004A94CE               call    sub_4A88D0
.text:004A94D3               mov   eax, ds:dword_53DF44
.text:004A94D8               mov   edx, offset dword_4A9EA4
.text:004A94DD               call    sub_4056B0
.text:004A94E2               jnz   short loc_4A9527
.text:004A94E4               call    sub_4A866C
.text:004A94E9               test    eax, eax
.text:004A94EB               jz      short loc_4A9501
.text:004A94ED               mov   eax, offset aIKST ; "请不要在虚拟机中播放!!\xFF\xFF\xFF\xFF\x17"
.text:004A94F2               call    @ShowMessage
.text:004A94F7               mov   eax, ds:dword_53DF0C
.text:004A94FC               call    @TCustomForm@Close ; TCustomForm::Close
.text:004A9501
.text:004A9501 loc_4A9501:                           ; CODE XREF:sub_4A8A1C+ACFj
.text:004A9501               call    sub_4A871C
.text:004A9506               test    al, al
.text:004A9508               jnz   short loc_4A9513
.text:004A950A               call    loc_4A86CC
.text:004A950F               test    al, al
.text:004A9511               jz      short loc_4A9527
.text:004A9513
.text:004A9513 loc_4A9513:                           ; CODE XREF:sub_4A8A1C+AECj
.text:004A9513               mov    eax, offset aIKST_0 ; "请不要在虚拟机中播放!!!\xFF\xFF\xFF\xFF\x01"
.text:004A9518               call    @ShowMessage
.text:004A951D               mov   eax, ds:dword_53DF0C
.text:004A9522               call    @TCustomForm@Close ; TCustomForm::Close
.text:004A9527
.text:004A9527 loc_4A9527:                           ; CODE XREF:sub_4A8A1C+AC6j
.text:004A9527                                       ;sub_4A8A1C+AF5j
.text:004A9527               mov   eax, ds:dword_53DF44
.text:004A952C               mov   edx, offset dword_4A9EA4
.text:004A9531               call    sub_4056B0
.text:004A9536               jnz   short loc_4A9567
.text:004A9538               call    sub_4A866C
.text:004A953D               test    eax, eax
.text:004A953F               jz      short loc_4A954B
.text:004A9541               mov   eax, ds:dword_53DF0C
.text:004A9546               call    @TCustomForm@Close ; TCustomForm::Close
.text:004A954B
.text:004A954B loc_4A954B:                           ; CODE XREF: sub_4A8A1C+B23j
.text:004A954B               call    sub_4A871C
.text:004A9550               test    al, al
.text:004A9552               jnz   short loc_4A955D
.text:004A9554               call    loc_4A86CC
.text:004A9559               test    al, al
.text:004A955B               jz      short loc_4A9567
.text:004A955D
.text:004A955D loc_4A955D:                           ; CODE XREF:sub_4A8A1C+B36j
.text:004A955D               mov   eax, ds:dword_53DF0C
.text:004A9562               call    @TCustomForm@Close ; TCustomForm::Close
。。。。。。。。。。。。后面一堆代码。。。。。。。。。。。
可以得到如下关键逻辑:
if(sub_4A866C() || sub_4A871C() ||sub_4A86CC())
{
ShowMessage(“请不要在虚拟机中播放”);
TcustomForm.Close();
}
。。。。。。。。。。。。。。。

先来看sub_4A866C:
.text:004A866C               push    ebp
.text:004A866D               mov   ebp, esp
.text:004A866F               push    ecx
.text:004A8670               push    ebx
.text:004A8671               push    esi
.text:004A8672               push    edi
.text:004A8673               xor   eax, eax
.text:004A8675               mov   , eax
.text:004A8678               xor   eax, eax
.text:004A867A               push    ebp
.text:004A867B               push    offset loc_4A86B4
.text:004A8680               push    dword ptr fs:
.text:004A8683               mov   fs:, esp
.text:004A8686               mov   eax, 564D5868h
.text:004A868B               mov   ebx, 0
.text:004A8690               mov   ecx, 0Ah
.text:004A8695               mov   edx, 5658h
.text:004A869A               in      eax, dx
.text:004A869B               cmp   ebx, 564D5868h
.text:004A86A1               jnz   short loc_4A86AA
.text:004A86A3               mov   , 1
.text:004A86AA
.text:004A86AA loc_4A86AA:                           ; CODE XREF:sub_4A866C+35j
.text:004A86AA               xor   eax, eax
.text:004A86AC               pop   edx
.text:004A86AD               pop   ecx
.text:004A86AE               pop   ecx
.text:004A86AF               mov   fs:, edx
.text:004A86B2               jmp    short loc_4A86C3
.text:004A86B4 ;---------------------------------------------------------------------------
.text:004A86B4
.text:004A86B4 loc_4A86B4:                           ; DATA XREF:sub_4A866C+Fo
.text:004A86B4               jmp   @@HandleAnyException ; __linkproc__HandleAnyException
.text:004A86B9 ;---------------------------------------------------------------------------
.text:004A86B9               xor   eax, eax
.text:004A86BB               mov   , eax
.text:004A86BE               call    @@DoneExcept    ; __linkproc__ DoneExcept
.text:004A86C3
.text:004A86C3 loc_4A86C3:                           ; CODE XREF:sub_4A866C+46j
.text:004A86C3               mov   eax,
.text:004A86C6               pop   edi
.text:004A86C7               pop   esi
.text:004A86C8               pop   ebx
.text:004A86C9               pop   ecx
.text:004A86CA               pop   ebp
.text:004A86CB               retn
可以得到如下代码:
bool IsUnderVM1()
{
bool flag=false;
__try
{
_asm
{
mov eax,’VMXh’;
mov ebx,0;
mov ecx,0Ah;
mov edx,’VX’;
in eax,dx;
cmp ebx,’VMXh’;
jnz RESULT;
mov flag,1;
RESULT:   ;
}
}
__except(1)
{
      flag=false;
}
return flag;
}

下面来看sub_4A871C函数:
.text:004A871C               push    ebp
.text:004A871D               mov   ebp, esp
.text:004A871F               push    ecx
.text:004A8720               push    ebx
.text:004A8721               push    esi
.text:004A8722               push   edi
.text:004A8723               mov   , 0
.text:004A8727               xor   eax, eax
.text:004A8729               push    ebp
.text:004A872A               push    offset loc_4A875F
.text:004A872F               push    dword ptr fs:
.text:004A8732               mov   fs:, esp
.text:004A8735               push    edx
.text:004A8736               push    ecx
.text:004A8737               push    ebx
.text:004A8738               mov   eax, 'VMXh'
.text:004A873D               mov   ecx, 0Ah
.text:004A8742               mov   edx, 5658h
.text:004A8747               in      eax, dx
.text:004A8748               cmp   ebx, 'VMXh'
.text:004A874E               setz   
.text:004A8752               pop   ebx
.text:004A8753               pop   ecx
.text:004A8754               pop   edx
.text:004A8755               xor   eax, eax
.text:004A8757               pop   edx
.text:004A8758               pop   ecx
.text:004A8759               pop   ecx
.text:004A875A               mov   fs:, edx
.text:004A875D               jmp   short loc_4A8769
.text:004A875F ;---------------------------------------------------------------------------
.text:004A875F
.text:004A875F loc_4A875F:                           ; DATA XREF:sub_4A871C+Eo
.text:004A875F               jmp   @@HandleAnyException ; __linkproc__HandleAnyException
.text:004A8764 ;---------------------------------------------------------------------------
.text:004A8764               call    @@DoneExcept    ; __linkproc__ DoneExcept
.text:004A8769
.text:004A8769 loc_4A8769:                           ; CODE XREF:sub_4A871C+41j
.text:004A8769               movzx   eax,
.text:004A876D               pop   edi
.text:004A876E               pop   esi
.text:004A876F               pop   ebx
.text:004A8770               pop   ecx
.text:004A8771               pop   ebp
.text:004A8772               retn
翻译以后为:
bool IsUnderVM2()
{
bool flag=false;
__try
{
_asm
{
mov eax,’VMXh’;
mov ecx,0Ah;//注意和IsUnderVM1不同
mov edx,’VX’;
in eax,dx;
cmp ebx,’VMXh’
setz flag;
}
}
__except(1)
{
      flag=false;
}
return flag;
}


下面来看004A86CC处的函数,IDA未能正确识别:
.text:004A86CC               push    ebp
.text:004A86CD               mov   ebp, esp
.text:004A86CF               push    ecx
.text:004A86D0               push    ebx
.text:004A86D1               push   esi
.text:004A86D2               push    edi
.text:004A86D3               mov   byte ptr , 0
.text:004A86D7               xor   eax, eax
.text:004A86D9               push    ebp
.text:004A86DA               push    offset unk_4A8705
.text:004A86DF               push    dword ptr fs:
.text:004A86E2               mov   fs:, esp
.text:004A86E5               push    ebx
.text:004A86E6               mov   ebx, 0
.text:004A86EB               mov   eax, 1
.text:004A86EB ;---------------------------------------------------------------------------
.text:004A86F0               db 0Fh
.text:004A86F1               db 3Fh
.text:004A86F2               db 7
.text:004A86F3               db 0Bh
.text:004A86F4               db85h ; ?
.text:004A86F5               db 0DBh ; ?
.text:004A86F6               db0Fh
.text:004A86F7               db94h ; ?
.text:004A86F8               db45h ; E
.text:004A86F9               db 0FFh
.text:004A86FA               db5Bh ; [
.text:004A86FB               db33h ; 3
.text:004A86FC               db 0C0h ; ?
.text:004A86FD               db5Ah ; Z
.text:004A86FE               db59h ; Y
.text:004A86FF               db59h ; Y
.text:004A8700               db64h ; d
.text:004A8701               db89h ; ?
.text:004A8702               db10h
.text:004A8703               db 0EBh ; ?
.text:004A8704               db0Ah
.text:004A8705 unk_4A8705      db 0E9h ; ?            ; DATA XREF: .text:004A86DAo
.text:004A8706               db5Eh ; ^
.text:004A8707               db 0C1h ; ?
.text:004A8708               db 0F5h ; ?
.text:004A8709               db 0FFh
.text:004A870A               db 0E8h ; ?
.text:004A870B               db69h ; i
.text:004A870C               db 0C5h ; ?
.text:004A870D               db 0F5h ; ?
.text:004A870E               db 0FFh
.text:004A870F               db0Fh
.text:004A8710               db 0B6h ; ?
.text:004A8711               db45h ; E
.text:004A8712               db 0FFh
.text:004A8713               db5Fh ; _
.text:004A8714               db5Eh ; ^
.text:004A8715               db5Bh ; [
.text:004A8716               db59h ; Y
.text:004A8717               db5Dh ; ]
.text:004A8718               db 0C3h ; ?

经过手动识别指令最终可以得到:
.text:004A86CC loc_4A86CC:                           ; CODE XREF:sub_4A8A1C+AEEp
.text:004A86CC                                       ;sub_4A8A1C+B38p
.text:004A86CC               push    ebp
.text:004A86CD               mov   ebp, esp
.text:004A86CF               push    ecx
.text:004A86D0               push    ebx
.text:004A86D1               push    esi
.text:004A86D2               push    edi
.text:004A86D3               mov   byte ptr , 0
.text:004A86D7               xor   eax,eax
.text:004A86D9               push    ebp
.text:004A86DA               push    offset loc_4A8705//注册异常回调
.text:004A86DF               push    dword ptr fs:
.text:004A86E2               mov   fs:, esp
.text:004A86E5               push    ebx
.text:004A86E6               mov   ebx, 0
.text:004A86EB               mov   eax, 1
.text:004A86EB ;---------------------------------------------------------------------------
.text:004A86F0               db 0Fh
.text:004A86F1               db 3Fh
.text:004A86F2               db 7
.text:004A86F3               db 0Bh
.text:004A86F4 ;---------------------------------------------------------------------------
.text:004A86F4               test    ebx, ebx//ZF位为1
.text:004A86F6               setz    byte ptr //ebp[-1]=1;
.text:004A86FA               pop   ebx
.text:004A86FB               xor   eax, eax
.text:004A86FD               pop   edx
.text:004A86FE               pop   ecx
.text:004A86FF               pop    ecx
.text:004A8700               mov   fs:, edx
.text:004A8703               jmp   short loc_4A870F
.text:004A8705 ;---------------------------------------------------------------------------
.text:004A8705
.text:004A8705 loc_4A8705:                           ; DATA XREF:.text:004A86DAo
.text:004A8705               jmp   @@HandleAnyException ; __linkproc__HandleAnyException
.text:004A870A ;---------------------------------------------------------------------------
.text:004A870A               call    @@DoneExcept    ; __linkproc__ DoneExcept
.text:004A870F
.text:004A870F loc_4A870F:                           ; CODE XREF:.text:004A8703j
.text:004A870F               movzx   eax, byte ptr
.text:004A8713               pop   edi
.text:004A8714               pop   esi
.text:004A8715               pop   ebx
.text:004A8716               pop   ecx
.text:004A8717               pop   ebp
.text:004A8718               retn
中间的0F3F070B是什么,我也不知道于是百度到了这个网站http://hp.vector.co.jp/authors/VA028184/OllyDbgQA.htm,原来是Virtual PC自造的一个指令(不过具体是什么指令我没查到,知道的可以说下),在其他架构下(如x86)为非法指令会产生异常(情况1),而Virutal PC下能正常执行(情况2)。下面分别讨论:
函数公共部分:ebp[-1]=0,eax=0;004A86D9~ 004A86E2为异常初始化,004A8705为发生异常的回调地址;ebx=0;eax=1情况1:走到004A86F0处,由于不识别指令因此引发异常,直接到004A8705,后面走到004A870F,eax=ebp[-1]=0。
情况2:004A86F0处指令正常执行,test指令置ZF位为1,setz的结果是ebp[-1]=1,eax=0,接下来几个pop抵销前面注册异常处理的push,edx刚好对应004A86DF代码处原先入栈的异常帧,movfs:, edx恢复了原始异常链,之后运行到004A870F,eax=ebp[-1]=1。原始代码是Delphi写的,这里用MSVC的C代码表示:
bool IsUnderVM3()
{
bool flag=false;
__try
{
_asm
{
_EMIT 0x0F;
_EMIT 0x3F;
_EMIT 0x07;
_EMIT 0x0B;
}
flag=true;
}
__except(1)
{
}
return flag;
}
现在我对BoxedAppSDK产生了兴趣,以后有时间会研究,感觉很有用,看了一下可以用虚拟方式操作文件、注册表、环境变量、内存、进程。收费蛮高的,从官网上下了一份demo,看了例子有4个:嵌入dll加载,嵌入flash加载,使用内存pe生成进程,使用notepad打开内存文件。这套技术应该是很有用的!!!

元始天尊 发表于 2015-2-8 13:26:48

用到的文件

0x01810 发表于 2015-2-10 09:26:12

学习了 不过这个检测虚拟机的方法都被用烂了.

元始天尊 发表于 2015-2-10 19:18:18

0x01810 发表于 2015-2-10 09:26
学习了 不过这个检测虚拟机的方法都被用烂了.

第三个方法没看到有谁弄过
页: [1]
查看完整版本: 虚拟机检测程序的分析