无DLL文件实现远程线程注入获取进程列表

简介

背景知识

ASLR (地址空间布局随机化): 是一种安全机制,用来防止内存攻击。它通过随机化程序在内存中的地址来防止攻击者预测代码的位置。DLL(如 kernel32.dll 和 ntdll.dll)的加载基址可能不同于不同的进程启动时。

导入表 (Import Address Table, IAT): 每个进程的IAT用于将导入的函数名称解析为实际的函数地址。这个表在进程加载时由操作系统填充。当一个进程调用一个导入的函数(例如 CloseHandle),它实际上通过IAT间接调用该函数的真实地址。

在之前的章节中,笔者曾介绍过有关于远程线程注入的知识,将后门.dll文件注入explorer.exe中实现绕过防火墙反弹后门。但一个.exe文件总要在注入时捎上一个.dll文件着实是怪麻烦的,那么有没有什么方法能够不适用.dll文件实现注入呢?
答案是有的,我们可以直接将功能写在线程函数中,然后直接将整个函数注入,这个方法相较之于DLL注入会稍微复杂一些,适用于对一些体积比较小的程序进行注入,实际上这种方式属于注入动态shellcode。但是要注意动态链接库的地址重定位问题,因为在不同进程中只有kernel32.dll、ntdll、kernelbase的基地址是相同的,而其他DLL由于ASLR,会导致这三个dll外的函数已经被随机化或者指向了错误的内存区域,这就导致注入进程的崩溃。所以最好要在远程线程函数中手动利用LoadLibrary和GetProcessAddress函数强制加载一遍DLL文件。
第二个问题是下面代码为啥要传入CloseHandle的指针,然后在远程线程函数中使用函数指针而不是直接调用对应函数呢?虽然Kernel32.dll在不同进程的基地址一样,在Kernel32.dll中相同的函数名的函数在不同进程的地址也应该一样,但是不同进程的导入表地址不一样 ,我们在代码中直接调用一个系统函数,实际上是从导入表中去调用的,导入表在开启ASLR进程下就会发生变化,如何直接调用CloseHandle,是将你原进程里面导入表地址用到了远程进程里,自然就导致注入进程崩溃了。
这就是一个典型的导入表调用汇编:
在这里插入图片描述
具体解释:
1.导入表问题: CloseHandle 函数在源进程的导入表中已经被解析为某个地址。但在远程进程中,由于每个进程的导入表是不同的,CloseHandle 的地址可能并不指向目标进程中的正确位置。目标进程中的 CloseHandle 地址也许已经被随机化或者指向了错误的内存区域,这就导致了崩溃。
2.ASLR的影响: 虽然 kernel32.dll 在所有进程的基地址是一样的,但每个进程的导入表(IAT)的位置可能不同,并且导入表本身可能由于 ASLR 而被随机化。直接调用源进程中的 CloseHandle 实际上是在使用源进程的IAT,而不是目标进程的IAT,这会导致引用无效的内存地址。
直接使用 CloseHandle(这是一个普通的函数调用,而不是函数指针调用),此调用依赖于导入表。在注入的远程线程中,导入表的地址是随机的(由于 ASLR),因此直接调用会导致目标进程崩溃,因为它试图通过一个不正确的地址(对应于源进程的导入表)调用 CloseHandle
总结:直接使用了普通函数调用(依赖导入表),导致在目标进程中,地址不正确,从而引发注入进程崩溃。

**解决方法:**Visual Studio在编译此类功能的文件时建议关闭编译器的“/GS”选项或者使用关键字__declspec(safebuffers),因为开启GS插入栈保护代码,这其中调用的函数由于重定位的问题会导致导致注入进程的崩溃。

typedef struct _RemoteProcessInfo {
    DWORD pid;  // 进程ID
    WCHAR processName[MAX_PATH];  // 进程名称
}RemoteProcessInfo;

//远程线程函数参数
typedef struct _RemoteParam
{
    LPVOID lpvMP32FWAdress;
    LPVOID lpvMP32NWAddress;
    LPVOID lpvMCHdlAddress;
    LPVOID lpvVirAllocAdress;
    LPVOID lpvCTH32SnapshotAddress;
    LPVOID lpvProcessInfoAddress;
    size_t nProcessSize;
}RemoteParam;

注入代码:

/************************************************************************
 * Function:远程线程函数(主体)
 * Name : ThreadProc
 * Param: lprp	传到远程线程的参数
 * Return: success->0,fail->win32 error code
 * **********************************************************************/
