前言:

  • 第一次设计vmp框架和编写,因设计得有点匆忙,导致后面茶饭不思,一直在改bug,现在基本上处理好了。在这里吐槽一下,如果时间充足,至少框架会设计的更好一点。
  • 因为要兼容64位,汇编引擎我用的是XEDParse,反汇编引擎用的BeaEngine。另外,这个vmp不能对驱动加vm,驱动vm的话,一些处理方法会不一样。
  • 在vmp设计中,我定义了几个模块:指令分析器,垃圾块指令构造器,IAT加密模块,反调试模块。
  • 这几个模块的说明篇幅有点长,这篇文章把指令分析器和垃圾块指令构造器做个说明。


0x00有些问题的说明。
1、有些奇葩的指令现在仍然无法处理,如下:[Asm] 纯文本查看 复制代码mov eax,nextcall //nextcall是函数地址
mov ecx,nextaddr //nextaddr是jmp ecx下一行的地址
push ecx
jmp eax
add esp,4

上面这些指令等价于:
mov eax,nextcall
call eax
add esp,4
都是可以处理的,归类于不可模拟指令。

但是下面这种情况,处理起来就有问题
mov ecx,nextaddr //nextaddr是jmp ecx下一行的地址
jmp ecx
add esp,4
….
这个jmp ecx是在函数内跳转,不能归于不可模拟指令。

在函数内跳转,一般情况是 jmp 后面跟的地址,比如
mov eax,nextcall
mov ecx,nextaddr
jmp L11
add esp,4
L11:
ret
标号L11其实就是地址标号,在vm指令解析器解析指令的时候,L11是随着活动的进行可以传递的属性,方便加了垃圾指令之后,让新地址和旧地址进行匹配,
然后进行回填。而jmp ecx,“ecx”只是一个寄存器,不是地址,即使进行传递,那么在新地址和旧地址进行匹配的时候(即新地址和“ecx”匹配),无法匹配,会出BUG。

2、把一个函数vm的时候,要写开始地址和结尾地址,不写结尾地址的话,那么就要写一个函数来自动识别要vm的函数的结尾地址。以下函数就是自动识别要vm的函数的结尾地址,
主要功能是遇到跳转指令比如JCC或者JMP指令,就记录下来,与后面遇到的ret指令所在的地址进行比较,如果小于ret指令所在的地址,那么,这条ret就是结束地址,否则继续往下判断。
[C++] 纯文本查看 复制代码struct FUNCANDINSLENGTH
{
int inslen = 0;//记录指令的条数
int hcodelength = 0;//记录硬编码长度
bool istrue = true;//读取指令是否成功
};

//计算函数的长度
FUNCANDINSLENGTH Disassembler_(DWORD virtualaddr,DWORD instruction)
{
FUNCANDINSLENGTH fuclen;
int inslength = 0;//记录指令的条数

vector<DWORD>JccAddr;//记录JCC后面的地址

DISASM disAsm = { 0 };

// 3. 配置结构体,初始化反汇编的opcode
disAsm.EIP = (UIntPtr)instruction;
disAsm.VirtualAddr = virtualaddr; // opcode 指令的地址
disAsm.Archi = 0; // 0 => 32 , 1 => 64
disAsm.Options = 0x000; // masm 汇编指令格式

int nCount = 0;// 用于记录在循环当中,反汇编了多少个字节
int nLen = 0; // 用于记录当前的汇编指令的字节数

while (true)
{
nLen = Disasm(&disAsm); // 每次只反汇编一条汇编指令, 并且返回当前得到的汇编指令的长度
unsigned int uAddr = disAsm.VirtualAddr;

printf("%08X | ", uAddr); // 打印地址
printOpcode((const unsigned char*)disAsm.EIP, nLen); // 打印opcode
printf(" | %s\\n", disAsm.CompleteInstr); // 打印反汇编指令

if (inslength == 0x1000)
{//防止死循环
MessageBoxA(NULL, "未知错误,读取指令失败!!", "提示", MB_OK);
fuclen.istrue = false;
return fuclen;
}

++inslength;//记录指令的条数,为后面申请内存提供依据
nCount += nLen; // 累加已经反汇编的字节数
disAsm.EIP += nLen; // 定位到下一条汇编指令
disAsm.VirtualAddr += nLen; // 设置到下一条汇编指令的地址

//如果遇到jcc指令或者jmp指令,记录后面的地址
for (int i = 0; i < JCCNUMSS; i++)
{
if (0 == stricmp(disAsm.Instruction.Mnemonic, JCCSTR[i]))
{
DWORD tempaddr = 0;
sscanf_s(disAsm.Argument1.ArgMnemonic, "%X", &tempaddr);
JccAddr.push_back(tempaddr);
break;
}
}

if (
(0 == stricmp(disAsm.Instruction.Mnemonic, "ret ")) ||
(0 == stricmp(disAsm.Instruction.Mnemonic, "retn ")) ||
(0 == stricmp(disAsm.Instruction.Mnemonic, "int3 "))
)
{
int isretn = 1;
for (int i = 0; i < JccAddr.size(); i++)
{
if ((*(JccAddr.begin()+i)) > disAsm.VirtualAddr- nLen)
{
isretn = 0;
break;
}
}
if (isretn || (0 == stricmp(disAsm.Instruction.Mnemonic, "int3 ")))
{
fuclen.hcodelength = nCount;
fuclen.inslen = inslength;
return fuclen;
}
}
}
}

3、没有处理异常。另外,如果要保护的函数里有检查堆栈的函数,一定要注意标志寄存器的处理问题,比如退出虚拟机的时候,出栈完成了,
后面还有改变标志寄存器的指令,则可以使用xor ecx,ecx(返回值一般是eax,所有可以用ecx寄存器)这指令再平衡回来,因为检查堆栈的函数只检查ZF标志位。

