如何获取GDI句柄列表

cad*_*ver 8 c c++ windows winapi gdi

我正在尝试使用DLL注入方法编写显示另一个程序使用的位图的应用程序,我想得到它正在使用的GDI句柄的特定进程列表(如GDIView.exe实用程序中的列表).我找到了有关NtQuerySystemInformation函数的文章,但此描述仅适用于内核对象的句柄.有人可以帮忙吗?

Sim*_*ier 14

这是一个控制台应用程序代码,它为给定的进程标识符转储所有GDI句柄.它应该编译并适用于32位或64位应用程序,以及在64位操作系统上运行的32位应用程序.它使用了很多未记录的函数,所以不要依赖它:-)共享GDI表的结构上的信用去了冯元的原创作品.(我必须适应WOW64).

以下是在记事本(64位)进程上运行时的示例输出:

[C:\Temp\EnumGdi\Debug]EnumGdi.exe 5916
DC handle:0xF20105DB
Bitmap handle:0xDF05118B
Font handle:0xDC0A19E0
Font handle:0xAB0A1A62
DC handle:0xA3011A63
Region handle:0xAF041B7B
Brush handle:0x11101C5B
Font handle:0x280A1CA1
Font handle:0xBB0A1D13
Bitmap handle:0xA3051DD8
Font handle:0xB40A1DDC
Region handle:0x3A041EE4
Brush handle:0x0B101F04
Region handle:0xC6041F3D
Font handle:0x2C0A2384
Brush handle:0xBA1024DA
Run Code Online (Sandbox Code Playgroud)

EnumGdi.cpp:

#include "stdafx.h"
#include "enumgdi.h"

