Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get list of GDI handles

I'm trying to write, using DLL injection method, application which displays bitmaps used by another program and I want to get for this specific process list of GDI Handles which it is using (list like in GDIView.exe utility). I found article about NtQuerySystemInformation function, but this description only works with handles to Kernel Objects. Can somebody help?

like image 658
cadaver Avatar asked Dec 16 '12 21:12

cadaver


2 Answers

Here is a console application code that dumps all GDI handles out for a given process identifier. It should compile and work fine for 32 or 64-bit applications, as well as 32-bit application running on 64-bit OSes. It uses a lot of undocumented functions so don't rely on it :-) Credits on structures for the shared GDI table goes to Feng Yuan original work. (I had to adapt it for WOW64).

Here is a sample output when ran on a Notepad (64-bit) process:

[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

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);
}

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;
like image 154
Simon Mourier Avatar answered Nov 20 '22 16:11

Simon Mourier


First you must define what do you mean exactly by "GDI handles" and why do you need to know about them, because there're different types of handles.

Technically there're 3 major types of handles:

  • Kernel handles. Examples: file handles, sync objects (events, mutexes, etc.), file mappings and etc.
  • User handles. Examples: HWND, HDC, HICON, desktop handles, and etc.
  • GDI handles. Examples: HBITMAP, HGDIOBJ (subtypes are HRGN, HPEN and etc.).

In particular some people confuse between user and GDI handles. Not everybody knows that HDC which is used for drawing is actually not a GDI handle.

From the implementation point of view there's a big difference between user and GDI handles. User handles are system-wide, they're managed in kernel. Hence there's a chance to collect all the information about them in the kernel mode. OTOH GDI handles are process-specific. Some of GDI objects are managed purely in the user-mode (such as regions and DIBs).

like image 5
valdo Avatar answered Nov 20 '22 16:11

valdo