首页
社区
课程
招聘
VMProtect进入虚拟机时
发表于: 2007-3-15 00:40 20815

VMProtect进入虚拟机时

2007-3-15 00:40
20815

【文章标题】: VMProtect进入虚拟机时
【文章作者】: BUG
【作者邮箱】: bughoho@hotmail.com
【作者QQ号】: 393277421
【使用工具】: OD and IDA5.0
【操作平台】: XP SP2
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  进入虚拟机时当前堆栈
  
  0013FEA8   00000000  偏移
  0013FEAC   7FFD6000  ebx
  0013FEB0   00000000  edi
  0013FEB4   00900C90  eax
  0013FEB8   00000246  e_flag
  0013FEBC   7C92EB94  edx
  0013FEC0   00000001  ecx
  0013FEC4   7C92EB94  edx
  0013FEC8   00000A28  esi
  0013FECC   0013FEE4  ebp
  
  0013FED4   93CA0014  Key
  
  根据forgot当时的堆栈
  
  [00000000]
  [EDI]
  [EFlags]
  [ESI]
  [EAX]
  [EBP]
  [EDX]
  [ECX]
  [EBX]
  [EAX]
  [VMSEG_CODE]
  [key0]//这里为E95243D1
  
  可以看出这个寄存器压入的位置是乱序的,并且在结构中的位置也是随机生成的
  
  进入虚拟机之前压入所有寄存器的值,
  然后用伪代码存放到自己的VM_Register中
  
  /*笔记*/
  esi                   存放的加密伪代码在当前位置
  edi                   寄存器的基地址
  ebx                   存放的加密伪代码起始地址,然后每解密一句就把bl变形,得到一个新的地址,
                        粗略看了下,好象当前解密的是1字节的就使用1字节解密,4字节就使用4字节解密
  /*笔记*/
  
  struct VM_Context
  {
     DWORD VM_Stack[256];//240*4+已占用堆栈长度*16,总长度1024字节
     //随机排序的
  //VM_Register:
     DWORD e_flag3;//+00  //多个地址保存多个符号?或者一行伪代码用一个地址保存符号?
     DWORD AddResult;//+04//加法结果?
     DWORD edi;//+08
     DWORD ;//+0c
     DWORD offset;//+10//弹出虚拟机进入时压入的偏移,通过这个偏移来找VM_CODE
     DWORD vmencode_code;//+14 //加密的伪代码存放地址.....又存放了符号?
     DWORD ebp;//+18
     DWORD XorResult;//+1c//异或结果
     DWORD eax;//+20
     DWORD ebx;//+24
     DWORD e_flag;//+28
     DWORD ecx;//+2c
     DWORD edx;//+30
     DWORD edx2;//+34
     DWORD e_flag2;//+38
     DWORD esi;//+3c
  }
  
          VMPBEGIN
          addr = (DWORD)str;
          _asm
          {
                  pushad
                  mov eax,addr
                  nop
                  popad
          }
          func2();
          VMPEND
  
  反汇编得到
  jump到执行虚拟机的地方
  
  .vmp0:0040F40B                 push    ebp
  .vmp0:0040F40C                 push    esi
  .vmp0:0040F40D                 push    edx
  .vmp0:0040F40E                 push    ecx
  .vmp0:0040F40F                 push    edx
  .vmp0:0040F410                 pushf
  .vmp0:0040F411                 push    eax
  .vmp0:0040F412                 push    edi
  .vmp0:0040F413                 push    ebx
  .vmp0:0040F414                 push    0
  .vmp0:0040F419                 mov     esi, [esp+28h]  ; esi = VMEncode_Code
  .vmp0:0040F41D                 mov     edx, offset VM_Stack ; 1024字节?
  .vmp0:0040F422                 call    ds:GetCurrentThreadId()
  .vmp0:0040F428                 mov     ebx, eax
  .vmp0:0040F42A                 mov     ecx, 100h
  .vmp0:0040F42F                 mov     edi, edx
  .vmp0:0040F431                 pushf
  .vmp0:0040F432                 cld
  .vmp0:0040F433                 repne scasd
  .vmp0:0040F435                 jz      short loc_40F444
  .vmp0:0040F435
  .vmp0:0040F437                 mov     eax, 100h
  .vmp0:0040F43C                 xchg    eax, ecx
  .vmp0:0040F43D                 mov     edi, edx
  .vmp0:0040F43F                 repne scasd
  .vmp0:0040F441                 mov     [edi-4], ebx    ; 堆栈第一项 = 当前线程ID
  .vmp0:0040F441
  .vmp0:0040F444
  .vmp0:0040F444 loc_40F444:                             ; CODE XREF: sub_40F40B+2Aj
  .vmp0:0040F444                 mov     ebp, edi
  .vmp0:0040F446                 sub     edi, edx
  .vmp0:0040F448                 shl     edi, 1
  .vmp0:0040F44A                 lea     edi, [edx+edi*8+3C0h] ; VM虚拟机环境寄存器基地址
  .vmp0:0040F451                 popf
  .vmp0:0040F451
  .vmp0:0040F452
  .vmp0:0040F452 loc_40F452:                             ; CODE XREF: sub_40F40B+581j
  .vmp0:0040F452                 mov     ebx, esi
  .vmp0:0040F454                 add     esi, [esp+0]    ; VMEncode_Code += 偏移,就是上面最后压入的一个0
  .vmp0:0040F454
  .vmp0:0040F457
  .vmp0:0040F457 executeEip:                             ; CODE XREF: sub_40F40B-E3EBj
  .vmp0:0040F457                                         ; sub_40F40B-5j
  .vmp0:0040F457                                         ; sub_40F40B+73j
  .vmp0:0040F457                                         ; sub_40F40B+81j
  .vmp0:0040F457                                         ; sub_40F40B+8Bj
  .vmp0:0040F457                                         ; sub_40F40B+92j ...
  .vmp0:0040F457                 mov     cl, [esi]       ; 解密当前的p_code
  .vmp0:0040F459                 add     cl, bl
  .vmp0:0040F45B                 xor     cl, 64h
  .vmp0:0040F45E                 neg     cl
  .vmp0:0040F460                 add     esi, 1
  .vmp0:0040F463                 sub     cl, 6Fh
  .vmp0:0040F466                 neg     cl
  .vmp0:0040F468                 xor     cl, 31h
  .vmp0:0040F46B                 add     bl, cl          ; 变换一下密钥
  .vmp0:0040F46D                 movzx   eax, cl         ; eax = 解密后的p_code
  .vmp0:0040F470                 jmp     dword ptr ds:JumpCommand[eax*4] ;
  
  看了下前面执行的一点点伪代码
  伪代码:
  vPop offset;//还没明白做什么的
  vPop ebx
  vPop edi
  vPop eax
  vPop e_flag
  vPop edx
  vPop ecx
  vPop edx
  vPop esi
  vPop ebp
  vPop VM_Context->vmencode_code
  vvPushdw dword      //压入一个DWORD到真实堆栈(写在伪代码中解密得到)
  vAdd [esp],[esp+4]   //add...
  vPop VM_Context->e_flag //保存符号
  
  vvPushdwResultAddr      //压入结果的地址
  vvPushdwResult          //压入结果
  
  vPop VM_Context->AddResult;//保存结果
  
  vvPushVM_Addresult           //从VM结构中将加法结果压入到真实堆栈
  
  vXor [esp],[esp-4]          //[esp-4]表示弹出的操作数..
                              //做异或操作,Key和这个操作数做异或操作
                              //VM的操作是 1 not 0;1 not 0;0 and 0 = 0; 1 xor 1 = 0;
  vPop VM_Context->XorResult//保存异或结果
  
  vvPushdw dword            //压入操作数
  vXor [esp],[esp-4]        //Key和这个操作数做异或操作
  vPop VM_Context->e_flag2  //把符号保存到这个地址.
  
  vvPushdw dword            //压入操作数
  vvPushVM_Addresult        //从VM结构中将加法结果压入到真实堆栈
  
  vXor [esp],[esp-4]        //做异或操作
  vPop VM_Context->e_flag3  //保存符号
  vXor [esp],[esp-4]        //Key和上一次保存的值做异或
  vPop VM_Context->e_flag2  //保存符号
  
  vvPushdwResultAddr        //压入结果的地址
  vvPushdwResult            //压入结果
  
  vXor [esp],[esp-4]        //Key和上一次保存的值做异或
  vPop VM_Context->e_flag*  //保存符号
  
  vvPushbyte                //压入操作数.长度byte
  
  vadd [esp],[esp-4]        //Key和上一次保存的值做异或
  vPop VM_Context->e_flag2  //保存符号
  
  ....单步时,OD卡住了....
  
  反正跟到这里时还没看到我想看到的伪代码.
  