0x01 指令分析器
1、指令分析器的功能就是把要保护的指令,翻译为中间表示,可以用一个结构体来保存这些中间表示和一些需要传递的属性,当然,也可以把指令分析器理解为一个有穷自动机(接收指令 -> 解析指令 -> 中间表示)。我用的是BeaEngine引擎,构造自动机来解析指令的时候就要遵循BeaEngine反汇编引擎的规则,指令分析器的主框架如下:[Asm] 纯文本查看 复制代码//解析要保护的指令,翻译为中间表示
void MiddleRepresent(DISASM disAsm)
{
/*———————————————————————————-*/
/* 1、是否有操作3 */
/*———————————————————————————-*/
if (NO_ARGUMENT != disAsm.Argument3.ArgType)
{
switch (disAsm.Argument3.ArgType & 0xF0000000)
{
case REGISTER_TYPE: //寄存器
break;
case MEMORY_TYPE: //内存
break;
case CONSTANT_TYPE://常数
break;
default:
break;
}
}

/*———————————————————————————-*/
/* 2、是否有操作2 */
/*———————————————————————————-*/
if (NO_ARGUMENT != disAsm.Argument2.ArgType)
{
switch (disAsm.Argument2.ArgType & 0xF0000000)
{
case REGISTER_TYPE: //寄存器
break;
case MEMORY_TYPE: //内存
break;
case CONSTANT_TYPE://常数
break;
default:
break;
}
}

/*———————————————————————————-*/
/* 3、是否有操作1 */
/*———————————————————————————-*/
if (NO_ARGUMENT != disAsm.Argument1.ArgType)
{
switch (disAsm.Argument1.ArgType & 0xF0000000)
{
case REGISTER_TYPE: //寄存器
break;
case MEMORY_TYPE: //内存
break;
case CONSTANT_TYPE://常数
break;
default:
break;
}
}

/*———————————————————————————-*/
/* 4、处理普通handler */
/*———————————————————————————-*/

//省略…

/*———————————————————————————-*/
/* 5、判断是否有辅助handler */
/*———————————————————————————-*/
if (
0x10000000 != disAsm.Argument1.ArgType ||
0x10000000 != disAsm.Argument2.ArgType ||
0x10000000 != disAsm.Argument3.ArgType
)
{

if (NO_ARGUMENT != disAsm.Argument1.ArgType)
{
switch (disAsm.Argument1.ArgType & 0xF0000000)
{
case REGISTER_TYPE: //寄存器
break;
case MEMORY_TYPE: //内存
break;
case CONSTANT_TYPE://常数
break;
default:
break;
}
}

}
}

2、指令是从右往左解析的,比如:
[Asm] 纯文本查看 复制代码
mov eax,ecx
翻译为中间表示就是
vPushReg VR_ecx //操作2
vPushReg VR_eax //操作1
vMOV //普通handler
vPopReg VR_eax //辅助handler

3、整个指令解析器的构造,如下:
[Asm] 纯文本查看 复制代码
//处理内存操作,把内存翻译为中间表示
void VMLoader2::MemoryMiddle(DISASM disAsm,MEMORYTYPE memtype)
{
/*
* vPushImm4
* vPushReg
* vMUL_MEM //内存操作专用乘法handler
* vPushReg
* vAdd
* vPushImm4
* vAdd
* vWriteMemDs4/2/1
*/
MIDDLESTRUCT midstr;
DATATABLE datatbl;
midstr.originaddr = disAsm.VirtualAddr;
if (memtype.Scale!=0)
{
char vpushreg4[] = "vPushImm4 ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示

datatbl.data = memtype.Scale;
datatbl.recodeOaddr = disAsm.VirtualAddr;
m_datatable.push_back(datatbl);//数据压入数据表

for (int i = 0; i < REGNUMS; i++)
{
if (memtype.IndexRegister == tempreg[i].index)
{
char vpushreg4[] = "vPushReg4 ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示

DATATABLE datatbl;
datatbl.data = i;
datatbl.recodeOaddr = disAsm.VirtualAddr;
m_datatable.push_back(datatbl);//数据压入数据表

break;
}
}

char vmul_mem[] = "vMUL_MEM ";
printf("%s\\n", vmul_mem);
memcpy(midstr.vmfunc, vmul_mem, sizeof(vmul_mem));
m_middle.push_back(midstr);//操作码压入中间表示
}

int i = 0;
for (; i < REGNUMS; i++)
{
if (memtype.BaseRegister == tempreg[i].index)
{
char vpushreg4[] = "vPushReg4 ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示

DATATABLE datatbl;
datatbl.data = i;
datatbl.recodeOaddr = disAsm.VirtualAddr;
m_datatable.push_back(datatbl);//数据压入数据表
break;
}
}

if ((memtype.Scale != 0) && (i != REGNUMS))
{
char vadd[] = "vAdd4 ";
printf("%s\\n", vadd);
memcpy(midstr.vmfunc, vadd, sizeof(vadd));
m_middle.push_back(midstr);//操作码压入中间表示
}

if (0 != memtype.Displacement)
{
char vpushreg4[] = "vPushImm4 ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示

datatbl.data = memtype.Displacement;
m_datatable.push_back(datatbl);//数据压入数据表
}

if ((0 != memtype.Displacement) && (i != REGNUMS))
{
char vadd[] = "vAdd4 ";
printf("%s\\n", vadd);
memcpy(midstr.vmfunc, vadd, sizeof(vadd));
m_middle.push_back(midstr);//操作码压入中间表示
}
}

