再发
// --------------------------------------------- CVirtualMemory.cpp -----------------------------
// 此文件代表了某段虚存空间,我们需要在PE文件中,为存放虚拟机的指令分配虚存空间
// VMPackLib
#include "stdafx.h"
#include "CVirtualMemory.h"
CVirtualMemory::CVirtualMemory()
{
m_BaseAddr = NULL;
m_addrlen = 0;
m_VirtualBaseAddress = 0;
CurrentAddress = NULL;
}
CVirtualMemory::CVirtualMemory(DWORD VirtualAddress,
int len)
{
m_BaseAddr = NULL;
m_addrlen = 0;
m_VirtualBaseAddress = 0;
CurrentAddress = NULL;
CreateVirtualMemory(VirtualAddress,len);
}
CVirtualMemory::~CVirtualMemory()
{
if( m_BaseAddr ) delete[] m_BaseAddr;m_BaseAddr = NULL;
}
/**
* 根据指定虚拟地址分配一块内存空间
*
* VirtualAddress:欲分配内存的虚拟地址
* len:分配内存的长度
*/
void CVirtualMemory::CreateVirtualMemory(DWORD VirtualAddress,
int len) // *
{
// 内存空间存在,则先释放之
if (m_BaseAddr)
delete[] m_BaseAddr;
m_BaseAddr = NULL;
// 分配内存空间并清0
m_BaseAddr = new char[len];
memset(m_BaseAddr, 0, len);
m_addrlen = len;
m_VirtualBaseAddress = VirtualAddress;
CurrentAddress = m_BaseAddr;
}
/**
* 根据内存地址addr计算出相应的虚拟地址并返回
*
* addr:欲换算的内存地址
*
* 返回值:换算后的虚拟地址,失败则为-1
*/
DWORD CVirtualMemory::GetVirtualAddress(char * addr) // *
{
// 检测地址合法性
if (addr < m_BaseAddr)
return -1;
return m_VirtualBaseAddress + (DWORD)(addr - m_BaseAddr);
}
// 根据虚拟地址计算出内存空间地址
char* CVirtualMemory::GetAddrOfVirtualAddr(DWORD VirtualAddress)
{
return m_BaseAddr + VirtualAddress - m_VirtualBaseAddress;
}
/**
* 将指定数据src复制到虚拟内存区间
*
* src:欲复制的数据
* len:欲复制数据的长度
*
* 返回值:成功返回复制数据的起始虚址,否则返回0
*/
DWORD CVirtualMemory::WriteData(char * src, int len) // *
{
// 检测参数是否OK
if (!src || !len)
{
MessageBox(0, "内部错误", "err", MB_OK);
return 0;
}
// 如果虚存空间不足,则出错返回
if (m_addrlen - (CurrentAddress - m_BaseAddr) < len)
{
MessageBox(0, "[memory to small]内部错误", "err", MB_OK);
return 0;
}
DWORD StartVirtualAddress = 0;
// 将src复制到当前地址位置
memcpy(CurrentAddress, src, len);
// 取得当前地址对应的虚址,即复制数据所在的虚址
StartVirtualAddress = m_VirtualBaseAddress + (DWORD)(CurrentAddress - m_BaseAddr);
CurrentAddress += len;
return StartVirtualAddress;
}
//复制数据到指令虚拟内存地址
DWORD CVirtualMemory::WriteData(DWORD VirtualCode,char* src,int len)
{
if( !src || !len )
return 0;
if( m_BaseAddr + m_addrlen - CurrentAddress < len )//空间不够
return 0;
DWORD StartVirtualAddress = 0;
memcpy(GetAddrOfVirtualAddr(VirtualCode),src,len);
return VirtualCode;
}
// 清空内存
void CVirtualMemory::ClearMemory()
{
memset(m_BaseAddr,0,m_addrlen);
CurrentAddress = m_BaseAddr;
}
// 获得还未使用的空间
char* CVirtualMemory::GetCurrentAddr(void)
{
return CurrentAddress;
}
/**
* 获取虚拟内存区间当前可用的虚拟地址
*/
DWORD CVirtualMemory::GetCurrentVirtualAddress(void) // *
{
return GetVirtualAddress(CurrentAddress);
}
// ----------------------------------------------- CVirtualMemory.h ------------------------------
// VMPackLib
#ifndef __CVIRTUALMEMORY__
#define __CVIRTUALMEMORY__
/**
* 代表一段虚拟内存
*/
class CVirtualMemory
{
public:
// 虚拟地址所对应的内存空间基址
char * m_BaseAddr;
// 内存空间的长度
int m_addrlen;
// 此段内存的虚拟地址,将对应m_BaseAddr所指向的内存空间
DWORD m_VirtualBaseAddress;
private:
// 此内存空间中当前可以使用的地址
// 初始化为m_BaseAddr
char * CurrentAddress;
public:
CVirtualMemory();
CVirtualMemory(DWORD VirtualAddress,
int len);
~CVirtualMemory();
// 创建一片对应虚拟地址的内存空间
void CreateVirtualMemory(DWORD VirtualAddress,
int len);
// 根据内存地址换算出虚拟线性地址
DWORD GetVirtualAddress(char * addr);
// 根据虚拟地址计算出内存空间地址
char * GetAddrOfVirtualAddr(DWORD VirtualAddress);
// 复制数据到虚拟内存, 返回数据的起始虚拟地址.
DWORD WriteData(char* src, int len);
// 复制数据到指令虚拟内存地址
DWORD WriteData(DWORD VirtualCode, char * src, int len);
// 清空内存
void ClearMemory();
// 获得当前可用空间
char * GetCurrentAddr(void);
// 获得当前可用的虚拟地质
DWORD GetCurrentVirtualAddress(void);
};
#endif // __CVIRTUALMEMORY__
// ----------------------------------- InterpretHandler.cpp ------------------------------------
// 每个普通的汇编指令,一般都有一个虚拟机指令对应着,而每个虚拟机指令,实际上又对应着某段普通的汇编代码,换句话说,一个普通的汇编指令就由另一段汇编代码来模拟完成了
// 此文件就是用于为某个汇编指令相应的虚拟机指令生成相应的汇编代码
// 对于普通汇编指令与虚拟机指令的对应关系由一张表定义着(即vmtable,见vmserv.cpp中定义)
#include "StdAfx.h"
#include "InterpretHandler.h"
#include "asm/disasm.h"
CInterpretHandler::CInterpretHandler(void)
{
}
CInterpretHandler::~CInterpretHandler(void)
{
}
/**
* 获取指定寄存器RegType在VMContext虚拟机上下文中的偏移
*
* 针对不同的寄存器,偏移值不同,如
* 32位寄存器:EAX
* 16位寄存器:AX
* 低8位寄存器:AL
* 以上情况直接取得偏移
* 高8位寄存器:AH
* 则需要对偏移进行 + 1,以便得到高8位值
*
* RegType:指明要取得偏移的寄存器,取值可见枚举RegType
*/
int CInterpretHandler::GetRegisterOffset(int RegType) // *
{
if (RegType < 0)
return RegType;
int offset = m_RegisterIdx[RegType] * 4;
// 判断是否取寄存器高8位
if (RegType >= 20)
offset++;
return offset;
}
/**
* 初始化操作
*/
BOOL CInterpretHandler::Init() // *
{
// 打乱寄存器偏移的索引
RandListIdx(m_RegisterIdx, REGCOUNT);
return TRUE;
}
/**
* 为指定虚拟机指令table设置所需操作数
*
* table: 包含某个虚拟机指令与汇编指令相关信息
* asmtext:保存生成的汇编代码
* len: asmtext缓存的长度
*/
void CInterpretHandler::SetArg(VMTable * table, char * asmtext, int len) // *
{
// 操作数不超过3个
for (int i = 0; i < 3; i++)
{
// 欲处理操作数超限制,则跳出
// OperandNum: 操作数个数
if (i >= table->OperandNum)
break;
// 以下将生成:mov EAX, [esp + (0 | 4 | 8)]
// 所以相当于将堆栈中压入的操作数复制到EAX寄存器中以作为虚拟机的操作数使用
sprintf_s(asmtext, len, "%smov %s,[esp+%02x]\n", asmtext, ArgReg[i], i * 4);
}
}
/**
* 将指定虚拟机指令table的操作数恢复到堆栈中(操作数一般都被压入栈)
*
* table: 包含某个虚拟机指令与汇编指令相关信息
* asmtext:保存生成的汇编代码
* len: asmtext缓存的长度
*/
void CInterpretHandler::RestoreArg(VMTable * table, char * asmtext, int len) // *
{
for (int i = 0; i < 3; i++)
{
if (i >= table->OperandNum)
break;
sprintf_s(asmtext, len, "%smov [esp+%02x],%s\n", asmtext, i * 4, ArgReg[i]);
}
}
/**
* 将VMContext上下文保存的EFlag寄存器值恢复到EFlag寄存器中
*
* asmtext:保存恢复所需的汇编代码
* len: asmtext缓存的长度
*/
void CInterpretHandler::RestoreFlag(char * asmtext, int len) // *
{
sprintf_s(asmtext, len, "%sPush [edi+0x%02x]\nPopfd\n", asmtext, GetRegisterOffset(RT_EFlag));
}
/**
* 将EFlag寄存器值保存到VMContext上下文的EFlag中
*
* asmtext:保存恢复所需的汇编代码
* len: asmtext缓存的长度
*/
void CInterpretHandler::SaveFlag(char * asmtext, int len) // *
{
sprintf_s(asmtext, len, "%sPushfd\nPop [edi+0x%02x]\n", asmtext, GetRegisterOffset(RT_EFlag));
}
/**
* 根据指定虚拟机指令-汇编指令信息table为虚拟机指令生成相应的汇编代码
*
* table: 包含某个虚拟机指令与汇编指令相关信息
* asmtext:保存生成的汇编代码
* len: asmtext缓存的长度
*
* 返回值:成功返回TRUE,否则FALSE
*/
BOOL CInterpretHandler::InterpretASMStr(VMTable * table, char * asmtext, int len) // *
{
// 检测参数的合法性
if (strcmp(table->VMInstrName, "") == 0 || asmtext == NULL)
return FALSE;
// 清0 asmtext缓存
memset(asmtext, 0, len);
// 为虚拟机指令设置操作数
SetArg(table, asmtext, len);
// 某些汇编指令在执行时,需要用到某些寄存器的值来控制其执行,那么,对于相应
// 的虚拟指令,当然也是需要,所以,现在需要恢复所需寄存器的值了
// 为虚拟机指令设置所需的寄存器
for (int i = 0; i < 4; i++)
{
// 判断虚拟机指令是否需要用 i 寄存器
if (table->NeedReg[i] != NONE && table->NeedReg[i] < 14)
{
// edi -> 指向VMContext
// 由于虚拟机将所有寄存器的值保存在VMContext中,所以,
// 现在将保存的值复制到所需寄存器中以便指令使用
sprintf_s(asmtext, len, "%smov %s,[edi+0x%02x]\n", asmtext,
vregname[2][table->NeedReg[i]], GetRegisterOffset(table->NeedReg[i]));
}
}
BOOL After = FALSE;
// 为vBegin指令生成汇编代码
if (_stricmp(table->strInstruction, "vBegin") == 0)
{
After = InterpretvBegin(table, asmtext, len);
}
// 为vtoReal指令生成汇编代码,vtoReal用于跳转至真实指令
else if (_stricmp(table->strInstruction, "vtoReal") == 0)
{
After = InterpretvtoReal(table, asmtext, len);
}
else if (_stricmp(table->strInstruction, "push") == 0)
{
After = InterpretPush(table, asmtext, len);
}
else if (_stricmp(table->strInstruction, "pop") == 0)
{
After = InterpretPop(table, asmtext, len);
}
else if (_stricmp(table->strInstruction, "pushfd") == 0)
{
After = InterpretPushfd(table, asmtext, len);
}
else if (_stricmp(table->strInstruction, "popfd") == 0)
{
After = InterpretPopfd(table, asmtext, len);
}
else if (_stricmp(table->strInstruction, "pushad") == 0)
{
After = InterpretPushad(table, asmtext, len);
}
else if(_stricmp(table->strInstruction, "popad") == 0)
{
After = InterpretPopad(table, asmtext, len);
}
else if (_stricmp(table->strInstruction, "enter") == 0)
{
After = InterpretEnter(table, asmtext, len);
}
else if (_stricmp(table->strInstruction, "leave") == 0)
{
After = InterpretLeave(table, asmtext, len);
}
// 为jcxz指令生成相应的汇编代码
// 注:jcxz指令,该指令实现当寄存器CX的值等于0时转移到标号,否则顺序执行
else if (_stricmp(table->strInstruction, "jcxz") == 0)
{
// 先将VMContext上下文的EFlag寄存器恢复到EFlag标志寄存器
RestoreFlag(asmtext, len);
After = InterpretJCXZ(table, asmtext, len);
}
else if (_stricmp(table->strInstruction, "jmp") == 0)
{
After = InterpretJMP(table, asmtext, len);
}
// 不是jcxz和jmp, 则说明是条件跳转指令
else if (table->strInstruction[0] == 'J' || table->strInstruction[0] == 'j')
{
RestoreFlag(asmtext, len);
After = InterpretJCC(table, asmtext, len);
}
else if (_stricmp(table->strInstruction, "loope") == 0)
{
RestoreFlag(asmtext, len);
After = InterpretLoope(table, asmtext, len);
SaveFlag(asmtext, len);
}
else if (_stricmp(table->strInstruction, "retn") == 0)
{
After = InterpretRetn(table, asmtext, len);
}
else if (_stricmp(table->strInstruction, "call") == 0)
{
After = InterpretCall(table, asmtext, len);
}
else if (_stricmp(table->strInstruction, "SaveEsp") == 0)
{
After = InterpretSaveEsp(table, asmtext, len);
}
else if (_stricmp(table->strInstruction, "RestoreEsp") == 0)
{
After = InterpretRestoreEsp(table, asmtext, len);
}
else
{
After = CommonInstruction(table, asmtext, len);
}
// 某些指令执行完成后,将会影响到某些寄存器的值,这里所做的,就是将
// 指令执行后改变的寄存器值保存回VMContext上下文中的各个寄存器中
for (int i = 0; i < 3; i++)
{
// 如果需保存i寄存器,即i寄存器被改变了
if (table->SaveReg[i] != NONE)
{
sprintf_s(asmtext, len, "%smov [edi+0x%02x],%s\n", asmtext,
GetRegisterOffset(table->NeedReg[i]), vregname[2][table->NeedReg[i]]);
}
}
if (After)
{
RestoreArg(table, asmtext, len);
}
// 将最终为指令生成的汇编代码转为大写
_strupr_s(asmtext, ASMTEXTLEN);
return TRUE;
}
/**
* 根据操作数的位数bit,获取相应的修饰符
*
* bit: 操作数的位数
* scalestr:用于返回得到的修饰符
*
* 返回值:返回操作数大小索引:
* 8位: 0
* 16位: 1
* 32位:2
*/
int GetScalestr(int bit, OUT char * scalestr) // *
{
int sizeidx = 0;
if (bit == 8)
{
sizeidx = 0;
strcpy_s(scalestr, 6, "byte");
}
else if (bit == 16)
{
sizeidx = 1;
strcpy_s(scalestr, 6, "word");
}
else if (bit == 32)
{
sizeidx = 2;
strcpy_s(scalestr, 6, "dword");
}
return sizeidx;
}
/**
* 为普通指令生成相应的汇编代码
*
* table: 存放某个普通指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回TRUE
*/
BOOL CInterpretHandler::CommonInstruction(VMTable * table, char * asmtext, int len) // *
{
char scalestr[6] = {0};
int sizeidx = 0;
char stroperand[1024] = {0};
// 处理指令的操作数
for (int i = 0; i < 3; i++)
{
// 操作数超限制
if (i >= table->OperandNum)
break;
// 获取i操作数的修饰符
sizeidx = GetScalestr(table->bitnum[i], scalestr);
// 如果i操作数是一个内存数
if (table->optype[i] == MEMTYPE )
{
sprintf_s(stroperand, 1024, "%s%s ptr %s[%s],", stroperand, scalestr, GetSegStr(table->Segment), vregname[2][i]);
}
// 立即数和寄存器
else
{
// 得到操作数
sprintf_s(stroperand, 1024, "%s%s,", stroperand, vregname[sizeidx][i]);
}
}
// 去掉最后操作数的逗号
if (table->OperandNum > 0)
stroperand[strlen(stroperand) - 1] = '\0';
RestoreFlag(asmtext, len);
// 执行指令
sprintf_s(asmtext,len, "%s%s %s\n", asmtext, table->strInstruction, stroperand);
SaveFlag(asmtext, len);
return TRUE;
}
/**
* 根据段号Segment获得段前缀信息
*
* segment: 段号,例如SEG_FS代表FS段寄存器
*
* 返回值:返回获取的段前缀
*/
char * CInterpretHandler::GetSegStr(int Segment) // *
{
static char segstr[10] = "";
memset(segstr, 0, 10);
if (Segment == SEG_FS)
strcpy_s(segstr, 10, "fs:");
else if (Segment == SEG_GS)
strcpy_s(segstr, 10, "gs:");
return segstr;
}
/**
* 虚拟机在执行时,并不会破坏真实堆栈中保存的寄存器,而是将真正堆栈中的寄存器值
* 复制至VMContext中,VMContext即为虚拟机上下文,以后,虚拟机用到的寄存器值都从
* 其中取得
*/
/**
* 此函数的使用即是将堆栈中的寄存器复制至VMContext上下文中以便虚拟机使用,虚拟机将
* 这种操作称为堆栈平衡
*
* table: 保存vBegin虚拟指令的相关信息
* asmtext:保存完成操作的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回FALSE值
*/
BOOL CInterpretHandler::InterpretvBegin(VMTable * table, char * asmtext, int len) // *
{
int s_reg[8] = {RT_EFlag, RT_Ebp, RT_Edi, RT_Esi, RT_Edx, RT_Ecx, RT_Ebx, RT_Eax};
// ebp -> 指向真正的堆栈
// edi -> 指向VMContext
// 将真实堆栈中保存的寄存器值复制至VMContext虚拟机上下文中
for (int i = 0; i < 8; i++)
{
// 先将堆栈中的寄存器值复制至eax中
sprintf_s(asmtext, len, "%smov eax,dword ptr [ebp]\n", asmtext);
// 再将eax的值复制到VMContext的某个寄存器中
sprintf_s(asmtext, len,"%smov [edi+%02X],eax\n", asmtext, GetRegisterOffset(s_reg[i]));
sprintf_s(asmtext, len, "%sadd ebp,4\n", asmtext);
}
sprintf_s(asmtext, len, "%sadd ebp,4\n", asmtext);
// 将ebp(即真正esp)保存到VMContext的esp
sprintf_s(asmtext, len, "%smov [edi+%02x],ebp\n", asmtext, GetRegisterOffset(RT_Esp));
return FALSE;
}
/**
* 为指令vtoReal生成汇编代码
*
* vtoReal:用于跳转至真实指令
*
* table:包含vtoReal指令的信息
* asmtext:保存生成的汇编代码
* len: asmtext缓存的长度
*
* 返回值:只返回FALSE
*/
BOOL CInterpretHandler::InterpretvtoReal(VMTable * table, char * asmtext, int len) // *
{
int s_reg[9] = {RT_Esp, RT_EFlag, RT_Ebp, RT_Edi, RT_Esi, RT_Edx, RT_Ecx, RT_Ebx, RT_Eax};
// 模拟pushimm32 xxxx指令,其中xxxx是真实指令的地址
// 将真实指令的地址复制至eax中
sprintf_s(asmtext, len, "%smov eax,dword ptr [esi]\n", asmtext);
sprintf_s(asmtext, len, "%sadd esi,4\n", asmtext);
// 在真实堆栈中分配4个字节并将真实指令的地址复制进去
sprintf_s(asmtext, len, "%ssub ebp,4\n", asmtext);
sprintf_s(asmtext, len, "%smov dword ptr [ebp],eax\n", asmtext);
// 将ebp(即真正esp)保存到VMContext的esp中
sprintf_s(asmtext, len, "%smov [edi+%02x],ebp\n", asmtext, GetRegisterOffset(RT_Esp));
// 将VMContext寄存器中的值压入栈
for (int i = 0; i < 9; i++)
{
sprintf_s(asmtext, len, "%spush [edi+%02X]\n", asmtext, GetRegisterOffset(s_reg[i]));
}
// 再将压入栈的值复制至相应寄存器中
sprintf_s(asmtext, len, "%spop eax\n", asmtext);
sprintf_s(asmtext, len, "%spop ebx\n", asmtext);
sprintf_s(asmtext, len, "%spop ecx\n", asmtext);
sprintf_s(asmtext, len, "%spop edx\n", asmtext);
sprintf_s(asmtext, len, "%spop esi\n", asmtext);
sprintf_s(asmtext, len, "%spop edi\n", asmtext);
sprintf_s(asmtext, len, "%spop ebp\n", asmtext);
sprintf_s(asmtext, len, "%spopfd\n", asmtext);
sprintf_s(asmtext, len, "%spop esp\n", asmtext);
// 至此,在堆栈中保存了真实的地址,并且寄存器环境也恢复了,所以,现在已组成一个调用环境了
// 返回,跳转到真正的地址
sprintf_s(asmtext, len, "%sretn\n", asmtext);
return FALSE;
}
/**
* 为指令push生成相应的汇编代码
*
* table: 存放push指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回TRUE
*
* 此函数生成的代码类似如下:
* mov eax,dword ptr [esp]
* sub ebp,4
* 内存数
* mov eax,[eax]
* mov word ptr [ebp],ax
*/
BOOL CInterpretHandler::InterpretPush(VMTable * table, char * asmtext, int len) // *
{
char scalestr[6] = {0};
int sizeidx = 0;
// 根据操作数位数设置修饰符
// 8位操作数
if (table->bitnum[0] == 8)
{
sizeidx = 0;
strcpy_s(scalestr, 6, "byte");
}
// 16位操作数
else if (table->bitnum[0] == 16)
{
sizeidx = 1;
strcpy_s(scalestr, 6, "word");
}
// 32位操作数
else if (table->bitnum[0] == 32)
{
sizeidx = 2;
strcpy_s(scalestr, 6, "dword");
}
// 从堆栈中分配空间用于存放操作数
sprintf_s(asmtext, len, "%ssub ebp,%d\n", asmtext, table->bitnum[0] / 8);
// 至此,将要将该数据放到堆栈中以模拟压栈过程
// 对于该数有两种情况:
// 1、如果该数不是一个内存数,则该数已经被放到EAX寄存器中了
// 2、如果该数是个内存数,则EAX寄存器中存放的不是该数,而是该数所在的偏移量
if (table->optype[0] == MEMTYPE)
{
// 得到该内存数的值
sprintf_s(asmtext, len, "%smov %s,%s ptr %s[eax]\n", asmtext, vregname[sizeidx][RT_Eax], scalestr, GetSegStr(table->Segment));
}
// 将内存数复制到堆栈中完成压栈操作
sprintf_s(asmtext, len, "%smov %s ptr [ebp],%s\n", asmtext, scalestr, vregname[sizeidx][RT_Eax]);
return TRUE;
}
/**
* 为指令pop生成相应的汇编代码
*
* table: 存放pop指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回TRUE
*
* 此函数生成的代码类似如下:
* mov eax,dword ptr [ebp]
* add ebp,4
* mov eax,[eax]
*/
BOOL CInterpretHandler::InterpretPop(VMTable * table, char * asmtext, int len) // *
{
char scalestr[6] = {0};
int sizeidx = 0;
// 根据操作数位数设置修饰符
// 8位操作数
if (table->bitnum[0] == 8)
{
sizeidx = 0;
strcpy_s(scalestr, 6, "byte");
}
else if ( table->bitnum[0] == 16 )
{
sizeidx = 1;
strcpy_s(scalestr, 6, "word");
}
else if( table->bitnum[0] == 32 )
{
sizeidx = 2;
strcpy_s(scalestr, 6, "dword");
}
// 得到堆栈ebp( ebp -> 真实堆栈 )的值
sprintf_s(asmtext, len, "%smov %s,%s ptr [ebp]\n", asmtext, vregname[sizeidx][RT_Ecx], scalestr);
// 释放操作数占用的空间
sprintf_s(asmtext, len, "%sadd ebp,%d\n", asmtext, table->bitnum[0] / 8);
// 如果是内存数
if (table->optype[0] == MEMTYPE)
{
// 得到内存数的值
sprintf_s(asmtext, len, "%smov %s,%s ptr %s[eax]\n", asmtext, vregname[sizeidx][RT_Eax], scalestr, GetSegStr(table->Segment));
}
// 将值给mem(只有mem,因为无论是reg还是mem在vm中都是内存地址)
sprintf_s(asmtext, len, "%smov eax,ecx\n", asmtext);
// 问题:对于上面判断是否内存数的操作似乎是多余的,或说是不对的,个人理解应把代码改为如下:
// if (table->optype[0] == MEMTYPE)
// {
// sprintf_s(asmtext, len, "%smov%s ptr %s[eax],%s\n", asmtext, scalestr, GetSegStr(table->Segment), vregname[sizeidx][RT_Ecx]);
// }
// else
// {
// sprintf_s(asmtext, len, "%smov eax,ecx\n", asmtext);
// }
return TRUE;
}
/**
* 为指令pushfd生成相应的汇编代码
*
* table: 存放pushfd指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回TRUE
*/
BOOL CInterpretHandler::InterpretPushfd(VMTable * table, char * asmtext, int len) // *
{
// 在堆栈中分配4字节空间用于存放标志寄存器值
sprintf_s(asmtext, len, "%ssub ebp,4\n", asmtext);
// 将VMContext上下文中EFlag标志寄存器值放入eax寄存器
sprintf_s(asmtext, len, "%smov eax,[edi+%02x]\n", asmtext, GetRegisterOffset(RT_EFlag));
// 再将其放入堆栈以实现压栈
sprintf_s(asmtext, len, "%smov dword ptr [ebp],eax\n", asmtext);
return TRUE;
}
/**
* 为指令popfd生成相应的汇编代码
*
* table: 存放popfd指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回TRUE
*/
BOOL CInterpretHandler::InterpretPopfd(VMTable * table, char * asmtext, int len) // *
{
// 将堆栈中的EFLAG寄存器值复制到eax寄存器中
sprintf_s(asmtext, len, "%smov eax,dword ptr [ebp]\n", asmtext);
// 释放堆栈中占用的4字节空间
sprintf_s(asmtext, len, "%sadd ebp,4\n", asmtext);
// 再将eax寄存器中的EFLAG值复制到VMContext上下文中实现出栈
sprintf_s(asmtext, len, "%smov [edi+%02x],eax\n", asmtext, GetRegisterOffset(RT_EFlag));
return TRUE;
}
/**
* 为指令pushad生成相应的汇编代码
*
* table: 存放pushad指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 注:pushad指令用于实现将多个通用寄存器压入堆栈中
*
* 返回值:只返回TRUE
*/
BOOL CInterpretHandler::InterpretPushad(VMTable * table, char * asmtext, int len) // *
{
// 循环压栈通用寄存器
for (int i = 0; i < 8; i++)
{
// 为i寄存器分配堆栈空间
sprintf_s(asmtext, len, "%ssub ebp,4\n", asmtext);
// 再将VMContext上下文中寄存器值复制到eax中
sprintf_s(asmtext, len, "%smov eax,[edi+%02x]\n", asmtext, GetRegisterOffset(i));
// 最后再将eax复制到堆栈中以完成压栈工作
sprintf_s(asmtext, len, "%smov dword ptr [ebp],eax\n", asmtext);
}
return TRUE;
}
/**
* 为指令popad生成相应的汇编代码
*
* table: 存放popad指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 注:popad指令用于实现将多个通用寄存器弹出堆栈
*
* 返回值:只返回TRUE
*/
BOOL CInterpretHandler::InterpretPopad(VMTable * table, char * asmtext, int len) // *
{
// 可见InterpretPushad()操作
for (int i = 8; i > 0; i--)
{
sprintf_s(asmtext, len, "%smov eax,dword ptr [ebp]\n", asmtext);
sprintf_s(asmtext, len, "%sadd ebp,4\n", asmtext);
sprintf_s(asmtext, len, "%smov [edi+%02x],eax\n", asmtext, GetRegisterOffset(i));
}
return TRUE;
}
/**
* 为指令enter生成相应的汇编代码
*
* table: 存放enter指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回TRUE
*
* 注:enter指令用于操作子过程栈框架,代码如下:
* push ebp
* mov ebp,esp
* sub esp,(L - 1) * 4 ; L > 0才有这步操作,用来存储嵌套的L - 1个子过程的栈框架指针(注意是指针)
* push ebp ; 当前过程的栈框架指针
* sub esp,N
*/
BOOL CInterpretHandler::InterpretEnter(VMTable * table, char * asmtext, int len) // *
{
// 模拟 push ebp
// 将VMContext上下文中ebp寄存器的值复制到edx中
sprintf_s(asmtext, len, "%smov edx,[edi+%02x]\n", asmtext, GetRegisterOffset(RT_Ebp));
// 然后,在真实堆栈中分配4字节空间用于放置ebp的值
sprintf_s(asmtext, len, "%ssub ebp,4\n", asmtext);
sprintf_s(asmtext, len, "%smov dword ptr [ebp],edx\n", asmtext);
// 将真实堆栈的esp值复制到edx中,并保存到VMContext上下文的esp中
sprintf_s(asmtext, len, "%smov edx,ebp\n", asmtext);
sprintf_s(asmtext, len, "%smov [edi+%02x],edx\n", asmtext, GetRegisterOffset(RT_Esp));
// 至此,ebp指向的就是真实堆栈,所以是不需要模拟mov ebp,esp的
// 模拟 sub esp,(L - 1) * 4 ; L > 0才有这步操作
// 将真实堆栈esp的值复制到edx中
sprintf_s(asmtext, len, "%smov edx,ebp\n", asmtext);
// ecx = ecx * 4
// ecx寄存器中存放的是 L 嵌套层数
sprintf_s(asmtext, len, "%slea ecx,[ecx*4]\n", asmtext);
// 在真实堆栈中分配 (L - 1) * 4 的空间存放子过程的栈框架信息
sprintf_s(asmtext, len, "%ssub edx,ecx\n", asmtext);
// 再减4
sprintf_s(asmtext, len, "%ssub edx,4\n", asmtext);
// 检测ecx是否为0
sprintf_s(asmtext, len, "%stest ecx,ecx\n", asmtext);
// 不为0则意味着确实需要在堆栈中分空栈框架空间,即ebp = ebp - edx
sprintf_s(asmtext, len, "%scmovne ebp,edx\n", asmtext);
// 模拟 push ebp
sprintf_s(asmtext, len, "%smov edx,[edi+%02x]\n", asmtext, GetRegisterOffset(RT_Ebp));
sprintf_s(asmtext, len, "%ssub ebp,4\n", asmtext);
sprintf_s(asmtext, len, "%smov dword ptr [ebp],edx\n", asmtext);
// 模拟 sub esp,N
sprintf_s(asmtext, len, "%ssub ebp,eax\n", asmtext);
return TRUE;
}
/**
* 为指令leave生成相应的汇编代码
*
* table: 存放leave指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回TRUE
*
* 注:enter指令用于操作子过程栈框架,代码如下:
* mov ebp,esp
* pop ebp
*/
BOOL CInterpretHandler::InterpretLeave(VMTable * table, char * asmtext, int len) // *
{
// 因为ebp已指向真实堆栈,所以是不需要模拟 mov ebp,esp 指令的
// 先将真实堆栈esp的值保存到VMContext上下文中
sprintf_s(asmtext, len, "%smov edx,ebp\n", asmtext);
sprintf_s(asmtext, len, "%smov [edi+%02x],edx\n", asmtext,GetRegisterOffset(RT_Esp));
// 模拟 pop ebp
// 将真实堆栈中的值复制到eax中
sprintf_s(asmtext, len, "%smov eax,[ebp]\n", asmtext);
// 释放堆栈占用的空间
sprintf_s(asmtext,len, "%sadd ebp,4\n", asmtext);
// 将eax复制到VMContext上下文的ebp中
sprintf_s(asmtext,len, "%smov [edi+%02x],eax\n", asmtext, GetRegisterOffset(RT_Ebp));
return TRUE;
}
/**
* 为指令jmp生成相应的汇编代码
*
* table: 存放jmp指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回FALSE
*/
BOOL CInterpretHandler::InterpretJMP(VMTable * table, char * asmtext, int len) // *
{
// 将跳转地址复制到esi,改变esi就相当于改变执行流程
sprintf_s(asmtext, len, "%smov esi,eax\n", asmtext);
sprintf_s(asmtext, len, "%sadd esp,4\n", asmtext);
return FALSE;
}
/**
* 为指令jcxz生成相应的汇编代码
*
* table: 存放jcxz指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回FALSE
*
* 注:jcxz指令,该指令实现当寄存器CX的值等于0时转移到标号,否则顺序执行
*/
BOOL CInterpretHandler::InterpretJCXZ(VMTable * table, char * asmtext, int len) // *
{
// 检测ecx是否为0
sprintf_s(asmtext, len, "%stest ecx,ecx\n", asmtext);
// 如果为0,则将eax复制到esi中,由于eax中存放的是跳转指令地址,所以,将
// esi的值改变为跳转指令地址,则相当于改变了虚拟机的执行流程
sprintf_s(asmtext, len, "%sCMOVZ esi,eax\n", asmtext);
//
sprintf_s(asmtext, len, "%sadd esp,4\n", asmtext);
return FALSE;
}
/**
* 为条件跳转指令jcc生成相应的汇编代码
*
* table: 存放jcc指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回FALSE
*/
BOOL CInterpretHandler::InterpretJCC(VMTable * table, char * asmtext, int len) // *
{
// 先根据指令得到跳转的条件后缀
char strPostfix[16] = {0};
strcpy_s(strPostfix, 16, &table->strInstruction[1]);
// 如果条件成立,则将esp的地址复制到esi中以改变流程,否则不复制,即流程不改变
sprintf_s(asmtext, len, "%scmov%s esi,[esp]\n", asmtext, strPostfix);
sprintf_s(asmtext,len, "%sadd esp,4\n", asmtext);
return FALSE;
}
/**
* 为指令loope生成相应的汇编代码
*
* table: 存放loope指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回FALSE
*/
BOOL CInterpretHandler::InterpretLoope(VMTable * table, char * asmtext, int len) // *
{
// 将标志寄存器压入堆栈中
sprintf_s(asmtext, len, "%spushfd\n", asmtext);
// 测试ecx是否为0
sprintf_s(asmtext, len, "%stest ecx,ecx\n", asmtext);
// 如果不为0,则说明需要继续循环,则将循环指令地址复制到edx中
sprintf_s(asmtext, len, "%scmovne edx,eax\n", asmtext);
sprintf_s(asmtext, len, "%spopfd\n", asmtext);
sprintf_s(asmtext, len, "%scmovne edx,esi\n", asmtext);
sprintf_s(asmtext, len, "%scmove edx,eax\n", asmtext);
// 释放堆栈
sprintf_s(asmtext, len, "%sadd ebp,4\n", asmtext);
return FALSE;
}
/**
* 为指令retn生成相应的汇编代码
*
* table: 存放retn指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回FALSE
*
* 注:retn在虚拟机里被认为是一个退出函数
*/
BOOL CInterpretHandler::InterpretRetn(VMTable * table, char * asmtext, int len) // *
{
int s_reg[9] = {RT_Esp, RT_EFlag, RT_Ebp, RT_Edi, RT_Esi, RT_Edx, RT_Ecx, RT_Ebx, RT_Eax};
// 如果指令带有一个操个数,即 retn XX
if (table->OperandNum == 1)
{
// 先将返回地址复制到edx中
// 问题:此语句似乎是错误的,应该如下:
// sprintf_s(asmtext, len, "%smov edx,[ebp]\n", asmtext);
sprintf_s(asmtext, len, "%smov edx,ebp\n", asmtext);
// 释放堆栈占用的空间XX
sprintf_s(asmtext, len, "%sadd ebp,eax\n", asmtext);
// 再将返回地址放入堆栈中
sprintf_s(asmtext, len, "%smov [ebp],edx\n", asmtext);
// 将ebp(即真实esp)保存到VMContext上下文的esp中
sprintf_s(asmtext, len, "%smov [edi+%02x],ebp\n", asmtext, GetRegisterOffset(RT_Esp));
}
// 将保存在VMContext上下文的寄存器压入堆栈
for (int i = 0; i < 9; i++)
{
sprintf_s(asmtext, len, "%spush [edi+%02X]\n", asmtext, GetRegisterOffset(s_reg[i]));
}
// 再将其恢复到各个寄存器中
sprintf_s(asmtext, len, "%spop eax\n", asmtext);
sprintf_s(asmtext, len, "%spop ebx\n", asmtext);
sprintf_s(asmtext, len, "%spop ecx\n", asmtext);
sprintf_s(asmtext, len, "%spop edx\n", asmtext);
sprintf_s(asmtext, len, "%spop esi\n", asmtext);
sprintf_s(asmtext, len, "%spop edi\n", asmtext);
sprintf_s(asmtext, len, "%spop ebp\n", asmtext);
sprintf_s(asmtext, len, "%spopfd\n", asmtext);
// 将esp恢复到真实堆栈
sprintf_s(asmtext, len, "%spop esp\n", asmtext);
// 正式返回
sprintf_s(asmtext, len, "%sretn\n", asmtext);
return FALSE;
}
/**
* 为指令call生成相应的汇编代码
*
* table: 存放call指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回FALSE
*
* 注:call anotherfunc调用子函数,会将控制权移交给另处的代码,这些代码是不受
* 虚拟机控制的,所以碰到call指令,必须退出虚拟机, 让子函数在真实CPU中执
* 行后再交回给虚拟机执行一下指令
*/
BOOL CInterpretHandler::InterpretCall(VMTable * table, char * asmtext, int len) // *
{
int s_reg[9] = {RT_Esp, RT_EFlag, RT_Ebp, RT_Edi, RT_Esi, RT_Edx, RT_Ecx, RT_Ebx, RT_Eax};
sprintf_s(asmtext, len, "%smov edx,[esi]\n", asmtext);
// 在堆栈中分配4字节空间
sprintf_s(asmtext, len, "%ssub ebp,4\n", asmtext);
// 将call的返回地址压入堆栈中
sprintf_s(asmtext, len, "%smov [ebp],edx\n", asmtext);
// 再分配4字节空间
sprintf_s(asmtext, len, "%ssub ebp,4\n", asmtext);
// 如果call调用地址是一个内存数
if (table->optype[0] == MEMTYPE)
{
sprintf_s(asmtext, len, "%smov eax,dword ptr [eax]\n", asmtext);
}
// 将call的地址压入堆栈中
sprintf_s(asmtext, len, "%smov [ebp],eax\n", asmtext);
// 将ebp(即真正esp)保存到VMContext上下文的esp中
sprintf_s(asmtext, len, "%smov [edi+%02x],ebp\n", asmtext, GetRegisterOffset(RT_Esp));
// 压入保存在VMContext的寄存器值
for (int i = 0; i < 9; i++)
{
sprintf_s(asmtext, len, "%spush [edi+%02X]\n", asmtext, GetRegisterOffset(s_reg[i]));
}
// 再将各寄存器值弹出,恢复到各寄存器中,以恢复环境
sprintf_s(asmtext, len, "%spop eax\n", asmtext);
sprintf_s(asmtext, len, "%spop ebx\n", asmtext);
sprintf_s(asmtext, len, "%spop ecx\n", asmtext);
sprintf_s(asmtext, len, "%spop edx\n", asmtext);
sprintf_s(asmtext, len, "%spop esi\n", asmtext);
sprintf_s(asmtext, len, "%spop edi\n", asmtext);
sprintf_s(asmtext, len, "%spop ebp\n", asmtext);
sprintf_s(asmtext, len, "%spopfd\n", asmtext);
// 恢复到真实的堆栈
sprintf_s(asmtext, len, "%spop esp\n", asmtext);
// 返回,直接回到call的地址执行
sprintf_s(asmtext, len, "%sretn\n", asmtext);
return FALSE;
}
/**
* 为指令saveesp生成相应的汇编代码,用于保存堆栈
*
* table: 存放saveesp指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回FALSE
*/
BOOL CInterpretHandler::InterpretSaveEsp(VMTable * table, char * asmtext, int len) // *
{
// 将ebp(即真正esp)保存到VMContext上下文的esp中
sprintf_s(asmtext, len, "%smov [edi+%02x],ebp\n", asmtext, GetRegisterOffset(RT_Esp));
return FALSE;
}
/**
* 为指令restoreesp生成相应的汇编代码,用于恢复堆栈
*
* table: 存放restoreesp指令的信息,根据这些信息可以为相应虚拟机生成汇编代码
* asmtext: 保存生成的汇编代码
* len:asmtext缓存的长度
*
* 返回值:只返回FALSE
*/
BOOL CInterpretHandler::InterpretRestoreEsp(VMTable * table, char * asmtext, int len) // *
{
// 将VMContext上下文中esp恢复到ebp真实堆栈指针中
sprintf_s(asmtext, len, "%smov ebp,[edi+%02x]\n", asmtext, GetRegisterOffset(RT_Esp));
return FALSE;
}
// ------------------------------------- vmserv.cpp ---------------------------------------------
// 此文件定义了如《汇编指令-虚拟机指令》对应表vmtable等信息
// 这里,只摘取了其中一部分
#include "stdafx.h"
#include "VCommand.h"
#include "asm\disasm.h"
#include "vmdisasm.h"
/**
* 寄存器名
*/
const char * vregname[3][14] =
{
{ "AL", "CL", "DL", "BL", "AH", "CH", "DH", "BH", "CS","DS","ES","FS","GS","SS" },
{ "AX", "CX", "DX", "BX", "SP", "BP", "SI", "DI", "CS","DS","ES","FS","GS","SS" },
{ "EAX","ECX","EDX","EBX","ESP","EBP","ESI","EDI","CS","DS","ES","FS","GS","SS" }
};
/**
* 可用于存放虚拟指令操作数的寄存器
*/
const char ArgReg[3][4] = {"EAX","ECX","EDX"};
/**
* 虚拟机有两部分的指令:
* 1、是需要手工编写生成的指令
* 2、是可以由程序动态分析生成的指令
*/
/**
* 需要手工编写生成的核心指令
*/
VM_DName vm_dname[COREHANDLERLEN] =
{
{"VStartVM", VStartVM}, // 启动函数
{"DCheckESP", DCheckESP}, // 检查ESP是否重复
{"DPushReg32", DPushReg32}, // dpush eax
{"DPushImm32", DPushImm32}, // dpush 1234
{"DPushMem32", DPushMem32}, // dpush 1234
{"DPopReg32", DPopReg32}, // dpop ebx
{"DFree", DFree}, // daddesp 4
{"", NULL},
{"", NULL},
};
/**
* 数组包含了虚拟机指令与相应的普通汇编指令的信息,根据这些信息,虚拟机
* 可以为虚拟机指令生成相应的汇编代码以代替原来对应的普通汇编指令
*/
VMTable vmtable[VMTABLEMAXLEN] =
{
// 信息格式如下:
// 虚拟机指令名、相对的汇编指令、操作数个数、段前缀、操作数类型、操作数位数、执行指令时需要使用的寄存器、执行指令后需要保存的寄存器、第2个寄存器是否恢复(一般为0不恢复)
{"VBEGIN","vBegin",0, SEG_UNDEF, NONETYPE, NONETYPE,0,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VTOREAL","vtoReal",1, SEG_UNDEF, IMMTYPE, NONETYPE,32,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VRETN","RETN",0, SEG_UNDEF, NONETYPE, NONETYPE,0,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VRETNIMM32","RETN",1, SEG_UNDEF, IMMTYPE, NONETYPE,32,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
// 保存堆栈
{"VSAVEESP","SAVEESP",0, SEG_UNDEF, IMMTYPE, NONETYPE,32,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
// 恢复堆栈
{"VRESTOREESP","RESTOREESP",0, SEG_UNDEF, IMMTYPE, NONETYPE,32,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
// 堆栈操作指令
{"VPUSH_IMM32","PUSH",1, SEG_UNDEF, IMMTYPE, NONETYPE,32,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
{"VPUSH_REG16","PUSH",1, SEG_UNDEF, REGTYPE, NONETYPE,16,0, NONE,NONE,NONE,NONE, NONE,NONE,NONE,NONE },
};
// ----------------------------------- InterpretHandler.h ---------------------------------------
#pragma once
#include "vmdisasm.h"
class CInterpretHandler
{
public:
CInterpretHandler(void);
public:
~CInterpretHandler(void);
private:
// 寄存器数组索引
// 前15个元素是15个不超15的随机数
int m_RegisterIdx[STACKLEN];
public:
// 初始化
BOOL Init();
public:
// 获得寄存器的偏移
int GetRegisterOffset(int RegType);
// 获得段前缀
char* GetSegStr(int Segment);
// 根据结构声称ASM字符串
BOOL InterpretASMStr(VMTable* table,char* asmtext,int len);
private:
//设置参数
void SetArg(VMTable* table,char* asmtext,int len);
//恢复参数
void RestoreArg(VMTable* table,char* asmtext,int len);
//恢复标志
void RestoreFlag(char* asmtext,int len);
//保存标志
void SaveFlag(char* asmtext,int len);
private:
// 首先执行的指令
BOOL InterpretvBegin(VMTable* table,char* asmtext,int len);
// 跳转到真实指令
BOOL InterpretvtoReal(VMTable* table,char* asmtext,int len);
// *********************堆栈类********************************* //
// 解释push
BOOL InterpretPush(VMTable* table,char* asmtext,int len);
// 解释pop
BOOL InterpretPop(VMTable* table,char* asmtext,int len);
// 解释pushfd
BOOL InterpretPushfd(VMTable* table,char* asmtext,int len);
// 解释popfd
BOOL InterpretPopfd(VMTable* table,char* asmtext,int len);
// 解释pushad
BOOL InterpretPushad(VMTable* table,char* asmtext,int len);
// 解释popad
BOOL InterpretPopad(VMTable* table,char* asmtext,int len);
// 解释enter
BOOL InterpretEnter(VMTable* table,char* asmtext,int len);
// 解释leave
BOOL InterpretLeave(VMTable* table,char* asmtext,int len);
// *********************流程类********************************* //
// 解释jmp
BOOL InterpretJMP(VMTable* table,char* asmtext,int len);
// 解释jcxz\jecxz
BOOL InterpretJCXZ(VMTable* table,char* asmtext,int len);
// 解释jcc
BOOL InterpretJCC(VMTable* table,char* asmtext,int len);
// 解释loope
BOOL InterpretLoope(VMTable* table,char* asmtext,int len);
// 解释返回
BOOL InterpretRetn(VMTable* table,char* asmtext,int len);
// 解释子调用
BOOL InterpretCall(VMTable* table,char* asmtext,int len);
// 普通指令
BOOL CommonInstruction(VMTable* table,char* asmtext,int len);
// 解释保护堆栈Handler
BOOL InterpretSaveEsp(VMTable* table,char* asmtext,int len);
// 解释恢复堆栈Handler
BOOL InterpretRestoreEsp(VMTable* table,char* asmtext,int len);
};
// ----------------------------------------------- vmdisasm.h -------------------------------------
////////////////////////////////////////////////////////////////////////////////////
// 新段各部分空间的长度
#define JumpTableLen (HANDLERMAXLEN * 4)
#define CodeEngineLen (4096 * 4) // 虚拟机引擎代码长度
#define EnterStubAddrLen (4096) // 重新进入VM的Stub,长度1024字节
#define VMEnterCodeLen (4096 * 2) // 空间存放重新进入的vmcode
#define VMCodeLen (10240 * 2) // 2K的空间存放VMCode
#define ASMTEXTLEN 1024 // 汇编文本长度
////////////////////////////////////////////////////////////////////////////////////
//8086指令字符串长度
#define X86NAMELEN 32
// 虚拟机指令名的长度限制
#define VMNAMELEN 32
//Handler的最大Handler长度
#define CODEMAXLEN 512
// 最上面存放寄存器, 后来的一些就当vm堆栈用了
#define STACKLEN 0x200 // 512
// 寄存器最大个数
#define REGCOUNT 15
// 命令Handler最大个数
// 伪指令最大限制数
#define HANDLERMAXLEN 0xFF
//VMTABEL表的个数
#define VMTABLEMAXLEN 0x400
// 核心伪指令个数
#define COREHANDLERLEN 10
// 没有用到寄存器
#define NONE -1
// 寄存器类型
enum RegType
{
RT_Eax,
RT_Ecx,
RT_Edx,
RT_Ebx,
RT_Esp,
RT_Ebp,
RT_Esi,
RT_Edi,
RT_CS,
RT_DS,
RT_ES,
RT_FS,
RT_GS,
RT_SS,
// 必须是最后一个(为什么?我忘了)
RT_EFlag,
RT_AH = 20,
RT_CH,
RT_DH,
RT_BH,
};
/**
* 指令操作数类型
*/
enum optype
{
NONETYPE,
// 立即数
IMMTYPE,
// 寄存器数
REGTYPE,
// 内存数
MEMTYPE,
CSTYPE,
DSTYPE,
ESTYPE,
SSTYPE,
FSTYPE,
GSTYPE,
};
/**
* 虚拟机指令对应着相应的普通汇编指令,此结构的作用,
* 用于存放虚拟机指令与普通汇编指令之间的相关信息
*/
struct VMTable
{
char VMInstrName[VMNAMELEN]; // 虚拟机指令名
char strInstruction[16]; // 相对的汇编指令
int OperandNum; // 操作数个数
int Segment; // 段前缀
int optype[2]; // 操作数类型(寄存器,立即数,内存数)
int bitnum[2]; // 操作数位数
// 某些汇编指令在执行过程中,需要根据某些寄存器的值来控制执行
int NeedReg[4]; // 执行指令时需要使用的寄存器
int SaveReg[4]; // 执行指令后需要保存的寄存器
BOOL Reg2Esp; // 第2个寄存器是否恢复,一般为0不恢复
};
/**
* 存放虚拟机指令处理代码信息
*/
struct VHandler
{
// 虚拟机指令名称,例如:VStartVM等
char VMInstrName[VMNAMELEN];
// 应该是虚拟机指令相应代码所在的虚拟地址
DWORD VirtualAddress;
// 指令的汇编代码,或放置编译后的机器码
char AssembleCode[CODEMAXLEN];
// 指令对应的处理代码(函数)长度
int CodeLen;
VHandler()
{
memset(this, 0, sizeof(VHandler));
}
};
/**
* 指令处理代码(即函数)定义
*/
typedef void (*HandlerFunc)();
//核心的一些手工VM函数名称和对应的函数
struct VM_DName
{
char vm_dname[VMNAMELEN];
HandlerFunc FuncAddr;
};
extern VM_DName vm_dname[COREHANDLERLEN];
//描述一些标准Handler的行为表
extern VMTable vmtable[VMTABLEMAXLEN];
//寄存器对应表
extern const char *vregname[3][14];
extern const char ArgReg[3][4];
// 虚拟机为每个指令产生一个索引号,虚拟机的执行是基于一张跳转表,跳转表中
// 存储每个虚拟机指令处理代码的地址,而当执行某个虚拟机指令时,就是根据虚
// 拟机指令索引号计算出在跳转表中的下标进行跳转操作的
// 而且,该索引号也将作为虚拟机指令的字节码使用
/**
* 保存虚拟机指令名与对应的索引号信息
*/
struct VM_RandomTable
{
char VMInstrName[VMNAMELEN]; // 虚拟机指令名
int idx; // 对应的索引
};
/**
* 存放虚拟机字节码指令节点(即字节码)
*/
struct VCodeNode
{
// 原指令原来所在的地址
DWORD InAddress;
// 虚拟机指令所在的地址
DWORD VMAddress;
char VMInstrName[VMNAMELEN]; //VM命令名称
// 虚拟机指令的字节码,HexCode[0]存放的是指令在跳转表中的索引号
BYTE HexCode[32];
// 字节码长度
int HexLen;
// 指令操作数个数
int OperandNum;
// 操作数:立即数(寄存器, 立即数, 内存数)
int immconst[2];
int bitnum[2]; //位数
VCodeNode()
{
InAddress = 0;
VMAddress = 0;
memset(VMInstrName,0,VMNAMELEN);
memset(HexCode,0,32);
HexLen = 0;
OperandNum = 0;
immconst[0] = immconst[1] = bitnum[0] = bitnum[1] = 0;
}
};
// 为一个索引数组随机排序.
void RandListIdx(int* idx,int cout);
