Handler Type 4

 

这类handler很简单, 1字节编码opcode中只有1,使用2字节OpcodeDetail数据,可以合并到Type 1里。之所以单独列出来是因为其功能比较特殊,用于执行非模仿指令。

 

只列出与功能相关的代码。

只使用Key1解码的OldArgument,switch-case压入的NewArgument未用。

 

VM_Context.num_of_pcode_items为当前VM保护代码可以在VM内执行的代码块数。在Part1中已经提到过了,当存在VM不能模仿的代码时,这个值比PUSH/JMP下实际的数据项数小。

 

OldArgument是个index,指定应使用非模仿指令数据的第几项,以解析退出VM后的执行地址。上面的代码跳过PUSH/JMP下的“正常”数据,根据index值定位非模仿指令所属数据。

 

还是以Part1里那段CVDemo的代码来说明。

 

dump窗口红框内的数据对应最后1段“正常”的VM保护代码。在其后的3组数据对应3条非模仿指令。

 

 

取数据项第3dword,DeltaOffset即非模仿指令地址。后面的就不多说了,恢复环境退出VM

 

至此我们已经分析完了Themida VM所有handler类型。下面给出完整的VM指令集。2字节opcode全部是1字节opcode的复制品,重叠的handler其对应关系也可变。

 

1 字节opcode 082字节opcode 1F 00使用的是同1handler,但换个程序与08对应的可能是另一个2字节opcode,Themida VM2字节opcode是个动态指令集。1字节opcode的含义及访问PCODEfield时使用的offset是固定的,当然作者完全可以把这个也搞成动态的J。下面只列出1字节opcode,功能以伪码说明(opcode的名字不一定合适J)

 

Themida VM Instruction Set_____________________________________________

 

Opcode

Type

Mnemonics

00

1

Vm_Push

只支持32位操作

 

if((flag.ByRef) || (flag.OperateEsp))

{

   if(flag.FS)

push fs:[NewArgument]

else

  push [NewArgument]

}

else

    push NewArgument

01

1

Vm_POP

if(flag.EFlag)

   dst = VM_Context.Eflag   ; 等价于popf

else

   dst = NewArgument

 

if(flag.FS)

   pop fs:[dst] ; (OperandSize = 8/16/32)

else

   pop [dst] ; (OperandSize = 8/16/32)

02

1

Vm_Mov  

只支持32位操作

mov [NewArgument],OldArgument

03

3

Vm_Jxx

这个难以用伪码描述;-)

04

1

Vm_Push0

VM栈内压入0(32)

push 0

05

4

Vm_ExecuteNoEmulatedOpcode

jmp NonEmulatedOpcode[OldArgument]

06

1

Vm_IMUL32

if(flag.Stack)

{

   imul [esp+4],[esp] (OperandSize = 16/32)

}

07

1

Vm_ADC

if(flag.Stack)

