之前写壳的时候想搞点vmp,不过网上讲解这个东西的博客比较少,翻来覆去只找到一篇:https://www.cnblogs.com/LittleHann/p/3344261.html
跟随大佬的脚步,作了一番尝试,Mark一下:
写了一个strcmp,设定了WINAPI的调用约定,被调函数自己平衡堆栈。开辟了一块栈帧,用来保存ecx的值。
#include <iostream> #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; }