--------------------------------------------------------------------------------
【经验总结】
  总结下,进入虚拟机时VMP随机把寄存器压入堆栈,存放当前ThreadID到VM_Stack.
  然后通过自己的伪代码取出存放到自己的VM_REG中去(当然这个随机顺序是生成EXE时定下来的).
  结构中的位置也是随机生成的.
  
  and al,0x3C     一得到VM结构一些寄存器或其他位置的骗移
  
  解密伪代码有一个庞大的算法,写出来不易..
  VM_CODE的地址当KEY,然后每解一个操作码会变形这个KEY的低字节,每一个伪代码都是不同的变形算法.
  这些涉及到立即数的地方应该是VMP随机生成的.
  换句话说,写解密算法得把每一个命令的函数里的那一小块加密代码全搞出来
  结论是..不是人干的活.
  
  VMP的伪代码都是在堆栈中做运算的,带vv的是涉及到传输VM数据的伪代码.
  另外相关伪代码会保存标志寄存器.防止出错.
  
  就研究了这么点...
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2007年03月15日 0:38:37


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 7
支持
分享
最新回复 (24)
雪    币: 331
活跃值: (56)
能力值: ( LV13,RANK:410 )
在线值:
发帖
回帖
粉丝
2
占个位置听讲
对于VM保护的代码?怎么patch呢?
2007-3-15 00:59
0
雪    币: 198
活跃值: (11)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
板凳.....没什么耍头得.
2007-3-15 01:09
0
雪    币: 311
活跃值: (124)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
学习ing,顶一个.
2007-3-15 04:49
0
雪    币: 7309
活跃值: (3788)
能力值: (RANK:1130 )
在线值:
发帖
回帖
粉丝
5
强人,学习一下
2007-3-15 08:35
0
雪    币: 217
活跃值: (15)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
6
学习
2007-3-15 09:04
0
雪    币: 846
活跃值: (221)
能力值: (RANK:570 )
在线值:
发帖
回帖
粉丝
7
强烈支持楼主把这东西逆了
2007-3-15 13:01
0
雪    币: 258
活跃值: (230)
能力值: ( LV12,RANK:770 )
在线值:
发帖
回帖
粉丝
8
呵呵.
支持...
2007-3-15 13:07
0
雪    币: 136
活跃值: (105)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
9
小七你逆吧 嘎嘎
2007-3-15 14:51
0
雪    币: 413
活跃值: (637)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
10
呵呵,终于找到家了。

