吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 15930|回复: 39
收起左侧

[原创] 简单分析MapoEngine虚拟化架构

  [复制链接]
wmsuper 发表于 2018-7-27 15:09
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;
}

会被虚拟化的代码如下:
1.png
0x02 寻找虚拟机handler
        这个虚拟机是非常典型的循环解码字节码执行的架构,而不是类似vmp3.x的线性解码的架构,这种架构可以通过OD记录trace,统计执行次数很快就能确定指令表了(jmp dword ptr ds:[eax*4+0x41A606])

2.png
下面是对应的指令表,一共37条指令
3.png

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的内容如下:
4.png

0x05 结束语
        本文并不是开发一种通用的分析工具,只是想分享一下分析分方法,如果文中有错误的地方,望大家不吝赐教。

免费评分

参与人数 23吾爱币 +23 热心值 +23 收起 理由
gegon + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
demoscene + 2 + 1 我很赞同!
泽雨天下 + 1 + 1 谢谢@Thanks!
evea + 1 + 1 我很赞同!
JacklyMa + 1 + 1 谢谢@Thanks!
如实知见 + 1 + 1 谢谢@Thanks!
jnez112358 + 1 + 1 谢谢@Thanks!
梁萧 + 1 + 1 谢谢@Thanks!
liphily + 1 用心讨论,共获提升!
小俊 + 2 + 1 谢谢@Thanks!
poisonbcat + 1 + 1 谢谢@Thanks!
laoxing + 1 + 1 晴天大师傅
baluluys + 1 + 1 谢谢@Thanks!
xinkui + 1 + 1 谢谢@Thanks!
610100 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
xiao_ya + 1 + 1 谢谢@Thanks!
zl9801 + 1 + 1 谢谢@Thanks!
感冒的猪baby + 3 + 1 谢谢@Thanks!
CrazyNut + 1 mapo被搞指日可待
lookerJ + 1 谢谢@Thanks!
WYWZ + 1 + 1 用心讨论,共获提升!
AmIzero + 1 + 1 谢谢@Thanks!
Ravey + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

hlrlqy 发表于 2018-7-27 15:17
作者对各种工具的运用可以说是炉火纯青了,佩服佩服,写的也比较简单明了
yAYa 发表于 2018-7-27 15:21
Aperodry 发表于 2018-7-27 15:32
厉害,python和符号执行一直想学,可惜都没有机会
zl9801 发表于 2018-7-27 16:28
完全看不懂...但是也得谢谢分享....赞一下...............
皓月苍穹之星 发表于 2018-7-27 17:08 来自手机
吾爱的手机排版看着好吃力啊。。
liuq2007 发表于 2018-7-27 19:18
这个一定要支持,感谢分享!!!
头像被屏蔽
vvking7 发表于 2018-7-27 20:52
膜拜.出了好几篇关于vm壳的精华帖子了.高兴
wenwuge 发表于 2018-7-28 09:02
谢谢分享 学习一下
头像被屏蔽
xtkj4382 发表于 2018-7-28 10:04
看看学习下。。。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-3-29 19:22

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表