原文链接:http://bbs.pediy.com/showthread.php?t=137265
【详细过程】
1.VM的解密算法
一般情况下Code Virtualizer的解密有三个运算:add,sub,xor,有如下计算;
loads/b/w/d
operand eax/ax/al ebx/bx/bl //与key进行计算
operand eax/ax/al imm32/imm16/imm8
operand eax/ax/al imm32/imm16/imm8
operand ebx/bx/bl eax/ax/al //更新key
其中esi指向pcode的数据,ebx为解码key,也就是进入虚拟机前压入堆栈的值:
push xxxxxxxx //解码key,正常等于esi
jmp VMStart
2.方法
让VM代码在emulator中执行,记录寄存器和堆栈状态,并HOOK相应的操作,应该可以得到pcode的id
3.x86 emulator
应该有好多,本次采用的是PyEmu,并结合IDA和IDAPython
4.明文pcode的获取
设置emulator初始环境,设置EIP,指向虚拟机取指令处,为了使emulator顺利执行,安装异常处理函数,当发现异常时记录异常状态,重设EIP,指向下一指令(有问题??),并在更新key处监视eax/ax/al的值,这就是明文pcode,对python不熟悉(缩进实在无语,有可能是我的编辑器有问题),这个emulator BUG多多,不可能完整跑下来,修改了几处,以dump_wmimmc.sys为例,简单测试了取指令:
代码:
import sys sys.path.append(r'D:\reverse\IDA\python\pyemu') sys.path.append(r'D:\reverse\IDA\python\pyemu\lib') from PyEmu import * emu = IDAPyEmu() #debug=0,1,2,>2 emu.debug(0) vlizer_start = SegByName(".reloc") vlizer_end = SegEnd( vlizer_start ) #load vlizer section for i in range(vlizer_start,vlizer_end,4): emu.set_memory(i,Dword(i)) print "[*] Finished loading vlizer section into memory,Start at %x,End at %x,size:%x"%(vlizer_start,vlizer_end,vlizer_end-vlizer_start) flag=1 instack=0 #eip=fetchcode emu.set_register("eip",0x10013bd6) #ebx=key emu.set_register("ebx",0x10018cca) #esi=ebx+delta emu.set_register("esi",0x10018cca) #edi=vmctx+delta emu.set_register("edi",0x10013880) while True: eip=emu.get_register("eip") mnemonic=GetMnem(eip) op1=GetOpnd(eip,0) op2=GetOpnd(eip,1) #push eax,ax if mnemonic=="push": if GetOpType(eip,0)==1 and GetOperandValue(eip,0)==0: instack=1 #pop eax,ax elif mnemonic=="pop": if GetOpType(eip,0)==1 and GetOperandValue(eip,0)==0: instack=0 #push eax,ax #pop eax,ax elif mnemonic=="mov": if op1=="[esp]": if GetOpType(eip,1)==1 and GetOperandValue(eip,1)==0: instack=1 elif op2=="[esp]": if GetOpType(eip,0)==1 and GetOperandValue(eip,0)==0: instack=0 elif mnemonic=="add" or mnemonic=="sub" or mnemonic=="xor": if GetOpType(eip,0)==1 and (GetOperandValue(eip,0)==0 or GetOperandValue(eip,0)==0x10): #operand eax/ax/al ebx/bx/bl if GetOpType(eip,1)==1 and (GetOperandValue(eip,1)==3 or GetOperandValue(eip,1)==0x13) and flag: print "%s %s,%s"%(mnemonic,op1,op2) flag=0 #operand eax/ax/al imm32/imm16/imm8 else: if not instack: if GetOpType(eip,1)==1: print "%s %s,%x"%(mnemonic,op1,emu.get_register(op2)) elif GetOpType(eip,1)==5: print "%s %s,%x"%(mnemonic,op1,GetOperandValue(eip,1)) #operand ebx/bx/bl eax/ax/al if (GetOpType(eip,0)==1 and (GetOperandValue(eip,0)==3 or GetOperandValue(eip,0)==0x13)) and (GetOpType(eip,1)==1 and (GetOperandValue(eip,1)==0 or GetOperandValue(eip,1)==0x10)): print "%s %s,%s"%(mnemonic,op1,op2) print "handleindex:%x"%emu.get_register("eax") break emu.execute()
输出:
sub al,e9
add al,bl
add al,e9
xor al,d2
sub al,f1
add bl,al
handleindex:2d
最后清理后的取指令为:
loadsb
add al,bl
xor al,d2
sub al,f1
add bl,al
movzx eax,al
jmp dword ptr [edi+eax*4]
测试可行,起个抛砖引玉的作用。