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.
999 lines
29 KiB
999 lines
29 KiB
/*++
|
|
|
|
Copyright (c) 1990, 1998 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dbgdumpx.c
|
|
*WAS* kdextlib.c
|
|
|
|
Abstract:
|
|
|
|
Library routines for dumping data structures given a meta level descrioption
|
|
|
|
Author:
|
|
|
|
Balan Sethu Raman (SethuR) 11-May-1994
|
|
|
|
Notes:
|
|
The implementation tends to avoid memory allocation and deallocation as much as possible.
|
|
Therefore We have choosen an arbitrary length as the default buffer size. A mechanism will
|
|
be provided to modify this buffer length through the debugger extension commands.
|
|
|
|
Revision History:
|
|
|
|
11-Nov-1994 SethuR Created
|
|
19-April-1998 Mikeswa Modify for Exchange Platinum
|
|
22-Sept-1998 Mikeswa moved to IIS
|
|
22-July-1999 Mikeswa and back to platinum
|
|
24-March-2000 Mikeswa and back to IIS
|
|
|
|
--*/
|
|
|
|
#include <windows.h>
|
|
#include <imagehlp.h>
|
|
#include <transdbg.h>
|
|
#include <dbgdumpx.h>
|
|
#include <stdlib.h>
|
|
|
|
char *s_rgszMonth[ 12 ] =
|
|
{
|
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
|
|
};
|
|
|
|
char *s_rgszWeekDays[7] =
|
|
{
|
|
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
|
|
};
|
|
|
|
char *NewLine = "\n";
|
|
#define FIELD_NAME_LENGTH 30
|
|
char FieldIndent[5 + 2*FIELD_NAME_LENGTH] = " ";
|
|
|
|
#define GET_STRUCT_VALUE(Type, pvStruct, Offset) \
|
|
(*(Type *)(((char *)pvStruct) + Offset))
|
|
|
|
#define FIELD_BUFFER_SIZE 100
|
|
|
|
BOOL
|
|
kdextAtoi(
|
|
LPSTR lpArg,
|
|
int *pRet
|
|
);
|
|
|
|
int
|
|
kdextStrlen(
|
|
LPSTR lpsz
|
|
);
|
|
|
|
int
|
|
kdextStrnicmp(
|
|
LPSTR lpsz1,
|
|
LPSTR lpsz2,
|
|
int cLen
|
|
);
|
|
|
|
|
|
PWINDBG_OUTPUT_ROUTINE g_lpOutputRoutine;
|
|
PWINDBG_GET_EXPRESSION g_lpGetExpressionRoutine;
|
|
PWINDBG_GET_SYMBOL g_lpGetSymbolRoutine;
|
|
PWINDBG_READ_PROCESS_MEMORY_ROUTINE g_lpReadMemoryRoutine;
|
|
HANDLE g_hCurrentProcess;
|
|
|
|
#define NL 1
|
|
#define NONL 0
|
|
|
|
#define DEFAULT_UNICODE_DATA_LENGTH 512
|
|
USHORT s_UnicodeStringDataLength = DEFAULT_UNICODE_DATA_LENGTH;
|
|
WCHAR s_UnicodeStringData[DEFAULT_UNICODE_DATA_LENGTH];
|
|
WCHAR *s_pUnicodeStringData = s_UnicodeStringData;
|
|
|
|
#define DEFAULT_ANSI_DATA_LENGTH 512
|
|
USHORT s_AnsiStringDataLength = DEFAULT_ANSI_DATA_LENGTH;
|
|
CHAR s_AnsiStringData[DEFAULT_ANSI_DATA_LENGTH];
|
|
CHAR *s_pAnsiStringData = s_AnsiStringData;
|
|
|
|
/*
|
|
* Fetches the data at the given address
|
|
*/
|
|
BOOLEAN
|
|
GetDataEx( DWORD_PTR dwAddress, PVOID ptr, ULONG size, PULONG pBytesRead)
|
|
{
|
|
BOOL b;
|
|
SIZE_T BytesRead;
|
|
|
|
if (pBytesRead)
|
|
*pBytesRead = 0;
|
|
|
|
b = KdExtReadMemory((LPVOID) dwAddress, ptr, size, &BytesRead );
|
|
|
|
if (!pBytesRead && (BytesRead != size)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!b) {
|
|
/* If we have an out param... try reading less */
|
|
if (!pBytesRead || !size)
|
|
return FALSE;
|
|
|
|
/* maybe our buffer size is too big... try to read 1 byte */
|
|
b = KdExtReadMemory((LPVOID) dwAddress, ptr, 1, &BytesRead );
|
|
if (!b)
|
|
return FALSE;
|
|
|
|
/* Try to find the best size... this is useful for strings */
|
|
while (!b && (--size > 0)) {
|
|
b = KdExtReadMemory((LPVOID) dwAddress, ptr, size, &BytesRead );
|
|
}
|
|
}
|
|
|
|
if (pBytesRead)
|
|
*pBytesRead = (ULONG)BytesRead;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
GetData( DWORD_PTR dwAddress, PVOID ptr, ULONG size)
|
|
{
|
|
return GetDataEx(dwAddress, ptr, size, NULL);
|
|
}
|
|
|
|
/*
|
|
* Displays a byte in hexadecimal
|
|
*/
|
|
VOID
|
|
PrintHexChar( UCHAR c )
|
|
{
|
|
PRINTF( "%c%c", "0123456789abcdef"[ (c>>4)&7 ], "0123456789abcdef"[ c&7 ] );
|
|
}
|
|
|
|
/*
|
|
* Displays a buffer of data in hexadecimal
|
|
*/
|
|
VOID
|
|
PrintHexBuf( PUCHAR buf, ULONG cbuf )
|
|
{
|
|
while( cbuf-- ) {
|
|
PrintHexChar( *buf++ );
|
|
PRINTF( " " );
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Displays a unicode string
|
|
*/
|
|
BOOL
|
|
PrintStringW(LPSTR msg, PUNICODE_STRING puStr, BOOL nl )
|
|
{
|
|
UNICODE_STRING UnicodeString;
|
|
BOOLEAN b;
|
|
|
|
if( msg )
|
|
PRINTF( msg );
|
|
|
|
if( puStr->Length == 0 ) {
|
|
if( nl )
|
|
PRINTF( "\n" );
|
|
return TRUE;
|
|
}
|
|
|
|
UnicodeString.Buffer = s_pUnicodeStringData;
|
|
UnicodeString.MaximumLength = s_UnicodeStringDataLength;
|
|
UnicodeString.Length = (puStr->Length > s_UnicodeStringDataLength)
|
|
? s_UnicodeStringDataLength
|
|
: puStr->Length;
|
|
|
|
b = GetData((DWORD_PTR) puStr->Buffer, UnicodeString.Buffer, (ULONG) UnicodeString.Length);
|
|
|
|
if (b) {
|
|
PRINTF("%wZ%s", &UnicodeString, nl ? "\n" : "" );
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
/*
|
|
* Displays a ANSI string
|
|
*/
|
|
BOOL
|
|
PrintStringA(LPSTR msg, PANSI_STRING pStr, BOOL nl )
|
|
{
|
|
ANSI_STRING AnsiString;
|
|
BOOL b;
|
|
|
|
if( msg )
|
|
PRINTF( msg );
|
|
|
|
if( pStr->Length == 0 ) {
|
|
if( nl )
|
|
PRINTF( "\n" );
|
|
return TRUE;
|
|
}
|
|
|
|
AnsiString.Buffer = s_pAnsiStringData;
|
|
AnsiString.MaximumLength = s_AnsiStringDataLength;
|
|
AnsiString.Length = (pStr->Length > (s_AnsiStringDataLength - 1))
|
|
? (s_AnsiStringDataLength - 1)
|
|
: pStr->Length;
|
|
|
|
b = KdExtReadMemory(
|
|
(LPVOID) pStr->Buffer,
|
|
AnsiString.Buffer,
|
|
AnsiString.Length,
|
|
NULL);
|
|
|
|
if (b) {
|
|
AnsiString.Buffer[ AnsiString.Length ] = '\0';
|
|
PRINTF("%s%s", AnsiString.Buffer, nl ? "\n" : "" );
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
/*
|
|
* Displays a GUID
|
|
*/
|
|
|
|
BOOL
|
|
PrintGuid(
|
|
GUID *pguid)
|
|
{
|
|
ULONG i;
|
|
|
|
PRINTF( "%08x-%04x-%04x", pguid->Data1, pguid->Data2, pguid->Data3 );
|
|
for (i = 0; i < 8; i++) {
|
|
PRINTF("%02x",pguid->Data4[i]);
|
|
}
|
|
return( TRUE );
|
|
}
|
|
|
|
/*
|
|
* Displays a LARGE_INTEGER
|
|
*/
|
|
|
|
BOOL
|
|
PrintLargeInt(
|
|
LARGE_INTEGER *bigint)
|
|
{
|
|
PRINTF( "%08x:%08x", bigint->HighPart, bigint->LowPart);
|
|
return( TRUE );
|
|
}
|
|
|
|
/*
|
|
* Displays a DWORD size class signature
|
|
*/
|
|
BOOL
|
|
PrintClassSignature(
|
|
CHAR * pch)
|
|
{
|
|
PRINTF("0x%08X (%c%c%c%c)", *((DWORD *)pch), *(pch), *(pch+1), *(pch+2), *(pch+3));
|
|
return( TRUE );
|
|
}
|
|
|
|
/*
|
|
* Displays a standard LIST_ENTRY structure
|
|
*/
|
|
BOOL
|
|
PrintListEntry(DWORD_PTR dwAddress, CHAR * pch)
|
|
{
|
|
PLIST_ENTRY pli = (PLIST_ENTRY) pch;
|
|
LIST_ENTRY liCurrent;
|
|
PLIST_ENTRY pliCurrent = pli->Flink;
|
|
DWORD cEntries= 0;
|
|
BOOL fListOK = TRUE;
|
|
|
|
//figure out how many entries there are
|
|
while (pliCurrent != (PLIST_ENTRY) dwAddress)
|
|
{
|
|
cEntries++;
|
|
if ((cEntries > 1000) ||
|
|
!GetData((DWORD_PTR) pliCurrent, &liCurrent, sizeof(LIST_ENTRY)))
|
|
{
|
|
fListOK = FALSE;
|
|
break;
|
|
}
|
|
pliCurrent = liCurrent.Flink;
|
|
}
|
|
|
|
PRINTF("0x%p ", dwAddress);
|
|
if (fListOK)
|
|
PRINTF("(%d entries)", cEntries);
|
|
else
|
|
PRINTF("(Unable to determine how many entries)");
|
|
|
|
PRINTF(NewLine);
|
|
PRINTF("%s FLINK: 0x%p%s", FieldIndent, pli->Flink, NewLine);
|
|
PRINTF("%s BLINK: 0x%p", FieldIndent, pli->Blink);
|
|
return( TRUE );
|
|
}
|
|
|
|
|
|
/*
|
|
* Displays a human readable FILETIME
|
|
*/
|
|
BOOL PrintFileTime(FILETIME *pft, BOOL fLocalize)
|
|
{
|
|
SYSTEMTIME st;
|
|
FILETIME ftDisplay = *pft;
|
|
BOOL fInit = TRUE;
|
|
|
|
ZeroMemory(&st, sizeof(SYSTEMTIME));
|
|
|
|
//Translate to local timezone if requested
|
|
if (fLocalize)
|
|
FileTimeToLocalFileTime(pft, &ftDisplay);
|
|
|
|
//Only convert if non-zero
|
|
if (!pft->dwLowDateTime && !pft->dwHighDateTime)
|
|
{
|
|
fInit = FALSE;
|
|
}
|
|
else if (!FileTimeToSystemTime(&ftDisplay, &st))
|
|
{
|
|
PRINTF("Unable to convert %08X %08X to a SYSTEMTIME - error %d",
|
|
ftDisplay.dwLowDateTime,ftDisplay.dwHighDateTime, GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
if (fInit)
|
|
{
|
|
PRINTF("%s, %d %s %04d %02d:%02d:%02d %s",
|
|
s_rgszWeekDays[st.wDayOfWeek],
|
|
st.wDay, s_rgszMonth[ st.wMonth - 1 ],
|
|
st.wYear, st.wHour, st.wMinute, st.wSecond,
|
|
fLocalize ? "(localized)" : "");
|
|
}
|
|
else
|
|
{
|
|
PRINTF("FILETIME is zero");
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Displays a the values of a bitmask
|
|
*/
|
|
BOOL
|
|
PrintBitMaskValues(
|
|
DWORD BitMaskValue,
|
|
FIELD_DESCRIPTOR *pFieldDescriptor)
|
|
{
|
|
BOOL fFirstFlag;
|
|
BIT_MASK_DESCRIPTOR *pBitMaskDescr;
|
|
|
|
pBitMaskDescr = pFieldDescriptor->AuxillaryInfo.pBitMaskDescriptor;
|
|
fFirstFlag = TRUE;
|
|
if (pBitMaskDescr != NULL)
|
|
{
|
|
while (pBitMaskDescr->BitmaskName != NULL)
|
|
{
|
|
if (((BitMaskValue & pBitMaskDescr->BitmaskValue) ==
|
|
pBitMaskDescr->BitmaskValue) && //need to check all bits of bit mask
|
|
//If descriptor value is 0.. it will always match any bit mask
|
|
//it should only when the actual BitMaskValue is 0 as well
|
|
(pBitMaskDescr->BitmaskValue || !BitMaskValue))
|
|
{
|
|
if (fFirstFlag)
|
|
{
|
|
fFirstFlag = FALSE;
|
|
PRINTF("%s ( %-s", FieldIndent, pBitMaskDescr->BitmaskName);
|
|
}
|
|
else
|
|
{
|
|
PRINTF( " |\n" );
|
|
PRINTF("%s %-s", FieldIndent, pBitMaskDescr->BitmaskName);
|
|
}
|
|
}
|
|
pBitMaskDescr++;
|
|
}
|
|
PRINTF(" )");
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* Displays all the fields of a given struct. This is the driver routine that is called
|
|
* with the appropriate descriptor array to display all the fields in a given struct.
|
|
*/
|
|
|
|
VOID
|
|
PrintStructFields( DWORD_PTR dwAddress, BYTE *ptr, FIELD_DESCRIPTOR *pFieldDescriptors, DWORD cIndentLevel)
|
|
{
|
|
DWORD i,j;
|
|
BYTE pbBuffer[FIELD_BUFFER_SIZE];
|
|
DWORD BitMaskValue = 0;
|
|
DWORD cbGetData = 0;
|
|
CHAR szTmpName[FIELD_NAME_LENGTH];
|
|
|
|
//Make sure FieldIndent is correct
|
|
for (j = 0; j < cIndentLevel%(FIELD_NAME_LENGTH/2); j++)
|
|
lstrcat(FieldIndent, " ");
|
|
|
|
// Display the fields in the struct.
|
|
for( i=0; pFieldDescriptors->Name; i++, pFieldDescriptors++ ) {
|
|
|
|
// Indentation to begin the struct display.
|
|
PRINTF( " " );
|
|
|
|
for (j = 0; j < cIndentLevel%(FIELD_NAME_LENGTH/2); j++)
|
|
PRINTF(" "); //print 2 spaces for every indent level
|
|
|
|
|
|
if( strlen( pFieldDescriptors->Name ) > FIELD_NAME_LENGTH ) {
|
|
memcpy(szTmpName, pFieldDescriptors->Name, FIELD_NAME_LENGTH-3);
|
|
szTmpName[FIELD_NAME_LENGTH-3] = '\0';
|
|
PRINTF( "%s... ", szTmpName);
|
|
} else {
|
|
PRINTF( "%-30s ", pFieldDescriptors->Name );
|
|
}
|
|
|
|
switch( pFieldDescriptors->FieldType ) {
|
|
case FieldTypeByte:
|
|
case FieldTypeChar:
|
|
PRINTF( "%-16d%s",
|
|
*(BYTE *)(((char *)ptr) + pFieldDescriptors->Offset ),
|
|
NewLine );
|
|
break;
|
|
case FieldTypeBoolean:
|
|
PRINTF( "%-16s%s",
|
|
*(BOOLEAN *)(((char *)ptr) + pFieldDescriptors->Offset ) ? "TRUE" : "FALSE",
|
|
NewLine);
|
|
break;
|
|
case FieldTypeBool:
|
|
PRINTF( "%-16s%s",
|
|
*(BOOL *)(((char *)ptr) + pFieldDescriptors->Offset ) ? "TRUE" : "FALSE",
|
|
NewLine);
|
|
break;
|
|
case FieldTypePointer:
|
|
PRINTF( "@0x%p%s",
|
|
*(DWORD_PTR *)(((char *)ptr) + pFieldDescriptors->Offset ),
|
|
NewLine );
|
|
break;
|
|
case FieldTypeLong:
|
|
PRINTF( "%-16d%s",
|
|
*(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ),
|
|
NewLine );
|
|
break;
|
|
case FieldTypeULong:
|
|
case FieldTypeDword:
|
|
PRINTF( "%-16u%s",
|
|
*(ULONG *)(((char *)ptr) + pFieldDescriptors->Offset ),
|
|
NewLine );
|
|
break;
|
|
case FieldTypeShort:
|
|
PRINTF( "%-16X%s",
|
|
*(SHORT *)(((char *)ptr) + pFieldDescriptors->Offset ),
|
|
NewLine );
|
|
break;
|
|
case FieldTypeUShort:
|
|
PRINTF( "%-16X%s",
|
|
*(USHORT *)(((char *)ptr) + pFieldDescriptors->Offset ),
|
|
NewLine );
|
|
break;
|
|
case FieldTypeGuid:
|
|
PrintGuid( (GUID *)(((char *)ptr) + pFieldDescriptors->Offset) );
|
|
PRINTF( NewLine );
|
|
break;
|
|
case FieldTypePStr: //pointer to a string
|
|
if (GetDataEx(GET_STRUCT_VALUE(DWORD_PTR, ptr,
|
|
pFieldDescriptors->Offset), pbBuffer, FIELD_BUFFER_SIZE, &cbGetData))
|
|
{
|
|
//make sure the string is terminated
|
|
pbBuffer[FIELD_BUFFER_SIZE - 1] = '\0';
|
|
PRINTF( "%s", (LPSTR) pbBuffer );
|
|
}
|
|
else if (!GET_STRUCT_VALUE(DWORD_PTR, ptr, pFieldDescriptors->Offset))
|
|
{
|
|
PRINTF( "<Null String>");
|
|
}
|
|
else
|
|
{
|
|
PRINTF("ERROR: Unable to read string a 0x%p",
|
|
GET_STRUCT_VALUE(DWORD_PTR, ptr, pFieldDescriptors->Offset));
|
|
}
|
|
PRINTF( NewLine );
|
|
break;
|
|
case FieldTypePWStr:
|
|
if (GetDataEx(GET_STRUCT_VALUE(DWORD_PTR, ptr, pFieldDescriptors->Offset),
|
|
pbBuffer, FIELD_BUFFER_SIZE, &cbGetData))
|
|
{
|
|
//make sure the string is terminated
|
|
pbBuffer[FIELD_BUFFER_SIZE - 1] = '\0';
|
|
pbBuffer[FIELD_BUFFER_SIZE - 2] = '\0';
|
|
PRINTF( "%ws", (LPWSTR) pbBuffer );
|
|
}
|
|
else
|
|
{
|
|
PRINTF("ERROR: Unable to read string a 0x%p",
|
|
GET_STRUCT_VALUE(DWORD_PTR, ptr, pFieldDescriptors->Offset));
|
|
}
|
|
PRINTF( NewLine );
|
|
break;
|
|
case FieldTypeStrBuffer: //member is a character array
|
|
PRINTF( "%.100s%s", (CHAR *)(((char *)ptr) + pFieldDescriptors->Offset), NewLine);
|
|
break;
|
|
case FieldTypeWStrBuffer:
|
|
PRINTF( "%.100ws%s", (WCHAR *)(((char *)ptr) + pFieldDescriptors->Offset), NewLine);
|
|
break;
|
|
case FieldTypeUnicodeString:
|
|
PrintStringW( NULL, (UNICODE_STRING *)(((char *)ptr) + pFieldDescriptors->Offset ), NONL );
|
|
PRINTF( NewLine );
|
|
break;
|
|
case FieldTypeAnsiString:
|
|
PrintStringA( NULL, (ANSI_STRING *)(((char *)ptr) + pFieldDescriptors->Offset ), NONL );
|
|
PRINTF( NewLine );
|
|
break;
|
|
case FieldTypeSymbol:
|
|
{
|
|
UCHAR SymbolName[ 200 ];
|
|
ULONG_PTR Displacement;
|
|
PVOID sym = (PVOID)(*(ULONG_PTR *)(((char *)ptr) + pFieldDescriptors->Offset ));
|
|
|
|
g_lpGetSymbolRoutine( sym, SymbolName, &Displacement );
|
|
PRINTF( "%-16s%s",
|
|
SymbolName,
|
|
NewLine );
|
|
}
|
|
break;
|
|
case FieldTypeEnum:
|
|
{
|
|
ULONG EnumValue;
|
|
ENUM_VALUE_DESCRIPTOR *pEnumValueDescr;
|
|
// Get the associated numerical value.
|
|
|
|
EnumValue = *((ULONG *)((BYTE *)ptr + pFieldDescriptors->Offset));
|
|
|
|
if ((pEnumValueDescr = pFieldDescriptors->AuxillaryInfo.pEnumValueDescriptor)
|
|
!= NULL) {
|
|
//
|
|
// An auxilary textual description of the value is
|
|
// available. Display it instead of the numerical value.
|
|
//
|
|
|
|
LPSTR pEnumName = NULL;
|
|
|
|
while (pEnumValueDescr->EnumName != NULL) {
|
|
if (EnumValue == pEnumValueDescr->EnumValue) {
|
|
pEnumName = pEnumValueDescr->EnumName;
|
|
break;
|
|
}
|
|
pEnumValueDescr++;
|
|
}
|
|
|
|
if (pEnumName != NULL) {
|
|
PRINTF( "%-16s ", pEnumName );
|
|
} else {
|
|
PRINTF( "%-4d (%-10s) ", EnumValue,"Unknown!");
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// No auxilary information is associated with the ehumerated type
|
|
// print the numerical value.
|
|
//
|
|
PRINTF( "%-16d",EnumValue);
|
|
}
|
|
PRINTF( NewLine );
|
|
}
|
|
break;
|
|
|
|
case FieldTypeByteBitMask:
|
|
BitMaskValue = GET_STRUCT_VALUE(BYTE, ptr, pFieldDescriptors->Offset);
|
|
PRINTF("0x%02X ", (BYTE) BitMaskValue);
|
|
PRINTF( NewLine );
|
|
if (PrintBitMaskValues(BitMaskValue, pFieldDescriptors))
|
|
PRINTF( NewLine );
|
|
break;
|
|
case FieldTypeWordBitMask:
|
|
BitMaskValue = GET_STRUCT_VALUE(WORD, ptr, pFieldDescriptors->Offset);
|
|
PRINTF("0x%04X ", (WORD) BitMaskValue);
|
|
PRINTF( NewLine );
|
|
if (PrintBitMaskValues(BitMaskValue, pFieldDescriptors))
|
|
PRINTF( NewLine );
|
|
break;
|
|
case FieldTypeDWordBitMask:
|
|
BitMaskValue = GET_STRUCT_VALUE(DWORD, ptr, pFieldDescriptors->Offset);
|
|
PRINTF("0x%08X ", (DWORD) BitMaskValue);
|
|
PRINTF( NewLine );
|
|
if (PrintBitMaskValues(BitMaskValue, pFieldDescriptors))
|
|
PRINTF( NewLine );
|
|
break;
|
|
case FieldTypeStruct:
|
|
PRINTF( "@0x%p%s",
|
|
(dwAddress + pFieldDescriptors->Offset ),
|
|
NewLine );
|
|
break;
|
|
case FieldTypeLargeInteger:
|
|
PrintLargeInt( (LARGE_INTEGER *)(((char *)ptr) + pFieldDescriptors->Offset) );
|
|
PRINTF( NewLine );
|
|
break;
|
|
case FieldTypeClassSignature:
|
|
PrintClassSignature(((char *)ptr) + pFieldDescriptors->Offset);
|
|
PRINTF( NewLine );
|
|
break;
|
|
case FieldTypeListEntry:
|
|
PrintListEntry(dwAddress + pFieldDescriptors->Offset,
|
|
((char *)ptr) + pFieldDescriptors->Offset);
|
|
PRINTF( NewLine );
|
|
break;
|
|
case FieldTypeLocalizedFiletime:
|
|
PrintFileTime((FILETIME *) (((char *)ptr) + pFieldDescriptors->Offset), TRUE);
|
|
PRINTF( NewLine );
|
|
break;
|
|
case FieldTypeFiletime:
|
|
PrintFileTime((FILETIME *) (((char *)ptr) + pFieldDescriptors->Offset), FALSE);
|
|
PRINTF( NewLine );
|
|
break;
|
|
case FieldTypeEmbeddedStruct:
|
|
PRINTF( "Dumping %s@0x%p%s",
|
|
((STRUCT_DESCRIPTOR *) (pFieldDescriptors->AuxillaryInfo.pStructDescriptor))->StructName,
|
|
dwAddress+ pFieldDescriptors->Offset, NewLine );
|
|
PrintStructFields(dwAddress+pFieldDescriptors->Offset,
|
|
((char *)ptr) + pFieldDescriptors->Offset,
|
|
((STRUCT_DESCRIPTOR *) (pFieldDescriptors->AuxillaryInfo.pStructDescriptor))->FieldDescriptors,
|
|
cIndentLevel+1);
|
|
break;
|
|
default:
|
|
PRINTF( "Unrecognized field type %d for %s\n", pFieldDescriptors->FieldType, pFieldDescriptors->Name );
|
|
break;
|
|
}
|
|
}
|
|
|
|
//Make sure FieldIndent is correct when we leave
|
|
FieldIndent[lstrlen(FieldIndent)-(cIndentLevel%(FIELD_NAME_LENGTH/2))] = '\0';
|
|
|
|
}
|
|
|
|
LPSTR LibCommands[] = {
|
|
"help -- This command ",
|
|
"dump <Struct Type Name>@<address expr> ",
|
|
0
|
|
};
|
|
|
|
PT_DEBUG_EXTENSION(_help)
|
|
{
|
|
int i;
|
|
|
|
SETCALLBACKS();
|
|
|
|
PRINTF("\n");
|
|
|
|
for( i=0; ExtensionNames[i]; i++ )
|
|
PRINTF( "%s\n", ExtensionNames[i] );
|
|
|
|
for( i=0; LibCommands[i]; i++ )
|
|
PRINTF( " %s\n", LibCommands[i] );
|
|
|
|
for( i=0; Extensions[i]; i++) {
|
|
PRINTF( " %s\n", Extensions[i] );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#define NAME_DELIMITER '@'
|
|
#define INVALID_INDEX 0xffffffff
|
|
#define MIN(x,y) ((x) < (y) ? (x) : (y))
|
|
|
|
ULONG SearchStructs(LPSTR lpArgument)
|
|
{
|
|
ULONG i = 0;
|
|
STRUCT_DESCRIPTOR *pStructs = Structs;
|
|
ULONG NameIndex = INVALID_INDEX;
|
|
int ArgumentLength = kdextStrlen(lpArgument);
|
|
BOOLEAN fAmbiguous = FALSE;
|
|
|
|
|
|
while ((pStructs->StructName != 0)) {
|
|
int StructLength;
|
|
StructLength = kdextStrlen(pStructs->StructName);
|
|
if (StructLength >= ArgumentLength) {
|
|
int Result = kdextStrnicmp(
|
|
lpArgument,
|
|
pStructs->StructName,
|
|
ArgumentLength);
|
|
|
|
if (Result == 0) {
|
|
if (StructLength == ArgumentLength) {
|
|
// Exact match. They must mean this struct!
|
|
fAmbiguous = FALSE;
|
|
NameIndex = i;
|
|
break;
|
|
} else if (NameIndex != INVALID_INDEX) {
|
|
// We have encountered duplicate matches. Print out the
|
|
// matching strings and let the user disambiguate.
|
|
fAmbiguous = TRUE;
|
|
break;
|
|
} else {
|
|
NameIndex = i;
|
|
}
|
|
}
|
|
}
|
|
pStructs++;i++;
|
|
}
|
|
|
|
if (fAmbiguous) {
|
|
PRINTF("Ambigous Name Specification -- The following structs match\n");
|
|
PRINTF("%s\n",Structs[NameIndex].StructName);
|
|
PRINTF("%s\n",Structs[i].StructName);
|
|
while (pStructs->StructName != 0) {
|
|
if (kdextStrnicmp(lpArgument,
|
|
pStructs->StructName,
|
|
MIN(kdextStrlen(pStructs->StructName),ArgumentLength)) == 0) {
|
|
PRINTF("%s\n",pStructs->StructName);
|
|
}
|
|
pStructs++;
|
|
}
|
|
PRINTF("Dumping Information for %s\n",Structs[NameIndex].StructName);
|
|
}
|
|
|
|
return(NameIndex);
|
|
}
|
|
|
|
VOID DisplayStructs()
|
|
{
|
|
STRUCT_DESCRIPTOR *pStructs = Structs;
|
|
|
|
PRINTF("The following structs are handled .... \n");
|
|
while (pStructs->StructName != 0) {
|
|
PRINTF("\t%s\n",pStructs->StructName);
|
|
pStructs++;
|
|
}
|
|
}
|
|
|
|
PT_DEBUG_EXTENSION(_dump)
|
|
{
|
|
DWORD_PTR dwAddress;
|
|
BYTE *pDataBuffer = NULL;
|
|
|
|
SETCALLBACKS();
|
|
|
|
if( szArg && *szArg ) {
|
|
// Parse the argument string to determine the structure to be displayed.
|
|
// Scan for the NAME_DELIMITER ( '@' ).
|
|
|
|
LPSTR lpName = (LPSTR) szArg;
|
|
LPSTR lpArgs;
|
|
ULONG Index;
|
|
|
|
for (lpArgs = (LPSTR) szArg;
|
|
*lpArgs != NAME_DELIMITER && *lpArgs != 0; lpArgs++) {
|
|
;
|
|
}
|
|
|
|
if (*lpArgs == NAME_DELIMITER) {
|
|
//
|
|
// The specified command is of the form
|
|
// dump <name>@<address expr.>
|
|
//
|
|
// Locate the matching struct for the given name. In the case
|
|
// of ambiguity we seek user intervention for disambiguation.
|
|
//
|
|
// We do an inplace modification of the argument string to
|
|
// facilitate matching.
|
|
//
|
|
*lpArgs = '\0';
|
|
|
|
Index = SearchStructs(lpName);
|
|
|
|
//
|
|
// Let us restore the original value back.
|
|
//
|
|
|
|
*lpArgs = NAME_DELIMITER;
|
|
|
|
if (INVALID_INDEX != Index) {
|
|
|
|
pDataBuffer = (BYTE *) LocalAlloc(0, Structs[Index].StructSize);
|
|
if (!pDataBuffer)
|
|
return;
|
|
|
|
//Eat up any extra @'s
|
|
do {lpArgs++;} while ('@' == *lpArgs);
|
|
|
|
dwAddress = (g_lpGetExpressionRoutine)( lpArgs );
|
|
if (pDataBuffer &&
|
|
GetData(dwAddress,pDataBuffer,Structs[Index].StructSize)) {
|
|
|
|
PRINTF(
|
|
"++++++++++++++++ %s@0x%p ++++++++++++++++\n",
|
|
Structs[Index].StructName,
|
|
dwAddress);
|
|
PrintStructFields(
|
|
dwAddress,
|
|
pDataBuffer,
|
|
Structs[Index].FieldDescriptors, 0);
|
|
PRINTF(
|
|
"++++++++++++++++ size is %.10d bytes +++++++++++++++\n",
|
|
Structs[Index].StructSize);
|
|
PRINTF(
|
|
"---------------- %s@0x%p ----------------\n",
|
|
Structs[Index].StructName,
|
|
dwAddress);
|
|
} else {
|
|
PRINTF("Error reading Memory @ %lx\n",dwAddress);
|
|
}
|
|
} else {
|
|
// No matching struct was found. Display the list of
|
|
// structs currently handled.
|
|
|
|
DisplayStructs();
|
|
}
|
|
} else {
|
|
//
|
|
// The command is of the form
|
|
// dump <name>
|
|
//
|
|
// Currently we do not handle this. In future we will map it to
|
|
// the name of a global variable and display it if required.
|
|
//
|
|
|
|
DisplayStructs();
|
|
}
|
|
} else {
|
|
//
|
|
// display the list of structs currently handled.
|
|
//
|
|
|
|
DisplayStructs();
|
|
}
|
|
|
|
if (pDataBuffer)
|
|
LocalFree(pDataBuffer);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
PT_DEBUG_EXTENSION(_dumpoffsets)
|
|
{
|
|
if( szArg && *szArg ) {
|
|
|
|
LPSTR lpName = (LPSTR) szArg;
|
|
LPSTR lpArgs = NULL;
|
|
CHAR chSave = '\0';
|
|
FIELD_DESCRIPTOR *pFieldDescriptors = NULL;
|
|
ULONG Index;
|
|
|
|
for (lpArgs = (LPSTR) szArg;
|
|
!isspace((UCHAR)*lpArgs) && *lpArgs != 0; lpArgs++) {
|
|
;
|
|
}
|
|
|
|
if (TRUE) {
|
|
chSave = *lpArgs;
|
|
*lpArgs = '\0';
|
|
|
|
Index = SearchStructs(lpName);
|
|
|
|
//
|
|
// Let us restore the original value back.
|
|
//
|
|
|
|
*lpArgs = chSave;
|
|
|
|
if (INVALID_INDEX != Index) {
|
|
|
|
PRINTF(
|
|
"++++++++++++++++ %s ++++++++++++++++\n",
|
|
Structs[Index].StructName);
|
|
for (pFieldDescriptors = Structs[Index].FieldDescriptors;
|
|
pFieldDescriptors && pFieldDescriptors->Name;
|
|
pFieldDescriptors++)
|
|
{
|
|
PRINTF("\t0x%08X\t%s\n",
|
|
pFieldDescriptors->Offset, pFieldDescriptors->Name);
|
|
}
|
|
PRINTF(
|
|
"++++++++++++++++ size is %.10d bytes +++++++++++++++\n",
|
|
Structs[Index].StructSize);
|
|
PRINTF(
|
|
"---------------- %s ----------------\n",
|
|
Structs[Index].StructName);
|
|
} else {
|
|
// No matching struct was found. Display the list of
|
|
// structs currently handled.
|
|
|
|
DisplayStructs();
|
|
}
|
|
} else {
|
|
//
|
|
// The command is of the form
|
|
// dump <name>
|
|
//
|
|
// Currently we do not handle this. In future we will map it to
|
|
// the name of a global variable and display it if required.
|
|
//
|
|
|
|
DisplayStructs();
|
|
}
|
|
} else {
|
|
//
|
|
// display the list of structs currently handled.
|
|
//
|
|
|
|
DisplayStructs();
|
|
}
|
|
|
|
return;
|
|
}
|
|
/*
|
|
* KD Extensions should not link with the C-Runtime library routines. So,
|
|
* we implement a few of the needed ones here.
|
|
*/
|
|
|
|
BOOL
|
|
kdextAtoi(
|
|
LPSTR lpArg,
|
|
int *pRet
|
|
)
|
|
{
|
|
int n, cbArg, val = 0;
|
|
BOOL fNegative = FALSE;
|
|
|
|
cbArg = kdextStrlen( lpArg );
|
|
|
|
if (cbArg > 0) {
|
|
for (n = 0; lpArg[n] == ' '; n++) {
|
|
;
|
|
}
|
|
if (lpArg[n] == '-') {
|
|
n++;
|
|
fNegative = TRUE;
|
|
}
|
|
for (; lpArg[n] >= '0' && lpArg[n] <= '9'; n++) {
|
|
val *= 10;
|
|
val += (int) (lpArg[n] - '0');
|
|
}
|
|
if (lpArg[n] == 0) {
|
|
*pRet = (fNegative ? -val : val);
|
|
return( TRUE );
|
|
} else {
|
|
return( FALSE );
|
|
}
|
|
} else {
|
|
return( FALSE );
|
|
}
|
|
|
|
}
|
|
|
|
int
|
|
kdextStrlen(
|
|
LPSTR lpsz
|
|
)
|
|
{
|
|
int c;
|
|
|
|
if (lpsz == NULL) {
|
|
c = 0;
|
|
} else {
|
|
for (c = 0; lpsz[c] != 0; c++) {
|
|
;
|
|
}
|
|
}
|
|
|
|
return( c );
|
|
}
|
|
|
|
|
|
#define UPCASE_CHAR(c) \
|
|
( (((c) >= 'a') && ((c) <= 'z')) ? ((c) - 'a' + 'A') : (c) )
|
|
|
|
int
|
|
kdextStrnicmp(
|
|
LPSTR lpsz1,
|
|
LPSTR lpsz2,
|
|
int cLen
|
|
)
|
|
{
|
|
int nDif, i;
|
|
|
|
for (i = nDif = 0; nDif == 0 && i < cLen; i++) {
|
|
nDif = UPCASE_CHAR(lpsz1[i]) - UPCASE_CHAR(lpsz2[i]);
|
|
}
|
|
|
|
return( nDif );
|
|
}
|