{

    adc [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register = adc([NewArgument],OldArgument) ; 32

}

08

1

Vm_ADD

if(flag.Stack)

{

    add [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register = add([NewArgument],OldArgument) ; 32

}

09

1

Vm_AND

if(flag.Stack)

{

    and [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register = and([NewArgument],OldArgument) ; 32

}

0A

1

Vm_CMP

if(flag.Stack)

{

    cmp [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register =  cmp([NewArgument],OldArgument) ; 32

这里显然有bug,可以看出这些代码是用同一个函数生成的,

MakeOpcodeWithTwoOperandsJ

}

0B

1

Vm_OR

if(flag.Stack)

{

    or [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register =  or([NewArgument],OldArgument) ; 32

}

0C

1

Vm_SUB

if(flag.Stack)

{

    sub [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register =  sub([NewArgument],OldArgument) ; 32

}

0D

1

Vm_TEST

if(flag.Stack)

{

    test [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register =  test([NewArgument],OldArgument) ;32

    Vm_CMP同样的bug

}

0E

1

Vm_XOR

if(flag.Stack)

{

    xor [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register =  xor([NewArgument],OldArgument) ; 32

}

0F

1

Vm_MOVZX8

if(flag.Stack)

{

if(1 == OperandSize) ; 16<-8

   movzx word ptr [esp+4],byte ptr [esp]

else if(2 == OperandSize) ; 32<-8

   movzx dword ptr [esp+4],byte ptr [esp]

}

10

1

Vm_MOVZX16

if(flag.Stack)

{

if(1 == OperandSize) ; 16<-8

   movzx word ptr [esp+4],byte ptr [esp]

else if(2 == OperandSize) ; 32<-16

   movzx dword ptr [esp+4],word ptr [esp]

       与上面的区别在可处理16位值

}

11

1

Vm_Push0

VM栈内压入0(32)

push 0(04)重复

12

1

Vm_INC

if(flag.Stack)

{

    add [esp+4],1 ; 8/16/32

}

执行前VM栈内有2个值,[esp]=1,[esp+4]dst,handler内直接用硬编码值1,对应INC

13

1

Vm_RCL

if(flag.Stack)

{

    rcl [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register =  rcl([NewArgument],OldArgument) ; 32

}

14

1

Vm_RCR

if(flag.Stack)

{

    rcr [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register =  rcr([NewArgument],OldArgument) ; 32

}

15

1

Vm_ROL

if(flag.Stack)

{

    rol [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register =  rol([NewArgument],OldArgument) ; 32

}

16

1

Vm_ROR

if(flag.Stack)

{

    ror [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register =  ror([NewArgument],OldArgument) ; 32

}

17

1

Vm_SAL

(实际代码用shl)

if(flag.Stack)

{

    sal [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register =  sal([NewArgument],OldArgument) ; 32

}

18

1

Vm_SAR

if(flag.Stack)

{

    sar [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register = sar([NewArgument],OldArgument) ; 32

}

19

1

Vm_SHL

(实际代码用shl实现,由于shl/shr = sal/sar,根据指令的前后关系,这里命名为shl)

if(flag.Stack)

{

    shl [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register = shl([NewArgument],OldArgument) ; 32

}

1A

1

Vm_SHR

if(flag.Stack)

{

    shr [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register = shr([NewArgument],OldArgument) ; 32

}

1B

1

Vm_DEC

if(flag.Stack)

{

    sub [esp+4],1 ; 8/16/32

}

执行前VM栈内有2个值,[esp]=1,[esp+4]dst,handler内直接用硬编码值1

1C

1

Vm_ExitVM

这里看起来没做什么,取进入handler时的[esp]->eax,mov eax,eax,再用stosd送回vm.如果写解码程序会发现,这里解出的下1pcode数据地址超出了当前pcode数据地址范围,2opcode也超出取值范围.估计这代表pcode数据结束

1D

1

Vm_MOVSX8

if(flag.Stack)

{

if(1 == OperandSize)

   movsx word ptr [esp+4],byte ptr [esp]

else if(2 == OperandSize)

   movsx dword ptr [esp+4],byte ptr [esp]

}

1E

1

Vm_MOVSX16

if(flag.Stack)

{

if(1 == OperandSize) ; 16<-8

   movsx word ptr [esp+4],byte ptr [esp]

else if(2 == OperandSize) ; 32<-16

   movsx dword ptr [esp+4],word ptr [esp]

} 与上面的区别在这里,可处理16位值

1F

1

Vm_CLC

clc

20

1

Vm_CLD

cld

21

1

Vm_NOP 

nop

22

1

Vm_CMC

cmc

23

1

Vm_STC

stc

24

1

Vm_STD

std

25

1

Vm_STI

sti

26

1

VM_SetEbpDeltaOffset

VM_Context.ebp = VM_Context.delta_offset

27

1

Vm_BT

if(flag.Stack)

{

bt [esp+4],[esp] ; (OperandSize = 16/32)

}

28

1

Vm_BTC

if(flag.Stack)

{

btc [esp+4],[esp] ; (OperandSize = 16/32)

}

29

1

Vm_BTR

if(flag.Stack)

{

btr [esp+4],[esp] ; (OperandSize = 16/32)

}

2A

1

Vm_BTS

if(flag.Stack)

{

bts [esp+4],[esp] ; (OperandSize = 16/32)

}

2B

1

Vm_SBB

if(flag.Stack)

{

    sbb [esp+4],[esp] (OperandSize = 8/16/32)

}

else if(flag.ByRef)

{

VM_Context.register = sbb([NewArgument],OldArgument) ; 32

}

2C

1

Vm_MUL

注意结果保存方式,[esp],[esp+4]都用了

if(flag.Stack)

{

   mul [esp+4],[esp] (OperandSize = 8/16/32)

}

2D

1

Vm_IMUL8

注意结果保存方式,[esp],[esp+4]都用了

if(flag.Stack)

{

   imul [esp+4],[esp] (OperandSize = 8/16/32)

}

opcode 06比较,前者不支持8,只保存结果的低位部分

2E

1

Vm_DIV

if(flag.Stack)

{

   div [esp+8]:[esp+4],[esp] (OperandSize = 8/16/32)

}

div16b/8b,32b/16b,64b/32b,vm栈上实际有3dword,按需要解释其size,注意计算结果的保存方式

2F

1

Vm_IDIV

if(flag.Stack)

{

   idiv [esp+8]:[esp+4],[esp] (OperandSize = 8/16/32)

}

idiv16b/8b,32b/16b,64b/32b,vm栈上实际有3dword,按需要解释其size,注意计算结果的保存方式

30

1

Vm_BSWAP

if(flag.Stack)

{

    bswap [esp] ; (OperandSize = 8/16/32)

}

Bug?Intel手册看,bswap只应对32位寄存器使用.Themida8/32位操作用bswap eax,16位用bswap ax(0x66前缀),可能有问题

31

1

Vm_NEG

if(flag.Stack)

{

    neg [esp] ; (OperandSize = 8/16/32)

}

32

1

Vm_NOT

if(flag.Stack)

{

    not [esp] ; (OperandSize = 8/16/32)

}

33

1

Vm_Push0

VM栈内压入0(32)

push 0

34

1

Vm_Push0

VM栈内压入0(32)

push 0

35

2

Vm_MovReg

 

36

2

Vm_AddReg

 

37

2

Vm_SubReg

 

38

2

Vm_OrReg

 

39

2

Vm_XorReg

 

3A

2

Vm_AndReg

 

3B

2

Vm_CmpReg

 

3C

1

Vm_Load

mov VM_Context.register,NewArgument

3D

1

Vm_Push0

VM栈内压入0(32)

push 0