Handler
Type 2__
第2类handler的结构要清晰一些,如果不算2字节opcode中的重复部分,这类handler有7个。特征是PCODE.OpcodeTail数据为3字节,操作数类型为RegReg/RegImm,即这类指令的dst为VM_Context内的通用寄存器,src为VM_Context内的通用寄存器或立即数。
下面以Vm_MovReg为例说明。
这类handler将VM_Context.flag的初始值预置为0。解码byte1,对这个字节的使用与Type1中的byte2相似, 若b7位为1,使用VM_Context内+60h,+64h的2个dword修改vm栈内由key1解码的OldArgument。由于这2个dword似乎总为0,这里不会产生实质的影响。
解码byte2,byte3(不再费事去取名了,就是按解码的顺序编号)。byte2的b0,b1对应值为OperandSize,复制到flag.OperandSize域。b2,b3被复制到flag的b3,b4,这2位的含义下面说明。
解析源操作数。检测byte3的b0位,若b0为1,则Operand_Src为立即数(即vm栈内的OldArgument),不需要额外处理,跳到解析Operand_Dst的代码。
否则,Operand_Src为VM_Context内的某个通用寄存器,取byte3的b2-b4 3位,其对应值为该寄存器在通用寄存器环内的index。根据当前滚动字节数计算出实际地址,取其所指dword替换掉vm栈内的OldArgument。
解析目的操作数。byte3的b5-b7 3位代表Operand_Dst寄存器在环内的index,取其实际地址压栈。此时vm栈内为:
[esp] -> Operand_Dst,VM_Context内通用寄存器地址
[esp+4] -> Operand_Src,立即数或VM_Context内某个通用寄存器值
开始执行实际操作。检测flag.OperandSize,非8位操作跳走。先看下面的8位操作。eax为源操作数,ecx为目的寄存器内的原值。对于MOV指令,这个原值并不需要,显然这些代码是程序生成的。
下面的代码要检测flag的b3,b4位,这里先给出Type2 handler对flag的解释。
b7 |
b6 |
b5 |
b4 |
b3 |
b2 b1 |
b0 |
|
|
|
DstUseHigher8B
对于8位操作,该位为1表示dst使用高8位字节 |
SrcUseHigher8B 对于8位操作,该位为1表示dst使用高8位字节 |
OperandSize
0 -> 8位 1 -> 16位 2 -> 32位 |
|
b3,b4位只对8位操作才有用。注意eax,ecx的数据是dword形式。b4位对应目的操作数,b3对应源操作数。b4或b3为1,代表使用高8位字节。这里的表达不大清楚,简单地说,b4为1表示真正的dst是ch,b3为1表示src用ah。
Vm_MovReg的8位操作,不需要多解释了J。
将栈内的结果写回VM_Context,这里还要检测1次b4。
下面看看16位操作,这个就直截了当了。
32位。