//解析要保护的指令,翻译为中间表示
void VMLoader2::MiddleRepresent(DISASM disAsm)
{
MIDDLESTRUCT midstr;
midstr.originaddr = disAsm.VirtualAddr;
bool IsSimulation = true;

//vm的环境准备
if (start_bool)
{
char vresumestart[] = "VMStartVM_2 ";
printf("%s\\n", vresumestart);
memcpy(midstr.vmfunc, vresumestart, sizeof(vresumestart));
m_middle.push_back(midstr);//压入VMStartVM_2
start_bool = false;
}

/*———————————————————————————-*/
/* 0、判断是否是不可模拟 */
/*———————————————————————————-*/
for (int i = 0; i < FUNNUMS; i++)
{
if (stricmp(disAsm.Instruction.Mnemonic, g_FunName[i].s_opecode) == 0)
{
IsSimulation = false;
break;
}
}
if (0 == stricmp(disAsm.Instruction.Mnemonic, "jmp ") && ((disAsm.Argument1.ArgType & 0xF0000000)== MEMORY_TYPE)||
0 == stricmp(disAsm.Instruction.Mnemonic, "push ") && ((disAsm.Argument2.ArgType & 0xF0000000) == MEMORY_TYPE)||
0 == stricmp(disAsm.Instruction.Mnemonic, "pop ") && ((disAsm.Argument1.ArgType & 0xF0000000) == MEMORY_TYPE)
)
{
IsSimulation = true;
}
//0、1 如果是不可模拟指令,处理好后直接返回
if (IsSimulation)
{
char retnotaddr[] = "vRetnNOT_ ";
printf("%s\\n", retnotaddr);
memcpy(midstr.vmfunc, retnotaddr, sizeof(retnotaddr));
m_middle.push_back(midstr);//先压入vRetnNOT

char notsimulate[] = "vNotSimulate ";
char vresumestartaddr[OPECODELENGTH];
sprintf(vresumestartaddr, "%X", m_vmps.vresumestartaddr);

printf("%s\\n", notsimulate);
memcpy(midstr.vmfunc, notsimulate, sizeof(notsimulate));
memcpy(midstr.param1, disAsm.CompleteInstr, STRUCTIONLENGTH);
memcpy(midstr.param2, vresumestartaddr, OPECODELENGTH);
m_middle.push_back(midstr);//再压入不可模拟指令的handler

char vresumestart[] = "vResumeStart_ ";
printf("%s\\n", vresumestart);
memcpy(midstr.vmfunc, vresumestart, sizeof(vresumestart));
m_middle.push_back(midstr);//压入vResumeStart_

return;
}

/*———————————————————————————-*/
/* 1、是否有操作3 */
/*———————————————————————————-*/
if (NO_ARGUMENT != disAsm.Argument3.ArgType)
{
switch (disAsm.Argument3.ArgType & 0xF0000000)
{
case REGISTER_TYPE: //寄存器
break;
case MEMORY_TYPE: //内存
break;
case CONSTANT_TYPE://常数
break;
default:
break;
}
}

/*———————————————————————————-*/
/* 2、是否有操作2 */
/*———————————————————————————-*/
if (NO_ARGUMENT != disAsm.Argument2.ArgType)
{
switch (disAsm.Argument2.ArgType & 0xF0000000)
{
case REGISTER_TYPE: //寄存器
{
for (int i = 0; i < REGNUMS; i++)
{
if (tempreg[i].index == (disAsm.Argument2.ArgType & 0xFFFF))
{
if (0x20 == disAsm.Argument2.ArgSize)
{//32位
char vpushreg4[] = "vPushReg4 ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示
}
else if (0x10 == disAsm.Argument2.ArgSize)
{//16位
char vpushreg4[] = "vPushReg2 ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示
}
else
{//8位(要判断高位还是低位)
for (int i = 0; i < 14; i++)
{
if (stricmp(disAsm.Argument2.ArgMnemonic, regname_[0][i]) == 0)
{
if (i<=3)//小于等于3是低位
{
char vpushreg4[] = "vPushReg1_low ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示
}
else
{
char vpushreg4[] = "vPushReg1_above ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示
}
break;
}
}
}
DATATABLE datatbl;
datatbl.data = i;
datatbl.recodeOaddr = disAsm.VirtualAddr;
m_datatable.push_back(datatbl);//数据压入数据表
break;
}
}

}
break;
case MEMORY_TYPE: //内存
{
if (0 == stricmp("pop ", disAsm.Instruction.Mnemonic)) break;
if (0 == stricmp("ret ", disAsm.Instruction.Mnemonic)) break;

MemoryMiddle(disAsm, disAsm.Argument2.Memory);

if (0x20 == disAsm.Argument2.ArgSize)
{//32位
char vReadMem[] = "vReadMemDs4 ";
printf("%s\\n", vReadMem);
memcpy(midstr.vmfunc, vReadMem, sizeof(vReadMem));
}
else if (0x10 == disAsm.Argument2.ArgSize)
{//16位
char vReadMem[] = "vReadMemDs2 ";
printf("%s\\n", vReadMem);
memcpy(midstr.vmfunc, vReadMem, sizeof(vReadMem));
}
else
{//8位
char vReadMem[] = "vReadMemDs1 ";
printf("%s\\n", vReadMem);
memcpy(midstr.vmfunc, vReadMem, sizeof(vReadMem));
}

memcpy(midstr.param1, disAsm.Instruction.Mnemonic, 16);//参数一
memcpy(midstr.param2, disAsm.Argument2.ArgMnemonic, 32);//参数二
m_middle.push_back(midstr);//操作码压入中间表示
}
break;
case CONSTANT_TYPE://常数
{
DWORD constnums = 0;
sscanf(disAsm.Argument2.ArgMnemonic, "%X", &constnums);

char vpushimm4[] = "vPushImm4 ";
printf("%s\\n", vpushimm4);

memcpy(midstr.vmfunc, vpushimm4, sizeof(vpushimm4));
m_middle.push_back(midstr);//操作码压入中间表示

DATATABLE datatbl;
datatbl.data = constnums;
datatbl.recodeOaddr = disAsm.VirtualAddr;
m_datatable.push_back(datatbl);//数据压入数据表

}
break;
default:
break;
}
}

/*———————————————————————————-*/
/* 3、是否有操作1 */
/*———————————————————————————-*/
if (NO_ARGUMENT != disAsm.Argument1.ArgType)
{
switch (disAsm.Argument1.ArgType & 0xF0000000)
{
case REGISTER_TYPE: //寄存器
{
for (int i = 0; i < REGNUMS; i++)
{
if (tempreg[i].index == (disAsm.Argument1.ArgType & 0xFFFF))
{
if (0x20 == disAsm.Argument1.ArgSize)
{//32位
char vpushreg4[] = "vPushReg4 ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示
}
else if (0x10 == disAsm.Argument1.ArgSize)
{//16位
char vpushreg4[] = "vPushReg2 ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示
}
else
{//8位(要判断高位还是低位)
for (int i = 0; i < 14; i++)
{
if (stricmp(disAsm.Argument1.ArgMnemonic, regname_[0][i]) == 0)
{
if (i <= 3)//小于等于3是低位
{
char vpushreg4[] = "vPushReg1_low ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示
}
else
{
char vpushreg4[] = "vPushReg1_above ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示
}
break;
}
}
}
DATATABLE datatbl;
datatbl.data = i;
datatbl.recodeOaddr = disAsm.VirtualAddr;
m_datatable.push_back(datatbl);//数据压入数据表
break;
}
}
}
break;
case MEMORY_TYPE: //内存(操作数1如果是内存,且操作数2不为空,则处理好后直接退出)
{
if (0 == stricmp("push ",disAsm.Instruction.Mnemonic)) break;

MemoryMiddle(disAsm, disAsm.Argument1.Memory);

if (NO_ARGUMENT == disAsm.Argument2.ArgType) break;//这步是对待单个操作数的

if (0x20 == disAsm.Argument1.ArgSize)
{//32位
char vWriteMem[] = "vWriteMemDs4 ";
printf("%s\\n", vWriteMem);
memcpy(midstr.vmfunc, vWriteMem, sizeof(vWriteMem));
}
else if (0x10 == disAsm.Argument1.ArgSize)
{//16位
char vWriteMem[] = "vWriteMemDs2 ";
printf("%s\\n", vWriteMem);
memcpy(midstr.vmfunc, vWriteMem, sizeof(vWriteMem));
}
else
{//8位
char vWriteMem[] = "vWriteMemDs1 ";
printf("%s\\n", vWriteMem);
memcpy(midstr.vmfunc, vWriteMem, sizeof(vWriteMem));
}

memcpy(midstr.param1, disAsm.Instruction.Mnemonic, 16);//参数一
memcpy(midstr.param2, disAsm.Argument1.ArgMnemonic, 32);//参数二
m_middle.push_back(midstr);//操作码压入中间表示

return;
}
break;
case CONSTANT_TYPE://常数
{
DWORD constnums = 0;
sscanf(disAsm.Argument1.ArgMnemonic, "%X", &constnums);

char vpushimm4[] = "vPushImm4 ";
printf("%s\\n", vpushimm4);

memcpy(midstr.vmfunc, vpushimm4, sizeof(vpushimm4));
m_middle.push_back(midstr);//操作码压入中间表示

DATATABLE datatbl;
datatbl.data = constnums;
datatbl.recodeOaddr = disAsm.VirtualAddr;
m_datatable.push_back(datatbl);//数据压入数据表
}
break;
default:
break;
}
}

/*———————————————————————————-*/
/* 4、处理普通handler */
/*———————————————————————————-*/
//4、1 如果是ret,直接返回
if (0 == stricmp("ret ", disAsm.Instruction.Mnemonic))
{
if (disAsm.Argument1.ArgType == 0x10000000)
{
char vpushreg[] = "vPushImm4 ";
printf("%s\\n", vpushreg);
memcpy(midstr.vmfunc, vpushreg, sizeof(vpushreg));
m_middle.push_back(midstr);//压入vPushReg4

DATATABLE datatbl;
datatbl.data = 0;
datatbl.recodeOaddr = disAsm.VirtualAddr;
m_datatable.push_back(datatbl);//数据压入数据表
}
char callmem[] = "vRETN ";
printf("%s\\n", callmem);
memcpy(midstr.vmfunc, callmem, sizeof(callmem));
m_middle.push_back(midstr);//压入vRETN
return;
}

//4、2 如果操作数2是内存(针对的是二地址指令),m_middle链表最后两个元素互换
if ((disAsm.Argument2.ArgType & 0xF0000000) == MEMORY_TYPE)
{
if (0 != stricmp("pop ", disAsm.Instruction.Mnemonic))
{
swap(m_middle[m_middle.size()-1], m_middle[m_middle.size() – 2]);

}
goto ttttt__;
}

//4、3 如果是调用函数,则处理好后,直接返回
if (0 == stricmp("call ", disAsm.Instruction.Mnemonic))
{
//如果操作数1是内存
if ((disAsm.Argument1.ArgType & 0xF0000000) == MEMORY_TYPE)
{
char callmem[] = "vCallMem ";
printf("%s\\n", callmem);
memcpy(midstr.vmfunc, callmem, sizeof(callmem));
m_middle.push_back(midstr);//先压入vCallMem
}
else
{
//删除vPushImm4
m_middle.pop_back();
}
char retnotaddr[] = "vRetnNOT_ ";
printf("%s\\n", retnotaddr);
memcpy(midstr.vmfunc, retnotaddr, sizeof(retnotaddr));
m_middle.push_back(midstr);//压入vRetnNOT

char vcall[] = "vCALL ";
printf("%s\\n", vcall);
memcpy(midstr.vmfunc, vcall, sizeof(vcall));
m_middle.push_back(midstr);

char vresumestart[] = "vResumeStart_ ";
printf("%s\\n", vresumestart);
memcpy(midstr.vmfunc, vresumestart, sizeof(vresumestart));
m_middle.push_back(midstr);//压入vResumeStart_
return;
}

//4、4 处理普通handler
for (int i = 0; i < FUNNUMS; i++)
{
if (0 == stricmp(g_FunName[i].s_opecode,disAsm.Instruction.Mnemonic))
{
printf("%s\\n", g_FunName[i].vm_opcoed);
memcpy(midstr.vmfunc, g_FunName[i].vm_opcoed,OPECODELENGTH);
m_middle.push_back(midstr);//操作码压入中间表示
break;
}

}

/*———————————————————————————-*/
/* 5、判断是否有辅助handler */
/*———————————————————————————-*/
if (
0x10000000 != disAsm.Argument1.ArgType ||
0x10000000 != disAsm.Argument2.ArgType ||
0x10000000 != disAsm.Argument3.ArgType
)
{
ttttt__:
//5.1 判断这条指令是否改变操作数1的值,比如cmp和test操作数就不会改变操作数1的值,直接返回
for (int i = 0; i < NOTREGNUMS; i++)
{
if (stricmp(cmp_opecode[i], disAsm.Instruction.Mnemonic) == 0)
{
return;
}
}

//5.2 判断是否是JCC或者JMP指令,是则处理好后直接返回
for (int i = 0; i < JCCNUMS; i++)
{//判断是否是JCC指令
if (0 == stricmp(disAsm.Instruction.Mnemonic, JCCstr[i]))
{
//改变数据表的数据
m_datatable.at(m_datatable.size() – 1).originaddr = disAsm.VirtualAddr;

//多压入一个空数据到数据表
DATATABLE datatbl;
datatbl.data = 0;
m_datatable.push_back(datatbl);//数据压入数据表

//再压入一个空数据到数据表
datatbl.data = 0;
m_datatable.push_back(datatbl);//数据压入数据表

//JCC指令前插入两个vPushImm4
char vpushimm4[] = "vPushImm4 ";
printf("%s\\n", vpushimm4);
memcpy(midstr.vmfunc, vpushimm4, sizeof(vpushimm4));
m_middle.insert(m_middle.end()-1, midstr);//操作码压入中间表示
m_middle.insert(m_middle.end() – 1, midstr);//操作码压入中间表示
return;
}
}

//5.2 处理普通辅助handler
switch (disAsm.Argument1.ArgType & 0xF0000000)
{
case REGISTER_TYPE://寄存器
{
for (int i = 0; i < REGNUMS; i++)
{
if (tempreg[i].index == (disAsm.Argument1.ArgType & 0xFFFF))
{
if (0x20 == disAsm.Argument1.ArgSize)
{//32位
char vpopreg4[] = "vPopReg4 ";
printf("%s\\n", vpopreg4);
memcpy(midstr.vmfunc, vpopreg4, sizeof(vpopreg4));
m_middle.push_back(midstr);//操作码压入中间表示
}
else if (0x10 == disAsm.Argument1.ArgSize)
{//16位
char vpopreg4[] = "vPopReg2 ";
printf("%s\\n", vpopreg4);
memcpy(midstr.vmfunc, vpopreg4, sizeof(vpopreg4));
m_middle.push_back(midstr);//操作码压入中间表示
}
else
{//8位(要判断高位还是低位)
for (int i = 0; i < 14; i++)
{
if (stricmp(disAsm.Argument1.ArgMnemonic, regname_[0][i]) == 0)
{
if (i <= 3)//小于等于3是低位
{
char vpushreg4[] = "vPopReg1_low ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示
}
else
{
char vpushreg4[] = "vPopReg1_above ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示
}
break;
}
}
}
DATATABLE datatbl;
datatbl.data = i;
m_datatable.push_back(datatbl);//数据压入数据表
}
}

//如果操作1是esp,即改变栈大小,比如指令sub esp,0x100,
//则要调用vCheckESP()函数,检查VMContext是否被覆盖
if (16 == (disAsm.Argument1.ArgType & 0xFFFF))
{//16在BegEngine反汇编引擎约定的是esp寄存器
char vcheckesp[] = "VCheckESP ";
printf("%s\\n", vcheckesp);
memcpy(midstr.vmfunc, vcheckesp, sizeof(vcheckesp));
m_middle.push_back(midstr);//操作码压入中间表示
}
//如果操作码是pop,把vPOP添加到m_middle链表,然后返回
if (0 == stricmp("pop ",disAsm.Instruction.Mnemonic))
{
if (0x20 == disAsm.Argument1.ArgSize)
{
char vpushreg4[] = "vPOP4 ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示
}
else
{
char vpushreg4[] = "vPOP2 ";
printf("%s\\n", vpushreg4);
memcpy(midstr.vmfunc, vpushreg4, sizeof(vpushreg4));
m_middle.push_back(midstr);//操作码压入中间表示
}
return;
}
}
break;

case MEMORY_TYPE://内存
{
//
}
break;
case CONSTANT_TYPE:
{
//
}
break;
}
}
}

