逆向VMP3.X虚拟化入门

虚拟机的概念与VMP

平常所用的安卓模拟器,VMware,VirtualBox等就是虚拟机,它们的工作流程很简单,即,读取一条指令,执行这条指令,读取下一条指令,执行这条指令…

写成伪代码:

while(true) // The opcode dispatcher
{
    opcode = opcodes[ip++]
    handler = GetHandler(opcode)
    handler();
}

这些虚拟机如安卓模拟器,它是模拟的真实CPU指令(Arm指令)。而VMP则设计了一套CPU,将x86指令编译到此CPU的字节码,虚拟执行。

在上文伪代码中出现的变量如opcode,handler与ip,都是提供虚拟机运行,而非虚拟机虚拟的内容。

被模拟的内容仅会在handler中被操作。

VMP虚拟机架构

VMP的CPU与x86CPU类似,存在栈,与寄存器。

当VMP的虚拟机启动时,VMP会把x86下的寄存器转移到VMP的寄存器中(虚拟寄存器,以下称VRegs)

当VMP的虚拟机退出时,VMP会把VRegs转移到x86的寄存器中

VMP的虚拟机(以下称VM)启动后,即开始像其他虚拟机一样,不断增加虚拟指令指针(Virtual Instruction Pointer,以下称VIP),通过VIP确定handler,执行handler

VM的计算过程在虚拟栈(Virtual Stack,以下称VStack)中完成,如果你熟悉C#的IL指令,VM不断地进行VPUSH与VPOP,这将是一个熟悉的过程

VMP(3.X)将以上虚拟机的“opcode dispatcher” unroll,脱离while不断执行handler

VM的启动

(VMP 3.4 x64 使用x64dbg trace得到 )

005 | push r8                                
006 | push rax                               
007 | mov r8w,71F4                           
008 | movsxd r8,r10d                         
009 | push rsi                               
00A | mov r8b,r15b                           
00B | movzx r8w,dil                          
00C | push r10                               
00D | mov r10b,BF                            
00E | push rdx                               
00F | cmovs r10w,r9w                         
010 | cwd                                    
011 | push rdi                               
012 | pushfq                                 
013 | shr r8b,cl                             
014 | sar dh,6F                              
015 | rol r10b,AD                            
016 | push r9                                
017 | adc r10b,43                            
018 | push rbp                               
019 | movzx r8w,r10b                         
01A | ror r8w,cl                             
01B | cmovns dx,r15w                         
01C | push r13                               
01D | push r14                               
01E | ror di,91                              
01F | push r11                               
020 | push rcx                               
021 | adc dil,3A                             
022 | cmc                                    
023 | jmp 1400AE88C                          
024 | push r15                               
025 | rcl r11d,cl                            
026 | shr dil,cl                             
027 | cdq                                    
028 | push rbx                               
029 | push r12                               

VMP启动时首先将x86寄存器压入x86栈中

在这里,x86寄存器对于VM的作用仅仅是为了被送入栈中,所以类似mov r8w,71F4这样的指令都是花指令,无需关注

047 | mov rdi,rsp 

随后VM将x86栈指针赋值给rdi,此时,rdi变为VM的VStack

(!!注意,并非每次VStack都为rdi,VMP每次编译虚拟机时都会产生变化)

(!!注意,此时尚未进入handler,VStack是给虚拟机运行用的而不是被虚拟的内容)

056 | mov r10d,dword ptr ss:[rbp]            
057 | cmp r10d,r8d                           
058 | test dx,FDC                            
059 | add rbp,4                              
05A | jmp 140081FC0                          
05B | xor r10d,r11d                          
05C | stc                                    
05D | not r10d                               
05E | add r10d,2BFF5AA0                      
05F | not r10d                               
060 | sub r10d,67D85431                      
061 | push r11                               
062 | movzx r11,cx                           
063 | xor r11b,r10b                          
064 | mov r11b,CB                            
065 | xor dword ptr ss:[rsp],r10d            
066 | rol r11,cl                             
067 | add r11b,A3                            
068 | sbb r11w,44EE                          
069 | pop r11                                
06A | movsxd r10,r10d                        
06B | add rbx,r10                            
06C | jmp 1400ABB4B                          
06D | jmp rbx                                

随后VM安排rbp作为VIP

第56行VM利用VIP读取opcode

第6B行VM利用opcode计算得到handler

第6D行VM开始执行handler

进入handler后就是真正的虚拟化世界

