好友
阅读权限 30
听众
最后登录 1970-1-1
0x00 前言
前段时间想研究虚拟机 技术,于是找了了一个简化版的‘VMP'——MapoEngine,这个壳是由看雪大佬开发的,相比VMP来说少了寄存器轮换和vm_key之类,比较适合入门分析。
0x01 准备样本
好的样本是必不可少的,可以通过一些实验性的代码来了解虚拟机的架构,下面是一个简单的循环弹窗代码:
[C] 纯文本查看 复制代码
#include "stdafx.h"
#include "windows.h"
#include"MapoSDK.h"
int main()
{
MAPO_PROTECT_START
for(int i=0;i<2;i++)
MessageBox(NULL, _T("hello"), _T("hi"), MB_OK);
MAPO_PROTECT_END
return 0;
}
会被虚拟化的代码如下:
0x02 寻找虚拟机handler
这个虚拟机是非常典型的循环解码字节码执行的架构,而不是类似vmp3.x的线性解码的架构,这种架构可以通过OD记录trace,统计执行次数很快就能确定指令表了(jmp dword ptr ds:[eax*4+0x41A606])
下面是对应的指令表,一共37条指令
0x03 分析handler
分析handler是一个体力活,因为由于代码膨胀和指令乱序,很容易就晕头转向(对于我这种菜鸟是这样的),可以通过逆向框架Miasm来简化这一工作。
首先让样本跑起来之后然后再dump下来,这时可以载入Miasm进行分析了,由于这个框架对一些多字节的NOP指令(nop eax nop ax这类)无法反汇编,导致分析失败(框架的bug,提了issue也没回复),可以先IDA 跑上一段脚本,将多字节nop替换成单字节nop,就是简单的搜索字节替换。
[Python] 纯文本查看 复制代码
import hexdump
import binascii
import idautils
def replace_inst(buf,to_inst):
tlen = len(buf)
bin_str = ' '.join(["%02X" % ord(x) for x in buf])
print "Searching for: [%s]" % bin_str
ea = MinEA()
ret = []
while True:
ea = FindBinary(ea, SEARCH_DOWN, bin_str)
if ea == idaapi.BADADDR:
break
for i in to_inst:
PatchByte(ea,ord(i))
ea+=1
replace_inst('\x0f\x1f\xc8','\x90\x90\x90')
replace_inst('\x0f\x1f\xd8','\x90\x90\x90')
replace_inst('\x0f\x1f\xd0','\x90\x90\x90')
replace_inst('\x0f\x1f\xe8','\x90\x90\x90')
replace_inst('\x66\x0f\x1f\xf0','\x90\x90\x90\x90')
利用Miasm自带的符号执行引擎,对37个handler进行模拟执行,去掉无关的符号,只关心ESI EBP EDI 内存读写(这个和vmp2.x一致 ESI=vm_eip EBP=vm_esp EDI作为vm_context指针,这块不清楚的同学可以看一下vmp虚拟机的架构的文章)
[Python] 纯文本查看 复制代码
from miasm2.analysis.machine import Machine
from miasm2.analysis.binary import Container
from miasm2.ir.symbexec import SymbolicExecutionEngine
from miasm2.arch.x86.sem import ir_x86_32
from miasm2.arch.x86 import regs
from miasm2.arch.x86.regs import *
from miasm2.expression.expression import *
from miasm2.expression.simplifications import expr_simp
from miasm2.core import asmblock
from miasm2.ir.translators import Translator
from miasm2.core.locationdb import LocationDB
import struct
import binascii
def my_eval_updt_irblock(symb,irb, step=False):
for assignblk in irb:
if step:
pstr='0x%x\t%s\t\t\t\t----------------------- %s'%(assignblk.instr.offset,str(assignblk.instr),binascii.hexlify(assignblk.instr.b))
if 'JMP' not in pstr and 'NOP' not in pstr:
print pstr
#pass
symb.eval_updt_assignblk(assignblk)
dst = symb.eval_expr(symb.ir_arch.IRDst)
return dst
def my_run_at(symb, ircfg, addr, lbl_stop=None, step=False):
while True:
irblock = ircfg.get_block(addr)
if irblock is None:
break
if irblock.loc_key == lbl_stop:
break
addr = my_eval_updt_irblock(symb,irblock, step=step)
return addr
if __name__ == '__main__':
machine = Machine("x86_32")
cont = Container.from_stream(open("test3.exe"))
loc_db = LocationDB()
bs = cont.bin_stream
mdis = machine.dis_engine(bs,loc_ab=cont.loc_db)
mdis.follow_call=True
ira = machine.ir(mdis.loc_db)
for i in range(0,37):
symb = SymbolicExecutionEngine(ira, symbols_init)
ad=struct.unpack('I',bs.getbytes(0x41a606+4*i, 4))[0]
asmcfg = mdis.dis_multiblock(ad)
ircfg = ira.new_ircfg_from_asmcfg(asmcfg)
loc_key=ircfg.get_or_create_loc_key(0x00412587) #在这个地址停止,(检查栈地址,调整栈,防止冲突)
print '----------------------0x%x-------------------'%ad
try:
symbolic_pc = my_run_at(symb,ircfg,ad,loc_key,False) #这里可以启用单步输出指令执行的信息
except:
print 'error'
print state_to_expr(symb) #过滤掉无关的符号
得出输出后根据执行的结果来手动命名指令,大多数很容易就知道执行的指令的含义
这里值得注意的是,有些并不能输出结果,原因可能是操作了不关心的符号或者结果非常复杂,如果对应的handler没输出,这时要启用单步来手动分析对应的handler执行结果。
比如0x4156c0没输出,这时候看函数汇编,很明显就是把虚拟flag寄存器弹到真实的flag寄存器,所以命名为VM_PopRealfd
[Asm] 纯文本查看 复制代码
----------------------0x4156c0-------------------
0x4156c0 PUSH DWORD PTR [EDI]
0x4156c4 POPFD
0x4123bf MOVZX EAX, WORD PTR [ESP + 0x4]
0x412577 MOVSX EAX, BYTE PTR [ESP + 0xF]
0x4124f7 MOV AX, 0x8F82
0x4124fb MOVSX AX, BL
0x412501 MOVSX EAX, WORD PTR [ESP + 0x9]
0x41250f BSWAP EAX
0x412515 MOV AX, 0x5BFB
0x4125f0 MOV AL, 0xE9
0x4125f2 PUSHFD
0x4125f5 XOR AX, 0x19
0x412584 ADC AL, 0xA
再比如0x41536a,可以看出是退出虚拟机的操作,得到了寄存器的对应关系,命名为VM_Retn:
[Asm] 纯文本查看 复制代码
----------------------0x41536a-------------------
0x41536a PUSH DWORD PTR [EDI + 0xFFFFFFFC]
0x415372 POP EAX
0x415378 PUSH DWORD PTR [EDI + 0xFFFFFFF8]
0x41537b POP ECX
0x416bae PUSH DWORD PTR [EDI + 0xFFFFFFF4]
0x416624 POP EDX
0x416627 PUSH DWORD PTR [EDI + 0xFFFFFFF0]
0x415539 POP EBX
0x415540 PUSH DWORD PTR [EDI + 0xFFFFFFEC]
0x414c05 POP ESI
0x414c0a ADD ESP, 0x4
0x414c11 ADD ESP, 0x200
0x414c1a ADD ESP, 0x40
0x414c1d PUSH DWORD PTR [EDI]
0x416fad POPFD
0x4146de PUSH EBP
0x4146e3 POP ESP
0x4146e4 PUSH DWORD PTR [EDI + 0xFFFFFFE4]
0x4146ed POP EBP
0x4146f3 PUSH DWORD PTR [EDI + 0xFFFFFFE8]
0x41722b POP EDI
0x416f06 RET
通过符号执行,可以快速的完成handler的分析工作,以下就是上述代码输出和命名的结果:
[Asm] 纯文本查看 复制代码
----------------------0x4156c0-------------------
VM_PopRealfd
1
----------------------0x41455b-------------------
VM_Extend8Sto32
1
EBP = EBP_init + 0xFFFFFFFD;
@32[EBP_init + 0xFFFFFFFD] = @8[EBP_init][7:8]?({@8[EBP_init] 0 8, 0xFFFFFF 8 32},{@8[EBP_init] 0 8, 0x0 8 32});
----------------------0x4155f4-------------------
VM_Imul32
1
@32[EBP_init + 0x4] = ((@32[EBP_init][31:32]?({@32[EBP_init] 0 32, 0xFFFFFFFF 32 64},{@32[EBP_init] 0 32, 0x0 32 64})) * (@32[EBP_init + 0x4][31:32]?({@32[EBP_init + 0x4] 0 32, 0xFFFFFFFF 32 64},{@32[EBP_init + 0x4] 0 32, 0x0 32 64})))[0:32];
@32[EBP_init] = ((@32[EBP_init][31:32]?({@32[EBP_init] 0 32, 0xFFFFFFFF 32 64},{@32[EBP_init] 0 32, 0x0 32 64})) * (@32[EBP_init + 0x4][31:32]?({@32[EBP_init + 0x4] 0 32, 0xFFFFFFFF 32 64},{@32[EBP_init + 0x4] 0 32, 0x0 32 64})))[32:64];
----------------------0x4128d4-------------------
VM_Nor32
1
EBP = EBP_init + 0x4;
@32[EBP_init + 0x4] = (@32[EBP_init] ^ 0xFFFFFFFF) & (@32[EBP_init + 0x4] ^ 0xFFFFFFFF);
----------------------0x416dbc-------------------
VM_PopR8
2
EBP = EBP_init + 0x1;
ESI = ESI_init + 0x1;
@8[ESI_init][7:8]?(@8[EDI_init + {@8[ESI_init] 0 8, 0xFFFFFF 8 32}],@8[EDI_init + {@8[ESI_init] 0 8, 0x0 8 32}]) = @8[EBP_init];
----------------------0x41578b-------------------
VM_PushI8
2
EBP = EBP_init + 0xFFFFFFFF;
ESI = ESI_init + 0x1;
@8[EBP_init + 0xFFFFFFFF] = @8[ESI_init];
----------------------0x4130db-------------------
VM_PopR32
2
EBP = EBP_init + 0x4;
ESI = ESI_init + 0x1;
@8[ESI_init][7:8]?(@32[EDI_init + {@8[ESI_init] 0 8, 0xFFFFFF 8 32}],@32[EDI_init + {@8[ESI_init] 0 8, 0x0 8 32}]) = @32[EBP_init];
----------------------0x4162df-------------------
VM_GetAddESP
2
EBP = EBP_init + 0xFFFFFFFC;
ESI = ESI_init + 0x1;
@32[EBP_init + 0xFFFFFFFC] = EBP_init + {@8[ESI_init] 0 8, 0x0 8 32};
----------------------0x414783-------------------
VM_Nop
1
----------------------0x415304-------------------
VM_ReadDs8
1
EBP = EBP_init + 0x3;
@8[EBP_init + 0x3] = @8[@32[EBP_init]];
----------------------0x4142ed-------------------
VM_PushI32
5
EBP = EBP_init + 0xFFFFFFFC;
ESI = ESI_init + 0x4;
@32[EBP_init + 0xFFFFFFFC] = @32[ESI_init];
----------------------0x4157da-------------------
VM_ShortJMP
10
ESI = ESI_init + ((@32[ESI_init + 0x5][31:32]?({@32[ESI_init + 0x5] 0 32, 0xFFFFFFFF 32 64},{@32[ESI_init + 0x5] 0 32, 0x0 32 64})) * (((@32[EBP_init] & {@16[ESI_init] 0 16, 0x0 16 32}) >> ({@8[ESI_init + 0x4] 0 8, 0x0 8 32} & 0x1F))[31:32]?({(@32[EBP_init] & {@16[ESI_init] 0 16, 0x0 16 32}) >> ({@8[ESI_init + 0x4] 0 8, 0x0 8 32} & 0x1F) 0 32, 0xFFFFFFFF 32 64},{(@32[EBP_init] & {@16[ESI_init] 0 16, 0x0 16 32}) >> ({@8[ESI_init + 0x4] 0 8, 0x0 8 32} & 0x1F) 0 32, 0x0 32 64})))[32:64] + 0x9;
----------------------0x41399e-------------------
VM_ReadDs16
1
EBP = EBP_init + 0x2;
@16[EBP_init + 0x2] = @16[@32[EBP_init]];
----------------------0x416b08-------------------
VM_PushR32
2
EBP = EBP_init + 0xFFFFFFFC;
ESI = ESI_init + 0x1;
@32[EBP_init + 0xFFFFFFFC] = @8[ESI_init][7:8]?(@32[EDI_init + {@8[ESI_init] 0 8, 0xFFFFFF 8 32}],@32[EDI_init + {@8[ESI_init] 0 8, 0x0 8 32}]);
----------------------0x41a5c3-------------------
VM_CALL #执行栈上的地址 附带的4字节为返回地址
5
EBP = @32[EDI_init + 0xFFFFFFE4];
ESI = @32[EDI_init + 0xFFFFFFEC];
EDI = @32[EDI_init + 0xFFFFFFE8];
@32[0x41A5FD] = @32[EBP_init] + 0xFFBE59FF;
@32[EBP_init] = @32[ESI_init];
----------------------0x4161d4-------------------
VM_ReadDs32
1
@32[EBP_init] = @32[@32[EBP_init]];
----------------------0x414327-------------------
VM_Neg32_
1
@32[EBP_init] = -@32[EBP_init] + 0x1;
----------------------0x413c7f-------------------
VM_PopR16
2
EBP = EBP_init + 0x2;
ESI = ESI_init + 0x1;
@8[ESI_init][7:8]?(@16[EDI_init + {@8[ESI_init] 0 8, 0xFFFFFF 8 32}],@16[EDI_init + {@8[ESI_init] 0 8, 0x0 8 32}]) = @16[EBP_init];
----------------------0x416d10-------------------
VM_JMP
1
EBP = EBP_init + 0xC;
ESI = @32[EBP_init + {@8[EBP_init] 0 8, 0x0 8 32} * 0x4 + 0x4];
----------------------0x4156d3-------------------
VM_Popfd
1
@32[EDI_init] = {0x2 0 12, iopl_f_init 12 14, 0x0 14 32};
----------------------0x416583-------------------
VM_PopSp
1
EBP = @32[EBP_init];
----------------------0x413cf3-------------------
VM_Add32
1
EBP = EBP_init + 0x4;
@32[EBP_init + 0x4] = @32[EBP_init] + @32[EBP_init + 0x4];
----------------------0x41323e-------------------
VM_WriteDs8
1
EBP = EBP_init + 0x5;
@8[@32[EBP_init]] = @8[EBP_init + 0x4];
----------------------0x4168d7-------------------
VM_Sub32
1
EBP = EBP_init + 0x4;
@32[EBP_init + 0x4] = @32[EBP_init + 0x4] + -@32[EBP_init];
----------------------0x41536a-------------------
VM_Retn
1
EBP = @32[EDI_init + 0xFFFFFFE4];
ESI = @32[EDI_init + 0xFFFFFFEC];
EDI = @32[EDI_init + 0xFFFFFFE8];
----------------------0x4126a2-------------------
VM_PushR8
2
EBP = EBP_init + 0xFFFFFFFF;
ESI = ESI_init + 0x1;
@8[EBP_init + 0xFFFFFFFF] = (@8[ESI_init][7:8]?({@8[EDI_init + {@8[ESI_init] 0 8, 0xFFFFFF 8 32}] 0 8, 0xFFFFFF 8 32},{@8[EDI_init + {@8[ESI_init] 0 8, 0x0 8 32}] 0 8, 0x0 8 32}))[0:8];
----------------------0x41478e-------------------
VM_PushStackTop32
1
EBP = EBP_init + 0xFFFFFFFC;
@32[EBP_init + 0xFFFFFFFC] = @32[ESP_init];
----------------------0x414542-------------------
VM_AddSp
3
EBP = EBP_init + {@16[ESI_init] 0 16, 0x0 16 32};
ESI = ESI_init + 0x2;
----------------------0x413515-------------------
VM_WriteDs32
1
EBP = EBP_init + 0x8;
@32[@32[EBP_init]] = @32[EBP_init + 0x4];
----------------------0x415519-------------------
VM_WriteDs16
1
EBP = EBP_init + 0x6;
@16[@32[EBP_init]] = @16[EBP_init + 0x4];
----------------------0x41340c-------------------
VM_PushStackTop32
1
EBP = EBP_init + 0xFFFFFFFC;
@32[EBP_init + 0xFFFFFFFC] = @32[EBP_init];
----------------------0x412f93-------------------
VM_PushR16
2
EBP = EBP_init + 0xFFFFFFFE;
ESI = ESI_init + 0x1;
@16[EBP_init + 0xFFFFFFFE] = (@8[ESI_init][7:8]?({@16[EDI_init + {@8[ESI_init] 0 8, 0xFFFFFF 8 32}] 0 16, 0xFFFF 16 32},{@16[EDI_init + {@8[ESI_init] 0 8, 0x0 8 32}] 0 16, 0x0 16 32}))[0:16];
----------------------0x415689-------------------
VM_Neg32
1
@32[EBP_init] = -@32[EBP_init];
----------------------0x412de1-------------------
VM_PushI16
1
EBP = EBP_init + 0xFFFFFFFE;
ESI = ESI_init + 0x2;
@16[EBP_init + 0xFFFFFFFE] = @16[ESI_init];
----------------------0x4133f3-------------------
VM_Extend16To32
1
EBP = EBP_init + 0xFFFFFFFE;
@32[EBP_init + 0xFFFFFFFE] = {@16[EBP_init] 0 16, 0x0 16 32};
----------------------0x4173b2-------------------
VM_ExecRealInst #变长指令,这里手动分析,不支持的指令(例如浮点之类的)会先退出虚拟机,在执行后重新进入虚拟机,x86的指令被固定在虚拟机字节码中 长度+x86机器码
-1
ESI = ESI_init + 0x1;
EDI = 0x41A473;
----------------------0x413880-------------------
VM_Shr32
1
EBP = EBP_init + 0x4;
@32[EBP_init + 0x4] = @32[EBP_init + 0x4] >> ({@8[EBP_init] 0 8, 0x0 8 32} & 0x1F);
0x04 简单的反汇编
在上面分析handler之后,就可以实现一个很简陋的反汇编器了,通过跟踪esi,可以知道虚拟机最开始执行的地址为0x42c17c, 反汇编代码如下:
[Python] 纯文本查看 复制代码
from miasm2.analysis.machine import Machine
from miasm2.analysis.binary import Container
from miasm2.ir.symbexec import SymbolicExecutionEngine
from miasm2.arch.x86.sem import ir_x86_32
from miasm2.arch.x86 import regs
from miasm2.arch.x86.regs import *
from miasm2.expression.expression import *
from miasm2.expression.simplifications import expr_simp
from miasm2.core import asmblock
from miasm2.ir.translators import Translator
from miasm2.core.locationdb import LocationDB
import struct
import binascii
if __name__ == '__main__':
machine = Machine("x86_32")
cont = Container.from_stream(open("test3.exe"))
loc_db = LocationDB()
bs = cont.bin_stream
mdis = machine.dis_engine(bs,loc_ab=cont.loc_db)
mdis.follow_call=True
ira = machine.ir(mdis.loc_db)
class Mapo_Engine():
def __init__(self,bs,veip,handler):
self.bs=bs
self.veip=veip
self.handler=handler
self.bStop=False
self.RegMap={0:'flag',1:'EAX',2:'ECX',3:'EDX',4:'EBX',5:'ESI',6:'EDI',7:'EBP'} #vm_context 对应关系 没有寄存器轮换 一一对应的
def GetI8(self,offset):
b=struct.unpack('B',self.bs.getbytes(self.veip+offset ,1))[0]
return b
def GetI16(self,offset):
b=struct.unpack('H',self.bs.getbytes(self.veip+offset ,2))[0]
return b
def GetI32(self,offset):
b=struct.unpack('I',self.bs.getbytes(self.veip+offset ,4))[0]
return b
def GetBytes(self,offset,len):
return bs.getbytes(self.veip+offset ,len)
def GetRegIndex(self,offset):
b=struct.unpack('b',self.bs.getbytes(self.veip+offset ,1))[0]
return (-b)/4
def GetRegDesc(self,index):
return self.RegMap[index]
def VM_PopRealfd(self):
print 'VM_PopRealfd'
self.veip+=1
def VM_Extend8Sto32(self):
print 'VM_Extend8Sto32'
self.veip+=1
def VM_Imul32(self):
print 'VM_Imul32'
self.veip+=1
def VM_Nor32(self):
print 'VM_Nor32'
self.veip+=1
def VM_PopR8(self):
print 'VM_PopR8'+" "+self.GetRegDesc(self.GetRegIndex(1))
self.veip+=2
def VM_PushI8(self):
print 'VM_PushI8'+" 0x%x"%self.GetI8(1)
self.veip+=2
def VM_PopR32(self):
print 'VM_PopR32'+" "+self.GetRegDesc(self.GetRegIndex(1))
self.veip+=2
def VM_GetAddESP(self):
print 'VM_GetAddESP'+" 0x%x"%self.GetI8(1)
self.veip+=2
def VM_Nop(self):
print 'VM_Nop'
self.veip+=1
def VM_ReadDs8(self):
print 'VM_ReadDs8'
self.veip+=1
def VM_PushI32(self):
print 'VM_PushI32'+" 0x%x"%self.GetI32(1)
self.veip+=5
def VM_ShortJMP(self):
print 'VM_ShortJMP'
self.veip+=5
def VM_ReadDs16(self):
print 'VM_ReadDs16'
self.veip+=1
def VM_PushR32(self):
print 'VM_PushR32'+" "+self.GetRegDesc(self.GetRegIndex(1))
self.veip+=2
def VM_CALL(self):
print 'VM_CALL retaddr='+" 0x%x"%self.GetI32(1)
self.veip+=5
def VM_ReadDs32(self):
print 'VM_ReadDs32'
self.veip+=1
def VM_Neg32_(self):
print 'VM_Neg32_'
self.veip+=1
def VM_PopR16(self):
print 'VM_PopR16'+" "+self.GetRegDesc(self.GetRegIndex(1))
self.veip+=2
def VM_JMP(self):
print 'VM_JMP'
self.veip+=1
def VM_Popfd(self):
print 'VM_Popfd'
self.veip+=1
def VM_PopSp(self):
print 'VM_PopSp'
self.veip+=1
def VM_Add32(self):
print 'VM_Add32'
self.veip+=1
def VM_WriteDs8(self):
print 'VM_WriteDs8'
self.veip+=1
def VM_Sub32(self):
print 'VM_Sub32'
self.veip+=1
def VM_Retn(self):
print 'VM_Retn'
self.veip+=1
def VM_PushR8(self):
print 'VM_PushR8'+" "+self.GetRegDesc(self.GetRegIndex(1))
self.veip+=1
def VM_PushStackTop32(self):
print 'VM_PushStackTop32'
self.veip+=1
def VM_AddSp(self):
print 'VM_AddSp'+" 0x%x"%self.GetI16(1)
self.veip+=3
def VM_WriteDs32(self):
print 'VM_WriteDs32'
self.veip+=1
def VM_WriteDs16(self):
print 'VM_WriteDs16'
self.veip+=1
def VM_PushR16(self):
print 'VM_PushR16'+" "+self.GetRegDesc(self.GetRegIndex(1))
self.veip+=2
def VM_Neg32(self):
print 'VM_Neg32'
self.veip+=1
def VM_PushI16(self):
print 'VM_PushI16'+" 0x%x"%self.GetI16(1)
self.veip+=3
def VM_Extend16To32(self):
print 'VM_Extend16To32'
self.veip+=1
def VM_ExecRealInst(self):
inst_len=self.GetI8(1)
inst=self.GetBytes(2,inst_len)
dis_inst=str(Machine('x86_32').mn.dis(inst,32)).strip()
print 'VM_ExecRealInst {'+dis_inst+'}'
self.veip=self.veip+inst_len+2
if dis_inst=='RET':
self.bStop=True
def VM_Shr32(self):
print 'VM_Shr32'
self.veip+=1
def disam(self):
while not self.bStop:
print '0x%x'%self.veip,
op=self.GetI8(0)
exec'self.'+self.handler[op][1]+'()'
#handler地址 名称 指令长度
handler={0: [4282048, 'VM_PopRealfd', 1], 1: [4277595, 'VM_Extend8Sto32', 1], 2: [4281844, 'VM_Imul32', 1], 3: [4270292, 'VM_Nor32', 1], 4: [4287932, 'VM_PopR8', 2], 5: [4282251, 'VM_PushI8', 2], 6: [4272347, 'VM_PopR32', 2], 7: [4285151, 'VM_GetAddESP', 2], 8: [4278147, 'VM_Nop', 1], 9: [4281092, 'VM_ReadDs8', 1], 10: [4276973, 'VM_PushI32', 5], 11: [4282330, 'VM_ShortJMP', 10], 12: [4274590, 'VM_ReadDs16', 1], 13: [4287240, 'VM_PushR32', 2], 14: [4302275, 'VM_CALL', 5], 15: [4284884, 'VM_ReadDs32', 1], 16: [4277031, 'VM_Neg32_', 1], 17: [4275327, 'VM_PopR16', 2], 18: [4287760, 'VM_JMP', 1], 19: [4282067, 'VM_Popfd', 1], 20: [4285827, 'VM_PopSp', 1], 21: [4275443, 'VM_Add32', 1], 22: [4272702, 'VM_WriteDs8', 1], 23: [4286679, 'VM_Sub32', 1], 24: [4281194, 'VM_Retn', 1], 25: [4269730, 'VM_PushR8', 2], 26: [4278158, 'VM_PushStackTop32', 1], 27: [4277570, 'VM_AddSp', 3], 28: [4273429, 'VM_WriteDs32', 1], 29: [4281625, 'VM_WriteDs16', 1], 30: [4273164, 'VM_PushStackTop32', 1], 31: [4272019, 'VM_PushR16', 2], 32: [4281993, 'VM_Neg32', 1], 33: [4271585, 'VM_PushI16', 3], 34: [4273139, 'VM_Extend16To32', 1], 35: [4289458, 'VM_ExecRealInst', 1], 36: [4274304, 'VM_Shr32', 1]}
veip=0x42c17c
ME=Mapo_Engine(bs,veip,handler)
ME.disam() #开始反汇编
注释后的结果如下,这个最好和最开始未被虚拟化的代码对比一下:
[Asm] 纯文本查看 复制代码
0x42c17c VM_PushI32 0x421f21
0x42c181 VM_CALL retaddr= 0x4456ae ;0x421f21这个函数是动态获取MessageBoxW的地址,并执行未虚拟化的代码mov edi,MessageBoxW_addr
0x42c186 VM_ExecRealInst {NOP}
0x42c189 VM_PushI32 0x2
0x42c18e VM_PopR32 ESI ;mov esi,0x2
0x42c190 VM_ExecRealInst {NOP DWORD PTR [EAX]}
0x42c196 VM_PushI32 0x0
0x42c19b VM_PushI32 0x4020f8 ;"hi"
0x42c1a0 VM_PushI32 0x402100 ;"hello"
0x42c1a5 VM_PushI32 0x0
0x42c1aa VM_PushR32 EDI
0x42c1ac VM_CALL retaddr= 0x445727 ;调用 MessageBoxW
0x42c1b1 VM_PushR32 ESI
0x42c1b3 VM_PushI8 0x1
0x42c1b5 VM_Extend8Sto32
0x42c1b6 VM_PopRealfd
0x42c1b7 VM_Sub32 ;sub esi, 1
0x42c1b8 VM_Popfd
0x42c1b9 VM_PopR32 ESI
0x42c1bb VM_PushI32 0x42c1d8 ;下面是经典的vmp式跳转 0x42c1d8是退出循环 0x42c196继续循环
0x42c1c0 VM_PushI32 0x42c196
0x42c1c5 VM_PushR32 flag
0x42c1c7 VM_PushStackTop32
0x42c1c8 VM_Nor32
0x42c1c9 VM_PushI32 0x40
0x42c1ce VM_PushStackTop32
0x42c1cf VM_Nor32
0x42c1d0 VM_Nor32
0x42c1d1 VM_PushI32 0x6
0x42c1d6 VM_Shr32
0x42c1d7 VM_JMP
0x42c1d8 VM_PushI32 0x401035
0x42c1dd VM_ExecRealInst {RET} ;这里没使用vm_retn退出,直接用ret改变eip到0x401035,不继续进入虚拟机
0x401035的内容如下:
0x05 结束语
本文并不是开发一种通用的分析工具,只是想分享一下分析分方法,如果文中有错误的地方,望大家不吝赐教。
免费评分
查看全部评分