windows安全:inject常见方式

183 阅读4分钟

远程线程注入

远程线程注入是最简单的一种注入方式.具体实现代码如下:

  1. OpenProcess 打开进程
  2. VirtualAllocEx 在目标进程中申请内存
  3. WriteProcessMemory在目标进程中的内存中写入数据.
  4. CreateRemoteThread 在目标进程中创建线程,线程执行函数LoadLibraryW(目标进程肯定会有该函数),入参为dll的路径.
// 远程线程注入
bool InjectRemoteThread(DWORD pid, const WCHAR* dllPath)
{
	//1.打开进程
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);

	//2.在目标进程内申请一块内存,要可读可写,主要用于写入模块地址
	LPVOID lpAddress = VirtualAllocEx(hProcess, NULL, 0x100, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	SIZE_T sWriteLength = 0;
	//写入模块地址
	WriteProcessMemory(hProcess, lpAddress, dllPath, ((wcslen(dllPath) + 1) * 2), &sWriteLength);

	//3.创建远程线程,把LoadLibrary作为回调函数,传入写入的地址,用于加载模块
	HANDLE hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryW, lpAddress, NULL, NULL);

	//4.等待线程结束
	WaitForSingleObject(hThread, INFINITE);
	//5.释放资源
	VirtualFreeEx(hProcess, lpAddress, 0, MEM_RELEASE);
	CloseHandle(hThread);
	CloseHandle(hProcess);
	return TRUE;
}

启动进程注入

启动进程注入,注入的方式与远程线程注入是一致的.只是一个是OpenProcess 来获取有权限的句柄,一个是在进程启动的时候,得到句柄.

// 启动进程注入
BOOL InjectStartProcess(const WCHAR* exePath, const WCHAR* dllPath)
{
	// 启动进程
	STARTUPINFO si = { 0 };
	si.cb = sizeof(STARTUPINFO);
	PROCESS_INFORMATION pi = { 0 };

	BOOL bRet = CreateProcess(exePath, NULL, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi);
	if (!bRet)
	{
		MessageBox(NULL, L"Error", L"启动进程失败", MB_OK);
		return FALSE;
	}

	// 在目标进程写入dll路径
	LPVOID lpAddress = VirtualAllocEx(pi.hProcess, NULL, 0x100, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	SIZE_T sWriteLength = 0;
	WriteProcessMemory(pi.hProcess, lpAddress, dllPath, (((wcslen(dllPath) + 1) * 2)), &sWriteLength);

	// 创建远程线程,把LoadLibrary作为回调函数,传入写入的地址,用于加载模块
	HANDLE hThread = CreateRemoteThreadEx(pi.hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibraryW, lpAddress, 0, NULL, NULL);

	// 等待线程结束
	WaitForSingleObject(hThread, INFINITE);

	// 唤起主线程
	ResumeThread(pi.hThread);

	// 释放资源
	VirtualFreeEx(pi.hProcess, lpAddress, 0, MEM_RELEASE);
	CloseHandle(hThread);
}

全局消息钩子注入

  1. 自身进程加载dll.
  2. 找到目标进程的主线程,将dll中的某个函数挂载到全局的消息钩子上实现dll加载.
// 全局消息钩子注入
BOOL InjectHook(DWORD dwPid, const WCHAR* dllPath)
{
	// dll 中Add函数地址
	HMODULE hModule = LoadLibrary(dllPath);
	DWORD dwFuncAddress = (DWORD)GetProcAddress(hModule, "Add");

	// 遍历线程
	HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwPid);
	THREADENTRY32 te32 = { sizeof(THREADENTRY32) };
	BOOL bRet = Thread32First(hSnap, &te32);

	HHOOK g_Hook;
	if (bRet)
	{
		do
		{
			if (te32.th32OwnerProcessID == dwPid)
			{
				// 钩子注入
				g_Hook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)dwFuncAddress, hModule, te32.th32ThreadID);
				break;
			}
		} while (Thread32Next(hSnap, &te32));
	}
	return TRUE;
}

UserAPC 异步调用注入

  1. 在目标进程空间分配dll路径的内存.
  2. 找到目标进程的主线程,在异步消息恢复队列中添加加载dll的函数.