年前研究了一个软件,加密就是用的这种加密,俺没有那么多分析壳
子的经验,按这个内容写了一个记录代码的程序,把程序的运行过程记录了下来,当然没有研究那么深,程序多次调用解码位置,我生成的
记录文件有200M左右的文本,通过这个文件,可以基础看清程序的流程了。只是代码量巨大,看完实在不易。

这个东西还是有规律可找的。但还是需要细心和最大的耐心。

指定KEY值做解码,按地址进行处理解码,前后指令相关的,前个字节用于
解码后一个字节,分配了自己的数据区和堆栈,进行数据的处理。
2007-3-15 15:46
0
雪    币: 304
活跃值: (82)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
11
VMProtect有麻烦了,让楼主盯上了~~~~~
2007-3-15 16:55
0
雪    币: 277
活跃值: (312)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
12
谁要是只加密算法部分,就倒霉了
2007-3-15 18:50
0
雪    币: 5
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
呵呵,越来越有趣了。
假如每次算法传过来用VM算,就是每次都不一样,恐怖!!!!
2007-3-15 19:50
0
雪    币: 392
活跃值: (909)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
14
支持~
要把Opcode的加密算法逆向出来确实要费一番功夫

BTW:Bughoho兄用的是哪个版本的VMP?我加的 1.22 进入Interpreter时没有调GetCurrentThreadId,这个跟被处理的代码有关吗?
2007-3-15 21:26
0
雪    币: 1946
活跃值: (248)
能力值: (RANK:330 )
在线值:
发帖
回帖
粉丝
15
我也是1.22的.我也不大明白为什么要保存ThreadID到VM堆栈,估计是做标志吧...
2007-3-15 21:38
0
雪    币: 293
活跃值: (110)
能力值: ( LV9,RANK:410 )
在线值:
发帖
回帖
粉丝
16
BUG兄的逆向功力令人佩服
2007-3-16 08:59
0
雪    币: 254
活跃值: (126)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
17
最初由 cyclotron 发布
支持~
要把Opcode的加密算法逆向出来确实要费一番功夫

