之前写壳的时候想搞点vmp,不过网上讲解这个东西的博客比较少,翻来覆去只找到一篇:https://www.cnblogs.com/LittleHann/p/3344261.html

跟随大佬的脚步,作了一番尝试,Mark一下:

与大佬有所区别的是,我所模拟执行一下自己写的函数,就写了一个strcmp。但是,总是失败。后来发现原文调用的MessageBoxA是__stacall,被调函数自己平衡堆栈。

设定了strcmp的调用约定为WINAPI就行了。

发现一个问题,原文里ecx在call之后好像不会改变,但是我这里不行。就在函数开始开辟了一块栈帧,用来保存ecx的值,call之后返回ecx的值。

#include 
#include "windows.h"

/* 下面是虚拟指令,模拟了2条指令 */
//push 0x12345678  push一个4字节的数
#define vPushData    0x10
//call 0x12345678  call一个4字节的地址
#define vCall      0x12
//结束符
#define vEnd       0xff

/*
	这是我们构造的虚拟指令,可以根据函数参数个数的不同,自行修改
	push offset str
	push offset str  
	call strcmp;
*/
BYTE bVmData[] = { vPushData,    0x00, 0x00, 0x00,0x00,
		   vPushData,    0x00, 0x00, 0x00,0x00,
		   vCall,      0x00, 0x00, 0x00,0x00,
		   vEnd };

//简单的虚拟引擎了
_declspec(naked) void  VM(PVOID pvmData)
{
	__asm
	{
		//开辟函数栈帧
		push ebp
		mov ebp,esp
		sub esp, 0x64
	    //取vCode地址放入ecx
	    mov ecx, dword ptr ss : [ebp + 8]
	__vstart :
	    //取第一个字节到al中
		mov bl, byte ptr ds : [ecx]
		//比较是不是vPushData
		cmp bl, vPushData
		//是就跳转__vPushData块
		je    __vPushData
		//比较是不是vCall
		cmp bl, vCall
		//是就跳转到__vCall
		je __vCall
		//比较是不是vEnd
		cmp bl, vEnd
		//是就跳转到__vEnd
		je __vEnd
		int 3
	__vPushData:
		//ecx+1,跳过vPushData,指向后面的4字节地址
	    inc ecx
		//取4个字节的数据到edx(即地址)
		mov edx, dword ptr ds : [ecx]
		//地址压栈
		push edx
		//ecx+4,指向后面的数据
		add ecx, 4
		//跳转到__vstart块
		jmp __vstart
	__vCall :
		//ecx+1,地址偏移1位
	    inc ecx
		//取出函数地址
		mov edx, dword ptr ds : [ecx]
		//保存ecx的值
		mov dword ptr ds :[ebp-20], ecx
		//call 函数地址
		call edx
		//返回ecx的值
		mov ecx,dword ptr ds : [ebp - 20]
		//ecx+1,地址偏移4位
		add ecx, 4
		//跳转到__vstart
		jmp __vstart
	__vEnd :
		//平衡堆栈
		add esp, 0x64
		pop ebp
		ret
	}
}

// 自己实现的一个字符串比较函数
int WINAPI MyStrCmp(char* src, char* dst)
{
	int nNum = 0;
	while (!(nNum = *(char*)src - *(char*)dst) && *dst)
		++src, ++dst;
	if (nNum < 0)
	{
		nNum = -1;
	}
	else if (nNum > 0)
	{
		nNum = 1;
	}
	return nNum;

}

int main()
{
	char str1[] = "Welcome!";
	char str2[] = "Welcome!";

	_asm pushad

	//获取函数指针
	int (WINAPI * pMyStrCmp)(char* src, char* dst);
	pMyStrCmp = MyStrCmp;
	//修改虚拟指令的数据
	*(DWORD*)(bVmData + 1) = (DWORD)str1;
	*(DWORD*)(bVmData + 5 + 1) = (DWORD)str2;
	*(DWORD*)(bVmData + 10 + 1) = (DWORD)pMyStrCmp;

	//执行虚拟指令
	VM(bVmData);
        
    //获取函数返回值
	int nRet = 0;
	_asm 
	{
	  mov nRet, eax
	  popad
	}
	printf("%d", nRet);

	return 0;
}