关于Code Virtualizer pcode解密的一种方法

 

原文链接: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]

  测试可行,起个抛砖引玉的作用。

点赞