4、绝大多数情况下,都是零地址指令、一地址指令、二地址指令,很少有三地址指令,所以把操作3省略没有处理。
5、把handler操作和数据分开保存,如:
[Asm] 纯文本查看 复制代码
mov eax,ecx
翻译为中间表示就是
vPushReg VR_ecx //操作2
vPushReg VR_eax //操作1
vMOV //普通handler
vPopReg VR_eax //辅助handler

把VR_ecx、VR_eax、VR_eax分离出来保存在一个数据表的结构体中。
翻译就可以这样表示了:
vPushReg
vPushReg
vMOV
vPopReg

6、内存操作处理起来比较麻烦,MemoryMiddle函数用来专门处理内存操作。例如这条指令 mov dword ptr[eax+ecx*4+0x401000],eax,可以译成如下的中间表示:[Asm] 纯文本查看 复制代码vPushReg //eax
vPushImm4 //4
vPushReg4 //ecx
vMUL_MEM //*
vPushReg4 //eax
vAdd4 //+
vPushImm4 //0x401000
vAdd //+
vWriteMemDs4

4 ecx * 可以理解为后缀表示法,其实这是和handler设计和堆栈操作有关的。

7、局部变量的操作,如 mov dword ptr[ebp-0x8],eax,仍然用MemoryMiddle函数来翻译:
[Asm] 纯文本查看 复制代码
vPushImm4 //0xFFFFFFF8
vPushReg4 //ebp
vAdd4
负8会被BeaEngine引擎解析为0xFFFFFFF8,ebp-0x8与0xFFFFFFF8+ebp是等价的