__declspec(safebuffers) DWORD WINAPI ThreadProc(RemoteParam* lprp) {
	// 类型定义
	typedef LPVOID(WINAPI* MVirtualAllocEx)(IN LPVOID lpAddress, IN SIZE_T dwSize, IN DWORD flAllocationType, IN DWORD flProtect);
	typedef HANDLE(WINAPI* MCreateToolhelp32Snapshot)(IN DWORD dwFlags, IN DWORD th32ProcessID);
	typedef BOOL(WINAPI* MProcess32FirstW)(IN HANDLE hSnapshot, OUT PROCESSENTRY32W* lppe);
	typedef BOOL(WINAPI* MProcess32NextW)(IN HANDLE hSnapshot, OUT PROCESSENTRY32W* lppe);
	typedef BOOL(WINAPI* MCloseHandle)(IN HANDLE hObject);

	// 初始化函数指针
	MVirtualAllocEx MAllocMem = (MVirtualAllocEx)lprp->lpvVirAllocAdress;
	MCreateToolhelp32Snapshot MCreateSnapshot = (MCreateToolhelp32Snapshot)lprp->lpvCTH32SnapshotAddress;
	MProcess32FirstW MProcessFirstW = (MProcess32FirstW)lprp->lpvMP32FWAdress;
	MProcess32NextW MProcessNextW = (MProcess32NextW)lprp->lpvMP32NWAddress;
	MCloseHandle MCloseHd = (MCloseHandle)lprp->lpvMCHdlAddress;

	if (!MCreateSnapshot || !MAllocMem || !MProcessFirstW || !MProcessNextW || !MCloseHd) {
		return 0;
	}

	// 拍摄进程快照
	HANDLE hSnapshot = MCreateSnapshot(TH32CS_SNAPPROCESS, 0);
	if (hSnapshot == INVALID_HANDLE_VALUE) {
		return 0;
	}

	PROCESSENTRY32W pe32;
	pe32.dwSize = sizeof(PROCESSENTRY32W);

	// 首先计算所需的内存大小
	size_t processCount = 0;

	// 获取进程数量
	if (MProcessFirstW(hSnapshot, &pe32)) {
		do {
			processCount++;
		} while (MProcessNextW(hSnapshot, &pe32));
	}

	// 根据进程数量动态分配内存
	SIZE_T allocSize = sizeof(RemoteProcessInfo) * processCount;
	RemoteProcessInfo* pProcessArray = (RemoteProcessInfo*)MAllocMem(NULL, allocSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (!pProcessArray) {
		MCloseHd(hSnapshot);
		return 0;
	}

	// 填充进程信息
	int i = 0;
	if (MProcessFirstW(hSnapshot, &pe32)) {
		do {
			if (i >= processCount) break;
			pProcessArray[i].pid = pe32.th32ProcessID;
			wcscpy(pProcessArray[i].processName, pe32.szExeFile);
			i++;
		} while (MProcessNextW(hSnapshot, &pe32));
	}

	// 将分配的内存地址和大小存回到 RemoteParam 结构体
	lprp->lpvProcessInfoAddress = pProcessArray;
	lprp->nProcessSize = processCount;

	MCloseHd(hSnapshot);
	return 0;
}


/************************************************************************
 * Function:从目标进程获取进程信息的函数
 * Name : GetProcessInfoByR3Injection
 * Param: vecProcessInfo	应用层进程信息
 * Return: success->0,fail->win32 error code
 * **********************************************************************/
DWORD GetProcessInfoByR3Injection(std::vector<PROCESS_INFORMATION*>& vecProcessInfo)
{
	ULONG pid = 0;
	HANDLE hSnapshot = NULL;
	PROCESSENTRY32 pe32 = { 0 };
	DWORD dwResult = ERROR_SUCCESS;

	// 启用调试权限
	EnableDebugPrivilege();

	dwResult = GetProcessPidByName(L"Taskmgr.exe", &pid);
	if (dwResult != ERROR_SUCCESS) {
		LOGI("get Taskmgr pid fail,%d", dwResult);
		dwResult = GetProcessPidByName(L"explorer.exe", &pid);
		if (dwResult != ERROR_SUCCESS) {
			LOGE("get explorer pid fail,%d", dwResult);
			return dwResult;
		}
		else {
			LOGI("get explorer pid success:%d", pid);
		}
	}

	HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
	if (hProc == NULL) {
		dwResult = GetLastError();
		LOGE("OpenProcess fail, pid:%d, err:%d", pid, dwResult);
		return dwResult;
	}

	RemoteParam* rp = (RemoteParam*)malloc(sizeof(RemoteParam));
	if (!rp) {
		LOGE("Failed to allocate memory for RemoteParam.");
		CloseHandle(hProc);
		return ERROR_OUTOFMEMORY;
	}
	ZeroMemory(rp, sizeof(RemoteParam));

	HMODULE hMod = GetModuleHandle(L"Kernel32.dll");
	if (hMod == NULL) {
		free(rp);
		CloseHandle(hProc);
		dwResult = GetLastError();
		LOGE("get Kernel32.dll handle fail, err:%d", dwResult);
		return dwResult;
	}

	rp->lpvMP32FWAdress = (LPVOID)GetProcAddress(hMod, "Process32FirstW");
	rp->lpvMP32NWAddress = (LPVOID)GetProcAddress(hMod, "Process32NextW");
	rp->lpvVirAllocAdress = (LPVOID)GetProcAddress(hMod, "VirtualAlloc");
	rp->lpvCTH32SnapshotAddress = (LPVOID)GetProcAddress(hMod, "CreateToolhelp32Snapshot");
	rp->lpvMCHdlAddress = (LPVOID)GetProcAddress(hMod, "CloseHandle");

	// 在目标进程中分配内存用于存储 RemoteParam
	RemoteParam* pRemoteParam = (RemoteParam*)VirtualAllocEx(hProc, 0, sizeof(RemoteParam), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
	if (!pRemoteParam) {
		LOGE("Failed to allocate memory in target process.");
		free(rp);
		CloseHandle(hProc);
		return ERROR_OUTOFMEMORY;
	}

	// 将参数数据写入目标进程
	if (!WriteProcessMemory(hProc, pRemoteParam, rp, sizeof(RemoteParam), 0)) {
		dwResult = GetLastError();
		LOGE("WriteProcessMemory failed. Error code: %d", dwResult);
		VirtualFreeEx(hProc, pRemoteParam, 0, MEM_RELEASE);
		free(rp);
		CloseHandle(hProc);
		return dwResult;
	}

	// 分配远程线程代码空间
	LPVOID pRemoteThread = VirtualAllocEx(hProc, 0, 1024 * 4, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
	if (!pRemoteThread) {
		dwResult = GetLastError();
		LOGE("Failed to allocate 4K memory in target process.");
		VirtualFreeEx(hProc, pRemoteParam, 0, MEM_RELEASE);
		free(rp);
		CloseHandle(hProc);
		return dwResult;
	}

	// 将线程函数写入到目标进程
	if (!WriteProcessMemory(hProc, pRemoteThread, &ThreadProc, 1024 * 4, 0)) {
		dwResult = GetLastError();
		LOGE("WriteProcessMemory failed. Error code: %d", dwResult);
		VirtualFreeEx(hProc, pRemoteThread, 0, MEM_RELEASE);
		VirtualFreeEx(hProc, pRemoteParam, 0, MEM_RELEASE);
		free(rp);
		CloseHandle(hProc);
		return dwResult;
	}

	// 在目标进程中创建远程线程来执行 ThreadProc
	HANDLE hThread = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)pRemoteThread, (LPVOID)pRemoteParam, 0, NULL);
	if (hThread == NULL) {
		dwResult = GetLastError();
		LOGE("CreateRemoteThread fail, pid:%d, err:%d", pid, dwResult);
		VirtualFreeEx(hProc, pRemoteThread, 0, MEM_RELEASE);
		VirtualFreeEx(hProc, pRemoteParam, 0, MEM_RELEASE);
		free(rp);
		CloseHandle(hProc);
		return dwResult;
	}
	else {
		LOGI("Create remote thread success, tid:%p,", hThread);
	}

	// 等待远程线程完成
	DWORD dwWait = 5 * 1000; // 5 秒超时
	WaitForSingleObject(hThread, dwWait);

	// 从目标进程读取 RemoteParam 信息
	if (!ReadProcessMemory(hProc, pRemoteParam, rp, sizeof(RemoteParam), NULL)) {
		dwResult = GetLastError();
		LOGE("Failed to read remote process memory, error:%d", dwResult);
		VirtualFreeEx(hProc, pRemoteThread, 0, MEM_RELEASE);
		VirtualFreeEx(hProc, pRemoteParam, 0, MEM_RELEASE);
		free(rp);
		CloseHandle(hThread);
		CloseHandle(hProc);
		return dwResult;
	}

	// 确保读取的 nProcessSize 值是有效的
	if (rp->nProcessSize == 0) {
		LOGE("Invalid process size value from remote process.");
		if (rp->lpvProcessInfoAddress) { // 释放远程线程分配的内存
			VirtualFreeEx(hProc, rp->lpvProcessInfoAddress, 0, MEM_RELEASE);
		}
		VirtualFreeEx(hProc, pRemoteThread, 0, MEM_RELEASE);
		VirtualFreeEx(hProc, pRemoteParam, 0, MEM_RELEASE);
		free(rp);
		CloseHandle(hThread);
		CloseHandle(hProc);
		return ERROR_INVALID_PARAMETER;
	}

	// 读取远程线程分配的内存中的进程信息
	SIZE_T processInfoSize = rp->nProcessSize * sizeof(RemoteProcessInfo);
	RemoteProcessInfo* pProcessInfoArray = (RemoteProcessInfo*)malloc(processInfoSize);
	if (!pProcessInfoArray) {
		LOGE("Failed to allocate memory on heap.");
		if (rp->lpvProcessInfoAddress) { // 释放远程线程分配的内存
			VirtualFreeEx(hProc, rp->lpvProcessInfoAddress, 0, MEM_RELEASE);
		}
		VirtualFreeEx(hProc, pRemoteThread, 0, MEM_RELEASE);
		VirtualFreeEx(hProc, pRemoteParam, 0, MEM_RELEASE);
		free(rp);
		CloseHandle(hThread);
		CloseHandle(hProc);
		return ERROR_OUTOFMEMORY;
	}

	// 从目标进程读取实际的进程信息
	if (!ReadProcessMemory(hProc, rp->lpvProcessInfoAddress, pProcessInfoArray, processInfoSize, NULL)) {
		dwResult = GetLastError();
		LOGE("Failed to read process information from remote process memory, error:%d", dwResult);
		free(pProcessInfoArray);
		if (rp->lpvProcessInfoAddress) { // 释放远程线程分配的内存
			VirtualFreeEx(hProc, rp->lpvProcessInfoAddress, 0, MEM_RELEASE);
		}
		VirtualFreeEx(hProc, pRemoteThread, 0, MEM_RELEASE);
		VirtualFreeEx(hProc, pRemoteParam, 0, MEM_RELEASE);
		free(rp);
		CloseHandle(hThread);
		CloseHandle(hProc);
		return dwResult;
	}

	// 将获取到的信息转换为 vecProcessInfo 格式
	for (size_t i = 0; i < rp->nProcessSize; ++i) {
		if (pProcessInfoArray[i].pid == 0 || wcslen(pProcessInfoArray[i].processName) == 0) {
			continue;
		}

		PROCESS_INFORMATION* pProcessInfo = (PROCESS_INFORMATION*)malloc(sizeof(PROCESS_INFORMATION));
		if (pProcessInfo == nullptr) {
			dwResult = ERROR_OUTOFMEMORY;
			LOGE("Failed to allocate memory for process information.");
			break;
		}
		memset(pProcessInfo, 0, sizeof(PROCESS_INFORMATION));

		pProcessInfo->Pid = pProcessInfoArray[i].pid;
		std::wstring wstrFilePath = std::wstring(pProcessInfoArray[i].processName);
		auto pTempPath = wcharTochar(wstrFilePath.c_str());
		if (pTempPath) {
			strcpy_s(pProcessInfo->szProcessName, MAX_PATH, pTempPath.get());
		}
		vecProcessInfo.push_back(pProcessInfo);
	}

	// 释放远程线程分配的内存
	if (rp->lpvProcessInfoAddress) {
		VirtualFreeEx(hProc, rp->lpvProcessInfoAddress, 0, MEM_RELEASE);
	}

	// 释放分配的内存和资源
	free(pProcessInfoArray);
	free(rp);

	VirtualFreeEx(hProc, pRemoteThread, 0, MEM_RELEASE);
	VirtualFreeEx(hProc, pRemoteParam, 0, MEM_RELEASE);
	CloseHandle(hThread);
	CloseHandle(hProc);

	return dwResult;
}

参考:
1.https://www.cnblogs.com/PeterZ1997/p/9532065.html
2.https://blog.csdn.net/sandeel/article/details/5387708

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值