06E | mov r8,qword ptr ds:[rdi]              
06F | test r9b,E9                            
070 | ror r10b,C1                            
071 | add rdi,8                              
072 | movzx r10d,byte ptr ss:[rbp]           
073 | stc                                    
074 | add rbp,1                              
075 | clc                                    
076 | test r9w,1E3D                          
077 | xor r10b,r11b                          
078 | jmp 14000CF51                          
079 | not r10b                               
07A | jmp 140098145                          
07B | dec r10b                               
07C | clc                                    
07D | cmc                                    
07E | stc                                    
07F | rol r10b,1                             
080 | neg r10b                               
081 | add r10b,6C                            
082 | clc                                    
083 | cmc                                    
084 | cmp r15b,r13b                          
085 | xor r11b,r10b                          
086 | test r14w,16D0                         
087 | mov qword ptr ss:[rsp+r10],r8          
088 | mov r8d,dword ptr ss:[rbp]             
089 | jmp 140086333                          
08A | add rbp,4                              
08B | xor r8d,r11d                           
08C | jmp 1400D0AA9                          
08D | inc r8d                                
08E | stc                                    
08F | cmc                                    
090 | ror r8d,1                              
091 | stc                                    
092 | xor r8d,31B317C7                       
093 | cmc                                    
094 | clc                                    
095 | jmp 14005C21D                          
096 | ror r8d,1                              
097 | test r9b,bl                            
098 | test r13w,4998                         
099 | push r11                               
09A | xadd r11d,r11d                         
09B | xor dword ptr ss:[rsp],r8d             
09C | cmp r13w,di                            
09D | pop r11                                
09E | movsxd r8,r8d                          
09F | stc                                    
0A0 | clc                                    
0A1 | add rbx,r8                             
0A2 | jmp 140094398                          
0A3 | push rbx                               
0A4 | ret                                    

在这个handler中做了很多事

  • 从rdi(VStack)中读取值到r8,并将r8存入[rsp+r10]
  • 第88行利用VIP读取OPCODE,第8A行VIP+=4,第A1行得到handler,随后利用push ip,ret的方式跳转到handler

翻译成人话

  • VM将VStack中暂存的x86寄存器转移至VRegs中,这里rsp+r10就是VReg的指针
  • 由于VM(3.X)unroll了opcode的分发器,在handler执行后,会执行dispatcher应该干的事情,即读取下一个opcode并跳转至下一个handler

以上操作将执行“寄存器个数”次

此时VM启动结束

VM的模拟执行

我这里仅对一行汇编进行VMP虚拟化

add rax, 1

摘抄handler关键部分如下

731 | mov rax,qword ptr ss:[rbp] 
748 | sub rdi,8 
74B | mov qword ptr ds:[rdi],rax
78B | mov r9,qword ptr ss:[rsp+rdx] 
78E | sub rdi,8 
78F | mov qword ptr ds:[rdi],r9
7B1 | mov rsi,qword ptr ds:[rdi]             
7B2 | or ax,r15w                             
7B3 | mov r8,qword ptr ds:[rdi+8]            
7B4 | bswap rax                              
7B5 | shl ah,6A                              
7B6 | add rsi,r8                             
7B7 | mov qword ptr ds:[rdi+8],rsi           
7DE | add rdi,8                              
81B | mov r8,qword ptr ds:[rdi]              
834 | mov qword ptr ss:[rsp+r10],r8          

翻译一下,这四段代码执行了五个不同的handler

VPUSH   [VIP]
VPUSH   VRegs[VRAX]
VADD    
VPOP
VRegs[VRAX] = [VStack]

翻译成伪代码

RAX = RAX + *VIP ; *VIP 为 1

VM的退出

其流程与VM的启动类似

VM清空VStack后不断地将VRegs 使用VPUSH hander压入VStack
通过

mov rsp,rdi

将x86栈指针指向VStack,此后VStack销毁
并不断使用POP指令将栈上的数值还原到x86真实寄存器
摘抄代码如下

C8F | mov rsp,rdi                            
C90 | pop r12                                
C91 | xchg esi,r15d                          
C92 | pop rbx                                
C93 | pop r15                                
C94 | shld esi,edi,6C                        
C95 | test sil,25                            
C96 | pop rcx                                
C97 | cdq                                    
C98 | pop r11                                
C99 | lahf                                   
C9A | shl rdx,cl                             
C9B | pop r14                                
C9C | or esi,738A3B0F                        
C9D | pop r13                                
C9E | pop rbp                                
C9F | test r9b,r12b                          
CA0 | sbb r8d,429264F6                       
CA1 | or di,7FAC                             
CA2 | pop r9                                 
CA3 | not sil                                
CA4 | popfq                                  
CA5 | movzx ax,r15b                          
CA6 | cdqe                                   
CA7 | pop rdi                                
CA8 | cwd                                    
CA9 | bswap dx                               
CAA | pop rdx                                
CAB | pop r10                                
CAC | bswap rax                              
CAD | cwde                                   
CAE | mov r8w,r9w                            
CAF | pop rsi                                
CB0 | cbw                                    
CB1 | cmovg eax,edx                          
CB2 | pop rax                                
CB3 | not r8b                                
CB4 | movsx r8w,r14b                         
CB5 | pop r8                                 
CB6 | ret                                    

至此一个VM过程结束


包括VM的启动与结束,它只执行了

ADD RAX, 1

而已

总结

不要分析VMP

15

关于 “逆向VMP3.X虚拟化入门” 的 1 个意见

发表评论

邮箱地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据