int _tmain(int argc, _TCHAR* argv[])
{   
    if (argc < 2)
    {
        printf("Format is EnumGdi <process id>\n");
        return 0;
    }

    // get process identifier
    DWORD dwId = _wtoi(argv[1]);

    // open the process
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwId);
    DWORD err = 0;
    if (hProcess == NULL)
    {
        printf("OpenProcess %u failed\n", dwId);
        err = GetLastError();
        return -1;
    }

    // determine if 64 or 32-bit processor
    SYSTEM_INFO si;
    GetNativeSystemInfo(&si);

    // NOTE: as this is undocumented, it *may vary* depending on bitness (32/64) and on Windows version.
    // use WinDbg "dt ntdll!_PEB" command and search for GdiSharedHandleTable offset to find the truth out
    DWORD GdiSharedHandleTableOffset  = si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 ? 0xF8 : 0x94;
    DWORD tableCount = 16384; // count of GDI table cells

    // determine if this process is running on WOW64
    BOOL wow;
    IsWow64Process(GetCurrentProcess(), &wow);

    // read basic info to get PEB address, we only need the beginning of PEB
    DWORD pebSize = GdiSharedHandleTableOffset + 8;
    LPBYTE peb = (LPBYTE)malloc(pebSize);
    ZeroMemory(peb, pebSize);

    if (wow)
    {
        // we're running as a 32-bit process in a 64-bit process
        PROCESS_BASIC_INFORMATION_WOW64 pbi;
        ZeroMemory(&pbi, sizeof(pbi));

        // get process information from 64-bit world
        _NtQueryInformationProcess query = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWow64QueryInformationProcess64");
        err = query(hProcess, 0, &pbi, sizeof(pbi), NULL);
        if (err != 0)
        {
            printf("NtWow64QueryInformationProcess64 failed\n");
            CloseHandle(hProcess);
            return -1;
        }

        // read PEB from 64-bit address space
        _NtWow64ReadVirtualMemory64 read = (_NtWow64ReadVirtualMemory64)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtWow64ReadVirtualMemory64");
        err = read(hProcess, pbi.PebBaseAddress, peb, pebSize, NULL);
        if (err != 0)
        {
            printf("NtWow64ReadVirtualMemory64 PEB failed\n");
            CloseHandle(hProcess);
            return -1;
        }

        // get GDI table ptr from PEB
        GDICELL_WOW64* gdiTable = (GDICELL_WOW64*)*(LPVOID*)(peb + GdiSharedHandleTableOffset); // address in remote process adress space
        if (gdiTable == NULL)
        {
            printf("GDI32.DLL is not loaded in the process\n");
            CloseHandle(hProcess);
            return -1;
        }
        free(peb);
        DWORD tableSize = sizeof(GDICELL_WOW64) * tableCount; // size of GDI table
        GDICELL_WOW64* table = (GDICELL_WOW64*)malloc(tableSize); // local table copied over to our address space

        // copy GDI table
        err = read(hProcess, gdiTable, table, tableSize, NULL);
        if (err != 0)
        {
            printf("NtWow64ReadVirtualMemory64 GdiTable failed\n");
            free(table);
            CloseHandle(hProcess);
            return -1;
        }

        for(DWORD i = 0; i < tableCount; i++)
        {
            GDICELL_WOW64 cell = table[i];
            if (cell.wProcessId != dwId)
                continue;

            HGDIOBJ gdiHandle = (HGDIOBJ)((cell.wUpper << 16) + i);
            WORD type = cell.wType & 0x7F;
            switch(type)
            {
                case 1:
                    printf("DC handle:0x%08X\n", gdiHandle );
                    break;

                case 4:
                    printf("Region handle:0x%08X\n", gdiHandle);
                    break;

                case 5:
                    printf("Bitmap handle:0x%08X\n", gdiHandle);
                    break;

                case 8:
                    printf("Palette handle:0x%08X\n", gdiHandle);
                    break;

                case 10:
                    printf("Font handle:0x%08X\n", gdiHandle);
                    break;

                case 16:
                    printf("Brush handle:0x%08X\n", gdiHandle);
                    break;

                case 48:
                    printf("Pen handle:0x%08X\n", gdiHandle);
                    break;

                default:
                    printf("Unknown type handle:0x%08X\n", gdiHandle);
                    break;
            }
        }
        free(table);
    }
    else
    {
        // we're running as a 32-bit process in a 32-bit OS, or as a 64-bit process in a 64-bit OS
        PROCESS_BASIC_INFORMATION pbi;
        ZeroMemory(&pbi, sizeof(pbi));

        // get process information
        _NtQueryInformationProcess query = (_NtQueryInformationProcess)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
        err = query(hProcess, 0, &pbi, sizeof(pbi), NULL);
        if (err != 0)
        {
            printf("NtQueryInformationProcess failed\n");
            CloseHandle(hProcess);
            return -1;
        }

        // read PEB
        _NtReadVirtualMemory read = (_NtReadVirtualMemory)GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtReadVirtualMemory");
        err = read(hProcess, pbi.PebBaseAddress, peb, pebSize, NULL);
        if (err != 0)
        {
            printf("NtReadVirtualMemory PEB failed\n");
            CloseHandle(hProcess);
            return -1;
        }

        // get GDI table ptr
        GDICELL* gdiTable = (GDICELL*)*(LPVOID*)(peb + GdiSharedHandleTableOffset); // address in remote process adress space
        if (gdiTable == NULL)
        {
            printf("GDI32.DLL is not loaded in the process\n");
            CloseHandle(hProcess);
            return -1;
        }
        free(peb);

        DWORD tableSize = sizeof(GDICELL) * tableCount; // size of GDI table
        GDICELL* table = (GDICELL*)malloc(tableSize); // local table copied over to our address space

        // read GDI table
        err = read(hProcess, gdiTable, table, tableSize, NULL);
        if (err != 0)
        {
            printf("NtReadVirtualMemory GdiTable failed\n");
            free(table);
            CloseHandle(hProcess);
            return -1;
        }

        for(DWORD i = 0; i < tableCount; i++)
        {
            GDICELL cell = table[i];
            if (cell.wProcessId != dwId)
                continue;

            HGDIOBJ gdiHandle = (HGDIOBJ)((cell.wUpper << 16) + i);
            WORD type = cell.wType & 0x7F;
            switch(type)
            {
                case 1:
                    printf("DC handle:0x%08X\n", gdiHandle );
                    break;

                case 4:
                    printf("Region handle:0x%08X\n", gdiHandle);
                    break;

                case 5:
                    printf("Bitmap handle:0x%08X\n", gdiHandle);
                    break;

                case 8:
                    printf("Palette handle:0x%08X\n", gdiHandle);
                    break;

                case 10:
                    printf("Font handle:0x%08X\n", gdiHandle);
                    break;

                case 16:
                    printf("Brush handle:0x%08X\n", gdiHandle);
                    break;

                case 48:
                    printf("Pen handle:0x%08X\n", gdiHandle);
                    break;

                default:
                    printf("Unknown type handle:0x%08X\n", gdiHandle);
                    break;
            }
        }
        free(table);
    }
    CloseHandle(hProcess);
}
Run Code Online (Sandbox Code Playgroud)