8、下面举个完整的例子:
[Asm] 纯文本查看 复制代码
void _declspec(naked) _stdcall code_vm_test(int x)
{
//MessageBoxA(NULL, 0, 0, 0);
_asm {
sub esp,0x150
push eax
push ecx
push edx
lea ecx, code_vm_test
add ecx,10h
push ecx
pop dword ptr[g_num + 4]
jmp L14
sub esp,0x150
L14:
mov ecx,1
xor eax,eax
mov ah,10h
mov bl,30h
L13:
add ecx,1
add ah,bl
cmp ecx,0x10
jle L13
//je L11
add eax,0x432
mov ebx,4
mov ecx,1
mov byte ptr[g_num + ebx + ecx * 4],ah
//mov word ptr[g_num + ebx + ecx * 4],ax
//mov dword ptr[g_num+ebx+ecx*4],eax
jmp L12
//L11:
mov g_num,eax

call test2
L12:
mov eax, 01h //eax=1:取CPU序列号
xor edx, edx
cpuid
mov acpuid, eax
mov dl,byte ptr[acpuid]
mov lcpuid, edx

pop edx
pop ecx
pop eax
add esp,0x150
retn 4
}
}

上面这个函数,翻译为中间表示如下:
[Asm] 纯文本查看 复制代码
VMStartVM_2
vPushImm4
vPushReg4
vSUB4
vPopReg4
VCheckESP
vPushReg4
vPUSH
vPushReg4
vPUSH
vPushReg4
vPUSH
vPushImm4
vReadMemDs4
vPushReg4
vPopReg4
vPushImm4
vPushReg4
vAdd4
vPopReg4
vPushReg4
vPUSH
vRetnNOT_
vNotSimulate
vResumeStart_
vPushImm4
vJMP
vPushImm4
vPushImm4
vPushReg4
vSUB4
vPopReg4
VCheckESP
vPushImm4
vPushReg4
vMOV4
vPopReg4
vPushReg4
vPushReg4
vXOR4
vPopReg4
vPushImm4
vPushReg1_above
vMOV4
vPopReg1_above
vPushImm4
vPushReg1_low
vMOV4
vPopReg1_low
vPushImm4
vPushReg4
vAdd4
vPopReg4
vPushReg1_low
vPushReg1_above
vAdd4
vPopReg1_above
vPushImm4
vPushReg4
vCMP
vPushImm4
vJLE
vPushImm4
vPushImm4
vPushReg4
vAdd4
vPopReg4
vPushImm4
vPushReg4
vMOV4
vPopReg4
vPushImm4
vPushReg4
vMOV4
vPopReg4
vPushReg1_above
vPushImm4
vPushReg4
vMUL_MEM
vPushReg4
vAdd4
vPushImm4
vAdd4
vWriteMemDs1
vPushImm4
vJMP
vPushImm4
vPushReg4
vPushImm4
vWriteMemDs4
vPushImm4
vRetnNOT_
vCALL
vResumeStart_
vPushImm4
vPushReg4
vMOV4
vPopReg4
vPushReg4
vPushReg4
vXOR4
vPopReg4
vRetnNOT_
vNotSimulate
vResumeStart_
vPushReg4
vPushImm4
vWriteMemDs4
vPushImm4
vReadMemDs1
vPushReg1_low
vPopReg1_low
vPushReg4
vPushImm4
vWriteMemDs4
vPushReg4
vPopReg4
vPOP4
vPushReg4
vPopReg4
vPOP4
vPushReg4
vPopReg4
vPOP4
vPushImm4
vPushReg4
vAdd4
vPopReg4
VCheckESP
vPushImm4
vRETN