BOOL InjectAPC(DWORD dwPid, const WCHAR* dllPath)
{
	SIZE_T dwWrite = 0;
	HMODULE hModule = LoadLibraryA("Kernel32.dll");
	auto dwFuncAddress = GetProcAddress(hModule, "LoadLibraryW");

	// 参数写入到目标内存中
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
	PVOID pAddress = VirtualAllocEx(hProcess, NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	WriteProcessMemory(hProcess, pAddress, dllPath, ((wcslen(dllPath) + 1) * 2), &dwWrite);

	// 指定进程内所有线程遍历
	HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, dwPid);
	THREADENTRY32 te32 = { sizeof(THREADENTRY32) };

	BOOL bRet = Thread32First(hSnap, &te32);
	if (bRet)
	{
		do
		{
			// 线程的进程ID与目标进程ID一致,则添加APC
			if (te32.th32OwnerProcessID == dwPid)
			{
				// 打开线程
				HANDLE hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);

				QueueUserAPC((PAPCFUNC)dwFuncAddress, hThread, (ULONG_PTR)pAddress);
			}
		} while (Thread32Next(hSnap, &te32));
	}
	return TRUE;
}

ZwCreateThreadEx 注入

  1. 在目标进程申请并覆写dll路径的内存.
  2. 查到当前进程ntdll.dll的函数ZwCreateThreadEx地址.通过ZwCreateThreadEx函数实现dll的加载函数执行.(类似于远程线程的注入)

eg. 这个我自己测试没成功,目前还没找到原因..

我测试的代码如下:


// 提权函数
BOOL EnableDebugPrivilege()
{
	HANDLE hToken;
	BOOL fOk = FALSE;
	if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
	{
		// 查找debug 权限
		TOKEN_PRIVILEGES tp;
		tp.PrivilegeCount = 1;
		LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);

		tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

		// 设置权限
		AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);

		fOk = (GetLastError() == ERROR_SUCCESS);
		CloseHandle(hToken);
	}
	return fOk;

}

// 定义ZwCreateThreadEx函数指针64位
typedef DWORD(WINAPI* _ZwCreateThreadEx64)(
	PHANDLE ThreadHandle,
	ACCESS_MASK DesiredAccess,
	LPVOID ObjectAttributes,
	HANDLE ProcessHandle,
	LPTHREAD_START_ROUTINE lpStartAddress,
	LPVOID lpParameter,
	ULONG CreateThreadFlags,
	SIZE_T ZeroBits,
	SIZE_T StackSize,
	SIZE_T MaximumStackSize,
	LPVOID pUnkown);

// 定义ZwCreateThreadEx函数指针32位
typedef DWORD(WINAPI* _ZwCreateThreadEx32)(
	PHANDLE ThreadHandle,
	ACCESS_MASK DesiredAccess,
	LPVOID ObjectAttributes,
	HANDLE ProcessHandle,
	LPTHREAD_START_ROUTINE lpStartAddress,
	LPVOID lpParameter,
	BOOL CreateSuspended,
	DWORD dwStackSize,
	DWORD dw1,
	DWORD dw2,
	LPVOID pUnkown);

// ZwCreateThreadEx 注入
BOOL InjectZwCreateThreadEx(DWORD pid, const WCHAR* dllPath)
{
	// 提权
	BOOL upRight = EnableDebugPrivilege();
	if (!upRight)
	{
		printf("提权失败\n");
		return FALSE;
	}

	//
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
	if (hProcess == NULL)
	{
		printf("OpenProcess - Error!\n\n");
		return FALSE;
	}

	// 申请一块内存,用于写入模块地址
	LPVOID dllAddress = VirtualAllocEx(hProcess, NULL, 0x100, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (NULL == dllAddress)
	{
		printf("申请内存失败 - Error!\n\n");
		return FALSE;
	}

	// 写入模块地址
	SIZE_T sWriteLength = 0;
	BOOL bool_ = WriteProcessMemory(hProcess, dllAddress, dllPath, ((wcslen(dllPath) + 1) * 2), &sWriteLength);
	if (!bool_)
	{
		printf("写入模块地址失败\n");
		return FALSE;
	}

	// 获取函数地址
	HMODULE hNtdll = LoadLibraryA("ntdll.dll");
	_ZwCreateThreadEx64 ZwCreateThreadEx = (_ZwCreateThreadEx64)GetProcAddress(hNtdll, "ZwCreateThreadEx");
	if (NULL == ZwCreateThreadEx)
	{
		printf("获取ZwCreateThreadEx函数地址失败\n");
		return FALSE;
	}
	// 创建线程
	HANDLE hRemoteThread = NULL;
	// FARPROC loadLibrary = GetProcAddress(GetModuleHandle(L"Kernel32.dll"), "LoadLibraryW");
	NTSTATUS dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)LoadLibraryW, dllAddress, 0, 0, 0, 0, NULL);
	if (dwStatus != 0)
	{
		printf("创建远程线程失败\n");
		return FALSE;
	}

	// 释放资源
	CloseHandle(hProcess);
	FreeLibrary(hNtdll);

	return TRUE;
}

利用输入法注入

待整理...

通过shellcode注入

待整理...