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条非模仿指令。
取数据项第3个dword,加DeltaOffset即非模仿指令地址。后面的就不多说了,恢复环境退出VM。
至此我们已经分析完了Themida VM所有handler类型。下面给出完整的VM指令集。2字节opcode全部是1字节opcode的复制品,重叠的handler其对应关系也可变。
1
字节opcode 08与2字节opcode 1F 00使用的是同1个handler,但换个程序与08对应的可能是另一个2字节opcode,即Themida VM的2字节opcode是个动态指令集。1字节opcode的含义及访问PCODE各field时使用的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_POPif(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_ExecuteNoEmulatedOpcodejmp NonEmulatedOpcode[OldArgument] |
06 |
1 |
Vm_IMUL32if(flag.Stack) { imul [esp+4],[esp] (OperandSize
= 16/32位) } |
07 |
1 |
Vm_ADCif(flag.Stack) { adc [esp+4],[esp] (OperandSize
= 8/16/32位) } else if(flag.ByRef) { VM_Context.register = adc([NewArgument],OldArgument)
; 32位 } |
08 |
1 |
Vm_ADDif(flag.Stack) { add [esp+4],[esp] (OperandSize
= 8/16/32位) } else if(flag.ByRef) { VM_Context.register = add([NewArgument],OldArgument)
; 32位 } |
09 |
1 |
Vm_ANDif(flag.Stack) { and [esp+4],[esp] (OperandSize
= 8/16/32位) } else if(flag.ByRef) { VM_Context.register = and([NewArgument],OldArgument)
; 32位 } |
0A |
1 |
Vm_CMPif(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_ORif(flag.Stack) { or [esp+4],[esp] (OperandSize
= 8/16/32位) } else if(flag.ByRef) { VM_Context.register =
or([NewArgument],OldArgument) ; 32位 } |
0C |
1 |
Vm_SUBif(flag.Stack) { sub [esp+4],[esp] (OperandSize
= 8/16/32位) } else if(flag.ByRef) { VM_Context.register =
sub([NewArgument],OldArgument) ; 32位 } |
0D |
1 |
Vm_TESTif(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_XORif(flag.Stack) { xor [esp+4],[esp] (OperandSize
= 8/16/32位) } else if(flag.ByRef) { VM_Context.register =
xor([NewArgument],OldArgument) ; 32位 } |
0F |
1 |
Vm_MOVZX8if(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_MOVZX16if(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_INCif(flag.Stack) { add [esp+4],1 ; 8/16/32位 } 执行前VM栈内有2个值,[esp]=1,[esp+4]为dst,handler内直接用硬编码值1,对应INC |
13 |
1 |
Vm_RCLif(flag.Stack) { rcl [esp+4],[esp] (OperandSize
= 8/16/32位) } else if(flag.ByRef) { VM_Context.register =
rcl([NewArgument],OldArgument) ; 32位 } |
14 |
1 |
Vm_RCRif(flag.Stack) { rcr [esp+4],[esp] (OperandSize
= 8/16/32位) } else if(flag.ByRef) { VM_Context.register =
rcr([NewArgument],OldArgument) ; 32位 } |
15 |
1 |
Vm_ROLif(flag.Stack) { rol [esp+4],[esp] (OperandSize
= 8/16/32位) } else if(flag.ByRef) { VM_Context.register =
rol([NewArgument],OldArgument) ; 32位 } |
16 |
1 |
Vm_RORif(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_SARif(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_SHRif(flag.Stack) { shr [esp+4],[esp] (OperandSize
= 8/16/32位) } else if(flag.ByRef) { VM_Context.register = shr([NewArgument],OldArgument)
; 32位 } |
1B |
1 |
Vm_DECif(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栈.如果写解码程序会发现,这里解出的下1个pcode数据地址超出了当前pcode数据地址范围,2个opcode也超出取值范围.估计这代表pcode数据结束 |
1D |
1 |
Vm_MOVSX8if(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_MOVSX16if(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_CLCclc |
20 |
1 |
Vm_CLDcld |
21 |
1 |
Vm_NOP
nop |
22 |
1 |
Vm_CMCcmc |
23 |
1 |
Vm_STCstc |
24 |
1 |
Vm_STDstd |
25 |
1 |
Vm_STIsti |
26 |
1 |
VM_SetEbpDeltaOffsetVM_Context.ebp
= VM_Context.delta_offset |
27 |
1 |
Vm_BTif(flag.Stack) { bt [esp+4],[esp] ; (OperandSize = 16/32位) } |
28 |
1 |
Vm_BTCif(flag.Stack) { btc [esp+4],[esp] ; (OperandSize = 16/32位) } |
29 |
1 |
Vm_BTRif(flag.Stack) { btr [esp+4],[esp] ; (OperandSize = 16/32位) } |
2A |
1 |
Vm_BTSif(flag.Stack) { bts [esp+4],[esp] ; (OperandSize = 16/32位) } |
2B |
1 |
Vm_SBBif(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_DIVif(flag.Stack) { div [esp+8]:[esp+4],[esp] (OperandSize
= 8/16/32位) } div为16b/8b,32b/16b,64b/32b,vm栈上实际有3个dword,按需要解释其size,注意计算结果的保存方式 |
2F |
1 |
Vm_IDIVif(flag.Stack) { idiv [esp+8]:[esp+4],[esp] (OperandSize
= 8/16/32位) } idiv为16b/8b,32b/16b,64b/32b,vm栈上实际有3个dword,按需要解释其size,注意计算结果的保存方式 |
30 |
1 |
Vm_BSWAPif(flag.Stack) { bswap [esp] ; (OperandSize
= 8/16/32位) } Bug?从Intel手册看,bswap只应对32位寄存器使用.Themida对8/32位操作用bswap
eax,对16位用bswap ax(用0x66前缀),可能有问题 |
31 |
1 |
Vm_NEGif(flag.Stack) { neg [esp] ; (OperandSize
= 8/16/32位) } |
32 |
1 |
Vm_NOTif(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_Loadmov VM_Context.register,NewArgument |
3D |
1 |
Vm_Push0在VM栈内压入0(32位) push 0 |