9、中间表示的设计非常的重要,它牵涉到后面的一系列的操作,需要好好考虑,这个指令解析器其实可以推翻重新设计的,我总感觉有点混乱,但时间原因,没弄了。
或者先不分离数据到数据表,把数据放到中间表示的数据结构里面,到后面再处理?
见仁见智,有初学者看到这篇文章的话可以少走一些弯路。

0x02 垃圾指令构造器
垃圾指令构造器的设计非常简单,难点在于垃圾指令的选择,有些指令是不能作为垃圾指令的,改变普通寄存器的指令不能用,比如AAA指令,会改变eax寄存器的值。具体参考Intel手册。
下面是垃圾指令的构造器
[C++] 纯文本查看 复制代码
//VMTABEL表的元素个数
#define VMTABLEMAXLEN 0x1000
//没有用到寄存器
#define NONE -1
//操作数类型

#define SEG_UNDEF -1 //没有段寄存器
#define SEG_ES 0 // Indexes of segment/selector registers
#define SEG_CS 1
#define SEG_SS 2
#define SEG_DS 3
#define SEG_FS 4
#define SEG_GS 5

enum optype
{
NONETYPE,
IMMTYPE,
REGTYPE,
MEMTYPE,
CSTYPE,
DSTYPE,
ESTYPE,
SSTYPE,
FSTYPE,
GSTYPE,
};