BTW:Bughoho兄用的是哪个版本的VMP?我加的 1.22 进入Interpreter时没有调GetCurrentThreadId,这个跟被处理的代码有关吗?


隐约记得似乎你的程序输入表里如果有GetCurrentThreadId,VMP就认为你的程序是需要支持多线程的,就会有这部分处理
2007-3-16 09:15
0
雪    币: 392
活跃值: (909)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
18
最初由 heXer 发布
隐约记得似乎你的程序输入表里如果有GetCurrentThreadId,VMP就认为你的程序是需要支持多线程的,就会有这部分处理

Many thanks~
2007-3-16 10:16
0
雪    币: 207
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
不错的东西。。。漫漫看
2007-3-16 10:47
0
雪    币: 70
活跃值: (74)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
20
原来GetCurrentThreadId是为后面多线程的啊
我就一直不明白为什么VMprotect把ID放入VMContext中干什么
2007-3-19 19:57
0
雪    币: 1946
活跃值: (248)
能力值: (RANK:330 )
在线值:
发帖
回帖
粉丝
21
最初由 heXer 发布
隐约记得似乎你的程序输入表里如果有GetCurrentThreadId,VMP就认为你的程序是需要支持多线程的,就会有这部分处理


最初由 yiyiguxing 发布
原来GetCurrentThreadId是为后面多线程的啊
我就一直不明白为什么VMprotect把ID放入VMContext中干什么


不过这部分处理好象没什么用...猜测而已.
2007-3-20 06:08
0
雪    币: 1946
活跃值: (248)
能力值: (RANK:330 )
在线值:
发帖
回帖
粉丝
22
[此言差矣,删掉为好,以免祸害苍生.]
2007-3-20 06:20
0
雪    币: 392
活跃值: (909)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
23
最初由 Bughoho 发布
另外我的思想有误差,一开始就认为VMP是虚拟寄存器和堆栈的,所以结构的命名也就错了.
VMP应该是模糊了寄存器,更没有它自己的堆栈,运算都是在堆栈实现,结果并不是存放在什么虚拟的寄存器中,而是堆栈中或者它自己指定的某个地址.

第一层里面跟现实寄存器对应的虚拟寄存器应该算是有的,堆栈倒是没有发现。。。
不过第二层很难说啊,没有分析下去
2007-3-20 11:34
0
雪    币: 1946
活跃值: (248)
能力值: (RANK:330 )
在线值:
发帖
回帖
粉丝
24
最初由 cyclotron 发布
第一层里面跟现实寄存器对应的虚拟寄存器应该算是有的,堆栈倒是没有发现。。。
不过第二层很难说啊,没有分析下去


呵呵,我开始在认为是寄存器偏移的地方加了命名之后发现竟然没有保存就将其他值写进去了.立即就认为VMP模糊了寄存器.也没有继续往下分析.

当时一直没转过弯来,觉得VMP扔掉了寄存器的概念,全都用地址来保存结果,现在想起来,其实一切都是理所当然的.

谢谢提醒,继续往下跟.
2007-3-21 17:36
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
VMP 太难了。暂时放弃
2007-3-25 22:23
0
游客
登录 | 注册 方可回帖
返回
//