//---------------------------------------------------------------------------- // // Functions dealing with memory access, such as reading, writing, // dumping and entering. // // Copyright (C) Microsoft Corporation, 1997-2001. // //---------------------------------------------------------------------------- #include "ntsdp.hpp" ULONG64 EXPRLastDump = 0L; ADDR g_DumpDefault; // default dump address /*** GetProcessMemString - get memory string values * * Purpose: * To read a string of a specified length with the memory * values selected. Break reads across page boundaries - * multiples of the page size. * * Input: * Addr - offset of memory to start reading * Value - pointer to byte string to set with memory values * * Output: * bytes at Value set if read successful * * Returns: * number of bytes actually read * *************************************************************************/ ULONG GetProcessMemString ( PPROCESS_INFO Process, PADDR Addr, PVOID Value, ULONG Length ) { ULONG cTotalBytesRead = 0; if (fFlat(*Addr) || fInstrPtr(*Addr)) { PPROCESS_INFO OldCur = g_CurrentProcess; g_CurrentProcess = Process; if (g_Target->ReadVirtual(Flat(*Addr), Value, Length, &cTotalBytesRead) != S_OK) { cTotalBytesRead = 0; } g_CurrentProcess = OldCur; } return cTotalBytesRead; } /*** SetProcessMemString - set memory string values * * Purpose: * To write a string of a specified length with the memory * values selected. * * Input: * Addr - offset of memory to start writing * Value - pointer to byte string to set with memory values * * Output: * bytes at Value set if write successful * * Returns: * number of bytes actually write * *************************************************************************/ ULONG SetProcessMemString ( PPROCESS_INFO Process, PADDR Addr, PVOID Value, ULONG Length ) { ULONG cTotalBytesWritten = 0; if (fFlat(*Addr) || fInstrPtr(*Addr)) { PPROCESS_INFO OldCur = g_CurrentProcess; g_CurrentProcess = Process; if (g_Target->WriteVirtual(Flat(*Addr), Value, Length, &cTotalBytesWritten) != S_OK) { cTotalBytesWritten = 0; } g_CurrentProcess = OldCur; } return cTotalBytesWritten; } BOOL CALLBACK LocalSymbolEnumerator( PSYMBOL_INFO pSymInfo, ULONG Size, PVOID Context ) { ULONG64 Value = pSymInfo->Register, Address = pSymInfo->Address; TranslateAddress(pSymInfo->Flags, pSymInfo->Register, &Address, &Value); VerbOut("%s ", FormatAddr64(Address)); dprintf("%15s = ", pSymInfo->Name); if (pSymInfo->Flags & SYMF_REGISTER) { dprintf( "%I64x\n", Value ); } else { if (!DumpSingleValue(pSymInfo)) { dprintf("??"); } dprintf("\n"); } if (CheckUserInterrupt()) { return FALSE; } return TRUE; } //---------------------------------------------------------------------------- // // parseDumpCommand // // Parses memory dump commands. // //---------------------------------------------------------------------------- void parseDumpCommand( void ) { CHAR ch; ULONG64 count; ULONG size; ULONG offset; BOOL DumpSymbols; static CHAR s_DumpPrimary = 'b'; static CHAR s_DumpSecondary = ' '; 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); fnDumpAsciiMemory(&g_DumpDefault, (ULONG)count); break; case 'b': count = 128; GetRange(&g_DumpDefault, &count, 1, SEGREG_DATA); fnDumpByteMemory(&g_DumpDefault, (ULONG)count); break; case 'c': count = 32; GetRange(&g_DumpDefault, &count, 4, SEGREG_DATA); fnDumpDwordAndCharMemory(&g_DumpDefault, (ULONG)count); break; case 'd': count = 32; DumpSymbols = s_DumpSecondary == 's'; GetRange(&g_DumpDefault, &count, 4, SEGREG_DATA); fnDumpDwordMemory(&g_DumpDefault, (ULONG)count, DumpSymbols); break; case 'D': count = 15; GetRange(&g_DumpDefault, &count, 8, SEGREG_DATA); fnDumpDoubleMemory(&g_DumpDefault, (ULONG)count); break; case 'f': count = 16; GetRange(&g_DumpDefault, &count, 4, SEGREG_DATA); fnDumpFloatMemory(&g_DumpDefault, (ULONG)count); break; case 'g': fnDumpSelector((ULONG)GetExpression()); 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(); } } } fnDumpListMemory(&g_DumpDefault, (ULONG)count, size, followBlink); break; case 'q': count = 16; DumpSymbols = s_DumpSecondary == 's'; GetRange(&g_DumpDefault, &count, 8, SEGREG_DATA); fnDumpQuadMemory(&g_DumpDefault, (ULONG)count, DumpSymbols); break; case 's': case 'S': UNICODE_STRING64 UnicodeString; ADDR BufferAddr; count = 1; GetRange(&g_DumpDefault, &count, 2, SEGREG_DATA); while (count--) { if (g_Target->ReadUnicodeString(g_Machine, Flat(g_DumpDefault), &UnicodeString) == S_OK) { ADDRFLAT(&BufferAddr, UnicodeString.Buffer); if (s_DumpPrimary == 'S') { fnDumpUnicodeMemory( &BufferAddr, UnicodeString.Length / sizeof(WCHAR)); } else { fnDumpAsciiMemory( &BufferAddr, UnicodeString.Length ); } } } break; case 't': case 'T': SymbolTypeDumpEx(g_CurrentProcess->Handle, g_CurrentProcess->ImageHead, g_CurCmd); break; case 'u': count = 384; GetRange(&g_DumpDefault, &count, 2, SEGREG_DATA); fnDumpUnicodeMemory(&g_DumpDefault, (ULONG)count); break; case 'v': RequireCurrentScope(); EnumerateLocals(LocalSymbolEnumerator, NULL); break; case 'w': count = 64; GetRange(&g_DumpDefault, &count, 2, SEGREG_DATA); fnDumpWordMemory(&g_DumpDefault, (ULONG)count); break; case 'y': switch(s_DumpSecondary) { case 'b': count = 32; GetRange(&g_DumpDefault, &count, 1, SEGREG_DATA); fnDumpByteBinaryMemory(&g_DumpDefault, (ULONG)count); break; case 'd': count = 8; GetRange(&g_DumpDefault, &count, 4, SEGREG_DATA); fnDumpDwordBinaryMemory(&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 ULONG64 GetValue(void) = 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); Read = GetMemString(Start, ReadBuffer, Block * m_Size) / 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; EXPRLastDump = GetValue(); } 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. } /*** fnDumpAsciiMemory - output ascii strings from memory * * Purpose: * Function of "da" 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 ULONG64 GetValue(void); virtual BOOL PrintValue(void); virtual void PrintUnknown(void); virtual void EndRow(void); UCHAR m_Buf[33]; }; ULONG64 DumpAscii::GetValue(void) { return *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 fnDumpAsciiMemory( PADDR Start, ULONG Count ) { DumpAscii Dumper; return Count - Dumper.Dump(Start, Count); } /*** fnDumpUnicodeMemory - output unicode strings from memory * * Purpose: * Function of "du" 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 ULONG64 GetValue(void); virtual BOOL PrintValue(void); virtual void PrintUnknown(void); virtual void EndRow(void); WCHAR m_Buf[33]; }; ULONG64 DumpUnicode::GetValue(void) { return *(WCHAR *)m_Value; } BOOL DumpUnicode::PrintValue(void) { WCHAR ch; ch = *(WCHAR *)m_Value; if (ch == UNICODE_NULL) { return FALSE; } if (ch < 0x20 || ch > 0x7e) { 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 fnDumpUnicodeMemory( PADDR Start, ULONG Count ) { DumpUnicode Dumper; return Count - Dumper.Dump(Start, Count); } /*** fnDumpByteMemory - output byte values from memory * * Purpose: * Function of "db" 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 ULONG64 GetValue(void); virtual BOOL PrintValue(void); virtual void PrintUnknown(void); virtual void EndRow(void); UCHAR m_Buf[17]; }; ULONG64 DumpByte::GetValue(void) { return *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 fnDumpByteMemory( PADDR Start, ULONG Count ) { DumpByte Dumper; Dumper.Dump(Start, Count); } /*** fnDumpWordMemory - output word values from memory * * Purpose: * Function of "dw" 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 ULONG64 GetValue(void); virtual BOOL PrintValue(void); virtual void PrintUnknown(void); }; ULONG64 DumpWord::GetValue(void) { return *(WORD *)m_Value; } BOOL DumpWord::PrintValue(void) { dprintf(" %04x", *(WORD *)m_Value); return TRUE; } void DumpWord::PrintUnknown(void) { dprintf(" ????"); } void fnDumpWordMemory( PADDR Start, ULONG Count ) { DumpWord Dumper; Dumper.Dump(Start, Count); } /*** fnDumpDwordMemory - output dword value from memory * * Purpose: * Function of "dd" 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 * fDumpSymbols - 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 ULONG64 GetValue(void); virtual BOOL PrintValue(void); virtual void PrintUnknown(void); BOOL m_DumpSymbols; }; ULONG64 DumpDword::GetValue(void) { return *(DWORD *)m_Value; } BOOL DumpDword::PrintValue(void) { CHAR SymBuf[MAX_SYMBOL_LEN]; USHORT StdCallArgs; ULONG64 Displacement; dprintf(" %08lx", *(DWORD *)m_Value); if (m_DumpSymbols) { GetSymbolStdCall(EXTEND64(*(LONG *)m_Value), SymBuf, sizeof(SymBuf), &Displacement, &StdCallArgs); 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 fnDumpDwordMemory( PADDR Start, ULONG Count, BOOL fDumpSymbols ) { DumpDword Dumper(fDumpSymbols); Dumper.Dump(Start, Count); } /*** fnDumpDwordAndCharMemory - output dword value from memory * * Purpose: * Function of "dc" 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 ULONG64 GetValue(void); virtual BOOL PrintValue(void); virtual void PrintUnknown(void); virtual void EndRow(void); UCHAR m_Buf[17]; }; ULONG64 DumpDwordAndChar::GetValue(void) { return *(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 fnDumpDwordAndCharMemory(PADDR Start, ULONG Count) { DumpDwordAndChar Dumper; Dumper.Dump(Start, Count); } /*** fnDumpListMemory - 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 fnDumpListMemory( PADDR Start, ULONG elemcount, ULONG size, BOOL followBlink ) { ULONG64 firstaddr; ULONG64 link; LIST_ENTRY64 list; ADDR curaddr; ULONG linkSize; PULONG plink; 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_Machine, Flat(*Start), &list) != S_OK) { break; } link = list.Blink; } else { if (g_Target->ReadPointer(g_Machine, Flat(*Start), &link) != S_OK) { break; } } curaddr = *Start; if (g_Machine->m_Ptr64) { fnDumpQuadMemory(&curaddr, size, FALSE); } else { fnDumpDwordMemory(&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; } } } //---------------------------------------------------------------------------- // // fnDumpFloatMemory // // Dumps float values. // //---------------------------------------------------------------------------- class DumpFloat : public DumpValues { public: DumpFloat(void) : DumpValues(sizeof(float), 4) {} protected: // Worker methods that derived classes must define. virtual ULONG64 GetValue(void); virtual BOOL PrintValue(void); virtual void PrintUnknown(void); }; ULONG64 DumpFloat::GetValue(void) { // NTRAID#72849-2000/02/09-drewb. // Expression results are always integers right now // so just return the raw bits for the float. return *(ULONG *)m_Value; } BOOL DumpFloat::PrintValue(void) { dprintf(" %16.8g", *(float *)m_Value); return TRUE; } void DumpFloat::PrintUnknown(void) { dprintf(" ????????????????"); } void fnDumpFloatMemory(PADDR Start, ULONG Count) { DumpFloat Dumper; Dumper.Dump(Start, Count); } //---------------------------------------------------------------------------- // // fnDumpDoubleMemory // // Dumps double values. // //---------------------------------------------------------------------------- class DumpDouble : public DumpValues { public: DumpDouble(void) : DumpValues(sizeof(double), 3) {} protected: // Worker methods that derived classes must define. virtual ULONG64 GetValue(void); virtual BOOL PrintValue(void); virtual void PrintUnknown(void); }; ULONG64 DumpDouble::GetValue(void) { // NTRAID#72849-2000/02/09-drewb. // Expression results are always integers right now // so just return the raw bits for the float. return *(ULONG64 *)m_Value; } BOOL DumpDouble::PrintValue(void) { dprintf(" %22.12lg", *(double *)m_Value); return TRUE; } void DumpDouble::PrintUnknown(void) { dprintf(" ????????????????????????"); } void fnDumpDoubleMemory(PADDR Start, ULONG Count) { DumpDouble Dumper; Dumper.Dump(Start, Count); } /*** fnDumpQuadMemory - output quad value from memory * * Purpose: * Function of "dq" 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 ULONG64 GetValue(void); virtual BOOL PrintValue(void); virtual void PrintUnknown(void); BOOL m_DumpSymbols; }; ULONG64 DumpQuad::GetValue(void) { return *(ULONG64 *)m_Value; } BOOL DumpQuad::PrintValue(void) { CHAR SymBuf[MAX_SYMBOL_LEN]; USHORT StdCallArgs; ULONG64 Displacement; ULONG64 Val = *(ULONG64*)m_Value; dprintf(" %08lx`%08lx", (ULONG)(Val >> 32), (ULONG)Val); if (m_DumpSymbols) { GetSymbolStdCall(Val, SymBuf, sizeof(SymBuf), &Displacement, &StdCallArgs); 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 fnDumpQuadMemory( PADDR Start, ULONG Count, BOOL fDumpSymbols ) { DumpQuad Dumper(fDumpSymbols); Dumper.Dump(Start, Count); } /*** fnDumpByteBinaryMemory - output binary value from memory * * Purpose: * Function of "dyb" 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 ULONG64 GetValue(void); virtual BOOL PrintValue(void); virtual void PrintUnknown(void); virtual void EndRow(void); UCHAR m_HexValue[13]; }; ULONG64 DumpByteBinary::GetValue(void) { return *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 fnDumpByteBinaryMemory( 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); } /*** fnDumpDwordBinaryMemory - output binary value from memory * * Purpose: * Function of "dyd" 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 ULONG64 GetValue(void); virtual BOOL PrintValue(void); virtual void PrintUnknown(void); virtual void EndRow(void); UCHAR m_HexValue[9]; }; ULONG64 DumpDwordBinary::GetValue(void) { return *(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 fnDumpDwordBinaryMemory( 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); } //---------------------------------------------------------------------------- // // fnDumpSelector // // Dumps an x86 selector. // //---------------------------------------------------------------------------- void fnDumpSelector( ULONG Selector ) { DESCRIPTOR64 Desc; ULONG Type; LPSTR TypeName; PSTR PreFill, PostFill, Dash; if (g_Target->GetSelDescriptor(g_Machine, g_CurrentProcess->CurrentThread->Handle, Selector, &Desc) != S_OK) { ErrOut("Unable to get selector %X description\n", Selector); return; } if (g_Machine->m_Ptr64) { PreFill = " "; PostFill = " "; Dash = "---------"; } else { PreFill = ""; PostFill = ""; Dash = ""; } dprintf("Selector %sBase%s %sLimit%s Type DPL Size Gran\n", PreFill, PostFill, PreFill, PostFill); dprintf("-------- --------%s --------%s ------ --- ------- ----\n", Dash, Dash); Type = X86_DESC_TYPE(Desc.Flags); if ( Type & 0x10 ) { if ( Type & 0x8 ) { // Code Descriptor TypeName = " Code "; } else { // Data Descriptor TypeName = " Data "; } } else { TypeName = " Sys. "; } // 1234 12345678 12345678 ?Type? 1 ....... .... dprintf(" %04X %s %s %s %d %s %s\n", Selector, FormatAddr64(Desc.Base), FormatAddr64(Desc.Limit), TypeName, X86_DESC_PRIVILEGE(Desc.Flags), (Desc.Flags & X86_DESC_DEFAULT_BIG) ? " Big " : "Not Big", (Desc.Flags & X86_DESC_GRANULARITY) ? "Page" : "Byte" ); } //---------------------------------------------------------------------------- // // parseEnterCommand // // Parses memory entry commands. // //---------------------------------------------------------------------------- void parseEnterCommand( void ) { CHAR ch; static CHAR s_EnterType = 'b'; ADDR addr1; UCHAR list[STRLISTSIZE * 2]; PUCHAR plist = &list[0]; ULONG count; ULONG size; ch = (CHAR)tolower(*g_CurCmd); if (ch == 'a' || ch == 'b' || ch == 'w' || ch == 'd' || ch == 'q' || ch == 'u') { g_CurCmd++; s_EnterType = ch; } GetAddrExpression(SEGREG_DATA, &addr1); if (s_EnterType == 'a' || s_EnterType == 'u') { AsciiList((PSTR)list, &count); if (count == 0) { error(UNIMPLEMENT); //TEMP } if (s_EnterType == 'u') { ULONG Ansi; // Expand ANSI to Unicode. Ansi = count; count *= 2; while (Ansi-- > 0) { list[Ansi * 2] = list[Ansi]; list[Ansi * 2 + 1] = 0; } } } else { size = 1; if (s_EnterType == 'w') { size = 2; } else if (s_EnterType == 'd') { size = 4; } else if (s_EnterType == 'q') { size = 8; } HexList(list, &count, size); if (count == 0) { fnInteractiveEnterMemory(&addr1, size); return; } } // // memory was entered at the command line. // just write it in, one byte at a time // while (count--) { if (SetMemString(&addr1, plist++, 1) != 1) { error(MEMORY); } AddrAdd(&addr1, 1); if (CheckUserInterrupt()) { WarnOut("-- User interrupt\n"); return; } } } //---------------------------------------------------------------------------- // // fnInteractiveEnterMemory // // Interactively walks through memory, displaying current contents // and prompting for new contents. // //---------------------------------------------------------------------------- void fnInteractiveEnterMemory( 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 (GetMemString(Address, (PUCHAR)&Content, Size) != Size) { error(MEMORY); } dprintAddr(Address); 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; } GetInput(" ", EnterBuf, 1024); 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; EnteredValue = HexValue(Size); if (SetMemString(Address, (PUCHAR)&EnteredValue, Size) != Size) { error(MEMORY); } AddrAdd(Address, Size); } } /*** fnCompareMemory - compare two ranges of memory * * Purpose: * Function of "c" 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 fnCompareMemory( PADDR src1addr, ULONG length, PADDR src2addr ) { ULONG compindex; UCHAR src1ch; UCHAR src2ch; for (compindex = 0; compindex < length; compindex++) { if (!GetMemByte(src1addr, &src1ch)) { error(MEMORY); } if (!GetMemByte(src2addr, &src2ch)) { 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; } } } /*** fnMoveMemory - move a range of memory to another * * Purpose: * Function of "m" 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 fnMoveMemory( 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 (GetMemString(srcaddr, &ch, 1) != 1) { error(MEMORY); } if (SetMemString(destaddr, &ch, 1) != 1) { error(MEMORY); } AddrAdd(srcaddr, incr); AddrAdd(destaddr, incr); if (CheckUserInterrupt()) { WarnOut("-- User interrupt\n"); return; } } } /*** fnFillMemory - fill memory with a byte list * * Purpose: * Function of "f" 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); HexList(Pattern, &PatternSize, 1); if (PatternSize == 0) { error(SYNTAX); } if (Virtual) { Status = g_Target->FillVirtual(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); } } /*** fnSearchMemory - search memory with for a byte list * * Purpose: * Function of "s" 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 fnSearchMemory( PADDR Start, ULONG64 length, PUCHAR plist, ULONG count, ULONG Granularity ) { ULONG searchindex; ULONG listindex; UCHAR ch; ADDR tAddr = *Start; ULONG64 Found; LONG64 SearchLength = length; HRESULT st; do { st = g_Target->SearchVirtual(Flat(*Start), SearchLength, plist, count, Granularity, &Found); if (st == S_OK) { ADDRFLAT(&tAddr, Found); switch(Granularity) { case 1: fnDumpByteMemory(&tAddr, 16); break; case 2: fnDumpWordMemory(&tAddr, 8); break; case 4: fnDumpDwordAndCharMemory(&tAddr, 4); break; case 8: fnDumpQuadMemory(&tAddr, 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("-- User interrupt\n"); return; } } } while (SearchLength > 0 && st == S_OK); } void ParseSearchMemory(void) { ADDR Addr; ULONG64 Length; UCHAR Pat[STRLISTSIZE]; ULONG PatLen; ULONG Gran; while (*g_CurCmd == ' ') { g_CurCmd++; } Gran = 1; if (*g_CurCmd == '-') { g_CurCmd++; switch(*g_CurCmd) { case 'w': Gran = 2; break; case 'd': Gran = 4; break; case 'q': Gran = 8; break; default: error(SYNTAX); break; } g_CurCmd++; } ADDRFLAT(&Addr, 0); Length = 16; GetRange(&Addr, &Length, Gran, SEGREG_DATA); if (Flat(Addr)) { HexList(Pat, &PatLen, Gran); if (PatLen == 0) { PCSTR Err = "Search pattern missing from"; ReportError(SYNTAX, &Err); } fnSearchMemory(&Addr, Length * Gran, Pat, PatLen, Gran); } } /*** fnInputIo - read and output io * * Purpose: * Function of "ib, iw, id
" 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 fnInputIo( 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"); } /*** fnOutputIo - output io * * Purpose: * Function of "ob, ow, od
" 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 fnOutputIo ( 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); }