VMCrackME----A Preliminary Virtual Machine From Top To Bottom
【目标】 本文的主角是来自 KuNgBiM 大侠发的一个带虚拟机的CrackME
链接:http://bbs.pediy.com/showthread.php?threadid=40679
【作者】 cyclotron
【目的】 研究虚拟机的运作机理
【联系方式】 cyclotron [at] citiz.net
【个人主页】 cyclotron.yculblog.com
这个CrackME比较有意思的地方是附带了一个完整的虚拟机解释引擎,代码量比起Themida,VMProtect和Code Virtualizer来自然是捉襟见肘。不过麻雀虽小,五脏俱全,我们可以通过这个引擎体验一下虚拟机世界的乐趣,同时再次瞻仰一下以softworm为代表的牛群。
首先简单介绍一下这个虚拟机的体系结构,下面这些本地变量的命名是根据cyclotron对虚拟机执行引擎的理解类比x86体系定义的,把它们定义为其他的名称完全不会影响到你对虚拟机的分析:
00401060 var_lpVMStack = dword ptr -130h ; 虚拟机堆栈区基址指针
; 注意这个堆栈是向下增长的
00401060 var_lpVArg = dword ptr -30h ; 指向输入参数的指针(处理为字节串)
00401060 var_VargIndex = dword ptr -2Ch ; 输入参数按字节顺序的访问序号
00401060 var_lpVRetVal = dword ptr -28h ; 指向返回值的指针(处理为字节串)
00401060 var_VRetValIndex= dword ptr -24h ; 返回值按字节顺序的访问序号
00401060 var_RegEsi = dword ptr -20h
00401060 var_RegEdi = dword ptr -1Ch
00401060 var_RegEcx = dword ptr -18h
00401060 var_RegEip = dword ptr -14h
00401060 var_RegDl = byte ptr -10h
00401060 var_RegEsp = dword ptr -0Fh
00401060 var_RegEax = dword ptr -0Bh
00401060 var_SFlag = byte ptr -3
00401060 var_ZFlag = byte ptr -2
00401060 arg_lpVArg = dword ptr 8 ; 指向输入参数的指针(处理为字节串)
00401060 arg_lpVRetVal = dword ptr 0Ch ; 指向返回值的指针(处理为字节串)
00401060 arg_lpVMCode = dword ptr 10h ; 虚拟机代码区指针
00401060 arg_lpVMData = dword ptr 14h ; 虚拟机数据区指针
这个CrackME中有6个地方调用了这个虚拟机执行引擎,这也就意味着我们必须分析6组虚拟机伪代码,其中与注册相关的调用代码列举如下:
004017AF push offset lpVMData ; 虚拟机数据区指针
004017B4 push offset lpVI_4020A9 ; 虚拟机代码区指针
004017B9 push 0
004017BB lea eax, [ebp+var_11] ; lpVArg --> USERNAME
004017BE push eax
004017BF call VI_Interpreter ; 调用解释引擎
004017C4 mov eax, offset lpVMData
004017C9 add eax, 4
004017CC mov dword ptr [eax], 0
004017D2 mov dword ptr [eax+4], 1
004017D9 push offset lpVMData
004017DE push offset lpVMCode_4020CF ; 虚拟机代码区指针
004017E3 push 0
004017E5 lea eax, [ebp+var_22] ; lpVArg --> Serial
004017E8 push eax
004017E9 call VI_Interpreter ; 调用解释引擎
004017EE push offset lpVMData
004017F3 push offset lpVI_4020F7 ; 虚拟机代码区指针
004017F8 push offset aWrong ; "Wrong!"
004017FD push 0
004017FF call VI_Interpreter ; 调用解释引擎
00401804 mov eax, uType
00401809 push eax ; uType
0040180A push offset aWrong ; lpCaption
0040180F push offset aWrong ; lpText
00401814 push hWnd ; hWnd
0040181A call MessageBoxA
0040181F jmp short loc_401831
接着我们来分析一下这个这个虚拟机解释引擎的全貌:
虚拟机解释器要访问的的跳转表:
00402000 lpJumpAddrTable dd offset VNop
00402004 dd offset VPushImm32
00402008 dd offset VLoadMem32
0040200C dd offset VSaveMem32
00402010 dd offset VAdd
00402014 dd offset VSub
00402018 dd offset VRet10
0040201C dd offset VPushRegEax ; All the Arithmetic Results are Returned in RegEax
0040201C ; and should be thereafter Pushed into the Stack Manually
00402020 dd offset VMul
00402024 dd offset VDiv
00402028 dd offset VAnd
0040202C dd offset Vor
00402030 dd offset VXor
00402034 dd offset VPop2RegEax
00402038 dd offset VPushByteVArg
0040203C dd offset VSaveRegEax2ByteVRetVal
00402040 dd offset VJmp
00402044 dd offset VJz
00402048 dd offset VJnz
0040204C dd offset VJs
虚拟机配备了一个基于堆栈,单操作数的伪指令系统,每条指令都会将返回值保存在var_RegEax,以便后面的指令访问,其指令集已经清晰地罗列在上面的跳转表中。
注意:伪指令的解释引擎在代码中并不是按照上述地址表的顺序排列的,cyclotron下面所有关于伪指令解释的序号均为该伪指令在跳转表中的序号,以方便大家整理反编译器。
00401060 ; ************** S U B R O U T I N E *****************************************
00401060
00401060 ; A Single-Operand Based Virtual Machine (Stack Based)
00401060 ; Attributes: bp-based frame
00401060
00401060 VI_Interpreter proc near ; CODE XREF: DialogFunc+109p
00401060 ; DialogFunc+20Bp
00401060 ; DialogFunc+22Dp
00401060 ; DialogFunc+24Dp
00401060 ; DialogFunc+277p
00401060 ; DialogFunc+28Dp
00401060
00401060 var_lpVMStack = dword ptr -130h
00401060 var_lpVArg = dword ptr -30h
00401060 var_VargIndex = dword ptr -2Ch
00401060 var_lpVRetVal = dword ptr -28h
00401060 var_VRetValIndex= dword ptr -24h
00401060 var_RegEsi = dword ptr -20h
00401060 var_RegEdi = dword ptr -1Ch
00401060 var_RegEcx = dword ptr -18h
00401060 var_RegEip = dword ptr -14h
00401060 var_RegDl = byte ptr -10h
00401060 var_RegEsp = dword ptr -0Fh
00401060 var_RegEax = dword ptr -0Bh
00401060 var_SFlag = byte ptr -3
00401060 var_ZFlag = byte ptr -2
00401060 arg_lpVArg = dword ptr 8
00401060 arg_lpVRetVal = dword ptr 0Ch
00401060 arg_lpVMCode = dword ptr 10h
00401060 arg_lpVMData = dword ptr 14h
00401060
00401060 push ebp
00401061 mov ebp, esp
00401063 add esp, 0FFFFFED0h
00401069 mov [ebp+var_RegEdi], 0
00401070 mov [ebp+var_RegEcx], 0
00401077 mov [ebp+var_RegEsp], 0
0040107E mov [ebp+var_SFlag], 0
00401082 mov [ebp+var_ZFlag], 0
00401086 mov [ebp+var_RegEax], 0
0040108D lea eax, [ebp+var_lpVMStack]
00401093 mov [ebp+var_RegEsp], eax
00401096 mov eax, [ebp+arg_lpVMData]
00401099 mov [ebp+var_RegEsi], eax
0040109C mov eax, [ebp+arg_lpVArg]
0040109F mov [ebp+var_lpVArg], eax
004010A2 mov eax, [ebp+arg_lpVRetVal]
004010A5 mov [ebp+var_lpVRetVal], eax
004010A8 mov [ebp+var_VRetValIndex], 0
004010AF mov [ebp+var_VargIndex], 0 ; 上面这段代码对虚拟机的寄存器系统进行初始化
004010B6 mov eax, [ebp+arg_lpVMCode]; 取得伪代码区指针
004010B9 mov [ebp+var_RegEip], eax
004010BC
004010BC InterpretingEntry: ; 每一条伪指令解释完毕都返回到这里
004010BC inc [ebp+var_RegEip]
004010BF mov eax, [ebp+var_RegEip]
004010C2 mov al, [eax] ; 从伪代码区取出一个字节
004010C4 mov [ebp+var_RegDl], al
004010C7 mov eax, offset lpJumpAddrTable
004010CC movzx ebx, [ebp+var_RegDl]
004010D0 shl ebx, 2 ; x4
004010D3 add eax, ebx ; 检索跳转表
004010D5 jmp dword ptr [eax] ; 进入解释引擎,类似于VB P-code
004010D5 VI_Interpreter endp
0. VNop,空操作伪指令
004010D7 ; ************** S U B R O U T I N E *****************************************
004010D7
004010D7 ; Attributes: thunk
004010D7
004010D7 VNop proc near
004010D7 jmp short InterpretingEntry
004010D7 VNop endp
1. VPushImm32,将该指令的操作数(长度为一个字节)无符号扩展为一个dword,压入虚拟机堆栈区,并保存至var_RegEax
004010D9 ; ************** S U B R O U T I N E *****************************************
004010D9
004010D9 ; Attributes: bp-based frame
004010D9
004010D9 VPushImm32 proc near
004010D9
004010D9 var_RegEdi = dword ptr -1Ch
004010D9 var_RegEcx = dword ptr -18h
004010D9 var_RegEip = dword ptr -14h
004010D9 var_RegDl = byte ptr -10h
004010D9 var_RegEsp = dword ptr -0Fh
004010D9 var_RegEax = dword ptr -0Bh
004010D9
004010D9 add [ebp+var_RegEsp], 4
004010DD mov eax, [ebp+var_RegEsp]
004010E0 mov [ebp+var_RegEdi], eax
004010E3 inc [ebp+var_RegEip]
004010E6 mov eax, [ebp+var_RegEip]
004010E9 mov al, [eax] ; Fetch the Immediate Byte Operand
004010EB mov [ebp+var_RegDl], al
004010EE movzx eax, [ebp+var_RegDl]
004010F2 mov [ebp+var_RegEax], eax
004010F5 mov [ebp+var_RegEcx], eax
004010F8 mov ebx, [ebp+var_RegEdi]
004010FB mov [ebx], eax
004010FD jmp short InterpretingEntry
004010FD VPushImm32 endp
2. VLoadMem32,操作数为一个字节的内存地址,从虚拟机数据区取出一个dword,压入虚拟机堆栈区,并保存至var_RegEax
004010FF ; ************** S U B R O U T I N E *****************************************
004010FF
004010FF ; Attributes: bp-based frame
004010FF
004010FF VLoadMem32 proc near
004010FF
004010FF var_RegEsi = dword ptr -20h
004010FF var_RegEdi = dword ptr -1Ch
004010FF var_RegEcx = dword ptr -18h
004010FF var_RegEip = dword ptr -14h
004010FF var_RegDl = byte ptr -10h
004010FF var_RegEsp = dword ptr -0Fh
004010FF var_RegEax = dword ptr -0Bh
004010FF
004010FF inc [ebp+var_RegEip]
00401102 mov eax, [ebp+var_RegEip]
00401105 movzx eax, byte ptr [eax] ; Fetch the Immediate Byte Operand
00401108 mov [ebp+var_RegDl], al
0040110B mov eax, [ebp+var_RegEsi] ; Pointer to VMData
0040110E movzx ebx, [ebp+var_RegDl]
00401112 shl ebx, 2
00401115 add eax, ebx
00401117 mov [ebp+var_RegEdi], eax
0040111A mov ebx, [eax] ; Fetch the Dword
0040111C mov [ebp+var_RegEcx], ebx
0040111F add [ebp+var_RegEsp], 4
00401123 mov eax, [ebp+var_RegEsp]
00401126 mov [ebp+var_RegEdi], eax
00401129 mov ebx, [ebp+var_RegEcx]
0040112C mov [eax], ebx ; Save the Dword to Stack
0040112E mov [ebp+var_RegEax], ebx
00401131 jmp short InterpretingEntry
00401131 VLoadMem32 endp
3. VSaveMem32,从虚拟机堆栈区弹出一个dword,保存var_RegEax至虚拟机数据区
00401133 ; ************** S U B R O U T I N E *****************************************
00401133
00401133 ; Attributes: bp-based frame
00401133
00401133 VSaveMem32 proc near
00401133
00401133 var_RegEsi = dword ptr -20h
00401133 var_RegEdi = dword ptr -1Ch
00401133 var_RegEcx = dword ptr -18h
00401133 var_RegEip = dword ptr -14h
00401133 var_RegDl = byte ptr -10h
00401133 var_RegEsp = dword ptr -0Fh
00401133 var_RegEax = dword ptr -0Bh
00401133
00401133 inc [ebp+var_RegEip]
00401136 mov eax, [ebp+var_RegEip]
00401139 mov al, [eax] ; Fetch the Immediate Byte Operand
0040113B mov [ebp+var_RegDl], al
0040113E mov eax, [ebp+var_RegEsi] ; Pointer to VMData
00401141 movzx ebx, [ebp+var_RegDl]
00401145 shl ebx, 2
00401148 add eax, ebx
0040114A mov ebx, [ebp+var_RegEax] ; the Return Value of the Last Virtual Instruction
0040114D mov [ebp+var_RegEdi], eax
00401150 mov [eax], ebx
00401152 sub [ebp+var_RegEsp], 4
00401156 mov eax, [ebp+var_RegEsp]
00401159 mov [ebp+var_RegEdi], eax
0040115C mov eax, [eax]
0040115E mov [ebp+var_RegEax], eax
00401161 mov [ebp+var_RegEcx], eax
00401164 jmp InterpretingEntry
00401164 VSaveMem32 endp
4. VAdd,从虚拟机堆栈区弹出一个dword,与上一条指令的返回值相加,结果保存在var_RegEax中,并设置标志位
00401169 ; ************** S U B R O U T I N E *****************************************
00401169
00401169 ; Attributes: bp-based frame
00401169
00401169 VAdd proc near
00401169
00401169 var_RegEdi = dword ptr -1Ch
00401169 var_RegEcx = dword ptr -18h
00401169 var_RegEsp = dword ptr -0Fh
00401169 var_RegEax = dword ptr -0Bh
00401169 var_SFlag = byte ptr -3
00401169 var_ZFlag = byte ptr -2
00401169
00401169 mov eax, [ebp+var_RegEax]
0040116C sub [ebp+var_RegEsp], 4
00401170 mov ebx, [ebp+var_RegEsp]
00401173 mov [ebp+var_RegEdi], ebx
00401176 mov edx, [ebx]
00401178 mov [ebp+var_RegEcx], edx
0040117B add [ebp+var_RegEcx], eax
0040117E setz [ebp+var_ZFlag] ; 根据计算结果置Z标志
00401182 sets [ebp+var_SFlag] ; 根据计算结果置S标志
00401186 push [ebp+var_RegEcx]
00401189 pop [ebp+var_RegEax]
0040118C mov eax, [ebp+var_RegEcx]
0040118F mov [ebx], eax
00401191 jmp InterpretingEntry
00401191 VAdd endp
5. VSub,从虚拟机堆栈区弹出一个dword,从上一条指令的返回值中减去之,结果保存在var_RegEax中,并设置标志位
00401196 ; ************** S U B R O U T I N E *****************************************
00401196
00401196 ; Attributes: bp-based frame
00401196
00401196 VSub proc near
00401196
00401196 var_RegEdi = dword ptr -1Ch
00401196 var_RegEcx = dword ptr -18h
00401196 var_RegEsp = dword ptr -0Fh
00401196 var_RegEax = dword ptr -0Bh
00401196 var_SFlag = byte ptr -3
00401196 var_ZFlag = byte ptr -2
00401196
00401196 mov eax, [ebp+var_RegEax]
00401199 sub [ebp+var_RegEsp], 4
0040119D mov ebx, [ebp+var_RegEsp]
004011A0 mov [ebp+var_RegEdi], ebx
004011A3 mov edx, [ebx]
004011A5 mov [ebp+var_RegEcx], edx
004011A8 xchg eax, [ebp+var_RegEcx]
004011AB sub [ebp+var_RegEcx], eax
004011AE setz [ebp+var_ZFlag] ; 根据计算结果置Z标志
004011B2 sets [ebp+var_SFlag] ; 根据计算结果置S标志
004011B6 push [ebp+var_RegEcx]
004011B9 pop [ebp+var_RegEax]
004011BC mov eax, [ebp+var_RegEcx]
004011BF mov [ebx], eax
004011C1 jmp InterpretingEntry
004011C1 VSub endp
8. VMul,从虚拟机堆栈区弹出一个dword,与上一条指令的返回值相乘,结果保存在var_RegEax中,并设置标志位
004011C6 ; ************** S U B R O U T I N E *****************************************
004011C6
004011C6 ; Attributes: bp-based frame
004011C6
004011C6 VMul proc near
004011C6
004011C6 var_RegEdi = dword ptr -1Ch
004011C6 var_RegEcx = dword ptr -18h
004011C6 var_RegEsp = dword ptr -0Fh
004011C6 var_RegEax = dword ptr -0Bh
004011C6 var_SFlag = byte ptr -3
004011C6 var_ZFlag = byte ptr -2
004011C6
004011C6 mov eax, [ebp+var_RegEax]
004011C9 sub [ebp+var_RegEsp], 4
004011CD mov ebx, [ebp+var_RegEsp]
004011D0 mov [ebp+var_RegEdi], ebx
004011D3 mov ebx, [ebx]
004011D5 mov [ebp+var_RegEcx], ebx
004011D8 xor edx, edx
004011DA mov word ptr ds:loc_4011E3, 0E3F7h ; SMC
004011E3
004011E3 loc_4011E3:
004011E3 neg eax ; Change to "mul ebx"
004011E5 mov [ebp+var_RegEcx], eax
004011E8 setz [ebp+var_ZFlag] ; 根据计算结果置Z标志
004011EC sets [ebp+var_SFlag] ; 根据计算结果置S标志
004011F0 push [ebp+var_RegEcx]
004011F3 pop [ebp+var_RegEax]
004011F6 mov eax, [ebp+var_RegEcx]
004011F9 mov ebx, [ebp+var_RegEsp]
004011FC mov [ebx], eax
004011FE jmp InterpretingEntry
004011FE VMul endp
9. VDiv,从虚拟机堆栈区弹出一个dword,用上一条指令的返回值中除以之,结果保存在var_RegEax中,并设置标志位
00401203 ; ************** S U B R O U T I N E *****************************************
00401203
00401203 ; Attributes: bp-based frame
00401203
00401203 VDiv proc near
00401203
00401203 var_RegEdi = dword ptr -1Ch
00401203 var_RegEcx = dword ptr -18h
00401203 var_RegEsp = dword ptr -0Fh
00401203 var_RegEax = dword ptr -0Bh
00401203 var_SFlag = byte ptr -3
00401203 var_ZFlag = byte ptr -2
00401203
00401203 mov eax, [ebp+var_RegEax]
00401206 sub [ebp+var_RegEsp], 4
0040120A mov ebx, [ebp+var_RegEsp]
0040120D mov [ebp+var_RegEdi], ebx
00401210 mov ebx, [ebx]
00401212 mov [ebp+var_RegEcx], ebx
00401215 xor edx, edx
00401217 mov word ptr ds:loc_401220, 0F3F7h ; SMC
00401220
00401220 loc_401220:
00401220 xor eax, eax ; change to "div ebx"
00401222 mov [ebp+var_RegEcx], eax
00401225 setz [ebp+var_ZFlag] ; 根据计算结果置Z标志
00401229 sets [ebp+var_SFlag] ; 根据计算结果置S标志
0040122D push [ebp+var_RegEcx]
00401230 pop [ebp+var_RegEax]
00401233 mov eax, [ebp+var_RegEcx]
00401236 mov ebx, [ebp+var_RegEsp]
00401239 mov [ebx], eax
0040123B jmp InterpretingEntry
0040123B VDiv endp
A. VAnd,从虚拟机堆栈区弹出一个dword,与上一条指令的返回值中相“与”,结果保存在var_RegEax中
00401240 ; ************** S U B R O U T I N E *****************************************
00401240
00401240 ; Attributes: bp-based frame
00401240
00401240 VAnd proc near
00401240
00401240 var_RegEdi = dword ptr -1Ch
00401240 var_RegEcx = dword ptr -18h
00401240 var_RegEsp = dword ptr -0Fh
00401240 var_RegEax = dword ptr -0Bh
00401240
00401240 mov eax, [ebp+var_RegEax]
00401243 sub [ebp+var_RegEsp], 4
00401247 mov ebx, [ebp+var_RegEsp]
0040124A mov [ebp+var_RegEdi], ebx
0040124D mov edx, [ebx]
0040124F mov [ebp+var_RegEcx], edx
00401252 xchg eax, [ebp+var_RegEcx]
00401255 and [ebp+var_RegEcx], eax
00401258 push [ebp+var_RegEcx]
0040125B pop [ebp+var_RegEax]
0040125E mov eax, [ebp+var_RegEcx]
00401261 mov [ebx], eax
00401263 jmp InterpretingEntry
00401263 VAnd endp
B. VOr,从虚拟机堆栈区弹出一个dword,与上一条指令的返回值中相“或”,结果保存在var_RegEax中
00401268 ; ************** S U B R O U T I N E *****************************************
00401268
00401268 ; Attributes: bp-based frame
00401268
00401268 VOr proc near
00401268
00401268 var_RegEdi = dword ptr -1Ch
00401268 var_RegEcx = dword ptr -18h
00401268 var_RegEsp = dword ptr -0Fh
00401268 var_RegEax = dword ptr -0Bh
00401268
00401268 mov eax, [ebp+var_RegEax]
0040126B sub [ebp+var_RegEsp], 4
0040126F mov ebx, [ebp+var_RegEsp]
00401272 mov [ebp+var_RegEdi], ebx
00401275 mov edx, [ebx]
00401277 mov [ebp+var_RegEcx], edx
0040127A xchg eax, [ebp+var_RegEcx]
0040127D or [ebp+var_RegEcx], eax
00401280 push [ebp+var_RegEcx]
00401283 pop [ebp+var_RegEax]
00401286 mov eax, [ebp+var_RegEcx]
00401289 mov [ebx], eax
0040128B jmp InterpretingEntry
0040128B VOr endp
C. VXor,从虚拟机堆栈区弹出一个dword,与上一条指令的返回值“异或”,结果保存在var_RegEax中
00401290 ; ************** S U B R O U T I N E *****************************************
00401290
00401290 ; Attributes: bp-based frame
00401290
00401290 VXor proc near
00401290
00401290 var_RegEdi = dword ptr -1Ch
00401290 var_RegEcx = dword ptr -18h
00401290 var_RegEsp = dword ptr -0Fh
00401290 var_RegEax = dword ptr -0Bh
00401290
00401290 mov eax, [ebp+var_RegEax]
00401293 sub [ebp+var_RegEsp], 4
00401297 mov ebx, [ebp+var_RegEsp]
0040129A mov [ebp+var_RegEdi], ebx
0040129D mov edx, [ebx]
0040129F mov [ebp+var_RegEcx], edx
004012A2 xchg eax, [ebp+var_RegEcx]
004012A5 xor [ebp+var_RegEcx], eax
004012A8 push [ebp+var_RegEcx]
004012AB pop [ebp+var_RegEax]
004012AE mov eax, [ebp+var_RegEcx]
004012B1 mov [ebx], eax
004012B3 jmp InterpretingEntry
004012B3 VXor endp
7. VPushRegEax,将var_RegEax的值压入虚拟机堆栈区
004012B8 ; ************** S U B R O U T I N E *****************************************
004012B8
004012B8 ; All the Arithmetic Results are Returned in RegEax
004012B8 ; and should be thereafter Pushed into the Stack Manually
004012B8 ; Attributes: bp-based frame
004012B8
004012B8 VPushRegEax proc near
004012B8
004012B8 var_RegEdi = dword ptr -1Ch
004012B8 var_RegEcx = dword ptr -18h
004012B8 var_RegEsp = dword ptr -0Fh
004012B8 var_RegEax = dword ptr -0Bh
004012B8
004012B8 add [ebp+var_RegEsp], 4
004012BC mov ebx, [ebp+var_RegEax]
004012BF mov [ebp+var_RegEcx], ebx
004012C2 mov eax, [ebp+var_RegEsp]
004012C5 mov [ebp+var_RegEdi], eax
004012C8 mov [eax], ebx
004012CA jmp InterpretingEntry
004012CA VPushRegEax endp
D. VPop2RegEax,从虚拟机堆栈区弹出一个dword,保存至var_RegEax
004012CF ; ************** S U B R O U T I N E *****************************************
004012CF
004012CF ; Attributes: bp-based frame
004012CF
004012CF VPop2RegEax proc near
004012CF
004012CF var_RegEdi = dword ptr -1Ch
004012CF var_RegEcx = dword ptr -18h
004012CF var_RegEsp = dword ptr -0Fh
004012CF var_RegEax = dword ptr -0Bh
004012CF
004012CF sub [ebp+var_RegEsp], 4
004012D3 mov eax, [ebp+var_RegEsp]
004012D6 mov [ebp+var_RegEdi], eax
004012D9 mov ebx, [eax]
004012DB mov [ebp+var_RegEcx], ebx
004012DE mov [ebp+var_RegEax], ebx
004012E1 jmp InterpretingEntry
004012E1 VPop2RegEax endp
E. VPushByteVArg,将输入参数的一个字节无符号扩展以后压入虚拟机堆栈区,并保存至var_RegEax
004012E6 ; ************** S U B R O U T I N E *****************************************
004012E6
004012E6 ; Attributes: bp-based frame
004012E6
004012E6 VPushByteVArg proc near
004012E6
004012E6 var_lpVArg = dword ptr -30h
004012E6 var_VArgIndex = dword ptr -2Ch
004012E6 var_RegEdi = dword ptr -1Ch
004012E6 var_RegEcx = dword ptr -18h
004012E6 var_RegEsp = dword ptr -0Fh
004012E6 var_RegEax = dword ptr -0Bh
004012E6
004012E6 mov eax, [ebp+var_lpVArg]
004012E9 add eax, [ebp+var_VArgIndex]
004012EC inc [ebp+var_VArgIndex]
004012EF movzx eax, byte ptr [eax]
004012F2 mov [ebp+var_RegEax], eax
004012F5 add [ebp+var_RegEsp], 4
004012F9 mov ebx, [ebp+var_RegEsp]
004012FC mov [ebp+var_RegEdi], ebx
004012FF mov [ebp+var_RegEcx], eax
00401302 mov [ebx], eax
00401304 jmp InterpretingEntry
00401304 PushByteVArg endp
F. VSaveRegEax2ByteVRetVal,从堆栈中弹出一个dword,保存var_RegEax的最低字节至返回字节串
00401309 ; ************** S U B R O U T I N E *****************************************
00401309
00401309 ; Attributes: bp-based frame
00401309
00401309 VSaveRegEax2ByteVRetVal proc near
00401309
00401309 var_lpVRetVal= dword ptr -28h
00401309 var_VRetValIndex= dword ptr -24h
00401309 var_RegEdi = dword ptr -1Ch
00401309 var_RegEcx = dword ptr -18h
00401309 var_RegEsp = dword ptr -0Fh
00401309 var_RegEax = dword ptr -0Bh
00401309
00401309 mov eax, [ebp+var_RegEax]
0040130C sub [ebp+var_RegEsp], 4
00401310 mov ebx, [ebp+var_RegEsp]
00401313 mov [ebp+var_RegEdi], ebx
00401316 mov [ebp+var_RegEcx], eax
00401319 mov ebx, [ebx]
0040131B mov [ebp+var_RegEax], ebx
0040131E mov ebx, [ebp+var_lpVRetVal]
00401321 add ebx, [ebp+var_VRetValIndex]
00401324 inc [ebp+var_VRetValIndex]
00401327 mov [ebx], al
00401329 jmp InterpretingEntry
00401329 VSaveRegEax2ByteVRetVal endp
10. VJmp,无条件跳转指令,偏移量为操作码后面紧跟的一个word(相对于伪代码区基址)
0040132E ; ************** S U B R O U T I N E *****************************************
0040132E
0040132E ; Attributes: bp-based frame
0040132E
0040132E VJmp proc near
0040132E
0040132E var_RegEip = dword ptr -14h
0040132E var_RegDl = byte ptr -10h
0040132E var_7 = dword ptr -7
0040132E arg_lpVMCode = dword ptr 10h
0040132E
0040132E inc [ebp+var_RegEip]
00401331 mov eax, [ebp+var_RegEip]
00401334 movzx eax, byte ptr [eax]
00401337 mov [ebp+var_RegDl], al
0040133A shl eax, 8
0040133D mov [ebp+var_7], eax
00401340 inc [ebp+var_RegEip]
00401343 mov eax, [ebp+var_RegEip]
00401346 movzx eax, byte ptr [eax]
00401349 mov [ebp+var_RegDl], al
0040134C add [ebp+var_7], eax
0040134F mov eax, [ebp+var_7]
00401352 add eax, [ebp+arg_lpVMCode]
00401355 mov [ebp+var_RegEip], eax
00401358 jmp InterpretingEntry
00401358 VJmp endp
11. VJz,条件跳转指令,当 var_ZFlag=1 时触发,偏移量为操作码后面紧跟的一个word(相对于伪代码区基址)
0040135D ; ************** S U B R O U T I N E *****************************************
0040135D
0040135D ; Attributes: bp-based frame
0040135D
0040135D VJz proc near
0040135D
0040135D var_RegEip = dword ptr -14h
0040135D var_RegDl = byte ptr -10h
0040135D var_7 = dword ptr -7
0040135D var_ZFlag = byte ptr -2
0040135D arg_lpVMCode = dword ptr 10h
0040135D
0040135D inc [ebp+var_RegEip]
00401360 mov eax, [ebp+var_RegEip]
00401363 movzx eax, byte ptr [eax]
00401366 mov [ebp+var_RegDl], al
00401369 shl eax, 8
0040136C mov [ebp+var_7], eax
0040136F inc [ebp+var_RegEip]
00401372 mov eax, [ebp+var_RegEip]
00401375 movzx eax, byte ptr [eax]
00401378 mov [ebp+var_RegDl], al
0040137B add [ebp+var_7], eax
0040137E mov eax, [ebp+var_7]
00401381 cmp [ebp+var_ZFlag], 1 ; 标志位测试
00401385 jnz short loc_40138D
00401387 add eax, [ebp+arg_lpVMCode]
0040138A mov [ebp+var_RegEip], eax
0040138D
0040138D loc_40138D:
0040138D jmp InterpretingEntry
0040138D VJz endp
12. VJnz,条件跳转指令,当 var_ZFlag=0 时触发,偏移量为操作码后面紧跟的一个word(相对于伪代码区基址)
00401392 ; ************** S U B R O U T I N E *****************************************
00401392
00401392 ; Attributes: bp-based frame
00401392
00401392 VJnz proc near
00401392
00401392 var_RegEip = dword ptr -14h
00401392 var_RegDl = byte ptr -10h
00401392 var_7 = dword ptr -7
00401392 var_ZFlag = byte ptr -2
00401392 arg_lpVMCode = dword ptr 10h
00401392
00401392 inc [ebp+var_RegEip]
00401395 mov eax, [ebp+var_RegEip]
00401398 movzx eax, byte ptr [eax]
0040139B mov [ebp+var_RegDl], al
0040139E shl eax, 8
004013A1 mov [ebp+var_7], eax
004013A4 inc [ebp+var_RegEip]
004013A7 mov eax, [ebp+var_RegEip]
004013AA movzx eax, byte ptr [eax]
004013AD mov [ebp+var_RegDl], al
004013B0 add [ebp+var_7], eax
004013B3 mov eax, [ebp+var_7]
004013B6 cmp [ebp+var_ZFlag], 0 ; 标志位测试
004013BA jnz short loc_4013C2
004013BC add eax, [ebp+arg_lpVMCode]
004013BF mov [ebp+var_RegEip], eax
004013C2
004013C2 loc_4013C2:
004013C2 jmp InterpretingEntry
004013C2 VJnz endp
13. VJs,条件跳转指令,当 var_SFlag=1 时触发,偏移量为操作码后面紧跟的一个word(相对于伪代码区基址)
004013C7 ; ************** S U B R O U T I N E *****************************************
004013C7
004013C7 ; Attributes: bp-based frame
004013C7
004013C7 VJs proc near
004013C7
004013C7 var_RegEip = dword ptr -14h
004013C7 var_RegDl = byte ptr -10h
004013C7 var_7 = dword ptr -7
004013C7 var_SFlag = byte ptr -3
004013C7 arg_lpVMCode = dword ptr 10h
004013C7
004013C7 inc [ebp+var_RegEip]
004013CA mov eax, [ebp+var_RegEip]
004013CD movzx eax, byte ptr [eax]
004013D0 mov [ebp+var_RegDl], al
004013D3 shl eax, 8
004013D6 mov [ebp+var_7], eax
004013D9 inc [ebp+var_RegEip]
004013DC mov eax, [ebp+var_RegEip]
004013DF movzx eax, byte ptr [eax]
004013E2 mov [ebp+var_RegDl], al
004013E5 add [ebp+var_7], eax
004013E8 mov eax, [ebp+var_7]
004013EB cmp [ebp+var_SFlag], 1 ; 标志位测试
004013EF jnz short loc_4013F7
004013F1 add eax, [ebp+arg_lpVMCode]
004013F4 mov [ebp+var_RegEip], eax
004013F7
004013F7 loc_4013F7:
004013F7 jmp InterpretingEntry
004013F7 VJs endp
6. VRet10,返回指令,从虚拟机解释引擎中返回
004013FC ; ************** S U B R O U T I N E *****************************************
004013FC
004013FC
004013FC VRet10 proc near
004013FC leave
004013FD retn 10h
004013FD VRet10 endp ; sp = 4
004013FD
在这套解释系统中,比较值得注意的是它的堆栈操作与x86下有所不同:
入栈 —— add esp, 4
mov [esp], xxx
出栈 —— sub esp, 4
mov xxxx, [esp]
看起来有些不可思议,不过由于该引擎同时用到了var_RegEax来传递数据,所以没有出现问题。
以上面的分析为基础,我们来反编译一下004020CF处的伪指令组——对序列号的处理:
004020BF lpVMData dd 0
004020C3 dd 0
004020C7 dd 1
004020CB ; UINT uType
004020CB uType dd 10h ;
004020CF lpVMCode_4020CF db 0 ; ; VNop
004020D0 db 1, 30h ; VPushImm32 30h
004020D2 db 0Eh ; ; VPushByteVArg
004020D3 db 5 ; ; VSub
004020D4 db 2 dup(2) ; VLoadMem32 004020C7
004020D6 db 8 ; ; VMul
004020D7 db 2, 1 ; VLoadMem32 004020C3
004020D9 db 4 ; ; VAdd
004020DA db 3, 1 ; VSaveMem32 004020C3
004020DC db 2 dup(2) ; VLoadMem32 004020C7
004020DE db 1, 0Ah ; VPushImm32 0Ah
004020E0 db 8 ; ; VMul
004020E1 db 3, 2 ; VSaveMem32 004020C7
004020E3 db 1, 30h ; VPushImm32 30h
004020E5 db 0Eh ; ; VPushByteVArg
004020E6 db 7 ; ; VPushRegEax
004020E7 db 1, 0 ; VPushImm32 0
004020E9 db 5 ; ; VSub
004020EA db 0Dh ; ; VPop2RegEax
004020EB db 13h, 0, 3 ; VJs 004020D2
004020EE db 0Dh ; ; VPop2RegEax
004020EF db 1, 0EBh ; VPushImm32 0EBh
004020F1 db 2, 1 ; VLoadMem32 004020C3
004020F3 db 5 ; ; VSub
004020F4 db 3, 1 ; VSaveMem32 004020C3
004020F6 db 6 ; ; VRet10
这段伪代码的作用是把输入的序列号字符串转换为十进制数,然后减去0EBh,保存至004020C3
其他部分的虚拟机代码也可以做类似的分析,这里留给读者自己思考:D
cyclotron
2007.3.10