struct VMTable
{
char VMInstrName[VMNAMELEN]; //VM命令名称
char strInstruction[16]; //相对的汇编指令
int OperandNum; //操作数个数
int Segment; //段前缀
int optype[2]; //操作类型(寄存器,立即数,内存数)
int bitnum[2]; //位数

int NeedReg[4]; //执行命令前要使用的寄存器
int SaveReg[4]; //执行命令后要保存的指令
BOOL Reg2Esp; //第2个寄存器是否恢复,一般为0不恢复
};

VMTable vmtable32[VMTABLEMAXLEN] =
{
//MOV
{"VMOV_REG08_REG08","MOV",2, SEG_UNDEF, REGTYPE, REGTYPE,8,8, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_REG16_REG16","MOV",2, SEG_UNDEF, REGTYPE, REGTYPE,16,16, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_REG32_REG32","MOV",2, SEG_UNDEF, REGTYPE, REGTYPE,32,32, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_REG08_IMM32","MOV",2, SEG_UNDEF, REGTYPE, IMMTYPE,8,8, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_REG16_IMM32","MOV",2, SEG_UNDEF, REGTYPE, IMMTYPE,16,16, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_REG32_IMM32","MOV",2, SEG_UNDEF, REGTYPE, IMMTYPE,32,32, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_REG08_MEM08","MOV",2, SEG_UNDEF, REGTYPE, MEMTYPE,8,8, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_REG16_MEM16","MOV",2, SEG_UNDEF, REGTYPE, MEMTYPE,16,16, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_REG32_MEM32","MOV",2, SEG_UNDEF, REGTYPE, MEMTYPE,32,32, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_MEM08_REG08","MOV",2, SEG_UNDEF, MEMTYPE, REGTYPE,8,8, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_MEM16_REG16","MOV",2, SEG_UNDEF, MEMTYPE, REGTYPE,16,16, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_MEM32_REG32","MOV",2, SEG_UNDEF, MEMTYPE, REGTYPE,32,32, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_MEM08_IMM32","MOV",2, SEG_UNDEF, MEMTYPE, IMMTYPE,8,8, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_MEM16_IMM32","MOV",2, SEG_UNDEF, MEMTYPE, IMMTYPE,16,16, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_MEM32_IMM32","MOV",2, SEG_UNDEF, MEMTYPE, IMMTYPE,32,32, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_FSMEM08_IMM32","MOV",2, SEG_FS, MEMTYPE, IMMTYPE,8,8, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_FSMEM16_IMM32","MOV",2, SEG_FS, MEMTYPE, IMMTYPE,16,16, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_FSMEM32_IMM32","MOV",2, SEG_FS, MEMTYPE, IMMTYPE,32,32, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_GSMEM08_IMM32","MOV",2, SEG_GS, MEMTYPE, IMMTYPE,8,8, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_GSMEM16_IMM32","MOV",2, SEG_GS, MEMTYPE, IMMTYPE,16,16, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_GSMEM32_IMM32","MOV",2, SEG_GS, MEMTYPE, IMMTYPE,32,32, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_REG08_FSMEM08","MOV",2, SEG_FS, REGTYPE, MEMTYPE,8,8, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_REG16_FSMEM16","MOV",2, SEG_FS, REGTYPE, MEMTYPE,16,16, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_REG32_FSMEM32","MOV",2, SEG_FS, REGTYPE, MEMTYPE,32,32, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_REG08_GSMEM08","MOV",2, SEG_GS, REGTYPE, MEMTYPE,8,8, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_REG16_GSMEM16","MOV",2, SEG_GS, REGTYPE, MEMTYPE,16,16, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_REG32_GSMEM32","MOV",2, SEG_GS, REGTYPE, MEMTYPE,32,32, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_FSMEM08_REG08","MOV",2, SEG_FS, MEMTYPE, REGTYPE,8,8, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_FSMEM16_REG16","MOV",2, SEG_FS, MEMTYPE, REGTYPE,16,16, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_FSMEM32_REG32","MOV",2, SEG_FS, MEMTYPE, REGTYPE,32,32, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_GSMEM08_REG08","MOV",2, SEG_GS, MEMTYPE, REGTYPE,8,8, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_GSMEM16_REG16","MOV",2, SEG_GS, MEMTYPE, REGTYPE,16,16, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VMOV_GSMEM32_REG32","MOV",2, SEG_GS, MEMTYPE, REGTYPE,32,32, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },

{ "VCMC","CMC",0, SEG_UNDEF, NONETYPE, NONETYPE,0,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{ "VNOP","NOP",0, SEG_UNDEF, NONETYPE, NONETYPE,0,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{ "VSTC","STC",0, SEG_UNDEF, NONETYPE, NONETYPE,0,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{ "VSTD","STD",0, SEG_UNDEF, NONETYPE, NONETYPE,0,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{ "VCLC","CLC",0, SEG_UNDEF, NONETYPE, NONETYPE,0,0, RT_Eax,NONE,NONE,NONE, RT_Eax,NONE,NONE,NONE },
{ "VNOT","NOT",1, SEG_UNDEF, REGTYPE, NONETYPE,8,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{ "VNOT","NOT",1, SEG_UNDEF, REGTYPE, NONETYPE,16,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{ "VNOT","NOT",1, SEG_UNDEF, REGTYPE, NONETYPE,32,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{ "VCLD","CLD",0, SEG_UNDEF, NONETYPE, NONETYPE,0,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },

……等等

//结束标志
{"end","end",0, 0, 0, 0,0×520000,0,0,0,0,0,0,0,0,0 }
}

//获取随机数(范围在Min_到MAX_)
DWORD SrandNum(int Min_, int Max_)
{
return rand() % (Max_ – Min_) + Min_;
}

//求vmtable32结构体数组的长度
int VMLoader2::VMLength()
{
for (int i = 0; i < VMTABLEMAXLEN; i++)
{
if (vmtable32[i].bitnum[0] == 0x520000)
{
return i;
}
}
return 0;
}

//vmtable32结构体数组的长度
int m_vmlength = 0;

//生成垃圾指令
CString VMLoader2::ProduceRubbishOpecode(char* reg04, char* reg05)
{
VMTable vmtbl = vmtable32[SrandNum(0, m_vmlength)];
CString str = vmtbl.strInstruction;
//1、目的操作
switch (vmtbl.optype[0])
{
case NONETYPE://没有操作数
break;
case IMMTYPE://立即数
{
if (8 == vmtbl.bitnum[0])
{
str = str + " " + 4;
}
else if (16 == vmtbl.bitnum[0])
{
str = str + " " + 4;
}
else
{
str = str + " " + 8;
}

}
break;
case REGTYPE://寄存器
{

if (8 == vmtbl.bitnum[0])
{
for (int i = 0; i < 14; i++)
{
if (stricmp(reg04, regname_[2][i]) == 0)
{
str = str + " " + regname_[0][i];
break;
}
}
}
else if (16 == vmtbl.bitnum[0])
{
for (int i = 0; i < 14; i++)
{
if (stricmp(reg05, regname_[2][i]) == 0)
{
str = str + " " + regname_[1][i];
break;
}
}
}
else
{
str = str + " " + reg05;
}
}
break;
case MEMTYPE://内存
{//随机选择vmp1节中没有用到的内存
DWORD dnum = SrandNum(m_vmps.vmp1_startaddr+0x4000, m_vmps.vmp1_startaddr+0x5000);
CString memstr = dnum;
if (8 == vmtbl.bitnum[0])
{
str = str + " byte ptr[" + memstr.GetString() + "]";
}
else if (16 == vmtbl.bitnum[0])
{
str = str + " word ptr[" + memstr.GetString() + "]";
}
else
{
str = str + " dword ptr[" + memstr.GetString() + "]";
}
}
break;
default:
break;
}

//2、源操作数
switch (vmtbl.optype[1])
{
case NONETYPE://没有操作数
break;
case IMMTYPE://立即数
{
if (8 == vmtbl.bitnum[1])
{
str = str + "," + 4;
}
else if (16 == vmtbl.bitnum[1])
{
str = str + "," + 8;
}
else
{
str = str + "," + 4;
}
}
break;
case REGTYPE://寄存器(操作数2的寄存器可以在8个寄存器中任意选择)
{
if (0 == stricmp(vmtbl.strInstruction,"xchg"))
{//如果是xchg,寄存器则选择reg04,或者reg05
if (8 == vmtbl.bitnum[1])
{
for (int i = 0; i < 14; i++)
{
if (stricmp(reg05, regname_[2][i]) == 0)
{
str = str + "," + regname_[0][i];
break;
}
}

}
else if (16 == vmtbl.bitnum[1])
{
for (int i = 0; i < 14; i++)
{
if (stricmp(reg04, regname_[2][i]) == 0)
{
str = str + "," + regname_[1][i];
break;
}
}
}
else
{
str = str + "," + reg04;
}
break;
}
if (8 == vmtbl.bitnum[1])
{
str = str + "," + regname_[0][SrandNum(0, 8)];
}
else if (16 == vmtbl.bitnum[1])
{
str = str + "," + regname_[1][SrandNum(0, 8)];
}
else
{
str = str + "," + regname_[2][SrandNum(0, 8)];
}
}
break;
case MEMTYPE://内存
{//随机选择vmp1节内的地址,或者选esp寄存器
DWORD dnum = SrandNum(m_vmps.vmp1_startaddr, m_vmps.vmstartaddr);
CString memstr = dnum;
const char* memchr[5] = { memstr.GetString(),"esp+20","esp+28","esp+0x30","esp+0x14" };
const char* srandstr = memchr[SrandNum(0, 5)];

if (8 == vmtbl.bitnum[1])
{
str = str + ",byte ptr[" + srandstr + "]";
}
else if (16 == vmtbl.bitnum[1])
{
str = str + ",word ptr[" + srandstr + "]";
}
else
{
str = str + ",dword ptr[" + srandstr + "]";
}
}
break;
default:
break;
}

return str;
}

每调用一次ProduceRubbishOpecode就可以构造一条垃圾指令,其实这个垃圾指令构造器还可以细化。想要怎么设计,看需求。

0x03
handler的设计可以把要用到的handler放到一个表格中归类。
举个vJAE的例子,如下:
[Asm] 纯文本查看 复制代码
CString vJAE(char* VR0, char* VR1)//jae jnc jnb(无符号 大于等于跳转 CF=0)
{
CString str = "push dword ptr[edi+0x20]\\n";
str = str + "pop "+ VR0 +"\\n";
str = str + "mov " + VR1 + ",0\\n";

str = str + "and "+ VR0 +",1\\n";
str = str + "mov "+ VR0 +",dword ptr[esp+4]\\n";
str = str + "cmove " + VR1 + ",dword ptr[esp]\\n";
str = str + "cmove "+ VR0 +",dword ptr[esp+8]\\n";
str = str + "add ebp," + VR1 + "\\n";
str = str + "add esi,"+ VR0 +"\\n";
str = str + "add esp,0xC\\n";
return str;
}

本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。

最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。 若排除这种情况,可在对应资源底部留言,或联络我们。

对于会员专享、整站源码、程序插件、网站模板、网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。

如果您已经成功付款但是网站没有弹出成功提示,请联系站长提供付款信息为您处理

源码素材属于虚拟商品,具有可复制性,可传播性,一旦授予,不接受任何形式的退款、换货要求。请您在购买获取之前确认好 是您所需要的资源