Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2269 lines
90 KiB

/*++
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;
}