private void DebuggerThread() { Console.WriteLine($"Attaching to {_process.ProcessName}, pid: {_process.Id}"); var metahost = CorDebugHelper.GetClrMetaHost(); var runtimes = metahost.EnumerateLoadedRuntimes(_process.Handle); string version = null; ICorDebug corDebug = null; while (runtimes.Next(1, out var rgelt, IntPtr.Zero) == 0) { var runtimeInfo = (ICLRRuntimeInfo)rgelt; var pwzBuffer = new StringBuilder(30); int capacity = pwzBuffer.Capacity; runtimeInfo.GetVersionString(pwzBuffer, ref capacity); version = pwzBuffer.ToString(); var riid = typeof(ICorDebug).GUID; var rclsid = typeof(ClrDebuggingLegacy).GUID; corDebug = (ICorDebug)runtimeInfo.GetInterface(ref rclsid, ref riid); } if (corDebug == null) { throw new Exception("error: cannot take corDebug"); } Console.WriteLine($"info: runtime: {version}"); corDebug.Initialize(); corDebug.SetManagedHandler(_debuggerCallbacks); corDebug.SetUnmanagedHandler(_debuggerCallbacks); corDebug.DebugActiveProcess((uint)_process.Id, 0, out _debugger); while (_debuggingActive) { Thread.Sleep(WAIT_INTERVAL); } if (!_process.HasExited) { _debugger.Stop(WAIT_INTERVAL); _debugger.Detach(); } }
private void DebugHandlersOnExceptionHandled(object sender, DebuggerCallbacks.ExceptionEventArgs e) { // TODO Test x64 offsets var isX64 = IntPtr.Size == 8; var exceptionInformationStart = 0; var exceptionInformationThreadId = exceptionInformationStart + 0; var exceptionInformationExceptionPointers = exceptionInformationStart + 4; var exceptionInformationClientPointers = exceptionInformationStart + (isX64 ? 12 : 8); var exceptionInformationSize = isX64 ? 16 : 12; var exceptionPointersStart = exceptionInformationStart + exceptionInformationSize; var exceptionPointersExceptionRecord = exceptionPointersStart + 0; var exceptionPointersThreadContext = exceptionPointersStart + (isX64 ? 8 : 4); var exceptionPointersSize = isX64 ? 16 : 8; var exceptionRecordStart = exceptionPointersStart + exceptionPointersSize; var exceptionRecordCode = exceptionRecordStart + 0; var exceptionRecordAddress = exceptionRecordStart + (isX64 ? 16 : 12); var exceptionRecordSize = isX64 ? 152 : 80; var threadContextStart = exceptionRecordStart + exceptionRecordSize; var threadContextFlags = threadContextStart + (isX64 ? 48 : 0); var threadContextEip = threadContextStart + (isX64 ? 248 : 184); var threadContextSize = isX64 ? 1232 : 716; var exCodeBuffer = Marshal.AllocHGlobal(4); var exceptionInformation = Marshal.AllocHGlobal(threadContextStart + threadContextSize); try { e.Thread.GetCurrentException(out var ex); ex.GetType(out var type); ((ICorDebugValue2)ex).GetExactType(out var exType); exType.GetClass(out var exClass); exClass.GetToken(out var exToken); exClass.GetModule(out var module); var riid = typeof(IMetadataImport).GUID; module.GetMetaDataInterface(ref riid, out var metadataImport); var exTypeString = CorDebugHelper.GetTypeName(exType); ((ICorDebugClass2)exClass).GetParameterizedType(CorElementType.ELEMENT_TYPE_CLASS, 0, null, out var exBaseType); while (CorDebugHelper.GetTypeName(exBaseType) != "System.Exception") { exBaseType.GetBase(out exBaseType); } exBaseType.GetClass(out var exBaseClass); exBaseClass.GetToken(out var exBaseToken); exBaseClass.GetModule(out var exBaseModule); exBaseModule.GetMetaDataInterface(ref riid, out var baseMetadataImport); baseMetadataImport.FindField((int)exBaseToken, "_message", null, 0, out var messageField); ((ICorDebugReferenceValue)ex).Dereference(out var exReal); ((ICorDebugObjectValue)exReal).GetFieldValue(exBaseClass, (uint)messageField, out var messageValue); ((ICorDebugReferenceValue)messageValue).Dereference(out var messageReal); var messageString = (ICorDebugStringValue)messageReal; messageString.GetLength(out var messageLength); var messageBuilder = new StringBuilder((int)messageLength + 1); messageString.GetString((uint)messageBuilder.Capacity, out _, messageBuilder); baseMetadataImport.FindField((int)exBaseToken, "_xcode", null, 0, out var codeField); ((ICorDebugObjectValue)exReal).GetFieldValue(exBaseClass, (uint)codeField, out var codeValue); ((ICorDebugGenericValue)codeValue).GetValue(exCodeBuffer); var code = Marshal.ReadInt32(exCodeBuffer); var message = $"{code:X8}.{exTypeString} {messageBuilder}"; Console.WriteLine($"exception: {message}"); var takeDump = e.EventType == CorDebugExceptionCallbackType.DEBUG_EXCEPTION_UNHANDLED || e.EventType == CorDebugExceptionCallbackType.DEBUG_EXCEPTION_FIRST_CHANCE && _exceptions.Any(x => message.Contains(x)); if (!takeDump) { return; } e.Thread.GetProcess(out var process); e.Thread.GetID(out var threadId); process.GetHandle(out var processHandle); e.Thread.GetRegisterSet(out var registerSet); // MINIDUMP_EXCEPTION_INFORMATION Marshal.WriteInt32(exceptionInformation, exceptionInformationThreadId, (int)threadId); Marshal.WriteIntPtr(exceptionInformation, exceptionInformationExceptionPointers, IntPtr.Add(exceptionInformation, exceptionPointersStart)); Marshal.WriteInt32(exceptionInformation, exceptionInformationClientPointers, 0); // EXCEPTION_POINTERS Marshal.WriteIntPtr(exceptionInformation, exceptionPointersExceptionRecord, IntPtr.Add(exceptionInformation, exceptionRecordStart)); Marshal.WriteIntPtr(exceptionInformation, exceptionPointersThreadContext, IntPtr.Add(exceptionInformation, threadContextStart)); // CONTEXT Marshal.WriteInt64(exceptionInformation, threadContextFlags, 0x1003F); // CONTEXT_ALL registerSet.GetThreadContext((uint)threadContextSize, IntPtr.Add(exceptionInformation, threadContextStart)); var eip = Marshal.ReadIntPtr(exceptionInformation, threadContextEip); // EXCEPTION_RECORD for (var i = 0; i < exceptionRecordSize; i += 4) { Marshal.WriteInt32(exceptionInformation, exceptionRecordStart + i, 0); } Marshal.WriteInt32(exceptionInformation, exceptionRecordCode, code); Marshal.WriteIntPtr(exceptionInformation, exceptionRecordAddress, eip); TakeDump(processHandle, null, exceptionInformation); } catch (Exception ex) { Console.WriteLine($"error: {ex.Message}"); } finally { Marshal.FreeHGlobal(exCodeBuffer); Marshal.FreeHGlobal(exceptionInformation); } }