|
|
/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
surface.cxx
Abstract:
This file contains the routines to page in surface data.
Author:
Jason Hartman (JasonHa) 2001-05-16
Environment:
User Mode
--*/
#include "precomp.hxx"
#define STRSAFE_NO_DEPRECATE
#include "strsafe.h"
BYTE x86_jmp_here[] = { 0xeb, 0xfe }; // spin jmp
BYTE x86_jmp_plus_0x0e[] = { 0xeb, 0x0e }; // jmp
BYTE x86_jb_plus_0x02[] = { 0x72, 0x02 }; // je
BYTE x86_jne_minus_0x18[] = { 0x75, 0xe7 }; // jne
BYTE x86_jmp_plus_0x06[] = { 0xeb, 0x06 }; // jmp
BYTE x86_jb_plus_0x0c[] = { 0x72, 0x0c }; // je
BYTE x86_jnz_minus_0x08[] = { 0x75, 0xf8 }; // jne
#define I_START_IP 0x00000001
#define I_WRITE_ADDRESS 0x00000002
typedef struct _Instruction { FLONG Flags; ULONG ByteLen; PSTR Code; } Instruction;
/******************************Public*Routine******************************\
* SURFACE * \**************************************************************************/
DECLARE_API( surface ) { BEGIN_API( surface );
HRESULT hr = S_OK; ULONG64 SurfAddr; DEBUG_VALUE Arg; BOOL BadSwitch = FALSE; BOOL AddressIsSURFOBJ = FALSE; BOOL AddressIsSURFACE = FALSE; BOOL ArgumentIsHandle = FALSE;
OutputControl OutCtl(Client);
while (!BadSwitch) { while (isspace(*args)) args++;
if (*args != '-') break;
args++; BadSwitch = (*args == '\0' || isspace(*args));
while (*args != '\0' && !isspace(*args)) { switch (tolower(*args)) { case 'a': if (ArgumentIsHandle || AddressIsSURFOBJ) { OutCtl.OutErr("Error: Only one of -a, -h, or -o may be specified.\n"); BadSwitch = TRUE; } else { AddressIsSURFACE = TRUE; } break; case 'h': if (AddressIsSURFACE || AddressIsSURFOBJ) { OutCtl.OutErr("Error: Only one of -a, -h, or -o may be specified.\n"); BadSwitch = TRUE; } else { ArgumentIsHandle = TRUE; } break; case 'o': if (ArgumentIsHandle || AddressIsSURFACE) { OutCtl.OutErr("Error: Only one of -a, -h, or -o may be specified.\n"); BadSwitch = TRUE; } else { AddressIsSURFOBJ = TRUE; } break; default: BadSwitch = TRUE; break; }
if (BadSwitch) break; args++; } }
if (BadSwitch || (hr = Evaluate(Client, args, DEBUG_VALUE_INT64, 0, &Arg, NULL)) != S_OK || Arg.I64 == 0) { OutCtl.Output("Usage: surface [-?] < [-h] HSURF | [-a] SURFACE Addr | -o SURFOBJ Addr>\n" "\n" " Note: HBITMAP is the same as HSURF.\n"); } else { if (AddressIsSURFOBJ) { PDEBUG_SYMBOLS Symbols;
ULONG64 SurfModule; ULONG SurfTypeId; ULONG BaseObjTypeId; ULONG SurfObjOffset;
if ((hr = Client->QueryInterface(__uuidof(IDebugSymbols), (void **)&Symbols)) == S_OK) { // Try to read SURFOBJ offset from SURFACE type, but
// if that fails assume it is directly after BASEOBJECT.
if ((hr = GetTypeId(Client, "SURFACE", &SurfTypeId, &SurfModule)) != S_OK || (hr = Symbols->GetFieldOffset(SurfModule, SurfTypeId, "so", &SurfObjOffset)) != S_OK) { if ((hr = Symbols->GetTypeId(Type_Module.Base, "_BASEOBJECT", &BaseObjTypeId)) == S_OK) { hr = Symbols->GetTypeSize(Type_Module.Base, BaseObjTypeId, &SurfObjOffset); } }
Symbols->Release(); }
if (hr != S_OK) { OutCtl.OutErr("Error: SURFOBJ to SURFACE lookup failed.\n"); } else { SurfAddr = Arg.I64 - SurfObjOffset; } } else if (AddressIsSURFACE) { SurfAddr = Arg.I64; } else { // Try to look value up as a SURFACE handle
hr = GetObjectAddress(Client, Arg.I64, &SurfAddr, SURF_TYPE, TRUE, TRUE);
if (hr != S_OK || SurfAddr == 0) { if (ArgumentIsHandle) { OutCtl.OutErr(" 0x%p is not a valid HSURF\n", Arg.I64); } else { // The value wasn't restricted to a handle
// so try as a SURFACE address.
SurfAddr = Arg.I64; hr = S_OK; } } else { ArgumentIsHandle = TRUE; } }
if (hr == S_OK && !ArgumentIsHandle) { DEBUG_VALUE ObjHandle; TypeOutputParser TypeParser(Client); OutputState OutState(Client); ULONG64 SurfAddrFromHmgr;
if ((hr = OutState.Setup(0, &TypeParser)) != S_OK || (hr = OutState.OutputTypeVirtual(SurfAddr, "SURFACE", 0)) != S_OK || (hr = TypeParser.Get(&ObjHandle, "hHmgr", DEBUG_VALUE_INT64)) != S_OK) { OutCtl.OutErr("Unable to get contents of SURFACE::hHmgr\n"); OutCtl.OutErr(" (Type Read returned %s)\n", pszHRESULT(hr)); if (AddressIsSURFOBJ) { OutCtl.OutErr(" 0x%p is not a valid SURFOBJ address\n", Arg.I64); } else if (AddressIsSURFACE) { OutCtl.OutErr(" 0x%p is not a valid SURFACE address\n", Arg.I64); } else { OutCtl.OutErr(" 0x%p is neither an HSURF nor valid SURFACE address\n", Arg.I64); } } else { if (GetObjectAddress(Client, ObjHandle.I64, &SurfAddrFromHmgr, SURF_TYPE, TRUE, FALSE) == S_OK && SurfAddrFromHmgr != SurfAddr) { OutCtl.OutWarn("\tNote: SURFACE may not be valid.\n" "\t It does not have a valid handle manager entry.\n"); } } }
if (hr == S_OK) { hr = DumpType(Client, "SURFACE", SurfAddr);
if (hr != S_OK) { OutCtl.OutErr("Type Dump for SURFACE returned %s.\n", pszHRESULT(hr)); } } }
EXIT_API(hr); }
DECLARE_API( dpso ) { INIT_API(); ExtOut("Obsolete: Use 'surfobj <SURFOBJ Addr>'.\n"); EXIT_API(S_OK); }
/******************************Public*Routine******************************\
* SURFLIST * * List readable surfaces and brief info * \**************************************************************************/
PCSTR SurfaceListFields[] = { "so.hsurf", "so.hdev", "so.sizlBitmap", "so.cjBits", "so.pvBits", "so.iBitmapFormat", "so.iType", "so.fjBitmap", NULL };
PCSTR ExtendedSurfaceListFields[] = { "ulShareCount", "BaseFlags", "so.dhsurf", "SurfFlags", NULL };
DECLARE_API( surflist ) { BEGIN_API( surflist );
HRESULT hr; HRESULT hrMask; ULONG64 index = 0; ULONG64 gcMaxHmgr; ULONG64 SurfAddr; BOOL BadSwitch = FALSE; BOOL DumpBaseObject = FALSE; BOOL DumpExtended = FALSE; BOOL DumpUserFields = FALSE;
OutputControl OutCtl(Client);
while (!BadSwitch) { while (isspace(*args)) args++;
if (*args != '-') break;
args++; BadSwitch = (*args == '\0' || isspace(*args));
while (*args != '\0' && !isspace(*args)) { switch (*args) { case 'b': DumpBaseObject = TRUE; break; case 'e': DumpExtended = TRUE; break; default: BadSwitch = TRUE; break; }
if (BadSwitch) break; args++; } }
if (BadSwitch) { OutCtl.Output("Usage: surflist [-?be] [<Start Index>] [<Member List>]\n" "\n" " b - Dump BASEOBJECT information\n" " e - Dump extended members\n" " ulShareCount, BaseFlags, dhsurf, SurfFlags\n" "\n" " Start Index - First hmgr entry index to begin listing\n" " Member List - Space seperated list of other SURFACE members\n" " to be included in the dump\n");
return S_OK; }
DEBUG_VALUE dvIndex; ULONG RemIndex; if (*args != '\0' && !iscsymf(*args) && ((hr = Evaluate(Client, args, DEBUG_VALUE_INT64, 0, &dvIndex, &RemIndex)) == S_OK)) { index = dvIndex.I64; args += RemIndex;
// Always keep args at the next unhandled argument
while (isspace(*args)) args++; }
if ((hr = GetMaxHandles(Client, &gcMaxHmgr)) != S_OK) { OutCtl.OutErr("Unable to get sizeof GDI handle table. HRESULT %s\n", pszHRESULT(hr)); return hr; }
gcMaxHmgr = (ULONG64)(ULONG)gcMaxHmgr;
if (index != 0 && index >= gcMaxHmgr) { OutCtl.Output("No remaining handle entries to be searched.\n"); return hr; }
OutCtl.Output("Searching %s 0x%I64x handle entries for Surfaces.\n", (index ? "remaining" : "all"), gcMaxHmgr - index);
OutputFilter OutFilter(Client); OutputState OutState(Client, FALSE); OutputControl OutCtlToFilter; ULONG64 Module; ULONG TypeId; ULONG OutputMask; BOOL IsPointer64Bit; ULONG PointerSize;
if ((hr = OutState.Setup(DEBUG_OUTPUT_NORMAL, &OutFilter)) == S_OK && (hr = OutCtlToFilter.SetControl(DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_NOT_LOGGED, OutState.Client)) == S_OK && (hr = GetTypeId(Client, "SURFACE", &TypeId, &Module)) == S_OK) { TypeOutputDumper TypeReader(OutState.Client, &OutCtlToFilter);
TypeReader.SelectMarks(1); TypeReader.IncludeMarked(); if (DumpBaseObject) TypeReader.MarkFields(BaseObjectFields); if (DumpExtended) TypeReader.MarkFields(ExtendedSurfaceListFields);
// Add user specified fields to dump list
PSTR MemberList = NULL; CHAR *pBOF = (CHAR *)args;
if (iscsymf(*pBOF)) { MemberList = (PSTR) HeapAlloc(GetProcessHeap(), 0, strlen(pBOF)+1);
if (MemberList != NULL) { strcpy(MemberList, pBOF); pBOF = MemberList;
DumpUserFields = TRUE;
while (iscsymf(*pBOF)) { CHAR *pEOF = pBOF; CHAR EOFChar;
// Get member
do { pEOF++; } while (iscsym(*pEOF) || *pEOF == '.' || *pEOF == '*'); EOFChar = *pEOF; *pEOF = '\0'; TypeReader.MarkField(pBOF);
// Advance to next
if (EOFChar != '\0') { do { pEOF++; } while (isspace(*pEOF)); }
pBOF = pEOF; } } else { OutCtl.OutErr("Error: Couldn't allocate memory for Member List.\n"); hr = E_OUTOFMEMORY; } }
if (hr == S_OK && *pBOF != '\0') { OutCtl.OutErr("Error: \"%s\" is not a valid member list.\n", pBOF); hr = E_INVALIDARG; }
if (hr == S_OK) { // Setup default dump specifications
TypeReader.SelectMarks(0); TypeReader.IncludeMarked(); TypeReader.MarkFields(SurfaceListFields);
OutFilter.Replace(OUTFILTER_REPLACE_THIS, " so _SURFOBJ ", NULL); OutFilter.Replace(OUTFILTER_REPLACE_THIS, " hsurf ", NULL); OutFilter.Replace(OUTFILTER_REPLACE_THIS, " hdev ", NULL); OutFilter.Replace(OUTFILTER_REPLACE_THIS, "(null)", "(null) "); OutFilter.Replace(OUTFILTER_REPLACE_THIS, " sizlBitmap", NULL); OutFilter.Replace(OUTFILTER_REPLACE_THIS, " cjBits", NULL); OutFilter.Replace(OUTFILTER_REPLACE_THIS, " pvBits", NULL); OutFilter.Replace(OUTFILTER_REPLACE_THIS, " iBitmapFormat", NULL); OutFilter.Replace(OUTFILTER_REPLACE_THIS, " iType", NULL); OutFilter.Replace(OUTFILTER_REPLACE_THIS, " fjBitmap", NULL);
// Output dump header
PointerSize = (OutCtl.IsPointer64Bit() == S_OK) ? 21 : 10; OutCtl.Output(" %-*s HSURF HDEV Dimensions cjBits %-*s Format\t\t\t", PointerSize, "&SURFACE", PointerSize, "pvBits"); if (DumpBaseObject) OutCtl.Output(" \tBASEOBJECT"); if (DumpExtended) OutCtl.Output(" ulShareCount BaseFlags dhsurf SurfFlags"); if (DumpUserFields) OutCtl.Output(" %s", args); OutCtl.Output("\n");
for (; index < gcMaxHmgr; index++) { if (OutCtl.GetInterrupt() == S_OK) break;
// Turn off error and verbose messages for this call to
// GetObjectAddress since it will spew for non-surfaces.
if ((hrMask = Client->GetOutputMask(&OutputMask)) == S_OK && OutputMask & (DEBUG_OUTPUT_ERROR | DEBUG_OUTPUT_VERBOSE)) { hrMask = Client->SetOutputMask(OutputMask & ~(DEBUG_OUTPUT_ERROR | DEBUG_OUTPUT_VERBOSE)); }
hr = GetObjectAddress(Client, index, &SurfAddr, SURF_TYPE, FALSE, FALSE);
// Restore mask
if (hrMask == S_OK && OutputMask & (DEBUG_OUTPUT_ERROR | DEBUG_OUTPUT_VERBOSE)) { Client->SetOutputMask(OutputMask); }
if (hr != S_OK || SurfAddr == 0) continue;
OutCtl.Output(" 0x%p ", SurfAddr);
// Read to fields to OutFilter: 'ppdevNext' and 'fl'
OutFilter.DiscardOutput();
hr = TypeReader.OutputVirtual(Module, TypeId, SurfAddr, DEBUG_OUTTYPE_NO_OFFSET | DEBUG_OUTTYPE_COMPACT_OUTPUT);
if (hr == S_OK) { if (DumpBaseObject || DumpExtended || DumpUserFields) { OutCtlToFilter.Output(" \t"); TypeReader.SelectMarks(1); TypeReader.OutputVirtual(Module, TypeId, SurfAddr, DEBUG_OUTTYPE_NO_OFFSET | DEBUG_OUTTYPE_COMPACT_OUTPUT); TypeReader.SelectMarks(0); }
OutFilter.OutputText(&OutCtl, DEBUG_OUTPUT_NORMAL);
OutCtl.Output("\n"); } else { OutCtl.Output("0x????%4.4I64x ** failed to read surface **\n", index); } }
hr = S_OK; }
if (MemberList != NULL) { HeapFree(GetProcessHeap(), 0, MemberList); } } else { OutCtl.OutErr(" Output state/control setup returned %s.\n", pszHRESULT(hr)); }
return hr; }
/******************************Public*Routine******************************\
* VSURF * * View the contents of a surface * \**************************************************************************/
DECLARE_API( vsurf ) { HRESULT hr = S_OK;
BEGIN_API( vsurf );
BOOL BadArg = FALSE; BOOL AddressIsSURFOBJ = FALSE; BOOL AddressIsSURFACE = FALSE; BOOL ArgumentIsHandle = FALSE;
BOOL DisplayToDesktop = FALSE;
PSTR pszMetaFile = NULL;
// Values that can be overridden
DEBUG_VALUE pvScan0 = { 0, DEBUG_VALUE_INVALID}; DEBUG_VALUE lDelta = { 0, DEBUG_VALUE_INVALID}; DEBUG_VALUE iBitmapFormat = { -1, DEBUG_VALUE_INVALID}; DEBUG_VALUE pPal = { 0, DEBUG_VALUE_INVALID};
// Dimension specifications
enum { left = 0, top = 1, cx = 2, cy = 3, right = 2, bottom = 3, }; BOOL GotAllDims = FALSE; ULONG DimsFound = 0; BOOL DimsSpecWxH = FALSE; DEBUG_VALUE dim[4];
// The address or handle
DEBUG_VALUE SurfSpec = { 0, DEBUG_VALUE_INVALID};
ULONG Rem;
OutputControl OutCtl(Client);
if (Client == NULL) { return E_INVALIDARG; }
while (!BadArg && hr == S_OK) { while (isspace(*args)) args++;
if (*args == '-') { // Dimensions must be adjacent - raise error later otherwise
if (DimsFound) { GotAllDims = TRUE; }
// Process switches
args++; BadArg = (*args == '\0' || isspace(*args));
while (*args != '\0' && !isspace(*args)) { switch (tolower(*args)) { case 'a': if (ArgumentIsHandle || AddressIsSURFOBJ) { OutCtl.OutErr("Error: Only one of -a, -h, or -o may be specified.\n"); BadArg = TRUE; } else { AddressIsSURFACE = TRUE; args++; } break; case 'b': { args++; if (Evaluate(Client, args, DEBUG_VALUE_INT64, EVALUATE_DEFAULT_RADIX, &pvScan0, &Rem, NULL, EVALUATE_COMPACT_EXPR) == S_OK) { args += Rem; } else { OutCtl.OutErr("Error: Couldn't evaluate pvScan0 value from '%s'.\n", args); BadArg = TRUE; }
if (!BadArg && Evaluate(Client, args, DEBUG_VALUE_INT64, EVALUATE_DEFAULT_RADIX, &lDelta, &Rem, NULL, EVALUATE_COMPACT_EXPR) == S_OK) { args += Rem; } else { OutCtl.OutErr("Error: Couldn't evaluate lDelta value from '%s'.\n", args); BadArg = TRUE; } } break; case 'd': DisplayToDesktop = TRUE; args++; break; case 'e': { PCSTR pszStart; SIZE_T FileNameLen;
// Find beginning of path
do { args++; } while (isspace(*args));
pszStart = args;
if (*pszStart == '\0') { OutCtl.OutErr("Error: Missing EMF filepath.\n"); BadArg = TRUE; } else { // Point args beyond path
while (*args != '\0' && !isspace(*args)) { args++; }
FileNameLen = args - pszStart; pszMetaFile = (PSTR) HeapAlloc(GetProcessHeap(), 0, FileNameLen+4+1); if (pszMetaFile != NULL) { RtlCopyMemory(pszMetaFile, pszStart, FileNameLen); pszMetaFile[FileNameLen] = 0;
// Append .emf if needed
if (FileNameLen < 4 || _strnicmp(&pszMetaFile[FileNameLen-4], ".emf", 4) != 0) { strcat(pszMetaFile, ".emf"); } } else { OutCtl.OutErr("Failed to allocate memory.\n"); hr = E_OUTOFMEMORY; } } } break; case 'f': { do { args++; } while (isspace(*args));
if (_strnicmp(args, "BMF_", sizeof("BMF_")) == 0) { ENUMDEF *ped = aedBMF; SIZE_T CheckLen;
for (ped = aedBMF; ped->psz != NULL; ped++) { CheckLen = strlen(ped->psz); if (_strnicmp(args, ped->psz, CheckLen) == 0 && !iscsym(args[CheckLen])) { iBitmapFormat.I64 = ped->ul; iBitmapFormat.Type = DEBUG_VALUE_INT64; break; } }
if (iBitmapFormat.Type != DEBUG_VALUE_INT64) { BadArg = TRUE; } } else if (Evaluate(Client, args, DEBUG_VALUE_INT64, EVALUATE_DEFAULT_RADIX, &iBitmapFormat, &Rem, NULL, EVALUATE_COMPACT_EXPR) == S_OK) { args += Rem; } else { BadArg = TRUE; }
if (BadArg) { OutCtl.OutErr("Error: Couldn't evaluate iBitmapFormat value from '%s'.\n", args); } } break; case 'h': if (AddressIsSURFACE || AddressIsSURFOBJ) { OutCtl.OutErr("Error: Only one of -a, -h, or -o may be specified.\n"); BadArg = TRUE; } else { ArgumentIsHandle = TRUE; args++; } break; case 'o': if (ArgumentIsHandle || AddressIsSURFACE) { OutCtl.OutErr("Error: Only one of -a, -h, or -o may be specified.\n"); BadArg = TRUE; } else { AddressIsSURFOBJ = TRUE; args++; } break; case 'p': { args++; if (Evaluate(Client, args, DEBUG_VALUE_INT64, EVALUATE_DEFAULT_RADIX, &pPal, &Rem, NULL, EVALUATE_COMPACT_EXPR) == S_OK) { args += Rem; } else { OutCtl.OutErr("Error: Couldn't evaluate PALETTE address from '%s'.\n", args); BadArg = TRUE; } } break; default: BadArg = TRUE; break; }
if (BadArg) break; } } else { if (*args == '\0') break;
if (SurfSpec.Type == DEBUG_VALUE_INVALID) { // This argument must be an address or handle.
if (Evaluate(Client, args, DEBUG_VALUE_INT64, EVALUATE_DEFAULT_RADIX, &SurfSpec, &Rem, NULL, EVALUATE_COMPACT_EXPR) == S_OK) { args += Rem; } else { OutCtl.OutErr("Error: Couldn't evaluate %s from '%s'.\n", (AddressIsSURFOBJ ? "SURFOBJ address" : "address or handle"), args); BadArg = TRUE; } } else if (!GotAllDims) { // This argument must be part of dimension specification.
// Check for 'x' indicating WxH specification
if (tolower(args[0]) == 'x' && (args[1] == '\0' || isspace(args[1]))) { if (DimsFound == 3) { args += 2; DimsSpecWxH = TRUE; } else { OutCtl.OutErr("Error: Malformed dimension specification.\n"); BadArg = TRUE; break; } } else { if (Evaluate(Client, args, DEBUG_VALUE_INT32, EVALUATE_DEFAULT_RADIX, &dim[DimsFound], &Rem, NULL, EVALUATE_COMPACT_EXPR) == S_OK) { args += Rem; DimsFound++;
GotAllDims = (DimsFound == 4); } else { OutCtl.OutErr("Error: Couldn't evaluate dimension from '%s'.\n", args); BadArg = TRUE; } } } else { OutCtl.OutErr("Error: Unexpected argument at '%s'.\n", args); BadArg = TRUE; break; } } }
if (hr == S_OK) { if (!BadArg) { if (DimsFound == 1 || DimsFound == 3) { OutCtl.OutErr("Error: Missing part of dimensions.\n"); BadArg = TRUE; } else if (SurfSpec.Type == DEBUG_VALUE_INVALID) { OutCtl.OutErr("Error: Missing address or handle.\n"); BadArg = TRUE; } else if (SurfSpec.I64 == 0) { OutCtl.OutErr("Error: Invalid (0) address or handle.\n"); BadArg = TRUE; } else if (!DimsSpecWxH && DimsFound == 4) { dim[cx].I32 = dim[right].I32 - dim[left].I32; dim[cy].I32 = dim[bottom].I32 - dim[top].I32; if ((LONG)dim[cx].I32 < 0 || (LONG)dim[cy].I32 < 0) { OutCtl.OutErr("Error: Invalid dimensions.\n"); BadArg = TRUE; } } }
if (BadArg) { if (*args == '?') OutCtl.Output("View (or save) the contents of a surface\n");
OutCtl.Output("\n" "Usage: vsurf [-?bdefp] < [-h] HSURF | [-a] SURFACE Addr | -o SURFOBJ Addr> [Dimensions]\n" " b <pvScan0> <lDelta> - Override those fields from surface\n" " Zero values will not override\n" " d - Blt contents to (0,0) on the desktop\n" " e <Filepath> - Full path to save surface as an EMF\n" " f <iBitmapFormat> - Overrides bitmap format\n" " p <PALETTE Addr> - Specifies PALETTE to use for indexed surfaces\n" " Dimensions = Left Top [Right Bottom | Width x Height]\n" "\n" " Notes:\n" " HBITMAP is the same as HSURF.\n" " When not saving to a file, a window will be opened.\n" " -> Use GDIView.exe if debugging remotely.\n"); } else { PDEBUG_SYMBOLS Symbols = NULL; PDEBUG_DATA_SPACES Data = NULL; ULONG64 SurfAddr;
if ((hr = Client->QueryInterface(__uuidof(IDebugSymbols), (void **)&Symbols)) != S_OK || (hr = Client->QueryInterface(__uuidof(IDebugDataSpaces), (void **)&Data)) != S_OK) { OutCtl.OutErr("Error setting up debugger interface.\n"); } else { if (AddressIsSURFOBJ) { ULONG64 SurfModule = 0; ULONG SurfTypeId = 0; ULONG SurfObjOffset; ULONG BaseObjTypeId = 0;
// Try to read SURFOBJ offset from SURFACE type, but
// if that fails assume it is directly after BASEOBJECT.
if ((hr = GetTypeId(Client, "SURFACE", &SurfTypeId, &SurfModule)) != S_OK || (hr = Symbols->GetFieldOffset(SurfModule, SurfTypeId, "so", &SurfObjOffset)) != S_OK) { if ((hr = Symbols->GetTypeId(Type_Module.Base, "_BASEOBJECT", &BaseObjTypeId)) == S_OK) { hr = Symbols->GetTypeSize(Type_Module.Base, BaseObjTypeId, &SurfObjOffset); } }
if (hr != S_OK) { OutCtl.OutErr("Error: SURFOBJ to SURFACE lookup failed.\n"); } else { SurfAddr = SurfSpec.I64 - SurfObjOffset; } } else if (AddressIsSURFACE) { SurfAddr = SurfSpec.I64; } else { hr = GetObjectAddress(Client, SurfSpec.I64, &SurfAddr, SURF_TYPE, TRUE, TRUE);
if (hr != S_OK) { if (ArgumentIsHandle) { OutCtl.OutErr(" 0x%p is not a valid HSURF\n", SurfSpec.I64); } else { SurfAddr = SurfSpec.I64; hr = S_OK; } } else { ArgumentIsHandle = TRUE; } } }
if (hr == S_OK) { enum { SF_hHmgr, //SF_ulShareCount,
//SF_cExclusiveLock,
//SF_Tid,
SF_so_dhsurf, SF_so_hsurf, SF_so_dhpdev, SF_so_hdev, SF_so_sizlBitmap_cx, SF_so_sizlBitmap_cy, SF_so_cjBits, SF_so_pvBits, SF_so_pvScan0, SF_so_lDelta, SF_so_iUniq, SF_so_iBitmapFormat, SF_so_iType, SF_so_fjBitmap, //SF_pdcoAA,
//SF_SurfFlags,
SF_pPal, //SF_EBitmap_hdc,
//SF_EBitmap_cRef,
//SF_EBitmap_hpalHint,
//SF_EBitmap_sizlDim_cx,
//SF_EBitmap_sizlDim_cy,
SF_TOTAL };
DEBUG_VALUE SurfValues[SF_TOTAL]; DEBUG_VALUE ObjHandle; TypeOutputParser SurfParser(Client); // SURFACE dump
OutputFilter OutFilter(Client); // For other misc types
OutputState OutState(Client); OutputControl OutCtlToFilter; ULONG64 SurfAddrFromHmgr; CHAR szFallbackDTCmd[128];
struct { BITMAPINFOHEADER bmiHeader; DWORD bmiColors[256]; } bmi;
for (int svi = 0; svi < SF_TOTAL; svi++) { SurfValues[svi].Type = DEBUG_VALUE_INVALID; }
if ((hr = OutState.Setup(0, &SurfParser)) != S_OK || ((hr = OutState.OutputTypeVirtual(SurfAddr, "SURFACE", DEBUG_OUTTYPE_BLOCK_RECURSE)) != S_OK && ((sprintf(szFallbackDTCmd, "dt " GDIType(SURFACE) " hHmgr -y so. -y so.sizlBitmap. pPal 0x%I64x", SurfAddr) == 0) || (hr = OutState.Execute(szFallbackDTCmd)) != S_OK)) || (hr = SurfParser.Get(&SurfValues[SF_hHmgr], "hHmgr", DEBUG_VALUE_INT64)) != S_OK) { OutCtl.OutErr("Unable to get contents of SURFACE::hHmgr\n"); OutCtl.OutErr(" (Type Read returned %s)\n", pszHRESULT(hr)); if (AddressIsSURFOBJ) { OutCtl.OutErr(" 0x%p is not a valid SURFOBJ address\n", SurfSpec.I64); } else if (AddressIsSURFACE) { OutCtl.OutErr(" 0x%p is not a valid SURFACE address\n", SurfSpec.I64); } else if (ArgumentIsHandle) { OutCtl.OutErr(" although 0x%p is a valid HSURF\n", SurfSpec.I64); } else { OutCtl.OutErr(" 0x%p is neither an HSURF nor valid SURFACE address\n", SurfSpec.I64); } } else if ((hr = OutState.Setup(0, &OutFilter)) != S_OK || (hr = OutCtlToFilter.SetControl(DEBUG_OUTCTL_THIS_CLIENT | DEBUG_OUTCTL_NOT_LOGGED | DEBUG_OUTCTL_OVERRIDE_MASK, OutState.Client)) != S_OK) { OutCtl.OutErr("Error preparing misc type reader.\n"); } else { if (!ArgumentIsHandle) { if (GetObjectAddress(Client, SurfValues[SF_hHmgr].I64, &SurfAddrFromHmgr, SURF_TYPE, TRUE, FALSE) == S_OK && SurfAddrFromHmgr != SurfAddr) { OutCtl.OutWarn("\tNote: SURFACE may not be valid.\n" "\t It does not have a valid handle manager entry.\n"); } }
if (SurfParser.Get(&SurfValues[SF_so_hsurf], "hsurf", DEBUG_VALUE_INT64) != S_OK) { OutCtl.OutWarn("Warning: Couldn't read hsurf.\n"); } else { if (SurfValues[SF_so_hsurf].I64 != SurfValues[SF_hHmgr].I64) { OutCtl.OutWarn("Warning: hsurf, 0x%p, != hHmgr, 0x%p.\n", SurfValues[SF_so_hsurf].I64, SurfValues[SF_hHmgr].I64); } }
// Check dimensions
if (DimsFound < 2) { dim[left].I32 = 0; dim[top].I32 = 0; }
if ((hr = SurfParser.Get(&SurfValues[SF_so_sizlBitmap_cx], "cx", DEBUG_VALUE_INT32)) != S_OK || (hr = SurfParser.Get(&SurfValues[SF_so_sizlBitmap_cy], "cy", DEBUG_VALUE_INT32)) != S_OK) { if (DimsFound == 4 && dim[cx].Type == DEBUG_VALUE_INT32 && dim[cy].Type == DEBUG_VALUE_INT32) { hr = S_OK; bmi.bmiHeader.biWidth = dim[cx].I32; bmi.bmiHeader.biHeight = dim[cy].I32; } else { OutCtl.OutErr("Error: Couldn't get SURFACE dimensions.\n"); } } else { bmi.bmiHeader.biWidth = SurfValues[SF_so_sizlBitmap_cx].I32; bmi.bmiHeader.biHeight = SurfValues[SF_so_sizlBitmap_cy].I32;
bmi.bmiHeader.biWidth -= dim[left].I32; bmi.bmiHeader.biHeight -= dim[top].I32;
if (DimsFound == 4) { if ((LONG)dim[cx].I32 < bmi.bmiHeader.biWidth) { bmi.bmiHeader.biWidth = (LONG)dim[cx].I32; } if ((LONG)dim[cy].I32 < bmi.bmiHeader.biHeight) { bmi.bmiHeader.biHeight = (LONG)dim[cy].I32; } } }
if (hr == S_OK) { if (bmi.bmiHeader.biWidth <= 0) { OutCtl.OutWarn("Error: Invalid x dimensions.\n"); hr = S_FALSE; } else if (bmi.bmiHeader.biHeight <= 0) { OutCtl.OutWarn("Error: Invalid y dimensions.\n"); hr = S_FALSE; } }
// Check pvScan0
if (hr == S_OK) { if ((hr = SurfParser.Get(&SurfValues[SF_so_pvScan0], "pvScan0", DEBUG_VALUE_INT64)) != S_OK) { if (pvScan0.Type == DEBUG_VALUE_INT64 && pvScan0.I64 != 0) { hr = S_OK; } else { OutCtl.OutErr("Error: Couldn't get address of first scanline.\n"); if (SurfParser.Get(&SurfValues[SF_so_dhsurf], "dhsurf", DEBUG_VALUE_INT64) == S_OK) { OutCtl.Output(" dhsurf is 0x%p.\n", SurfValues[SF_so_dhsurf].I64); } } } else if (SurfValues[SF_so_pvScan0].I64 == 0 && pvScan0.Type == DEBUG_VALUE_INT64 && pvScan0.I64 != 0) { OutCtl.OutWarn(" Overriding pvScan0, 0x%p, with 0x%p.\n", SurfValues[SF_so_pvScan0].I64, pvScan0.I64); } else { pvScan0 = SurfValues[SF_so_pvScan0]; } }
// Check lDelta
if (hr == S_OK) { if ((hr = SurfParser.Get(&SurfValues[SF_so_lDelta], "lDelta", DEBUG_VALUE_INT64)) != S_OK) { if (lDelta.Type == DEBUG_VALUE_INT64 && lDelta.I64 != 0) { hr = S_OK; } else { OutCtl.OutErr("Error: Couldn't get SURFACE lDelta.\n"); if (SurfParser.Get(&SurfValues[SF_so_dhsurf], "dhsurf", DEBUG_VALUE_INT64) == S_OK) { OutCtl.Output(" dhsurf is 0x%p.\n", SurfValues[SF_so_dhsurf].I64); } } } else if (SurfValues[SF_so_lDelta].I64 == 0 && lDelta.Type == DEBUG_VALUE_INT64 && lDelta.I64 != 0) { OutCtl.OutWarn(" Overriding lDelta, 0x%p, with 0x%p.\n", SurfValues[SF_so_lDelta].I64, lDelta.I64); } else { lDelta = SurfValues[SF_so_lDelta]; } }
// Check iBitmapFormat
if (hr == S_OK) { if ((hr = SurfParser.Get(&SurfValues[SF_so_iBitmapFormat], "iBitmapFormat", DEBUG_VALUE_INT64)) != S_OK) { if (iBitmapFormat.Type == DEBUG_VALUE_INT64 && iBitmapFormat.I64 != 0) { hr = S_OK; } else { OutCtl.OutErr("Error: Couldn't get SURFACE iBitmapFormat.\n"); } } else if (SurfValues[SF_so_iBitmapFormat].I64 == 0 && iBitmapFormat.Type == DEBUG_VALUE_INT64 && iBitmapFormat.I64 != 0) { OutCtl.OutWarn(" Overriding iBitmapFormat, 0x%p, with 0x%p.\n", SurfValues[SF_so_iBitmapFormat].I64, iBitmapFormat.I64); } else { iBitmapFormat = SurfValues[SF_so_iBitmapFormat]; } }
if (hr == S_OK) { bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); bmi.bmiHeader.biPlanes = 1; bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = 0; bmi.bmiHeader.biXPelsPerMeter = bmi.bmiHeader.biYPelsPerMeter = 0; bmi.bmiHeader.biClrUsed = 0; bmi.bmiHeader.biClrImportant = 0;
if (SurfValues[SF_so_iBitmapFormat].I32 == BMF_32BPP) { bmi.bmiHeader.biBitCount = 32; } else if (SurfValues[SF_so_iBitmapFormat].I32 == BMF_24BPP) { bmi.bmiHeader.biBitCount = 24; } else { if (SurfValues[SF_so_iBitmapFormat].I32 == BMF_16BPP) { bmi.bmiHeader.biBitCount = 16; } else if (SurfValues[SF_so_iBitmapFormat].I32 == BMF_8BPP) { bmi.bmiHeader.biBitCount = 8; } else if (SurfValues[SF_so_iBitmapFormat].I32 == BMF_4BPP) { bmi.bmiHeader.biBitCount = 4; } else if (SurfValues[SF_so_iBitmapFormat].I32 == BMF_1BPP) { bmi.bmiHeader.biBitCount = 1; } else { OutCtl.OutErr("Unrecognized iBitmapFormat %ld.\n", SurfValues[SF_so_iBitmapFormat].I32); hr = S_FALSE; } } }
if (hr == S_OK && bmi.bmiHeader.biBitCount < 24) { // Get PALETTE
if (SurfParser.Get(&SurfValues[SF_pPal], "pPal", DEBUG_VALUE_INT64) == S_OK) { if (pPal.Type == DEBUG_VALUE_INT64) { if (SurfValues[SF_pPal].I64 != 0 && pPal.I64 != SurfValues[SF_pPal].I64) { OutCtl.OutWarn(" Overriding PALETTE address, 0x%p, with 0x%p.\n", SurfValues[SF_pPal].I64, pPal.I64); } } else { pPal = SurfValues[SF_pPal]; } } else if (pPal.Type != DEBUG_VALUE_INT64) { OutCtl.OutWarn(" Error reading PALETTE address from SURFACE.\n"); }
// Try getting the PALETTE from the PDEV if need be
if (pPal.Type != DEBUG_VALUE_INT64 || pPal.I64 == 0) { if ((hr = SurfParser.Get(&SurfValues[SF_so_hdev], "hdev", DEBUG_VALUE_INT64)) == S_OK && SurfValues[SF_so_hdev].I64 != 0) { OutFilter.DiscardOutput();
if ((hr = OutState.OutputTypeVirtual(SurfValues[SF_so_hdev].I64, "PDEV", 0)) != S_OK || (hr = OutFilter.Query("ppalSurf", &pPal, DEBUG_VALUE_INT64)) != S_OK) { OutCtl.OutErr(" Error reading PALETTE address from PDEV.\n"); pPal.Type = DEBUG_VALUE_INVALID; } } }
// Read PALETTE settings
if (pPal.Type == DEBUG_VALUE_INT64 && pPal.I64 != 0) { PCSTR ReqPALETTEFields[] = { "flPal", "cEntries", "apalColor", NULL };
TypeOutputDumper PALReader(OutState.Client, &OutCtlToFilter); DEBUG_VALUE cEntries; DEBUG_VALUE ppalColor; BOOL Check565 = FALSE; BOOL Check555 = FALSE;
PALReader.IncludeMarked(); PALReader.MarkFields(ReqPALETTEFields);
OutFilter.DiscardOutput();
if ((hr = PALReader.OutputVirtual("PALETTE", pPal.I64)) == S_OK && (hr = OutFilter.Query("cEntries", &cEntries, DEBUG_VALUE_INT32)) == S_OK) { bmi.bmiHeader.biClrUsed = cEntries.I32;
if ((hr = OutFilter.Query("PAL_FIXED")) != S_OK && (hr = OutFilter.Query("PAL_INDEXED")) != S_OK) { OutCtl.OutErr(" Error: vsurf only supports fixed and indexed palettes.\n"); } else if (OutFilter.Query("PAL_BITFIELDS") == S_OK) { if (bmi.bmiHeader.biClrUsed > 0) { OutCtl.OutErr(" Error: PALETTE @ 0x%p is BITFIELDS, but has Entries.\n", pPal.I64); hr = S_FALSE; } else { bmi.bmiHeader.biCompression = BI_BITFIELDS; bmi.bmiHeader.biClrUsed = 3;
if (OutFilter.Query("PAL_RGB16_565") == S_OK) { Check565 = TRUE; } else if (OutFilter.Query("PAL_RGB16_555") == S_OK) { Check555 = TRUE; } else { OutCtl.OutWarn(" Warning: Nonstandard bitfields format in PALETTE @ 0x%p.\n", pPal.I64); } } }
if (hr == S_OK && bmi.bmiHeader.biClrUsed > 0) { if (bmi.bmiHeader.biClrUsed > 256 || OutFilter.Query("apalColor", &ppalColor, DEBUG_VALUE_INT64) != S_OK || ppalColor.I64 == 0) { OutCtl.OutErr(" Error: PALETTE @ 0x%p is invalid.\n", pPal.I64); hr = S_FALSE; } else { ULONG PALBytes = bmi.bmiHeader.biClrUsed * sizeof(DWORD); ULONG BytesRead;
OutCtl.OutVerb("Reading %ld palette entries @ 0x%p.\n", bmi.bmiHeader.biClrUsed, ppalColor.I64);
hr = Data->ReadVirtual(ppalColor.I64, bmi.bmiColors, PALBytes, &BytesRead); if (hr != S_OK) { OutCtl.OutErr("Error: Couldn't read any PALETTE entries at 0x%p.\n", ppalColor.I64); } else if (BytesRead != PALBytes) { OutCtl.OutErr("Error: Only read %lu of %lu bytes from PALETTE entries at 0x%p.\n", BytesRead, PALBytes, ppalColor.I64); hr = S_FALSE; } else { if (Check565) { if (bmi.bmiColors[0] != 0xf800 || bmi.bmiColors[1] != 0x07e0 || bmi.bmiColors[2] != 0x001f) { OutCtl.OutWarn(" Palette bitfields don't match standard 565 format.\n"); } } else if (Check555) { if (bmi.bmiColors[0] != 0x7c00 || bmi.bmiColors[1] != 0x03e0 || bmi.bmiColors[2] != 0x001f) { OutCtl.OutWarn(" Palette bitfields don't match standard 555 format.\n"); } } } } } } else { OutCtl.OutErr(" Error reading PALETTE.\n"); } } else { OutCtl.OutErr("Error: Unable to obtain valid PALETTE address.\n"); if (hr == S_OK) hr = S_FALSE; } }
if (hr == S_OK) { // We have all the format information we need from the target.
ULONG FirstBit = dim[left].I32*bmi.bmiHeader.biBitCount; LONG xOrigin; LONG Width; HBITMAP hBitmap; LPVOID pDIBits;
if (SurfParser.Get(&SurfValues[SF_so_iUniq], "iUniq", DEBUG_VALUE_INT32) != S_OK) { SurfValues[SF_so_iUniq].I32 = 0; }
// Save original width and adjust for full byte reads if needed
xOrigin = (LONG)(FirstBit & 0x7); Width = (LONG)bmi.bmiHeader.biWidth; bmi.bmiHeader.biWidth += (FirstBit & 0x7); bmi.bmiHeader.biWidth = (bmi.bmiHeader.biWidth + 0x7) & ~0x7;
// Create a Top-Down DIB Section
bmi.bmiHeader.biHeight = -bmi.bmiHeader.biHeight;
hBitmap = CreateDIBSection(NULL, (LPBITMAPINFO)&bmi, DIB_RGB_COLORS, &pDIBits, NULL, 0);
if (hBitmap) { DIBSECTION ds;
if (GetObject(hBitmap, sizeof(ds), &ds) != 0) { HRESULT hr = S_OK; PBYTE pBits = (PBYTE)pDIBits; ULONG64 ScanAddr = pvScan0.I64; ULONG ScanSize; ULONG ScanBytesRead; BOOL GoodRead = FALSE; LONG LastScanWithData = -1;
ScanSize = ds.dsBm.bmWidthBytes;
ScanAddr += FirstBit/8 + dim[top].I32*lDelta.I64;
for (LONG y = 0; y < ds.dsBm.bmHeight; y++) { OutCtl.Output("."); if (y % 70 == 69) OutCtl.Output("%ld%% read\n", 100*y/ds.dsBm.bmHeight);
if (OutCtl.GetInterrupt() == S_OK) { break; }
if ((hr = Data->ReadVirtual(ScanAddr, pBits, ScanSize, &ScanBytesRead)) != S_OK) { ScanBytesRead = 0; hr = S_OK; } else { GoodRead = TRUE; }
if (ScanBytesRead != ScanSize) { if (LastScanWithData+1 == y || ScanBytesRead != 0) { OutCtl.OutErr("ReadVirtual(0x%p) failed read @ 0x%p (scan %ld).\n", ScanAddr, ScanAddr+ScanBytesRead, y); } RtlZeroMemory(pBits+ScanBytesRead,ScanSize-ScanBytesRead);
// If this scan crosses into next page,
// try reading a partial scan from that page.
if (ScanBytesRead == 0 && PageSize != 0) { ULONG64 NextPage; ULONG ReadSize;
for (NextPage = (ScanAddr + PageSize) & ~((ULONG64)PageSize-1); NextPage < ScanAddr + ScanSize; NextPage += PageSize ) { ReadSize = ScanSize - (ULONG)(NextPage - ScanAddr); if (Data->ReadVirtual(NextPage, pBits + ScanSize - ReadSize, ReadSize, &ScanBytesRead) == S_OK) { GoodRead = TRUE; if (ScanBytesRead != 0) { OutCtl.OutErr("Partial scan read of %lu bytes succeeded @ 0x%p (scan %ld).\n", ScanBytesRead, NextPage, y); if (ScanBytesRead == ReadSize) break; } } } } }
if (ScanBytesRead != 0) { if (LastScanWithData+1 != y && ScanBytesRead == ScanSize) { OutCtl.OutErr("Next fully successful ReadVirtual @ 0x%p (scan %ld).\n", ScanAddr, y); } LastScanWithData = y; }
pBits += ScanSize; ScanAddr += lDelta.I64; }
OutCtl.Output("\n");
if (LastScanWithData + 1 != ds.dsBm.bmHeight) { OutCtl.OutErr("Scans %ld to %ld weren't read.\n", LastScanWithData + 1, ds.dsBm.bmHeight - 1); }
SURF_INFO SurfInfo = { {0}, hBitmap, xOrigin, 0, Width, ds.dsBm.bmHeight, ds.dsBm.bmBitsPixel };
_stprintf(SurfInfo.SurfName, "%s @ 0x%I64x (%lu,%lu)-(%lu,%lu) [Uniq=0x%lx]", ((AddressIsSURFOBJ) ? _T("SURFOBJ") : _T("SURFACE")), ((AddressIsSURFOBJ) ? SurfSpec.I64 : SurfAddr), dim[left].I32, dim[top].I32, dim[left].I32 + Width, dim[top].I32 + ds.dsBm.bmHeight, SurfValues[SF_so_iUniq].I32);
if (GoodRead) { OutCtl.OutVerb("%s %s\n", ((pszMetaFile != NULL) ? "Saving" : "Displaying"), SurfInfo.SurfName); }
if (!GoodRead) { OutCtl.OutErr("No image data was read.\n"); DeleteObject(hBitmap); } else if (pszMetaFile != NULL || DisplayToDesktop) { HDC hdcSurface = CreateCompatibleDC(NULL);
if (hdcSurface == NULL || SelectObject(hdcSurface, hBitmap) == NULL) { OutCtl.OutErr("Error: Failed to prepare captured surface for Blt.\n"); OutCtl.OutVerb(" Last error: 0x%lx.\n", GetLastError()); } else { if (pszMetaFile != NULL) { HDC hdcMeta = CreateEnhMetaFile(NULL, pszMetaFile, NULL, SurfInfo.SurfName);
if (hdcMeta == NULL || !BitBlt(hdcMeta, 0, 0, Width, ds.dsBm.bmHeight, hdcSurface, xOrigin, 0, SRCCOPY)) { OutCtl.OutErr("Error: Save to metafile failed.\n"); OutCtl.OutVerb(" Last error: 0x%lx.\n", GetLastError()); }
DeleteEnhMetaFile(CloseEnhMetaFile(hdcMeta)); }
if (DisplayToDesktop) { HDC hdcScreen = GetDC(NULL);
if (hdcScreen == NULL || !Rectangle(hdcScreen, 0, 0, ds.dsBm.bmWidth+2, ds.dsBm.bmHeight+2) || !BitBlt(hdcScreen, 1, 1, Width, ds.dsBm.bmHeight, hdcSurface, xOrigin, 0, SRCCOPY)) { OutCtl.OutErr("Error: Display to screen failed.\n"); OutCtl.OutVerb(" Last error: 0x%lx.\n", GetLastError()); }
ReleaseDC(NULL, hdcScreen); } }
DeleteObject(hBitmap); DeleteDC(hdcSurface); } else if (CreateViewer(Client, &SurfInfo) == 0) { OutCtl.OutErr("CreateViewer failed.\n"); DbgPrint("CreateViewer failed.\n"); DeleteObject(hBitmap); } } else { OutCtl.OutErr("GetDIBits failed.\n"); DeleteObject(hBitmap); } } else { OutCtl.OutErr("CreateDIBSection failed.\n"); OutCtl.OutVerb(" GetLastError: 0x%lx.\n", GetLastError()); } } else { OutCtl.OutErr("Error: Couldn't read required SURFACE fields.\n"); } } }
if (Data != NULL) Data->Release(); if (Symbols != NULL) Symbols->Release(); } }
if (pszMetaFile != NULL) { HeapFree(GetProcessHeap(), 0, pszMetaFile); }
return hr; }
LONG DebuggerExceptionFilter( struct _EXCEPTION_POINTERS *ExceptionInfo, PDEBUG_CLIENT Client, PCSTR Interface, PCSTR Func ) { // Any references to objects will be leaked.
OutputControl OutCtl(Client); CHAR szBuffer[80];
if (Interface != NULL && Func != NULL) { OutCtl.OutErr("%08x Exception in debugger %s.%s.\n", ExceptionInfo->ExceptionRecord->ExceptionCode, Interface, Func ); } else { OutCtl.OutErr("%08x Exception in extension %s.\n", ExceptionInfo->ExceptionRecord->ExceptionCode, Func ); }
StringCbPrintfA(szBuffer, sizeof(szBuffer), " PC: 0x%p ExceptionInformation: 0x%p 0x%p 0x%p\n", ExceptionInfo->ExceptionRecord->ExceptionAddress, ExceptionInfo->ExceptionRecord->ExceptionInformation[0], ExceptionInfo->ExceptionRecord->ExceptionInformation[1], ExceptionInfo->ExceptionRecord->ExceptionInformation[2] ); OutCtl.OutErr("%s", szBuffer);
return EXCEPTION_EXECUTE_HANDLER; }
HRESULT AssembleTempRoutine( PDEBUG_CLIENT Client, Instruction *instructions, PCSTR Arguments ) { static BOOL CodeWritten = FALSE; static PDEBUG_CONTROL2 Control = NULL; static PDEBUG_DATA_SPACES Data = NULL; static PDEBUG_REGISTERS Registers = NULL; static PDEBUG_SYMBOLS Symbols = NULL; static ULONG RegisterCount = 0; static PDEBUG_VALUE SavedRegisters = NULL; static ULONG64 IP = 0; static BYTE OldCode[256] = ""; static ULONG CodeRead = 0; static ULONG64 FinalIP = 0;
HRESULT hr; OutputControl OutCtl(Client);
ULONG CodeRestored;
if (!CodeWritten && RegisterCount == 0) { if ((hr = Client->QueryInterface(__uuidof(IDebugControl2), (void **)&Control)) == S_OK && (hr = Client->QueryInterface(__uuidof(IDebugDataSpaces), (void **)&Data)) == S_OK && (hr = Client->QueryInterface(__uuidof(IDebugRegisters), (void **)&Registers)) == S_OK && (hr = Client->QueryInterface(__uuidof(IDebugSymbols), (void **)&Symbols)) == S_OK) { DEBUG_VALUE Argument; ULONG Rem;
ULONG64 LimitIP; ULONG64 AsmIP; ULONG64 StartIP; ULONG64 EndIP; ULONG CodeEmitted;
if (hr == S_OK && (hr = Registers->GetInstructionOffset(&IP)) == S_OK && (hr = Data->ReadVirtual(IP, OldCode, sizeof(OldCode), &CodeRead)) == S_OK && CodeRead == sizeof(OldCode)) { OutCtl.Output("Code from 0x%p to 0x%p saved.\n", IP, IP+CodeRead);
LimitIP = IP + CodeRead - 0x20; // Enough space for any instruction
StartIP = EndIP = IP;
for (int i = 0; hr == S_OK && instructions[i].Code != NULL; i++) { AsmIP = EndIP;
if (AsmIP > LimitIP) { OutCtl.OutErr("\nError: There may not be enough code saved to assemble @ 0x%p.\n" "\n Aborting to be on the safe side.", AsmIP);
hr = E_OUTOFMEMORY; }
if (hr == S_OK && instructions[i].Flags & I_WRITE_ADDRESS) { hr = Evaluate(Client, Arguments, DEBUG_VALUE_INT32, EVALUATE_DEFAULT_RADIX, &Argument, &Rem, NULL, EVALUATE_COMPACT_EXPR);
if (hr == S_OK) { Arguments += Rem; } else { OutCtl.OutErr("\nMissing address for instruction %#lu '%s'", i, (instructions[i].ByteLen == 0) ? instructions[i].Code : "Emitted Bytes"); } }
if (hr == S_OK) { if (instructions[i].Flags & I_START_IP) { StartIP = AsmIP; }
OutCtl.Output("."); if (instructions[i].ByteLen > 0) { OutCtl.OutVerb(" Emitting %lu bytes @ 0x%p...\n", instructions[i].ByteLen, AsmIP); hr = Data->WriteVirtual(AsmIP, instructions[i].Code, instructions[i].ByteLen, &CodeEmitted); if (hr == S_OK) { if (CodeEmitted == instructions[i].ByteLen) EndIP = AsmIP + CodeEmitted; else hr = E_FAIL; } } else { OutCtl.OutVerb(" Assembling '%s' @ 0x%p...\n", instructions[i].Code, AsmIP); hr = Control->Assemble(AsmIP, instructions[i].Code, &EndIP); } }
if (hr == S_OK) { if (instructions[i].Flags & I_WRITE_ADDRESS) { hr = Data->WriteVirtual(EndIP-sizeof(Argument.I32), &Argument.I32, sizeof(Argument.I32), &CodeEmitted); if (hr == S_OK) { if (CodeEmitted != sizeof(Argument.I32)) { hr = E_FAIL; } else { OutCtl.OutVerb(" Wrote argument 0x%lx at 0x%p.\n", Argument.I32, EndIP - sizeof(Argument.I32)); } }
if (hr != S_OK) { OutCtl.Output("\nCouldn't write argument at 0x%p.", EndIP - sizeof(Argument.I32)); } } } }
OutCtl.Output("\n");
if (hr == S_OK) { FinalIP = AsmIP;
if (EndIP - IP > sizeof(OldCode)) { OutCtl.OutErr("Error: Didn't save enough code!\n" " Code from 0x%p to 0x%p was trashed.\n", IP + sizeof(OldCode), EndIP); hr = E_FAIL; } }
if (hr == S_OK) { // Save registers
if ((hr = Registers->GetNumberRegisters(&RegisterCount)) == S_OK) { SavedRegisters = (PDEBUG_VALUE) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DEBUG_VALUE)*RegisterCount); if (SavedRegisters != NULL) { if ((hr = Registers->GetValues(RegisterCount, NULL, 0, SavedRegisters)) != S_OK) { HeapFree(GetProcessHeap(), 0, SavedRegisters); SavedRegisters = NULL; RegisterCount = 0; } else { OutCtl.OutVerb(" Saved %lu registers.\n", RegisterCount); } } else { RegisterCount = 0; hr = E_OUTOFMEMORY; } }
if (hr != S_OK) { OutCtl.Output("Error saving register values.\n"); } }
if (hr == S_OK) { CHAR szDisasmCmd[64];
OutCtl.Output(" Temporary routine placed from 0x%p to 0x%p.\n", IP, EndIP); OutCtl.Output("Please verify code, especially jumps:\n"); sprintf(szDisasmCmd, "u 0x%I64x 0x%I64x", IP, EndIP); Control->Execute(DEBUG_OUTCTL_THIS_CLIENT, szDisasmCmd, DEBUG_EXECUTE_NOT_LOGGED | DEBUG_EXECUTE_NO_REPEAT); OutCtl.Output("Then set IP to 0x%p and run machine.\n", StartIP);
CodeWritten = TRUE; } else { if (Data->WriteVirtual(IP, OldCode, CodeRead, &CodeRestored) != S_OK || CodeRestored != CodeRead) { OutCtl.OutErr("Error restoring original code.\n"); } } } } } else { HRESULT hrGetIP; ULONG64 ResultIP; HRESULT hrRestore = S_OK;
hr = S_OK;
if ((hrGetIP = Registers->GetInstructionOffset(&ResultIP)) == S_OK) { if (ResultIP != FinalIP) { OutCtl.OutWarn("Result IP = 0x%p differs from expected 0x%p.\n", ResultIP, FinalIP); } } else { OutCtl.OutWarn("Unable to confirm result IP.\n"); ResultIP = (FinalIP == 0) ? -1 : 0; }
if (CodeWritten) { if (ResultIP == FinalIP || (hrRestore = GetYNInput((PDEBUG_CONTROL)Control, "Restore code anyway?")) == S_OK) { if ((hr = Data->WriteVirtual(IP, OldCode, CodeRead, &CodeRestored)) == S_OK && CodeRestored == CodeRead) { CodeWritten = FALSE; IP = 0;
OutCtl.Output("Original code restored.\n");
} else { OutCtl.OutErr("Error restoring original code.\n"); if (hr == S_OK) hr = E_FAIL; } } }
if (hr == S_OK && RegisterCount != 0) { if (ResultIP == FinalIP || (hrRestore = GetYNInput((PDEBUG_CONTROL)Control, "Restore register values anyway?")) == S_OK) { DEBUG_VALUE NewRegValue; ULONG Register;
for (Register = 0; Register < RegisterCount && hr == S_OK; Register++) { __try { hr = Registers->SetValue(Register, &SavedRegisters[Register]); } __except(DebuggerExceptionFilter(GetExceptionInformation(), Client, "IDebugRegisters", "SetValue")) { hr = S_FALSE; }
if (hr != S_OK) { RtlZeroMemory(&NewRegValue, sizeof(NewRegValue)); if (Registers->GetValue(Register, &NewRegValue) == S_OK) { if (!RtlEqualMemory(&SavedRegisters[Register], &NewRegValue, sizeof(NewRegValue))) { OutCtl.OutErr(" Registers %lu's value had changed.\n", Register); } else { OutCtl.Output(" However, registers %lu's value had NOT changed.\n", Register); hr = S_OK; } } else { OutCtl.OutErr(" Unable to check register %lu's current value.\n", Register); }
if (hr != S_OK) { OutCtl.OutErr(" Register %lu's value has not been restored.\n", Register); hr = GetYNInput((PDEBUG_CONTROL)Control, "Continue restoring registers?"); } } }
if (hr == S_OK) { OutCtl.Output("Original register values restored.\n"); } else { OutCtl.OutErr("Error restoring original register values.\n"); } } else { hr = S_FALSE; }
if (hr != S_OK) { hr = GetYNInput((PDEBUG_CONTROL)Control, "Discard saved register values?"); }
if (hr == S_OK) { HeapFree(GetProcessHeap(), 0, SavedRegisters); SavedRegisters = NULL; RegisterCount = 0; } }
if (hr == S_OK) hr = hrRestore; }
if (!CodeWritten && RegisterCount == 0) { if (Symbols != NULL) { Symbols->Release(); Symbols = NULL; } if (Registers != NULL) { Registers->Release(); Registers = NULL; } if (Data != NULL) { Data->Release(); Data = NULL; } if (Control != NULL) { Control->Release(); Control = NULL; } }
return hr; }
Instruction PageInSurfs_x86_Instructions[] = { { 0, sizeof(x86_jmp_here), (PSTR) x86_jmp_here}, // To loop here
{ 0, 0, "int 3" }, { I_START_IP, 0, "push @eax" }, { 0, 0, "push @ecx" }, { 0, 0, "push @edx" }, { 0, 0, "mov @ecx, DWORD PTR [win32k!ghsemShareDevLock]" }, { 0, 0, "call win32k!GreAcquireSemaphore" }, { 0, 0, "mov @ecx, DWORD PTR [win32k!ghsemDriverMgmt]" }, { 0, 0, "call win32k!GreAcquireSemaphore" }, { 0, 0, "mov @ecx, DWORD PTR [win32k!ghsemHmgr]" }, { 0, 0, "call win32k!GreAcquireSemaphore" }, { 0, 0, "xor @ecx, @ecx" }, { 0, sizeof(x86_jmp_plus_0x0e), (PSTR) x86_jmp_plus_0x0e}, // To loop condition
// Loop start
{ 0, 0, "mov @ecx, DWORD PTR [@eax]" }, { 0, 0, "mov @eax, DWORD PTR [@eax+0x30]" }, { 0, 0, "cmp @eax, 0x80000000" }, { 0, sizeof(x86_jb_plus_0x02), (PSTR) x86_jb_plus_0x02}, // To loop condition
{ 0, 0, "mov @eax, DWORD PTR [@eax]" }, // Loop condition
{ 0, 0, "mov @dl, 5" }, { 0, 0, "call win32k!HmgSafeNextObjt" }, { 0, 0, "test @eax, @eax" }, { 0, sizeof(x86_jne_minus_0x18), (PSTR) x86_jne_minus_0x18}, // To loop start
{ 0, 0, "mov @ecx, DWORD PTR [win32k!ghsemHmgr]" }, { 0, 0, "call win32k!GreReleaseSemaphore" }, { 0, 0, "mov @ecx, DWORD PTR [win32k!ghsemDriverMgmt]" }, { 0, 0, "call win32k!GreReleaseSemaphore" }, { 0, 0, "mov @ecx, DWORD PTR [win32k!ghsemShareDevLock]" }, { 0, 0, "call win32k!GreReleaseSemaphore" }, { 0, 0, "pop @edx" }, { 0, 0, "pop @ecx" }, { 0, 0, "pop @eax" }, { 0, 0, "int 3" }, { 0, 0, NULL } };
DECLARE_API( pageinsurfs ) { BEGIN_API( pageinsurfs );
HRESULT hr = S_OK;
OutputControl OutCtl(Client); Instruction *instructions;
switch (TargetMachine) { case IMAGE_FILE_MACHINE_I386: instructions = PageInSurfs_x86_Instructions; break;
default: { OutCtl.OutWarn("This extension is only supported on x86 architectures.\n"); hr = E_NOTIMPL; break; } }
if (hr == S_OK) { hr = AssembleTempRoutine(Client, instructions, args); }
return hr; }
Instruction PageInSurface_x86_Instructions[] = { { 0, sizeof(x86_jmp_here), (PSTR) x86_jmp_here}, // To loop here
{ 0, 0, "int 3" }, {I_START_IP, 0, "push @eax" }, { 0, 0, "push @ecx" }, { 0, 0, "push @edx" }, { 0, 0, "push @esi" }, { 0, 0, "mov @ecx, DWORD PTR [win32k!ghsemShareDevLock]" }, { 0, 0, "call win32k!GreAcquireSemaphore" }, { 0, 0, "mov @ecx, DWORD PTR [win32k!ghsemDriverMgmt]" }, { 0, 0, "call win32k!GreAcquireSemaphore" }, { 0, 0, "mov @ecx, DWORD PTR [win32k!ghsemHmgr]" }, { 0, 0, "call win32k!GreAcquireSemaphore" }, {I_WRITE_ADDRESS, 0, "mov @edx, 0xDeadBeef" }, { 0, 0, "mov @esi, DWORD PTR [@edx+0x30]" }, // pvScan0
{ 0, 0, "cmp @esi, 0x80000000" }, // Check User address/NULL
{ 0, 0, "mov @ecx, DWORD PTR [@edx+0x24]" }, // sizlBitmap.cy
{ 0, sizeof(x86_jb_plus_0x0c), (PSTR) x86_jb_plus_0x0c}, // To end
{ 0, 0, "test @ecx, @ecx" }, // Check scan
{ 0, sizeof(x86_jmp_plus_0x06), (PSTR) x86_jmp_plus_0x06}, // To loop condition
// Loop start
{ 0, 0, "mov @eax, DWORD PTR [@esi]" }, { 0, 0, "add @esi, DWORD PTR [@edx+0x34]" }, // lDelta
{ 0, 0, "dec @ecx" }, // Loop condition
{ 0, sizeof(x86_jnz_minus_0x08), (PSTR) x86_jnz_minus_0x08}, // To loop start
// End
{ 0, 0, "mov @ecx, DWORD PTR [win32k!ghsemHmgr]" }, { 0, 0, "call win32k!GreReleaseSemaphore" }, { 0, 0, "mov @ecx, DWORD PTR [win32k!ghsemDriverMgmt]" }, { 0, 0, "call win32k!GreReleaseSemaphore" }, { 0, 0, "mov @ecx, DWORD PTR [win32k!ghsemShareDevLock]" }, { 0, 0, "call win32k!GreReleaseSemaphore" }, { 0, 0, "pop @esi" }, { 0, 0, "pop @edx" }, { 0, 0, "pop @ecx" }, { 0, 0, "pop @eax" }, { 0, 0, "int 3" }, { 0, 0, NULL } };
DECLARE_API( pageinsurface ) { BEGIN_API( pageinsurface );
HRESULT hr = S_OK;
OutputControl OutCtl(Client); Instruction *instructions;
switch (TargetMachine) { case IMAGE_FILE_MACHINE_I386: instructions = PageInSurface_x86_Instructions; break;
default: { OutCtl.OutWarn("This extension is only supported on x86 architectures.\n"); hr = E_NOTIMPL; break; } }
if (hr == S_OK) { hr = AssembleTempRoutine(Client, instructions, args); }
return hr; }
|