EnumGdi.h:

#pragma once
#include "stdafx.h"

// defines a GDI CELL
typedef struct
{
    LPVOID pKernelAddress;
    USHORT wProcessId;
    USHORT wCount;
    USHORT wUpper;
    USHORT wType;
    LPVOID pUserAddress;
} GDICELL;

// defines a GDI CELL for WOW64
typedef struct
{
    PVOID64 pKernelAddress;
    USHORT wProcessId;
    USHORT wCount;
    USHORT wUpper;
    USHORT wType;
    PVOID64 pUserAddress;
} GDICELL_WOW64;

// NtQueryInformationProcess for pure 32 and 64-bit processes
typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
    IN HANDLE ProcessHandle,
    ULONG ProcessInformationClass,
    OUT PVOID ProcessInformation,
    IN ULONG ProcessInformationLength,
    OUT PULONG ReturnLength OPTIONAL
    );

typedef NTSTATUS (NTAPI *_NtReadVirtualMemory)(
    IN HANDLE ProcessHandle,
    IN PVOID BaseAddress,
    OUT PVOID Buffer,
    IN SIZE_T Size,
    OUT PSIZE_T NumberOfBytesRead);

// NtQueryInformationProcess for 32-bit process on WOW64
typedef NTSTATUS (NTAPI *_NtWow64ReadVirtualMemory64)(
    IN HANDLE ProcessHandle,
    IN PVOID64 BaseAddress,
    OUT PVOID Buffer,
    IN ULONG64 Size,
    OUT PULONG64 NumberOfBytesRead);

// PROCESS_BASIC_INFORMATION for pure 32 and 64-bit processes
typedef struct _PROCESS_BASIC_INFORMATION {
    PVOID Reserved1;
    PVOID PebBaseAddress;
    PVOID Reserved2[2];
    ULONG_PTR UniqueProcessId;
    PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;

// PROCESS_BASIC_INFORMATION for 32-bit process on WOW64
// The definition is quite funky, as we just lazily doubled sizes to match offsets...
typedef struct _PROCESS_BASIC_INFORMATION_WOW64 {
    PVOID Reserved1[2];
    PVOID64 PebBaseAddress;
    PVOID Reserved2[4];
    ULONG_PTR UniqueProcessId[2];
    PVOID Reserved3[2];
} PROCESS_BASIC_INFORMATION_WOW64;
Run Code Online (Sandbox Code Playgroud)

  • 您可能应该记录已知的Windows版本. (2认同)

val*_*ldo 5

首先,您必须定义“GDI 句柄”的确切含义以及为什么需要了解它们,因为句柄有不同类型。

从技术上讲,手柄有 3 种主要类型:

  • 内核句柄。示例:文件句柄、同步对象(事件、互斥体等)、文件映射等。
  • 用户句柄。例如:HWNDHDCHICON、桌面手柄等。
  • GDI 手柄。示例:HBITMAP, HGDIOBJ(子类型为HRGN,HPEN等)。

特别是有些人混淆了用户句柄和 GDI 句柄。并不是每个人都知道HDC用于绘图的实际上不是GDI 句柄。

从实现的角度来看,用户句柄和 GDI 句柄之间存在很大差异。用户句柄是系统范围的,它们在内核中管理。因此,有机会在内核模式下收集有关它们的所有信息。OTOH GDI 句柄是特定于流程的。某些 GDI 对象纯粹在用户模式下进行管理(例如区域和 DIB)。