HRESULT DecodeILFromAddress(IMetaDataImport *pImport, TADDR ilAddr) { HRESULT Status = S_OK; ULONG Size = GetILSize(ilAddr); if (Size == 0) { ExtOut("error decoding IL\n"); return Status; } ExtOut("ilAddr = %p\n", (ULONG64) ilAddr); // Read the memory into a local buffer ArrayHolder<BYTE> pArray = new BYTE[Size]; Status = g_ExtData->ReadVirtual(TO_CDADDR(ilAddr), pArray, Size, NULL); if (Status != S_OK) { ExtOut("Failed to read memory\n"); return Status; } DecodeIL(pImport, pArray, Size); return Status; }
void ExtExec(PCSTR Command) { ExtOut("Exec: %s\n", Command); g_ExtControl->Execute(DEBUG_OUTCTL_ALL_CLIENTS, Command,DEBUG_EXECUTE_DEFAULT); //g_ExtControl->Execute(DEBUG_OUTCTL_LOG_ONLY, Command,DEBUG_EXECUTE_DEFAULT); }
char* PrintOneLine (__in_z char *begin, __in_z char *limit) { if (begin == NULL || begin >= limit) { return NULL; } char line[128]; size_t length; char *end; while (1) { if (IsInterrupt()) return NULL; length = strlen (begin); end = strstr (begin, "\r\xa"); if (end == NULL) { ExtOut ("%s", begin); end = begin+length+1; if (end >= limit) { return NULL; } } else { end += 2; length = end-begin; while (length) { if (IsInterrupt()) return NULL; size_t n = length; if (n > 127) { n = 127; } strncpy_s (line,_countof(line), begin, n); line[n] = '\0'; ExtOut ("%s", line); begin += n; length -= n; } return end; } } }
void ExtCleanup(void) { /** * clean up any resources */ ExtOut(__FUNCTION__"()\n"); if (g_DataSpaces) { g_DataSpaces->Release(); } if (g_Registers) { g_Registers->Release(); } if (g_Control) { g_Control->Release(); } if (g_Symbols) { g_Symbols->Release(); } if (g_SystemObjects) { g_SystemObjects->Release(); } if (g_Client) { g_Client->Release(); } }
// Evaluates and prints a tree version of the active watch list // The tree will be expanded along the nodes in expansionPath // Optionally the list is filtered to only show differences from pFilterName (the name of a persisted watch list) HRESULT WatchCmd::Print(int expansionIndex, __in_z WCHAR* expansionPath, __in_z WCHAR* pFilterName) { HRESULT Status = S_OK; INIT_API_EE(); INIT_API_DAC(); EnableDMLHolder dmlHolder(TRUE); IfFailRet(InitCorDebugInterface()); PersistList* pFilterList = NULL; if(pFilterName != NULL) { pFilterList = pPersistListHead; while(pFilterList != NULL) { if(_wcscmp(pFilterList->pName, pFilterName)==0) break; pFilterList = pFilterList->pNext; } } PersistWatchExpression* pHeadFilterExpr = (pFilterList != NULL) ? pFilterList->pHeadExpr : NULL; WatchExpression* pExpression = pExpressionListHead; int index = 1; while(pExpression != NULL) { ExpressionNode* pResult = NULL; if(FAILED(Status = ExpressionNode::CreateExpressionNode(pExpression->pExpression, &pResult))) { ExtOut(" %d) Error: HRESULT 0x%x while evaluating expression \'%S\'", index, Status, pExpression->pExpression); } else { //check for matching absolute expression PersistWatchExpression* pCurFilterExpr = pHeadFilterExpr; while(pCurFilterExpr != NULL) { if(_wcscmp(pCurFilterExpr->pExpression, pResult->GetAbsoluteExpression())==0) break; pCurFilterExpr = pCurFilterExpr->pNext; } // check for matching persist evaluation on the matching expression BOOL print = TRUE; if(pCurFilterExpr != NULL) { WCHAR pCurPersistResult[MAX_EXPRESSION]; FormatPersistResult(pCurPersistResult, MAX_EXPRESSION, pResult); if(_wcscmp(pCurPersistResult, pCurFilterExpr->pPersistResult)==0) { print = FALSE; } } //expand and print if(print) { if(index == expansionIndex) pResult->Expand(expansionPath); PrintCallbackData data; data.index = index; WCHAR pCommand[MAX_EXPRESSION]; swprintf_s(pCommand, MAX_EXPRESSION, L"!watch -expand %d", index); data.pCommand = pCommand; pResult->DFSVisit(EvalPrintCallback, (VOID*)&data); } delete pResult; } pExpression = pExpression->pNext; index++; } return Status; }
void UnassemblyUnmanaged(DWORD_PTR IP, BOOL bSuppressLines) { char filename[MAX_PATH_FNAME+1]; char line[256]; int lcount = 10; ULONG linenum = 0; ULONG64 Displacement = 0; BOOL fLineAvailable = FALSE; ULONG64 vIP = 0; if (!bSuppressLines) { ReloadSymbolWithLineInfo(); fLineAvailable = SUCCEEDED (g_ExtSymbols->GetLineByOffset (TO_CDADDR(IP), &linenum, filename, MAX_PATH_FNAME+1, NULL, &Displacement)); } ULONG FileLines = 0; ArrayHolder<ULONG64> Buffer = NULL; if (fLineAvailable) { g_ExtSymbols->GetSourceFileLineOffsets (filename, NULL, 0, &FileLines); if (FileLines == 0xFFFFFFFF || FileLines == 0) fLineAvailable = FALSE; } if (fLineAvailable) { Buffer = new ULONG64[FileLines]; if (Buffer == NULL) fLineAvailable = FALSE; } if (!fLineAvailable) { vIP = TO_CDADDR(IP); // There is no line info. Just disasm the code. while (lcount-- > 0) { if (IsInterrupt()) return; g_ExtControl->Disassemble (vIP, 0, line, 256, NULL, &vIP); ExtOut (line); } return; } g_ExtSymbols->GetSourceFileLineOffsets (filename, Buffer, FileLines, NULL); int beginLine = 0; int endLine = 0; int lastLine; linenum --; for (lastLine = linenum; lastLine >= 0; lastLine --) { if (IsInterrupt()) return; if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) { g_ExtSymbols->GetNameByOffset(Buffer[lastLine],NULL,0,NULL,&Displacement); if (Displacement == 0) { beginLine = lastLine; break; } } } if (lastLine < 0) { int n = lcount / 2; lastLine = linenum-1; beginLine = lastLine; while (lastLine >= 0) { if (IsInterrupt()) return; if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) { beginLine = lastLine; n --; if (n == 0) { break; } } lastLine --; } } while (beginLine > 0 && Buffer[beginLine-1] == DEBUG_INVALID_OFFSET) { if (IsInterrupt()) return; beginLine --; } int endOfFunc = 0; for (lastLine = linenum+1; (ULONG)lastLine < FileLines; lastLine ++) { if (IsInterrupt()) return; if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) { g_ExtSymbols->GetNameByOffset(Buffer[lastLine],NULL,0,NULL,&Displacement); if (Displacement == 0) { endLine = lastLine; break; } endOfFunc = lastLine; } } if ((ULONG)lastLine == FileLines) { int n = lcount / 2; lastLine = linenum+1; endLine = lastLine; while ((ULONG)lastLine < FileLines) { if (IsInterrupt()) return; if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) { endLine = lastLine; n --; if (n == 0) { break; } } lastLine ++; } } PVOID MappedBase = NULL; ULONG MappedSize = 0; class ToUnmap { PVOID *m_Base; public: ToUnmap (PVOID *base) :m_Base(base) {} ~ToUnmap () { if (*m_Base) { UnmapViewOfFile (*m_Base); *m_Base = NULL; } } }; ToUnmap toUnmap(&MappedBase); #define MAX_SOURCE_PATH 1024 char Found[MAX_SOURCE_PATH]; char *pFile; if (g_ExtSymbols->FindSourceFile(0, filename, DEBUG_FIND_SOURCE_BEST_MATCH | DEBUG_FIND_SOURCE_FULL_PATH, NULL, Found, sizeof(Found), NULL) != S_OK) { pFile = filename; } else { MappedBase = GenOpenMapping ( Found, &MappedSize ); pFile = Found; } lastLine = beginLine; char *pFileCh = (char*)MappedBase; if (MappedBase) { ExtOut ("%s\n", pFile); int n = beginLine; while (n > 0) { while (!(pFileCh[0] == '\r' && pFileCh[1] == 0xa)) { if (IsInterrupt()) return; pFileCh ++; } pFileCh += 2; n --; } } char filename1[MAX_PATH_FNAME+1]; for (lastLine = beginLine; lastLine < endLine; lastLine ++) { if (IsInterrupt()) return; if (MappedBase) { ExtOut ("%4d ", lastLine+1); pFileCh = PrintOneLine (pFileCh, (char*)MappedBase+MappedSize); } if (Buffer[lastLine] != DEBUG_INVALID_OFFSET) { if (MappedBase == 0) { ExtOut (">>> %s:%d\n", pFile, lastLine+1); } vIP = Buffer[lastLine]; ULONG64 vNextLineIP; int i; for (i = lastLine + 1; (ULONG)i < FileLines && Buffer[i] == DEBUG_INVALID_OFFSET; i ++) { if (IsInterrupt()) return; } if ((ULONG)i == FileLines) { vNextLineIP = 0; } else vNextLineIP = Buffer[i]; while (1) { if (IsInterrupt()) return; g_ExtControl->Disassemble (vIP, 0, line, 256, NULL, &vIP); ExtOut (line); if (vIP > vNextLineIP || vNextLineIP - vIP > 40) { if (FAILED (g_ExtSymbols->GetLineByOffset (vIP, &linenum, filename1, MAX_PATH_FNAME+1, NULL, &Displacement))) { if (lastLine != endOfFunc) { break; } if (strstr (line, "ret") || strstr (line, "jmp")) { break; } } if (linenum != (ULONG)lastLine+1 || strcmp (filename, filename1)) { break; } } else if (vIP == vNextLineIP) { break; } } } } }
HRESULT CALLBACK DebugExtensionInitialize(PULONG Version, PULONG Flags) { IDebugClient *DebugClient; PDEBUG_CONTROL DebugControl; HRESULT Hr; *Version = DEBUG_EXTENSION_VERSION(1, 0); *Flags = 0; if (g_Initialized) { return S_OK; } g_Initialized = true; if ((Hr = DebugCreate(__uuidof(IDebugClient), (void **)&DebugClient)) != S_OK) { return Hr; } if ((Hr = DebugClient->QueryInterface(__uuidof(IDebugControl), (void **)&DebugControl)) != S_OK) { return Hr; } ExtensionApis.nSize = sizeof (ExtensionApis); if ((Hr = DebugControl->GetWindbgExtensionApis64(&ExtensionApis)) != S_OK) { return Hr; } // Fixes the "Unable to read dynamic function table entries" error messages by disabling the WinDbg security // feature that prevents the loading of unknown out of proc tack walkers. DebugControl->Execute(DEBUG_OUTCTL_IGNORE, ".settings set EngineInitialization.VerifyFunctionTableCallbacks=false", DEBUG_EXECUTE_NOT_LOGGED | DEBUG_EXECUTE_NO_REPEAT); ExtQuery(DebugClient); if (IsMiniDumpFileNODAC()) { ExtOut ( "----------------------------------------------------------------------------\n" "The user dump currently examined is a minidump. Consequently, only a subset\n" "of sos.dll functionality will be available. If needed, attaching to the live\n" "process or debugging a full dump will allow access to sos.dll's full feature\n" "set.\n" "To create a full user dump use the command: .dump /ma <filename>\n" "----------------------------------------------------------------------------\n"); } ExtRelease(); OnUnloadTask::Register(CleanupEventCallbacks); g_pCallbacksClient = DebugClient; EventCallbacks* pCallbacksObj = new EventCallbacks(DebugClient); IDebugEventCallbacks* pCallbacks = NULL; pCallbacksObj->QueryInterface(__uuidof(IDebugEventCallbacks), (void**)&pCallbacks); pCallbacksObj->Release(); if(FAILED(Hr = g_pCallbacksClient->SetEventCallbacks(pCallbacks))) { ExtOut ("SOS: Failed to register callback events\n"); pCallbacks->Release(); return Hr; } pCallbacks->Release(); #ifndef _ARM_ // Make sure we do not tear down the debugger when a security function fails // Since we link statically against CRT this will only affect the SOS module. _set_invalid_parameter_handler(_SOS_invalid_parameter); #endif DebugControl->Release(); return S_OK; }
//-------------------------------------------------------------------------------------- VOID WDBGAPI WinDbgExtensionDllInit( PWINDBG_EXTENSION_APIS lpExtensionApis, USHORT usMajorVersion, USHORT usMinorVersion) { if (g_RefCount > 0) { // extension is allready initialized return; } HRESULT Hr = DebugCreate(__uuidof(IDebugClient), (void **)&g_Client); if (Hr != S_OK) { MessageBoxA(0, "DebugCreate() fails", __FUNCTION__, MB_ICONERROR); return; } Hr = g_Client->QueryInterface(__uuidof(IDebugControl), (void **)&g_Control); if (Hr != S_OK) { MessageBoxA( 0, "DebugClient::QueryInterface(IDebugControl) fails", __FUNCTION__, MB_ICONERROR ); ExitProcess(0); } ULONG TargetMachine = 0; Hr = g_Control->GetActualProcessorType(&TargetMachine); if (Hr == S_OK) { switch (TargetMachine) { case IMAGE_FILE_MACHINE_I386: g_bIs64 = FALSE; g_RegPtrType = DEBUG_VALUE_INT32; break; case IMAGE_FILE_MACHINE_AMD64: g_bIs64 = TRUE; g_RegPtrType = DEBUG_VALUE_INT64; break; default: MessageBoxA( 0, "Target architecture is not supported", __FUNCTION__, MB_ICONERROR ); ExitProcess(0); break; } } else { MessageBoxA( 0, "DebugControl::GetActualProcessorType() fails", __FUNCTION__, MB_ICONERROR ); ExitProcess(0); } Hr = g_Client->QueryInterface(__uuidof(IDebugSymbols3), (void **)&g_Symbols); if (Hr != S_OK) { MessageBoxA( 0, "DebugClient::QueryInterface(IDebugSymbols3) fails", __FUNCTION__, MB_ICONERROR ); ExitProcess(0); } Hr = g_Client->QueryInterface(__uuidof(IDebugSystemObjects), (void **)&g_SystemObjects); if (Hr != S_OK) { MessageBoxA( 0, "DebugClient::QueryInterface(IDebugSystemObjects) fails", __FUNCTION__, MB_ICONERROR ); ExitProcess(0); } Hr = g_Client->QueryInterface(__uuidof(IDebugRegisters), (void **)&g_Registers); if (Hr != S_OK) { MessageBoxA( 0, "DebugClient::QueryInterface(IDebugRegisters) fails", __FUNCTION__, MB_ICONERROR ); ExitProcess(0); } Hr = g_Client->QueryInterface(__uuidof(IDebugDataSpaces), (void **)&g_DataSpaces); if (Hr != S_OK) { MessageBoxA( 0, "DebugClient::QueryInterface(IDebugDataSpaces) fails", __FUNCTION__, MB_ICONERROR ); ExitProcess(0); } char *lpszEip = "eip", *lpszEax = "eax", *lpszEcx = "ecx"; if (g_bIs64) { // use 64-bit registers for parameter and return value lpszEip = "rip"; lpszEax = "rax"; lpszEcx = "rcx"; } // Find the register index for eip/rip Hr = g_Registers->GetIndexByName(lpszEip, &g_EipIndex); if (Hr != S_OK) { MessageBoxA( 0, "DebugRegisters::GetIndexByName() fails", __FUNCTION__, MB_ICONERROR ); ExitProcess(0); } // Find the register index for eax/rax Hr = g_Registers->GetIndexByName(lpszEax, &g_EaxIndex); if (Hr != S_OK) { MessageBoxA( 0, "DebugRegisters::GetIndexByName() fails", __FUNCTION__, MB_ICONERROR ); ExitProcess(0); } // Find the register index for ecx/rcx Hr = g_Registers->GetIndexByName(lpszEcx, &g_EcxIndex); if (Hr != S_OK) { MessageBoxA( 0, "DebugRegisters::GetIndexByName() fails", __FUNCTION__, MB_ICONERROR ); ExitProcess(0); } // Find the register index for edx Hr = g_Registers->GetIndexByName("edx", &g_EdxIndex); if (Hr != S_OK) { MessageBoxA( 0, "DebugRegisters::GetIndexByName() fails", __FUNCTION__, MB_ICONERROR ); ExitProcess(0); } // Register our event callbacks. Hr = g_Client->SetEventCallbacks(&g_EventCb); if (Hr != S_OK) { MessageBoxA( 0, "DebugClient::SetEventCallbacks() fails", __FUNCTION__, MB_ICONERROR ); ExitProcess(0); } ExtOut("<col fg=\"srckw\">" __FUNCTION__"(): Initialized (x64: %s)</col>\n", g_bIs64 ? "Yes" : "No"); }
STDMETHODIMP EventCallbacks::Exception( THIS_ IN PEXCEPTION_RECORD64 Exception, IN ULONG FirstChance) { g_ResumeState = FALSE; if (Exception->ExceptionCode == STATUS_BREAKPOINT) { if (FirstChance) { DEBUG_VALUE Reg, Ecx, Edx; // Query EIP, EAX and ECX value. if (g_Registers->GetValue(g_EipIndex, &Reg) == S_OK && g_Registers->GetValue(g_EdxIndex, &Edx) == S_OK && g_Registers->GetValue(g_EcxIndex, &Ecx) == S_OK) { char szParam[MAX_PATH]; ULONG ReadedBytes = 0; // Read current instruction opcode value. ZeroMemory(szParam, sizeof(szParam)); HRESULT Hr = g_DataSpaces->ReadVirtual(RegPtrGet(&Reg), &szParam, 1, &ReadedBytes); if (Hr != S_OK) { ExtErr(__FUNCTION__"() ERROR: IDebugDataSpaces::ReadVirtual() fails: %lx\n", Hr); return DEBUG_STATUS_NO_CHANGE; } // Check for int 3 at EIP. if (szParam[0] != '\xCC') { return DEBUG_STATUS_NO_CHANGE; } // Check for the magic engine constnat in EDX. if (Edx.I32 != DBGCB_GET_SYMBOL && Edx.I32 != DBGCB_EXECUTE && Edx.I32 != DBGCB_FIELD_OFFSET) { return DEBUG_STATUS_NO_CHANGE; } g_ResumeState = TRUE; // Read ASCII string with command arguments. ZeroMemory(szParam, sizeof(szParam)); Hr = g_DataSpaces->ReadVirtual(RegPtrGet(&Ecx), &szParam, sizeof(szParam), &ReadedBytes); if (Hr != S_OK) { ExtErr(__FUNCTION__"() ERROR: IDebugDataSpaces::ReadVirtual() fails: %lx\n", Hr); return DEBUG_STATUS_NO_CHANGE; } switch (Edx.I32) { case DBGCB_GET_SYMBOL: { ExtOut("<col fg=\"srccmnt\">" __FUNCTION__"(): DBGCB_GET_SYMBOL \"%s\"</col>\n", szParam); RegPtrSet(&Reg, 0); g_Registers->SetValue(g_EaxIndex, &Reg); Hr = g_Control->Evaluate(szParam, g_RegPtrType, &Reg, NULL); if (Hr == S_OK) { // Return symbol address in EAX. g_Registers->SetValue(g_EaxIndex, &Reg); } else { ExtErr(__FUNCTION__"() WARNING: IDebugControl::Evaluate() fails: %lx\n", Hr); } break; } case DBGCB_EXECUTE: { ExtOut("<col fg=\"srccmnt\">" __FUNCTION__ "(): DBGCB_EXECUTE</col>\n"); // execute debugger command Hr = g_Control->Execute( DEBUG_OUTCTL_ALL_CLIENTS | DEBUG_OUTCTL_AMBIENT_DML, szParam, DEBUG_EXECUTE_DEFAULT ); if (Hr == S_OK) { // Return TRUE in EAX RegPtrSet(&Reg, 1); g_Registers->SetValue(g_EaxIndex, &Reg); } else { ExtErr(__FUNCTION__"() WARNING: IDebugControl::Execute() fails: %lx\n", Hr); } break; } case DBGCB_FIELD_OFFSET: { RegPtrSet(&Reg, (ULONG64)-1); char *lpszModule = szParam, *lpszStruct = NULL, *lpszField = NULL; ExtOut("<col fg=\"srccmnt\">" __FUNCTION__"(): DBGCB_FIELD_OFFSET \"%s\"</col>\n", szParam); // parse structure and field description string if (lpszStruct = strstr(lpszModule, "!")) { *lpszStruct = '\x00'; lpszStruct += 1; if (lpszField = strstr(lpszStruct, "::")) { *lpszField = '\x00'; lpszField += 2; } } if (lpszStruct && lpszField) { // enumerate fields for (ULONG i = 0; ;i++) { ULONG64 Module = 0; ULONG TypeId = 0; // get ID of this symbol Hr = g_Symbols->GetSymbolTypeId(lpszStruct, &TypeId, &Module); if (Hr == S_OK) { char szFieldName[MAX_PATH]; // query name of the filed HRESULT Hr = g_Symbols->GetFieldName(Module, TypeId, i, szFieldName, MAX_PATH, NULL); if (Hr == S_OK) { ULONG Offset = 0, FieldTypeId = 0; // query filed type and offset Hr = g_Symbols->GetFieldTypeAndOffset(Module, TypeId, szFieldName, &FieldTypeId, &Offset); if (Hr == S_OK) { if (!strcmp(szFieldName, lpszField)) { // Return symbol offset in EAX RegPtrSet(&Reg, (ULONG64)Offset); break; } } else { ExtErr(__FUNCTION__"() WARNING: IDebugSymbols3::GetFieldTypeAndOffset() fails: %lx\n", Hr); } } else if (Hr == E_INVALIDARG) { // All Fields done break; } else { ExtErr(__FUNCTION__"() WARNING: IDebugSymbols3::GetFieldName() fails: %lx\n", Hr); } } else { ExtErr(__FUNCTION__"() WARNING: IDebugSymbols3::GetSymbolTypeId() fails: %lx\n", Hr); } } } else { ExtErr(__FUNCTION__"() WARNING: Bad name format (must be <module>!<struct_name>::<field_name>)\n"); } g_Registers->SetValue(g_EaxIndex, &Reg); break; } default: return DEBUG_STATUS_NO_CHANGE; } // Skip current int 3 instruction and continue execution if (g_Registers->GetValue(g_EipIndex, &Reg) == S_OK && Reg.Type == DEBUG_VALUE_INT32) { if (g_bIs64) { Reg.I64 += 1; } else { Reg.I32 += 1; } g_Registers->SetValue(g_EipIndex, &Reg); } return DEBUG_STATUS_GO_HANDLED; } } } return DEBUG_STATUS_NO_CHANGE; }