How to get a list of GDI descriptors

I am trying to write, using the DLL injection method, an application that displays bitmaps used by another program, and I want to get for this specific list of processes using the GDI descriptors that it uses (for example, in the GDIView.exe utility). I found an article about the NtQuerySystemInformation function, but this description only works with kernel object descriptors. Can anyone help?

+7
source share
3 answers

Here is the console application code that resets all GDI descriptors for a given process identifier. It should compile and work fine for 32 or 64-bit applications, as well as for a 32-bit application running on 64-bit operating systems. It uses a lot of undocumented features, so donโ€™t rely on it :-) Loans by structure for a common GDI table belong to Feng Yuan original work, (I had to adapt it for WOW64).

Here is an example of output when starting the Notepad process (64-bit):

[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; 
+12
source

You must first determine what you mean by โ€œGDI descriptorsโ€ and why you need to know about them because there are different types of descriptors.

Technically there are 3 main types of pens:

  • The kernel is processing. Examples: file descriptors, synchronization objects (events, mutexes, etc.), file associations, etc.
  • The user is processing. Examples: HWND , HDC , HICON , desktop descriptors, etc.
  • GDI pens. Examples: HBITMAP , HGDIOBJ (subtypes of HRGN , HPEN , etc.).

In particular, some people confuse the user and the GDI descriptors. Not everyone knows that the HDC that is used for drawing is not really a GDI descriptor.

In terms of implementation, there is a big difference between user handles and GDI. User handles are system-wide, they are controlled in the kernel. Therefore, it is possible to collect all information about them in kernel mode. OTOH GDI knobs are process dependent. Some GDI objects are managed exclusively in user mode (for example, in regions and DIB).

+3
source
 Compilable and Buildable //Lets name it GDIInquiry.cpp #pragma once #include "stdafx.h" #include "StdAfx.h" #include <tchar.h> #include <stdio.h> #include <malloc.h> #include <dbghelp.h> #include <shlwapi.h> #include <ShlObj.h> #include "GDIInquiry.h" int _tmain(int argc, _TCHAR* argv[]) { if (argc < 2) { printf("Format is EnumGdi <process id>\n"); system("pause"); 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(); system("pause"); 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); int nDCHandle, nRegionHandle, nBitmapHandle, nPaletteHandle, nFontHandle, nPenHandle, nBrushHandle, nOtherHandle; nDCHandle = nRegionHandle = nBitmapHandle = nPaletteHandle = nFontHandle = nPenHandle = nBrushHandle = nOtherHandle = 0; 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); system("pause"); 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); system("pause"); 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); system("pause"); 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); system("pause"); 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); nDCHandle++; break; case 4: //printf("Region handle:0x%08X\n", gdiHandle); nRegionHandle++; break; case 5: //printf("Bitmap handle:0x%08X\n", gdiHandle); nBitmapHandle++; break; case 8: //printf("Palette handle:0x%08X\n", gdiHandle); nPaletteHandle++; break; case 10: //printf("Font handle:0x%08X\n", gdiHandle); nFontHandle++; break; case 16: //printf("Brush handle:0x%08X\n", gdiHandle); nPenHandle++; break; case 48: //printf("Pen handle:0x%08X\n", gdiHandle); nBrushHandle++; break; default: //printf("Unknown type handle:0x%08X\n", gdiHandle); nOtherHandle++; 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); system("pause"); 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); system("pause"); 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); system("pause"); 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); system("pause"); 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); nDCHandle++; break; case 4: //printf("Region handle:0x%08X\n", gdiHandle); nRegionHandle++; break; case 5: //printf("Bitmap handle:0x%08X\n", gdiHandle); nBitmapHandle++; break; case 8: //printf("Palette handle:0x%08X\n", gdiHandle); nPaletteHandle++; break; case 10: //printf("Font handle:0x%08X\n", gdiHandle); nFontHandle++; break; case 16: //printf("Brush handle:0x%08X\n", gdiHandle); nPenHandle++; break; case 48: //printf("Pen handle:0x%08X\n", gdiHandle); nBrushHandle++; break; default: //printf("Unknown type handle:0x%08X\n", gdiHandle); nOtherHandle++; break; } } free(table); } CloseHandle(hProcess); int nTotalGDI = nDCHandle + nRegionHandle + nBitmapHandle + nPaletteHandle + nFontHandle + nPenHandle + nBrushHandle + nOtherHandle; printf("Bitmap:%d\n", nBitmapHandle); printf("Brush:%d\n", nPenHandle); printf("DeviceContext:%d\n", nDCHandle); printf("Font:%d\n", nFontHandle); printf("Palette:%d\n", nPaletteHandle); printf("Pen:%d\n", nBrushHandle); printf("Region:\%d\n", nRegionHandle); printf("Unknown:%d\n", nOtherHandle); printf("GDITotal:%d\n", nTotalGDI); return 1; } 

and GDII inquiry.h code below

 #pragma once #include "stdafx.h" #include "Winternl.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 { 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; 

Done

 Run commands: ..\..\GDIInquiry\Debug GDIInquiry.exe PID Sample Output: cmd Microsoft Windows [Version 6.1.7601] Copyright (c) 2009 Microsoft Corporation. All rights reserved. ..\..\GDIInquiry\Debug>GDIInquiry.exe 6772 Bitmap:302 Brush:139 DeviceContext:133 Font:75 Palette:1 Pen:0 Region:11 Unknown:0 GDITotal:661 
0
source

All Articles