之前写壳的时候想搞点vmp,不过网上讲解这个东西的博客比较少,翻来覆去只找到一篇:https://www.cnblogs.com/LittleHann/p/3344261.html
跟随大佬的脚步,作了一番尝试,Mark一下:
与大佬有所区别的是,我所模拟执行一下自己写的函数,就写了一个strcmp。但是,总是失败。后来发现原文调用的MessageBoxA是__stacall,被调函数自己平衡堆栈。
设定了strcmp的调用约定为WINAPI就行了。
发现一个问题,原文里ecx在call之后好像不会改变,但是我这里不行。就在函数开始开辟了一块栈帧,用来保存ecx的值,call之后返回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;
}
转载于:https://blog.51cto.com/14317856/2409555