Windows-Server-2003/sdktools/debuggers/ntsd64/memcmd.cpp

2519 lines
58 KiB
C++

//----------------------------------------------------------------------------
//
// Functions dealing with memory access, such as reading, writing,
// dumping and entering.
//
// Copyright (C) Microsoft Corporation, 1997-2002.
//
//----------------------------------------------------------------------------
#include "ntsdp.hpp"
TypedData g_LastDump;
ADDR g_DumpDefault; // default dump address
#define MAX_VFN_TABLE_OFFSETS 16
ULONG g_ObjVfnTableOffset[MAX_VFN_TABLE_OFFSETS];
ULONG64 g_ObjVfnTableAddr[MAX_VFN_TABLE_OFFSETS];
ULONG g_NumObjVfnTableOffsets;
BOOL CALLBACK
LocalSymbolEnumerator(PSYMBOL_INFO SymInfo,
ULONG Size,
PVOID Context)
{
ULONG64 Value = g_Machine->CvRegToMachine((CV_HREG_e)SymInfo->Register);
ULONG64 Address = SymInfo->Address;
TranslateAddress(SymInfo->ModBase,
SymInfo->Flags, (ULONG)Value, &Address, &Value);
VerbOut("%s ", FormatAddr64(Address));
dprintf("%15s = ", SymInfo->Name);
if (SymInfo->Flags & SYMFLAG_REGISTER)
{
dprintf("%I64x\n", Value);
}
else
{
if (!DumpSingleValue(SymInfo))
{
dprintf("??");
}
dprintf("\n");
}
if (CheckUserInterrupt())
{
return FALSE;
}
return TRUE;
}
void
ParseDumpCommand(void)
{
CHAR Ch;
ULONG64 Count;
ULONG Size;
ULONG Offset;
BOOL DumpSymbols;
static CHAR s_DumpPrimary = 'b';
static CHAR s_DumpSecondary = ' ';
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
error(BADTHREAD);
}
Ch = (CHAR)tolower(*g_CurCmd);
if (Ch == 'a' || Ch == 'b' || Ch == 'c' || Ch == 'd' ||
Ch == 'f' || Ch == 'g' || Ch == 'l' || Ch == 'u' ||
Ch == 'w' || Ch == 's' || Ch == 'q' || Ch == 't' ||
Ch == 'v' || Ch == 'y' || Ch == 'p')
{
if (Ch == 'd' || Ch == 's')
{
s_DumpPrimary = *g_CurCmd;
}
else if (Ch == 'p')
{
// 'p' maps to the effective pointer size dump.
s_DumpPrimary = g_Machine->m_Ptr64 ? 'q' : 'd';
}
else
{
s_DumpPrimary = Ch;
}
g_CurCmd++;
s_DumpSecondary = ' ';
if (s_DumpPrimary == 'd' || s_DumpPrimary == 'q')
{
if (*g_CurCmd == 's')
{
s_DumpSecondary = *g_CurCmd++;
}
}
else if (s_DumpPrimary == 'l')
{
if (*g_CurCmd == 'b')
{
s_DumpSecondary = *g_CurCmd++;
}
}
else if (s_DumpPrimary == 'y')
{
if (*g_CurCmd == 'b' || *g_CurCmd == 'd')
{
s_DumpSecondary = *g_CurCmd++;
}
}
}
switch(s_DumpPrimary)
{
case 'a':
Count = 384;
GetRange(&g_DumpDefault, &Count, 1, SEGREG_DATA,
DEFAULT_RANGE_LIMIT);
DumpAsciiMemory(&g_DumpDefault, (ULONG)Count);
break;
case 'b':
Count = 128;
GetRange(&g_DumpDefault, &Count, 1, SEGREG_DATA,
DEFAULT_RANGE_LIMIT);
DumpByteMemory(&g_DumpDefault, (ULONG)Count);
break;
case 'c':
Count = 32;
GetRange(&g_DumpDefault, &Count, 4, SEGREG_DATA,
DEFAULT_RANGE_LIMIT);
DumpDwordAndCharMemory(&g_DumpDefault, (ULONG)Count);
break;
case 'd':
Count = 32;
DumpSymbols = s_DumpSecondary == 's';
GetRange(&g_DumpDefault, &Count, 4, SEGREG_DATA,
DEFAULT_RANGE_LIMIT);
DumpDwordMemory(&g_DumpDefault, (ULONG)Count, DumpSymbols);
break;
case 'D':
Count = 15;
GetRange(&g_DumpDefault, &Count, 8, SEGREG_DATA,
DEFAULT_RANGE_LIMIT);
DumpDoubleMemory(&g_DumpDefault, (ULONG)Count);
break;
case 'f':
Count = 16;
GetRange(&g_DumpDefault, &Count, 4, SEGREG_DATA,
DEFAULT_RANGE_LIMIT);
DumpFloatMemory(&g_DumpDefault, (ULONG)Count);
break;
case 'g':
Offset = (ULONG)GetExpression();
Count = 8;
if (*g_CurCmd && *g_CurCmd != ';')
{
Count = (ULONG)GetExpression() - Offset;
// It's unlikely that people want to dump hundreds
// of selectors. People often mistake the second
// number for a count and if it's less than the
// first they'll end up with a negative count.
if (Count > 0x800)
{
error(BADRANGE);
}
}
DumpSelector(Offset, (ULONG)Count);
break;
case 'l':
BOOL followBlink;
Count = 32;
Size = 4;
followBlink = s_DumpSecondary == 'b';
if ((Ch = PeekChar()) != '\0' && Ch != ';')
{
GetAddrExpression(SEGREG_DATA, &g_DumpDefault);
if ((Ch = PeekChar()) != '\0' && Ch != ';')
{
Count = GetExpression();
if ((Ch = PeekChar()) != '\0' && Ch != ';')
{
Size = (ULONG)GetExpression();
}
}
}
DumpListMemory(&g_DumpDefault, (ULONG)Count, Size, followBlink);
break;
case 'q':
Count = 16;
DumpSymbols = s_DumpSecondary == 's';
GetRange(&g_DumpDefault, &Count, 8, SEGREG_DATA,
DEFAULT_RANGE_LIMIT);
DumpQuadMemory(&g_DumpDefault, (ULONG)Count, DumpSymbols);
break;
case 's':
case 'S':
UNICODE_STRING64 UnicodeString;
ADDR BufferAddr;
Count = 1;
GetRange(&g_DumpDefault, &Count, 2, SEGREG_DATA,
DEFAULT_RANGE_LIMIT);
while (Count--)
{
if (g_Target->ReadUnicodeString(g_Process,
g_Machine, Flat(g_DumpDefault),
&UnicodeString) == S_OK)
{
ADDRFLAT(&BufferAddr, UnicodeString.Buffer);
if (s_DumpPrimary == 'S')
{
DumpUnicodeMemory( &BufferAddr,
UnicodeString.Length / sizeof(WCHAR));
}
else
{
DumpAsciiMemory( &BufferAddr, UnicodeString.Length );
}
}
}
break;
case 't':
case 'T':
SymbolTypeDumpEx(g_Process->m_SymHandle,
g_Process->m_ImageHead,
g_CurCmd);
break;
case 'u':
Count = 384;
GetRange(&g_DumpDefault, &Count, 2, SEGREG_DATA,
DEFAULT_RANGE_LIMIT);
DumpUnicodeMemory(&g_DumpDefault, (ULONG)Count);
break;
case 'v':
RequireCurrentScope();
EnumerateLocals(LocalSymbolEnumerator, NULL);
break;
case 'w':
Count = 64;
GetRange(&g_DumpDefault, &Count, 2, SEGREG_DATA,
DEFAULT_RANGE_LIMIT);
DumpWordMemory(&g_DumpDefault, (ULONG)Count);
break;
case 'y':
switch(s_DumpSecondary)
{
case 'b':
Count = 32;
GetRange(&g_DumpDefault, &Count, 1, SEGREG_DATA,
DEFAULT_RANGE_LIMIT);
DumpByteBinaryMemory(&g_DumpDefault, (ULONG)Count);
break;
case 'd':
Count = 8;
GetRange(&g_DumpDefault, &Count, 4, SEGREG_DATA,
DEFAULT_RANGE_LIMIT);
DumpDwordBinaryMemory(&g_DumpDefault, (ULONG)Count);
break;
default:
error(SYNTAX);
}
break;
default:
error(SYNTAX);
break;
}
}
//----------------------------------------------------------------------------
//
// DumpValues
//
// Generic columnar value dumper. Returns the number of values
// printed.
//
//----------------------------------------------------------------------------
class DumpValues
{
public:
DumpValues(ULONG Size, ULONG Columns);
ULONG Dump(PADDR Start, ULONG Count);
protected:
// Worker methods that derived classes must define.
virtual void GetValue(TypedData* Val) = 0;
virtual BOOL PrintValue(void) = 0;
virtual void PrintUnknown(void) = 0;
// Optional worker methods. Base implementations do nothing.
virtual void EndRow(void);
// Fixed members controlling how this instance dumps values.
ULONG m_Size;
ULONG m_Columns;
// Work members during dumping.
UCHAR* m_Value;
ULONG m_Col;
PADDR m_Start;
// Optional per-row values. Out is automatically reset to
// Base at the beginning of every row.
UCHAR* m_Base;
UCHAR* m_Out;
};
DumpValues::DumpValues(ULONG Size, ULONG Columns)
{
m_Size = Size;
m_Columns = Columns;
}
ULONG
DumpValues::Dump(PADDR Start, ULONG Count)
{
ULONG Read;
UCHAR ReadBuffer[512];
ULONG Idx;
ULONG Block;
BOOL First = TRUE;
ULONG64 Offset;
ULONG Printed;
BOOL RowStarted;
ULONG PageVal;
ULONG64 NextOffs, NextPage;
Offset = Flat(*Start);
Printed = 0;
RowStarted = FALSE;
m_Start = Start;
m_Col = 0;
m_Out = m_Base;
while (Count > 0)
{
Block = sizeof(ReadBuffer) / m_Size;
Block = min(Count, Block);
g_Target->NearestDifferentlyValidOffsets(Offset, &NextOffs, &NextPage);
PageVal = (ULONG)(NextPage - Offset + m_Size - 1) / m_Size;
Block = min(Block, PageVal);
if (fnotFlat(*Start) ||
g_Target->ReadVirtual(g_Process, Flat(*Start),
ReadBuffer, Block * m_Size, &Read) != S_OK)
{
Read = 0;
}
Read /= m_Size;
if (Read < Block && NextOffs < NextPage)
{
// In dump files data validity can change from
// one byte to the next so we cannot assume that
// stepping by pages will always be correct. Instead,
// if we didn't have a successful read we step just
// past the end of the valid data or to the next
// valid offset, whichever is farther.
if (Offset + (Read + 1) * m_Size < NextOffs)
{
Block = (ULONG)(NextOffs - Offset + m_Size - 1) / m_Size;
}
else
{
Block = Read + 1;
}
}
m_Value = ReadBuffer;
Idx = 0;
if (First && Read >= 1)
{
First = FALSE;
GetValue(&g_LastDump);
g_LastDump.SetDataSource(TDATA_MEMORY, Flat(*Start), 0);
}
while (Idx < Block)
{
while (m_Col < m_Columns && Idx < Block)
{
if (m_Col == 0)
{
dprintAddr(Start);
RowStarted = TRUE;
}
if (Idx < Read)
{
if (!PrintValue())
{
// Increment address since this value was
// examined, but do not increment print count
// or column since no output was produced.
AddrAdd(Start, m_Size);
goto Exit;
}
m_Value += m_Size;
}
else
{
PrintUnknown();
}
Idx++;
Printed++;
m_Col++;
AddrAdd(Start, m_Size);
}
if (m_Col == m_Columns)
{
EndRow();
m_Out = m_Base;
dprintf("\n");
RowStarted = FALSE;
m_Col = 0;
}
if (CheckUserInterrupt())
{
return Printed;
}
}
Count -= Block;
Offset += Block * m_Size;
}
Exit:
if (RowStarted)
{
EndRow();
m_Out = m_Base;
dprintf("\n");
}
return Printed;
}
void
DumpValues::EndRow(void)
{
// Empty base implementation.
}
/*** DumpAsciiMemory - output ascii strings from memory
*
* Purpose:
* Function of "da<range>" command.
*
* Outputs the memory in the specified range as ascii
* strings up to 32 characters per line. The default
* display is 12 lines for 384 characters total.
*
* Input:
* Start - starting address to begin display
* Count - number of characters to display as ascii
*
* Output:
* None.
*
* Notes:
* memory locations not accessible are output as "?",
* but no errors are returned.
*
*************************************************************************/
class DumpAscii : public DumpValues
{
public:
DumpAscii(void)
: DumpValues(sizeof(UCHAR), (sizeof(m_Buf) / sizeof(m_Buf[0]) - 1))
{
m_Base = m_Buf;
}
protected:
// Worker methods that derived classes must define.
virtual void GetValue(TypedData* Val);
virtual BOOL PrintValue(void);
virtual void PrintUnknown(void);
virtual void EndRow(void);
UCHAR m_Buf[33];
};
void
DumpAscii::GetValue(TypedData* Val)
{
Val->SetToNativeType(DNTYPE_CHAR);
Val->m_S8 = *m_Value;
}
BOOL
DumpAscii::PrintValue(void)
{
UCHAR ch;
ch = *m_Value;
if (ch == 0)
{
return FALSE;
}
if (ch < 0x20 || ch > 0x7e)
{
ch = '.';
}
*m_Out++ = ch;
return TRUE;
}
void
DumpAscii::PrintUnknown(void)
{
*m_Out++ = '?';
}
void
DumpAscii::EndRow(void)
{
*m_Out++ = 0;
dprintf(" \"%s\"", m_Base);
}
ULONG
DumpAsciiMemory(PADDR Start, ULONG Count)
{
DumpAscii Dumper;
return Count - Dumper.Dump(Start, Count);
}
/*** DumpUnicodeMemory - output unicode strings from memory
*
* Purpose:
* Function of "du<range>" command.
*
* Outputs the memory in the specified range as unicode
* strings up to 32 characters per line. The default
* display is 12 lines for 384 characters total (768 bytes)
*
* Input:
* Start - starting address to begin display
* Count - number of characters to display as ascii
*
* Output:
* None.
*
* Notes:
* memory locations not accessible are output as "?",
* but no errors are returned.
*
*************************************************************************/
class DumpUnicode : public DumpValues
{
public:
DumpUnicode(void)
: DumpValues(sizeof(WCHAR), (sizeof(m_Buf) / sizeof(m_Buf[0]) - 1))
{
m_Base = (PUCHAR)m_Buf;
}
protected:
// Worker methods that derived classes must define.
virtual void GetValue(TypedData* Val);
virtual BOOL PrintValue(void);
virtual void PrintUnknown(void);
virtual void EndRow(void);
WCHAR m_Buf[33];
};
void
DumpUnicode::GetValue(TypedData* Val)
{
Val->SetToNativeType(DNTYPE_WCHAR_T);
Val->m_U16 = *(WCHAR *)m_Value;
}
BOOL
DumpUnicode::PrintValue(void)
{
WCHAR ch;
ch = *(WCHAR *)m_Value;
if (ch == UNICODE_NULL)
{
return FALSE;
}
if (!iswprint(ch))
{
ch = L'.';
}
*(WCHAR *)m_Out = ch;
m_Out += sizeof(WCHAR);
return TRUE;
}
void
DumpUnicode::PrintUnknown(void)
{
*(WCHAR *)m_Out = L'?';
m_Out += sizeof(WCHAR);
}
void
DumpUnicode::EndRow(void)
{
*(WCHAR *)m_Out = UNICODE_NULL;
m_Out += sizeof(WCHAR);
dprintf(" \"%ws\"", m_Base);
}
ULONG
DumpUnicodeMemory(PADDR Start, ULONG Count)
{
DumpUnicode Dumper;
return Count - Dumper.Dump(Start, Count);
}
/*** DumpByteMemory - output byte values from memory
*
* Purpose:
* Function of "db<range>" command.
*
* Output the memory in the specified range as hex
* byte values and ascii characters up to 16 bytes
* per line. The default display is 16 lines for
* 256 byte total.
*
* Input:
* Start - starting address to begin display
* Count - number of bytes to display as hex and characters
*
* Output:
* None.
*
* Notes:
* memory location not accessible are output as "??" for
* byte values and "?" as characters, but no errors are returned.
*
*************************************************************************/
class DumpByte : public DumpValues
{
public:
DumpByte(void)
: DumpValues(sizeof(UCHAR), (sizeof(m_Buf) / sizeof(m_Buf[0]) - 1))
{
m_Base = m_Buf;
}
protected:
// Worker methods that derived classes must define.
virtual void GetValue(TypedData* Val);
virtual BOOL PrintValue(void);
virtual void PrintUnknown(void);
virtual void EndRow(void);
UCHAR m_Buf[17];
};
void
DumpByte::GetValue(TypedData* Val)
{
Val->SetToNativeType(DNTYPE_UINT8);
Val->m_U8 = *m_Value;
}
BOOL
DumpByte::PrintValue(void)
{
UCHAR ch;
ch = *m_Value;
if (m_Col == 8)
{
dprintf("-");
}
else
{
dprintf(" ");
}
dprintf("%02x", ch);
if (ch < 0x20 || ch > 0x7e)
{
ch = '.';
}
*m_Out++ = ch;
return TRUE;
}
void
DumpByte::PrintUnknown(void)
{
if (m_Col == 8)
{
dprintf("-??");
}
else
{
dprintf(" ??");
}
*m_Out++ = '?';
}
void
DumpByte::EndRow(void)
{
*m_Out++ = 0;
while (m_Col < m_Columns)
{
dprintf(" ");
m_Col++;
}
if ((m_Start->type & ADDR_1632) == ADDR_1632)
{
dprintf(" %s", m_Base);
}
else
{
dprintf(" %s", m_Base);
}
}
void
DumpByteMemory(PADDR Start, ULONG Count)
{
DumpByte Dumper;
Dumper.Dump(Start, Count);
}
/*** DumpWordMemory - output word values from memory
*
* Purpose:
* Function of "dw<range>" command.
*
* Output the memory in the specified range as word
* values up to 8 words per line. The default display
* is 16 lines for 128 words total.
*
* Input:
* Start - starting address to begin display
* Count - number of words to be displayed
*
* Output:
* None.
*
* Notes:
* memory locations not accessible are output as "????",
* but no errors are returned.
*
*************************************************************************/
class DumpWord : public DumpValues
{
public:
DumpWord(void)
: DumpValues(sizeof(WORD), 8) {}
protected:
// Worker methods that derived classes must define.
virtual void GetValue(TypedData* Val);
virtual BOOL PrintValue(void);
virtual void PrintUnknown(void);
};
void
DumpWord::GetValue(TypedData* Val)
{
Val->SetToNativeType(DNTYPE_UINT16);
Val->m_U16 = *(WORD *)m_Value;
}
BOOL
DumpWord::PrintValue(void)
{
dprintf(" %04x", *(WORD *)m_Value);
return TRUE;
}
void
DumpWord::PrintUnknown(void)
{
dprintf(" ????");
}
void
DumpWordMemory(PADDR Start, ULONG Count)
{
DumpWord Dumper;
Dumper.Dump(Start, Count);
}
/*** DumpDwordMemory - output dword value from memory
*
* Purpose:
* Function of "dd<range>" command.
*
* Output the memory in the specified range as double
* word values up to 4 double words per line. The default
* display is 16 lines for 64 double words total.
*
* Input:
* Start - starting address to begin display
* Count - number of double words to be displayed
* ShowSymbols - Dump symbol for DWORD.
*
* Output:
* None.
*
* Notes:
* memory locations not accessible are output as "????????",
* but no errors are returned.
*
*************************************************************************/
class DumpDword : public DumpValues
{
public:
DumpDword(BOOL DumpSymbols)
: DumpValues(sizeof(DWORD), DumpSymbols ? 1 : 4)
{
m_DumpSymbols = DumpSymbols;
}
protected:
// Worker methods that derived classes must define.
virtual void GetValue(TypedData* Val);
virtual BOOL PrintValue(void);
virtual void PrintUnknown(void);
BOOL m_DumpSymbols;
};
void
DumpDword::GetValue(TypedData* Val)
{
Val->SetToNativeType(DNTYPE_UINT32);
Val->m_U32 = *(DWORD *)m_Value;
}
BOOL
DumpDword::PrintValue(void)
{
CHAR SymBuf[MAX_SYMBOL_LEN];
ULONG64 Displacement;
dprintf(" %08lx", *(DWORD *)m_Value);
if (m_DumpSymbols)
{
GetSymbol(EXTEND64(*(LONG *)m_Value),
SymBuf, sizeof(SymBuf), &Displacement);
if (*SymBuf)
{
dprintf(" %s", SymBuf);
if (Displacement)
{
dprintf("+0x%s", FormatDisp64(Displacement));
}
if (g_SymOptions & SYMOPT_LOAD_LINES)
{
OutputLineAddr(EXTEND64(*(LONG*)m_Value), " [%s @ %d]");
}
}
}
return TRUE;
}
void
DumpDword::PrintUnknown(void)
{
dprintf(" ????????");
}
void
DumpDwordMemory(PADDR Start, ULONG Count, BOOL ShowSymbols)
{
DumpDword Dumper(ShowSymbols);
Dumper.Dump(Start, Count);
}
/*** DumpDwordAndCharMemory - output dword value from memory
*
* Purpose:
* Function of "dc<range>" command.
*
* Output the memory in the specified range as double
* word values up to 4 double words per line, followed by
* an ASCII character representation of the bytes.
* The default display is 16 lines for 64 double words total.
*
* Input:
* Start - starting address to begin display
* Count - number of double words to be displayed
*
* Output:
* None.
*
* Notes:
* memory locations not accessible are output as "????????",
* but no errors are returned.
*
*************************************************************************/
class DumpDwordAndChar : public DumpValues
{
public:
DumpDwordAndChar(void)
: DumpValues(sizeof(DWORD), (sizeof(m_Buf) - 1) / sizeof(DWORD))
{
m_Base = m_Buf;
}
protected:
// Worker methods that derived classes must define.
virtual void GetValue(TypedData* Val);
virtual BOOL PrintValue(void);
virtual void PrintUnknown(void);
virtual void EndRow(void);
UCHAR m_Buf[17];
};
void
DumpDwordAndChar::GetValue(TypedData* Val)
{
Val->SetToNativeType(DNTYPE_UINT32);
Val->m_U32 = *(DWORD *)m_Value;
}
BOOL
DumpDwordAndChar::PrintValue(void)
{
UCHAR ch;
ULONG byte;
dprintf(" %08x", *(DWORD *)m_Value);
for (byte = 0; byte < sizeof(DWORD); byte++)
{
ch = *(m_Value + byte);
if (ch < 0x20 || ch > 0x7e)
{
ch = '.';
}
*m_Out++ = ch;
}
return TRUE;
}
void
DumpDwordAndChar::PrintUnknown(void)
{
dprintf(" ????????");
*m_Out++ = '?';
*m_Out++ = '?';
*m_Out++ = '?';
*m_Out++ = '?';
}
void
DumpDwordAndChar::EndRow(void)
{
*m_Out++ = 0;
while (m_Col < m_Columns)
{
dprintf(" ");
m_Col++;
}
dprintf(" %s", m_Base);
}
void
DumpDwordAndCharMemory(PADDR Start, ULONG Count)
{
DumpDwordAndChar Dumper;
Dumper.Dump(Start, Count);
}
/*** DumpListMemory - output linked list from memory
*
* Purpose:
* Function of "dl addr length size" command.
*
* Output the memory in the specified range as a linked list
*
* Input:
* Start - starting address to begin display
* Count - number of list elements to be displayed
*
* Output:
* None.
*
* Notes:
* memory locations not accessible are output as "????????",
* but no errors are returned.
*
*************************************************************************/
void
DumpListMemory(PADDR Start,
ULONG ElemCount,
ULONG Size,
BOOL FollowBlink)
{
ULONG64 FirstAddr;
ULONG64 Link;
LIST_ENTRY64 List;
ADDR CurAddr;
if (Type(*Start) & (ADDR_UNKNOWN | ADDR_V86 | ADDR_16 | ADDR_1632))
{
dprintf("[%u,%x:%x`%08x,%08x`%08x] - bogus address type.\n",
Type(*Start),
Start->seg,
(ULONG)(Off(*Start)>>32),
(ULONG)Off(*Start),
(ULONG)(Flat(*Start)>>32),
(ULONG)Flat(*Start)
);
return;
}
//
// Setup to follow forward or backward links. Avoid reading more
// than the forward link here if going forwards. (in case the link
// is at the end of a page).
//
FirstAddr = Flat(*Start);
while (ElemCount-- != 0 && Flat(*Start) != 0)
{
if (FollowBlink)
{
if (g_Target->ReadListEntry(g_Process, g_Machine,
Flat(*Start), &List) != S_OK)
{
break;
}
Link = List.Blink;
}
else
{
if (g_Target->ReadPointer(g_Process, g_Machine,
Flat(*Start), &Link) != S_OK)
{
break;
}
}
CurAddr = *Start;
if (g_Machine->m_Ptr64)
{
DumpQuadMemory(&CurAddr, Size, FALSE);
}
else
{
DumpDwordMemory(&CurAddr, Size, FALSE);
}
//
// If we get back to the first entry, we're done.
//
if (Link == FirstAddr)
{
break;
}
//
// Bail if the link is immediately circular.
//
if (Flat(*Start) == Link)
{
break;
}
Flat(*Start) = Start->off = Link;
if (CheckUserInterrupt())
{
WarnOut("-- User interrupt\n");
return;
}
}
}
//----------------------------------------------------------------------------
//
// DumpFloatMemory
//
// Dumps float values.
//
//----------------------------------------------------------------------------
class DumpFloat : public DumpValues
{
public:
DumpFloat(void)
: DumpValues(sizeof(float), 4) {}
protected:
// Worker methods that derived classes must define.
virtual void GetValue(TypedData* Val);
virtual BOOL PrintValue(void);
virtual void PrintUnknown(void);
};
void
DumpFloat::GetValue(TypedData* Val)
{
Val->SetToNativeType(DNTYPE_FLOAT32);
Val->m_F32 = *(float *)m_Value;
}
BOOL
DumpFloat::PrintValue(void)
{
dprintf(" %16.8g", *(float *)m_Value);
return TRUE;
}
void
DumpFloat::PrintUnknown(void)
{
dprintf(" ????????????????");
}
void
DumpFloatMemory(PADDR Start, ULONG Count)
{
DumpFloat Dumper;
Dumper.Dump(Start, Count);
}
//----------------------------------------------------------------------------
//
// DumpDoubleMemory
//
// Dumps double values.
//
//----------------------------------------------------------------------------
class DumpDouble : public DumpValues
{
public:
DumpDouble(void)
: DumpValues(sizeof(double), 3) {}
protected:
// Worker methods that derived classes must define.
virtual void GetValue(TypedData* Val);
virtual BOOL PrintValue(void);
virtual void PrintUnknown(void);
};
void
DumpDouble::GetValue(TypedData* Val)
{
Val->SetToNativeType(DNTYPE_FLOAT64);
Val->m_F64 = *(double *)m_Value;
}
BOOL
DumpDouble::PrintValue(void)
{
dprintf(" %22.12lg", *(double *)m_Value);
return TRUE;
}
void
DumpDouble::PrintUnknown(void)
{
dprintf(" ????????????????????????");
}
void
DumpDoubleMemory(PADDR Start, ULONG Count)
{
DumpDouble Dumper;
Dumper.Dump(Start, Count);
}
/*** DumpQuadMemory - output quad value from memory
*
* Purpose:
* Function of "dq<range>" command.
*
* Output the memory in the specified range as quad
* word values up to 2 quad words per line. The default
* display is 16 lines for 32 quad words total.
*
* Input:
* Start - starting address to begin display
* Count - number of double words to be displayed
*
* Output:
* None.
*
* Notes:
* memory locations not accessible are output as "????????",
* but no errors are returned.
*
*************************************************************************/
class DumpQuad : public DumpValues
{
public:
DumpQuad(BOOL DumpSymbols)
: DumpValues(sizeof(ULONGLONG), DumpSymbols ? 1 : 2)
{
m_DumpSymbols = DumpSymbols;
}
protected:
// Worker methods that derived classes must define.
virtual void GetValue(TypedData* Val);
virtual BOOL PrintValue(void);
virtual void PrintUnknown(void);
BOOL m_DumpSymbols;
};
void
DumpQuad::GetValue(TypedData* Val)
{
Val->SetToNativeType(DNTYPE_UINT64);
Val->m_U64 = *(ULONG64 *)m_Value;
}
BOOL
DumpQuad::PrintValue(void)
{
CHAR SymBuf[MAX_SYMBOL_LEN];
ULONG64 Displacement;
ULONG64 Val = *(ULONG64*)m_Value;
dprintf(" %08lx`%08lx", (ULONG)(Val >> 32), (ULONG)Val);
if (m_DumpSymbols)
{
GetSymbol(Val, SymBuf, sizeof(SymBuf), &Displacement);
if (*SymBuf)
{
dprintf(" %s", SymBuf);
if (Displacement)
{
dprintf("+0x%s", FormatDisp64(Displacement));
}
if (g_SymOptions & SYMOPT_LOAD_LINES)
{
OutputLineAddr(Val, " [%s @ %d]");
}
}
}
return TRUE;
}
void
DumpQuad::PrintUnknown(void)
{
dprintf(" ????????`????????");
}
void
DumpQuadMemory(PADDR Start, ULONG Count, BOOL ShowSymbols)
{
DumpQuad Dumper(ShowSymbols);
Dumper.Dump(Start, Count);
}
/*** DumpByteBinaryMemory - output binary value from memory
*
* Purpose:
* Function of "dyb<range>" command.
*
* Output the memory in the specified range as binary
* values up to 32 bits per line. The default
* display is 8 lines for 32 bytes total.
*
* Input:
* Start - starting address to begin display
* Count - number of double words to be displayed
*
* Output:
* None.
*
* Notes:
* memory locations not accessible are output as "????????",
* but no errors are returned.
*
*************************************************************************/
class DumpByteBinary : public DumpValues
{
public:
DumpByteBinary(void)
: DumpValues(sizeof(UCHAR), (DIMA(m_HexValue) - 1) / 3)
{
m_Base = m_HexValue;
}
protected:
// Worker methods that derived classes must define.
virtual void GetValue(TypedData* Val);
virtual BOOL PrintValue(void);
virtual void PrintUnknown(void);
virtual void EndRow(void);
UCHAR m_HexValue[13];
};
void
DumpByteBinary::GetValue(TypedData* Val)
{
Val->SetToNativeType(DNTYPE_UINT8);
Val->m_U8 = *m_Value;
}
BOOL
DumpByteBinary::PrintValue(void)
{
ULONG i;
UCHAR RawVal;
RawVal = *m_Value;
sprintf((PSTR)m_Out, " %02x", RawVal);
m_Out += 3;
dprintf(" ");
for (i = 0; i < 8; i++)
{
dprintf("%c", (RawVal & 0x80) ? '1' : '0');
RawVal <<= 1;
}
return TRUE;
}
void
DumpByteBinary::PrintUnknown(void)
{
dprintf(" ????????");
strcpy((PSTR)m_Out, " ??");
m_Out += 3;
}
void
DumpByteBinary::EndRow(void)
{
while (m_Col < m_Columns)
{
dprintf(" ");
m_Col++;
}
dprintf(" %s", m_HexValue);
}
void
DumpByteBinaryMemory(PADDR Start, ULONG Count)
{
DumpByteBinary Dumper;
PSTR Blanks = g_Machine->m_Ptr64 ? " " : " ";
dprintf("%s 76543210 76543210 76543210 76543210\n", Blanks);
dprintf("%s -------- -------- -------- --------\n", Blanks);
Dumper.Dump(Start, Count);
}
/*** DumpDwordBinaryMemory - output binary value from memory
*
* Purpose:
* Function of "dyd<range>" command.
*
* Output the memory in the specified range as binary
* values of 32 bits per line. The default
* display is 8 lines for 8 dwords total.
*
* Input:
* Start - starting address to begin display
* Count - number of double words to be displayed
*
* Output:
* None.
*
* Notes:
* memory locations not accessible are output as "????????",
* but no errors are returned.
*
*************************************************************************/
class DumpDwordBinary : public DumpValues
{
public:
DumpDwordBinary(void)
: DumpValues(sizeof(ULONG), 1)
{
}
protected:
// Worker methods that derived classes must define.
virtual void GetValue(TypedData* Val);
virtual BOOL PrintValue(void);
virtual void PrintUnknown(void);
virtual void EndRow(void);
UCHAR m_HexValue[9];
};
void
DumpDwordBinary::GetValue(TypedData* Val)
{
Val->SetToNativeType(DNTYPE_UINT32);
Val->m_U32 = *(PULONG)m_Value;
}
BOOL
DumpDwordBinary::PrintValue(void)
{
ULONG i;
ULONG RawVal;
RawVal = *(PULONG)m_Value;
sprintf((PSTR)m_HexValue, "%08lx", RawVal);
for (i = 0; i < sizeof(ULONG) * 8; i++)
{
if ((i & 7) == 0)
{
dprintf(" ");
}
dprintf("%c", (RawVal & 0x80000000) ? '1' : '0');
RawVal <<= 1;
}
return TRUE;
}
void
DumpDwordBinary::PrintUnknown(void)
{
dprintf(" ???????? ???????? ???????? ????????");
strcpy((PSTR)m_HexValue, "????????");
}
void
DumpDwordBinary::EndRow(void)
{
dprintf(" %s", m_HexValue);
}
void
DumpDwordBinaryMemory(PADDR Start, ULONG Count)
{
DumpDwordBinary Dumper;
PSTR Blanks = g_Machine->m_Ptr64 ? " " : " ";
dprintf("%s 3 2 1 0\n", Blanks);
dprintf("%s 10987654 32109876 54321098 76543210\n", Blanks);
dprintf("%s -------- -------- -------- --------\n", Blanks);
Dumper.Dump(Start, Count);
}
//----------------------------------------------------------------------------
//
// DumpSelector
//
// Dumps x86 selectors.
//
//----------------------------------------------------------------------------
void
DumpSelector(ULONG Selector, ULONG Count)
{
DESCRIPTOR64 Desc;
ULONG Type;
LPSTR TypeName, TypeProtect, TypeAccess;
PSTR PreFill, PostFill, Dash;
if (g_Machine->m_Ptr64)
{
PreFill = " ";
PostFill = " ";
Dash = "---------";
}
else
{
PreFill = "";
PostFill = "";
Dash = "";
}
dprintf("Selector %sBase%s %sLimit%s "
"Type DPL Size Gran Pres\n",
PreFill, PostFill, PreFill, PostFill);
dprintf("-------- --------%s --------%s "
"---------- --- ------- ---- ----\n",
Dash, Dash);
while (Count >= 8)
{
if (CheckUserInterrupt())
{
WarnOut("-- User interrupt\n");
break;
}
dprintf(" %04X ", Selector);
if (g_Target->GetSelDescriptor(g_Thread, g_Machine,
Selector, &Desc) != S_OK)
{
ErrOut("Unable to get descriptor\n");
Count -= 8;
Selector += 8;
continue;
}
Type = X86_DESC_TYPE(Desc.Flags);
if (Type & 0x10)
{
if (Type & 0x8)
{
// Code Descriptor
TypeName = "Code ";
TypeProtect = (Type & 2) ? "RE" : "EO";
}
else
{
// Data Descriptor
TypeName = "Data ";
TypeProtect = (Type & 2) ? "RW" : "RO";
}
TypeAccess = (Type & 1) ? " Ac" : " ";
}
else
{
TypeProtect = "";
TypeAccess = "";
switch(Type)
{
case 2:
TypeName = "LDT ";
break;
case 1:
case 3:
case 9:
case 0xB:
TypeName = (Type & 0x8) ? "TSS32" : "TSS16";
TypeAccess = (Type & 0x2) ? " Busy" : " Avl ";
break;
case 4:
TypeName = "C-GATE16 ";
break;
case 5:
TypeName = "TSK-GATE ";
break;
case 6:
TypeName = "I-GATE16 ";
break;
case 7:
TypeName = "TRP-GATE16";
break;
case 0xC:
TypeName = "C-GATE32 ";
break;
case 0xF:
TypeName = "T-GATE32 ";
break;
default:
TypeName = "<Reserved>";
break;
}
}
dprintf("%s %s %s%s%s %d %s %s %s\n",
FormatAddr64(Desc.Base),
FormatAddr64(Desc.Limit),
TypeName, TypeProtect, TypeAccess,
X86_DESC_PRIVILEGE(Desc.Flags),
(Desc.Flags & X86_DESC_DEFAULT_BIG) ? " Big " : "Not Big",
(Desc.Flags & X86_DESC_GRANULARITY) ? "Page" : "Byte",
(Desc.Flags & X86_DESC_PRESENT) ? " P " : " NP ");
Count -= 8;
Selector += 8;
}
}
void
ParseEnterCommand(void)
{
CHAR Ch;
ADDR Addr1;
UCHAR ListBuffer[STRLISTSIZE * 2];
ULONG Count;
ULONG Size;
static CHAR s_EnterType = 'b';
if (!IS_CUR_MACHINE_ACCESSIBLE())
{
error(BADTHREAD);
}
Ch = (CHAR)tolower(*g_CurCmd);
if (Ch == 'a' || Ch == 'b' || Ch == 'w' || Ch == 'd' || Ch == 'q' ||
Ch == 'u' || Ch == 'p' || Ch == 'f')
{
if (*g_CurCmd == 'D')
{
s_EnterType = *g_CurCmd;
}
else
{
s_EnterType = Ch;
}
g_CurCmd++;
}
GetAddrExpression(SEGREG_DATA, &Addr1);
if (s_EnterType == 'a' || s_EnterType == 'u')
{
AsciiList((PSTR)ListBuffer, sizeof(ListBuffer), &Count);
if (Count == 0)
{
error(UNIMPLEMENT); //TEMP
}
if (s_EnterType == 'u')
{
ULONG Ansi;
// Expand ANSI to Unicode.
Ansi = Count;
Count *= 2;
while (Ansi-- > 0)
{
ListBuffer[Ansi * 2] = ListBuffer[Ansi];
ListBuffer[Ansi * 2 + 1] = 0;
}
Size = 2;
}
else
{
Size = 1;
}
}
else
{
Size = 1;
if (s_EnterType == 'w')
{
Size = 2;
}
else if (s_EnterType == 'd' ||
s_EnterType == 'f' ||
(s_EnterType == 'p' && !g_Machine->m_Ptr64))
{
Size = 4;
}
else if (s_EnterType == 'q' ||
s_EnterType == 'D' ||
(s_EnterType == 'p' && g_Machine->m_Ptr64))
{
Size = 8;
}
if (s_EnterType == 'f' || s_EnterType == 'D')
{
FloatList(ListBuffer, sizeof(ListBuffer), Size, &Count);
}
else
{
HexList(ListBuffer, sizeof(ListBuffer), Size, &Count);
}
if (Count == 0)
{
InteractiveEnterMemory(s_EnterType, &Addr1, Size);
return;
}
}
//
// Memory was entered at the command line.
// Write it out in the same chunks as it was entered.
//
PUCHAR List = &ListBuffer[0];
while (Count)
{
if (fnotFlat(Addr1) ||
g_Target->WriteAllVirtual(g_Process, Flat(Addr1),
List, Size) != S_OK)
{
error(MEMORY);
}
AddrAdd(&Addr1, Size);
if (CheckUserInterrupt())
{
WarnOut("-- User interrupt\n");
return;
}
List += Size;
Count -= Size;
}
}
//----------------------------------------------------------------------------
//
// InteractiveEnterMemory
//
// Interactively walks through memory, displaying current contents
// and prompting for new contents.
//
//----------------------------------------------------------------------------
void
InteractiveEnterMemory(CHAR Type, PADDR Address, ULONG Size)
{
CHAR EnterBuf[1024];
PSTR Enter;
ULONG64 Content;
PSTR CmdSaved = g_CurCmd;
PSTR StartSaved = g_CommandStart;
ULONG64 EnteredValue;
CHAR Ch;
g_PromptLength = 9 + 2 * Size;
while (TRUE)
{
if (fnotFlat(*Address) ||
g_Target->ReadAllVirtual(g_Process, Flat(*Address),
&Content, Size) != S_OK)
{
error(MEMORY);
}
dprintAddr(Address);
switch(Type)
{
case 'f':
dprintf("%12.6g", *(float*)&Content);
break;
case 'D':
dprintf("%22.12g", *(double*)&Content);
break;
default:
switch(Size)
{
case 1:
dprintf("%02x", (UCHAR)Content);
break;
case 2:
dprintf("%04x", (USHORT)Content);
break;
case 4:
dprintf("%08lx", (ULONG)Content);
break;
case 8:
dprintf("%08lx`%08lx", (ULONG)(Content>>32), (ULONG)Content);
break;
}
break;
}
GetInput(" ", EnterBuf, 1024, GETIN_LOG_INPUT_LINE);
RemoveDelChar(EnterBuf);
Enter = EnterBuf;
if (*Enter == '\0')
{
g_CurCmd = CmdSaved;
g_CommandStart = StartSaved;
return;
}
Ch = *Enter;
while (Ch == ' ' || Ch == '\t' || Ch == ';')
{
Ch = *++Enter;
}
if (*Enter == '\0')
{
AddrAdd(Address, Size);
continue;
}
g_CurCmd = Enter;
g_CommandStart = Enter;
if (Type == 'f' || Type == 'D')
{
EnteredValue = FloatValue(Size);
}
else
{
EnteredValue = HexValue(Size);
}
if (fnotFlat(*Address) ||
g_Target->WriteAllVirtual(g_Process, Flat(*Address),
&EnteredValue, Size) != S_OK)
{
error(MEMORY);
}
AddrAdd(Address, Size);
}
}
/*** CompareTargetMemory - compare two ranges of memory
*
* Purpose:
* Function of "c<range><addr>" command.
*
* To compare two ranges of memory, starting at offsets
* src1addr and src2addr, respectively, for length bytes.
* Bytes that mismatch are displayed with their offsets
* and contents.
*
* Input:
* src1addr - start of first memory region
* length - count of bytes to compare
* src2addr - start of second memory region
*
* Output:
* None.
*
* Exceptions:
* error exit: MEMORY - memory read access failure
*
*************************************************************************/
void
CompareTargetMemory(PADDR Src1Addr, ULONG Length, PADDR Src2Addr)
{
ULONG CompIndex;
UCHAR Src1Ch;
UCHAR Src2Ch;
for (CompIndex = 0; CompIndex < Length; CompIndex++)
{
if (fnotFlat(*Src1Addr) ||
fnotFlat(*Src2Addr) ||
g_Target->ReadAllVirtual(g_Process, Flat(*Src1Addr),
&Src1Ch, sizeof(Src1Ch)) != S_OK ||
g_Target->ReadAllVirtual(g_Process, Flat(*Src2Addr),
&Src2Ch, sizeof(Src2Ch)) != S_OK)
{
error(MEMORY);
}
if (Src1Ch != Src2Ch)
{
dprintAddr(Src1Addr);
dprintf(" %02x - ", Src1Ch);
dprintAddr(Src2Addr);
dprintf(" %02x\n", Src2Ch);
}
AddrAdd(Src1Addr, 1);
AddrAdd(Src2Addr, 1);
if (CheckUserInterrupt())
{
WarnOut("-- User interrupt\n");
return;
}
}
}
/*** MoveTargetMemory - move a range of memory to another
*
* Purpose:
* Function of "m<range><addr>" command.
*
* To move a range of memory starting at srcaddr to memory
* starting at destaddr for length bytes.
*
* Input:
* srcaddr - start of source memory region
* length - count of bytes to move
* destaddr - start of destination memory region
*
* Output:
* memory at destaddr has moved values
*
* Exceptions:
* error exit: MEMORY - memory reading or writing access failure
*
*************************************************************************/
void
MoveTargetMemory(PADDR SrcAddr, ULONG Length, PADDR DestAddr)
{
UCHAR Ch;
ULONG64 Incr = 1;
if (AddrLt(*SrcAddr, *DestAddr))
{
AddrAdd(SrcAddr, Length - 1);
AddrAdd(DestAddr, Length - 1);
Incr = (ULONG64)-1;
}
while (Length--)
{
if (fnotFlat(*SrcAddr) ||
fnotFlat(*DestAddr) ||
g_Target->ReadAllVirtual(g_Process, Flat(*SrcAddr),
&Ch, sizeof(Ch)) ||
g_Target->WriteAllVirtual(g_Process, Flat(*DestAddr),
&Ch, sizeof(Ch)) != S_OK)
{
error(MEMORY);
}
AddrAdd(SrcAddr, Incr);
AddrAdd(DestAddr, Incr);
if (CheckUserInterrupt())
{
WarnOut("-- User interrupt\n");
return;
}
}
}
/*** ParseFillMemory - fill memory with a byte list
*
* Purpose:
* Function of "f<range><bytelist>" command.
*
* To fill a range of memory with the byte list specified.
* The pattern repeats if the range size is larger than the
* byte list size.
*
* Input:
* Start - offset of memory to fill
* length - number of bytes to fill
* *plist - pointer to byte array to define values to set
* length - size of *plist array
*
* Exceptions:
* error exit: MEMORY - memory write access failure
*
* Output:
* memory at Start filled.
*
*************************************************************************/
void
ParseFillMemory(void)
{
HRESULT Status;
BOOL Virtual = TRUE;
ADDR Addr;
ULONG64 Size;
UCHAR Pattern[STRLISTSIZE];
ULONG PatternSize;
ULONG Done;
if (*g_CurCmd == 'p')
{
Virtual = FALSE;
g_CurCmd++;
}
GetRange(&Addr, &Size, 1, SEGREG_DATA,
DEFAULT_RANGE_LIMIT);
HexList(Pattern, sizeof(Pattern), 1, &PatternSize);
if (PatternSize == 0)
{
error(SYNTAX);
}
if (Virtual)
{
Status = g_Target->FillVirtual(g_Process, Flat(Addr), (ULONG)Size,
Pattern, PatternSize,
&Done);
}
else
{
Status = g_Target->FillPhysical(Flat(Addr), (ULONG)Size,
Pattern, PatternSize,
&Done);
}
if (Status != S_OK)
{
error(MEMORY);
}
else
{
dprintf("Filled 0x%x bytes\n", Done);
}
}
/*** SearchTargetMemory - search memory for a set of bytes
*
* Purpose:
* Function of "s<range><bytelist>" command.
*
* To search a range of memory with the byte list specified.
* If a match occurs, the offset of memory is output.
*
* Input:
* Start - offset of memory to start search
* length - size of range to search
* *plist - pointer to byte array to define values to search
* count - size of *plist array
*
* Output:
* None.
*
* Exceptions:
* error exit: MEMORY - memory read access failure
*
*************************************************************************/
void
SearchTargetMemory(PADDR Start,
ULONG64 Length,
PUCHAR List,
ULONG Count,
ULONG Granularity)
{
ADDR TmpAddr = *Start;
ULONG64 Found;
LONG64 SearchLength = Length;
HRESULT Status;
do
{
Status = g_Target->SearchVirtual(g_Process,
Flat(*Start),
SearchLength,
List,
Count,
Granularity,
&Found);
if (Status == S_OK)
{
ADDRFLAT(&TmpAddr, Found);
switch(Granularity)
{
case 1:
DumpByteMemory(&TmpAddr, 16);
break;
case 2:
DumpWordMemory(&TmpAddr, 8);
break;
case 4:
DumpDwordAndCharMemory(&TmpAddr, 4);
break;
case 8:
DumpQuadMemory(&TmpAddr, 2, FALSE);
break;
}
// Flush out the output immediately so that
// the user can see partial results during long searches.
FlushCallbacks();
SearchLength -= Found - Flat(*Start) + Granularity;
AddrAdd(Start, (ULONG)(Found - Flat(*Start) + Granularity));
if (CheckUserInterrupt())
{
WarnOut("-- Memory search interrupted at %s\n",
FormatAddr64(Flat(*Start)));
return;
}
}
}
while (SearchLength > 0 && Status == S_OK);
}
ULONG
ObjVfnTableCallback(PFIELD_INFO FieldInfo,
PVOID Context)
{
HRESULT Status;
ULONG Index = g_NumObjVfnTableOffsets++;
if (g_NumObjVfnTableOffsets > MAX_VFN_TABLE_OFFSETS)
{
return S_OK;
}
if ((Status = g_Target->
ReadPointer(g_Process, g_Machine, FieldInfo->address,
&g_ObjVfnTableAddr[Index])) != S_OK)
{
ErrOut("Unable to read vtable pointer at %s\n",
FormatAddr64(FieldInfo->address));
g_NumObjVfnTableOffsets--;
return Status;
}
g_ObjVfnTableOffset[Index] = FieldInfo->FieldOffset;
return S_OK;
}
void
SearchForObjectByVfnTable(ULONG64 Start, ULONG64 Length, ULONG Granularity)
{
HRESULT Status;
ULONG i;
char Save;
PSTR Str = StringValue(STRV_SPACE_IS_SEPARATOR ||
STRV_TRIM_TRAILING_SPACE ||
STRV_ESCAPED_CHARACTERS,
&Save);
//
// Search all of the object's members for vtable references
// and collect them.
//
SYM_DUMP_PARAM Symbol;
FIELD_INFO Fields[] =
{
{(PUCHAR)"__VFN_table", NULL, 0, DBG_DUMP_FIELD_FULL_NAME, 0,
(PVOID)&ObjVfnTableCallback},
};
ULONG Err;
ZeroMemory(&Symbol, sizeof(Symbol));
Symbol.sName = (PUCHAR)Str;
Symbol.nFields = 1;
Symbol.Context = (PVOID)Str;
Symbol.Fields = Fields;
Symbol.size = sizeof(Symbol);
Symbol.Options = NO_PRINT;
g_NumObjVfnTableOffsets = 0;
SymbolTypeDumpNew(&Symbol, &Err);
if (g_NumObjVfnTableOffsets == 0)
{
ErrOut("Object '%s' has no vtables\n", Str);
*g_CurCmd = Save;
return;
}
TypedData SymbolType;
if ((Err = SymbolType.
FindSymbol(g_Process, Str, TDACC_REQUIRE,
g_Machine->m_Ptr64 ? 8 : 4)) ||
(SymbolType.IsPointer() &&
(Err = SymbolType.ConvertToDereference(TDACC_REQUIRE,
g_Machine->m_Ptr64 ? 8 : 4))))
{
error(Err);
}
if (!SymbolType.m_Image)
{
error(TYPEDATA);
}
if (g_NumObjVfnTableOffsets > MAX_VFN_TABLE_OFFSETS)
{
WarnOut("%s has %d vtables, limiting search to %d\n",
Str, g_NumObjVfnTableOffsets, MAX_VFN_TABLE_OFFSETS);
g_NumObjVfnTableOffsets = MAX_VFN_TABLE_OFFSETS;
}
dprintf("%s size 0x%x, vtables: %d\n",
Str, Symbol.TypeSize, g_NumObjVfnTableOffsets);
for (i = 0; i < g_NumObjVfnTableOffsets; i++)
{
dprintf(" +%03x - %s ", g_ObjVfnTableOffset[i],
FormatAddr64(g_ObjVfnTableAddr[i]));
OutputSymAddr(g_ObjVfnTableAddr[i], 0, NULL);
dprintf("\n");
}
dprintf("Searching...\n");
*g_CurCmd = Save;
//
// Scan memory for the first vtable pointer found. On
// a hit, check that all of the other vtable pointers
// match also.
//
do
{
ULONG64 Found;
Status = g_Target->SearchVirtual(g_Process,
Start,
Length,
&g_ObjVfnTableAddr[0],
Granularity,
Granularity,
&Found);
if (Status == S_OK)
{
// We found a hit on the first pointer. Check
// the others now.
Found -= g_ObjVfnTableOffset[0];
for (i = 1; i < g_NumObjVfnTableOffsets; i++)
{
ULONG64 Ptr;
if (g_Target->ReadPointer(g_Process, g_Machine,
Found + g_ObjVfnTableOffset[i],
&Ptr) != S_OK ||
Ptr != g_ObjVfnTableAddr[i])
{
break;
}
}
if (i == g_NumObjVfnTableOffsets)
{
OutputTypeByIndex(g_Process->m_SymHandle,
SymbolType.m_Image->m_BaseOfImage,
SymbolType.m_BaseType,
Found);
}
// Flush out the output immediately so that
// the user can see partial results during long searches.
FlushCallbacks();
Found += Symbol.TypeSize;
if (Found > Start)
{
Length -= Found - Start;
}
else
{
Length = 0;
}
Start = Found;
if (CheckUserInterrupt())
{
WarnOut("-- User interrupt\n");
return;
}
}
}
while (Length > 0 && Status == S_OK);
}
void
ParseSearchMemory(void)
{
ADDR Addr;
ULONG64 Length;
UCHAR Pat[STRLISTSIZE];
ULONG PatLen;
ULONG Gran;
char SearchType;
while (*g_CurCmd == ' ')
{
g_CurCmd++;
}
Gran = 1;
SearchType = 'b';
if (*g_CurCmd == '-')
{
g_CurCmd++;
SearchType = *g_CurCmd;
switch(SearchType)
{
case 'a':
Gran = 1;
break;
case 'u':
case 'w':
Gran = 2;
break;
case 'd':
Gran = 4;
break;
case 'q':
Gran = 8;
break;
case 'v':
// Special object vtable search. It's basically a multi-pointer
// search, so the granularity is pointer-size.
if (g_Machine->m_Ptr64)
{
Gran = 8;
}
else
{
Gran = 4;
}
break;
default:
error(SYNTAX);
break;
}
g_CurCmd++;
}
// Allow very large ranges for search as it is used
// to search large areas of memory.
ADDRFLAT(&Addr, 0);
Length = 16;
GetRange(&Addr, &Length, Gran, SEGREG_DATA, 0x10000000);
if (!fFlat(Addr))
{
error(BADRANGE);
}
if (SearchType == 'v')
{
SearchForObjectByVfnTable(Flat(Addr), Length * Gran, Gran);
return;
}
else if (SearchType == 'a' || SearchType == 'u')
{
char Save;
PSTR Str = StringValue(STRV_SPACE_IS_SEPARATOR ||
STRV_TRIM_TRAILING_SPACE ||
STRV_ESCAPED_CHARACTERS,
&Save);
PatLen = strlen(Str);
if (PatLen * Gran > STRLISTSIZE)
{
error(LISTSIZE);
}
if (SearchType == 'u')
{
MultiByteToWideChar(CP_ACP, 0,
Str, PatLen,
(PWSTR)Pat, STRLISTSIZE / sizeof(WCHAR));
PatLen *= sizeof(WCHAR);
}
else
{
memcpy(Pat, Str, PatLen);
}
*g_CurCmd = Save;
}
else
{
HexList(Pat, sizeof(Pat), Gran, &PatLen);
}
if (PatLen == 0)
{
PCSTR Err = "Search pattern missing from";
ReportError(SYNTAX, &Err);
}
SearchTargetMemory(&Addr, Length * Gran, Pat, PatLen, Gran);
}
/*** InputIo - read and output io
*
* Purpose:
* Function of "ib, iw, id <address>" command.
*
* Read (input) and print the value at the specified io address.
*
* Input:
* IoAddress - Address to read.
* InputType - The size type 'b', 'w', or 'd'
*
* Output:
* None.
*
* Notes:
* I/O locations not accessible are output as "??", "????", or
* "????????", depending on size. No errors are returned.
*
*************************************************************************/
void
InputIo(ULONG64 IoAddress, UCHAR InputType)
{
ULONG InputValue;
ULONG InputSize = 1;
HRESULT Status;
CHAR Format[] = "%01lx";
InputValue = 0;
if (InputType == 'w')
{
InputSize = 2;
}
else if (InputType == 'd')
{
InputSize = 4;
}
Status = g_Target->ReadIo(Isa, 0, 1, IoAddress, &InputValue, InputSize,
NULL);
dprintf("%s: ", FormatAddr64(IoAddress));
if (Status == S_OK)
{
Format[2] = (CHAR)('0' + (InputSize * 2));
dprintf(Format, InputValue);
}
else
{
while (InputSize--)
{
dprintf("??");
}
}
dprintf("\n");
}
/*** OutputIo - output io
*
* Purpose:
* Function of "ob, ow, od <address>" command.
*
* Write a value to the specified io address.
*
* Input:
* IoAddress - Address to read.
* OutputValue - Value to be written
* OutputType - The output size type 'b', 'w', or 'd'
*
* Output:
* None.
*
* Notes:
* No errors are returned.
*
*************************************************************************/
void
OutputIo(ULONG64 IoAddress, ULONG OutputValue, UCHAR OutputType)
{
ULONG OutputSize = 1;
if (OutputType == 'w')
{
OutputSize = 2;
}
else if (OutputType == 'd')
{
OutputSize = 4;
}
g_Target->WriteIo(Isa, 0, 1, IoAddress,
&OutputValue, OutputSize, NULL);
}