private static (uint, ulong) PopulateReferencedObjects(ClrHeap heap, ClrObjectModel clrObjModel) { // Evaluation stack var eval = new Stack <ulong>(); var addressToClrObjectModel = new Dictionary <ulong, ClrObjectModel> { { clrObjModel.Address, clrObjModel } }; // To make sure we don't count the same object twice, we'll keep a set of all objects // we've seen before. Note the ObjectSet here is basically just "HashSet<ulong>". // However, HashSet<ulong> is *extremely* memory inefficient. So we use our own to // avoid OOMs. var currentObj = clrObjModel.Address; var considered = new ObjectSet(heap); uint count = 0; ulong size = 0; eval.Push(currentObj); while (eval.Count > 0) { // Pop an object, ignore it if we've seen it before. currentObj = eval.Pop(); if (considered.Contains(currentObj)) { continue; } considered.Add(currentObj); // Grab the type. We will only get null here in the case of heap corruption. ClrType type = heap.GetObjectType(currentObj); if (type == null) { continue; } count++; size += type.GetSize(currentObj); // Now enumerate all objects that this object points to, add them to the // evaluation stack if we haven't seen them before. type.EnumerateRefsOfObject(currentObj, delegate(ulong child, int offset) { if (child != 0 && !considered.Contains(child)) { var childObj = heap.GetObject(child); ClrObjectModel childClrModel = null; if (addressToClrObjectModel.ContainsKey(childObj)) { childClrModel = addressToClrObjectModel[childObj]; } else { childClrModel = new ClrObjectModel { TypeName = childObj.Type.Name, Address = childObj, ReferencedObjects = new List <ClrObjectModel>(), }; addressToClrObjectModel[currentObj] = childClrModel; } addressToClrObjectModel[currentObj].ReferencedObjects.Add(childClrModel); eval.Push(child); } }); } return(count, size); }
public void OptimizedClassHistogram() { using (DataTarget dt = TestTargets.Types.LoadFullDump()) { ClrRuntime runtime = dt.ClrVersions.Single().CreateRuntime(); ClrHeap heap = runtime.Heap; // ensure that the optimized version is faster than the default one Stopwatch timing = new Stopwatch(); var notOptimizedStatistics = new Dictionary <ClrType, TypeEntry>(32768); int notOptimizedObjectCount = 0; // with an optimized version timing.Start(); foreach (var objDetails in heap.EnumerateObjectDetails()) { ClrType type = objDetails.Item3; ulong size = objDetails.Item4; TypeEntry entry; if (!notOptimizedStatistics.TryGetValue(type, out entry)) { entry = new TypeEntry() { TypeName = type.Name, Size = 0 }; notOptimizedStatistics[type] = entry; } entry.Count++; entry.Size += size; notOptimizedObjectCount++; } timing.Stop(); var notOptimizedTime = TimeSpan.FromMilliseconds(timing.ElapsedMilliseconds); // with an non-optimized version var statistics = new Dictionary <ClrType, TypeEntry>(32768); int objectCount = 0; timing.Restart(); foreach (ulong objAddress in heap.EnumerateObjectAddresses()) { ClrType type = heap.GetObjectType(objAddress); ulong size = type.GetSize(objAddress); TypeEntry entry; if (!statistics.TryGetValue(type, out entry)) { entry = new TypeEntry() { TypeName = type.Name, Size = 0 }; statistics[type] = entry; } entry.Count++; entry.Size += size; objectCount++; } timing.Stop(); // check object count Assert.AreEqual(notOptimizedObjectCount, objectCount); // check heap content var types = notOptimizedStatistics.Keys; foreach (var type in types) { var notOptimizedDetails = notOptimizedStatistics[type]; var details = statistics[type]; Assert.AreEqual(notOptimizedDetails.TypeName, details.TypeName); Assert.AreEqual(notOptimizedDetails.Count, details.Count); Assert.AreEqual(notOptimizedDetails.Size, details.Size); } Assert.AreEqual(types.Count, statistics.Count); // checking that optimized is faster could be flaky // Assert.IsTrue(notOptimizedTime > TimeSpan.FromMilliseconds(timing.ElapsedMilliseconds)); } }
private Dictionary<int, string> GetManagedThreadNames(ClrHeap heap) { var result = new Dictionary<int, string>(); if (!heap.CanWalkHeap) return result; var threadObjects = from obj in heap.EnumerateObjectAddresses() let type = heap.GetObjectType(obj) where type != null && type.Name == "System.Threading.Thread" select obj; var threadType = heap.GetTypeByName("System.Threading.Thread"); var nameField = threadType.GetFieldByName("m_Name"); var managedIdField = threadType.GetFieldByName("m_ManagedThreadId"); foreach (var threadObject in threadObjects) { string name = (string)nameField.GetValue(threadObject); int id = (int)managedIdField.GetValue(threadObject); result.Add(id, name); } return result; }
private static KeyValuePair <bool, string> Enumerate() { //Attach to target process DataTarget dt = null; try { dt = DataTarget.AttachToProcess(targetPID, 10000, AttachFlag.NonInvasive); } catch (Exception e) { return(new KeyValuePair <bool, string>(false, e.ToString())); } //If no ClrVersions, return if (dt.ClrVersions.Count == 0) { return(new KeyValuePair <bool, string>(false, "[!] No Clr Versions detected")); } #if DEBUG foreach (var ver in dt.ClrVersions) { Console.WriteLine("Clr Runtime Version Found: " + ver.Version.ToString()); } #endif ClrInfo Version = dt.ClrVersions[0]; try { cRun = Version.CreateRuntime(); #if DEBUG Console.WriteLine("[+] Created Runtime"); #endif } catch (Exception e) { #if DEBUG Console.WriteLine("[!] Failed to create runtime"); #endif return(new KeyValuePair <bool, string>(false, e.ToString())); } ClrHeap Heap = cRun.GetHeap(); //if we can't walk the heap, return if (!Heap.CanWalkHeap) { return(new KeyValuePair <bool, string>(false, "[!] Unable to walk the heap")); } Console.WriteLine("[+] Walking the heap...."); string m = WildCardToRegWithQM("System.*"); string m1 = WildCardToRegWithQM("_*"); string m2 = WildCardToRegWithQM("Microsoft.*"); foreach (ulong obj in Heap.EnumerateObjectAddresses()) { //Grab each object, check if it has a simple value, if so, display it ClrType type = Heap.GetObjectType(obj); if (TypeName != null) { if (type == null && type.Name == TypeName || Regex.IsMatch(type.Name, m) || Regex.IsMatch(type.Name, m1) || Regex.IsMatch(type.Name, m2) || type.Name == "Free") { continue; } } else { if (type == null || Regex.IsMatch(type.Name, m) || Regex.IsMatch(type.Name, m1) || Regex.IsMatch(type.Name, m2) || type.Name == "Free") { continue; } } if (!type.IsPrimitive) { #if DEBUG Console.WriteLine("[+] Enumerating type: " + type.Name); #endif //if the type has a simple value, add the type and its value to the results resultOutput.Append("\r\nType: " + type.Name + "\r\n\r\n"); //Enumerate all of the instance fields for the given type if (showFields) { if (type.Fields != null) { GetInstanceFields(type.Fields, obj); } } if (showstaticFields) { if (type.StaticFields != null) { GetStaticFields(type.StaticFields, cRun.AppDomains[0]); } } if (showMethods) { if (type.Methods != null) { GetMethods(type.Methods); } } if (referenceObjects) { resultOutput.Append("\r\nReferencedTypes\r\n\r\n"); List <ulong> referencedObjects = ClrMDHelper.GetReferencedObjects(Heap, obj); foreach (ulong refObj in referencedObjects) { ClrType refObjType = Heap.GetObjectType(refObj); if (refObjType == null || Regex.IsMatch(refObjType.Name, m) || Regex.IsMatch(refObjType.Name, m1) || Regex.IsMatch(refObjType.Name, m2) || refObjType.Name == "Free") { continue; } if (showFields) { if (refObjType.Fields != null) { GetInstanceFields(refObjType.Fields, obj, fieldName); } } if (showstaticFields) { if (refObjType.StaticFields != null) { GetStaticFields(refObjType.StaticFields, cRun.AppDomains[0], fieldName); } } if (showMethods) { if (refObjType.Methods != null) { GetMethods(refObjType.Methods); } } } } } } return(new KeyValuePair <bool, string>(true, "[+] Successfully walked the heap.")); }
private static void DumpRetention(DataTarget dataTarget, ClrInfo clrVersion, ClrRuntime runtime, ClrAppDomain appDomain, ClrHeap heap, string targetType) { var graph = new Graph(); Console.WriteLine("## What's the retention path of the {0} object?", targetType); Console.WriteLine(""); foreach (var ptr in heap.EnumerateObjectAddresses()) { var type = heap.GetObjectType(ptr); if (type == null || type.Name != targetType) { continue; } // Enumerate roots and try to find the current object var stack = new Stack <ulong>(); foreach (var root in heap.EnumerateRoots()) { stack.Clear(); stack.Push(root.Object); if (GetPathToObject(heap, ptr, stack, new HashSet <ulong>())) { // Print retention path var depth = 0; var previousAddress = (ulong)0; foreach (var address in stack) { var t = heap.GetObjectType(address); if (t == null) { continue; } Console.WriteLine("{0} {1} - {2} - {3} bytes", new string('+', depth++), address, t.Name, t.GetSize(address)); graph.AddNode(address.ToString()).LabelText = $"{t.Name} ({address})"; if (previousAddress > 0 && !graph.Edges.Any(e => e.Source == previousAddress.ToString() && e.Target == address.ToString())) { graph.AddEdge(previousAddress.ToString(), address.ToString()); } previousAddress = address; } Console.WriteLine(); } } } Console.ReadLine(); // Render graph var width = 1600; var renderer = new GraphRenderer(graph); renderer.CalculateLayout(); var bitmap = new Bitmap(width, (int)(graph.Height * (width / graph.Width)), PixelFormat.Format32bppRgb); renderer.Render(bitmap); bitmap.Save("test.png"); Process.Start("test.png"); }
private IEnumerable <ulong> EnumerateManagedThreadpoolObjects() { _heap = _runtime.Heap; ClrModule mscorlib = GetMscorlib(); if (mscorlib != null) { ClrType queueType = mscorlib.GetTypeByName("System.Threading.ThreadPoolGlobals"); if (queueType != null) { ClrStaticField workQueueField = queueType.GetStaticFieldByName("workQueue"); if (workQueueField != null) { foreach (ClrAppDomain appDomain in _runtime.AppDomains) { object workQueueValue = workQueueField.GetValue(appDomain); ulong workQueue = workQueueValue == null ? 0L : (ulong)workQueueValue; ClrType workQueueType = _heap.GetObjectType(workQueue); if (workQueue == 0 || workQueueType == null) { continue; } ulong queueHead; do { if (!GetFieldObject(workQueueType, workQueue, "queueHead", out ClrType queueHeadType, out queueHead)) { break; } if (GetFieldObject(queueHeadType, queueHead, "nodes", out ClrType nodesType, out ulong nodes) && nodesType.IsArray) { int len = nodesType.GetArrayLength(nodes); for (int i = 0; i < len; ++i) { ulong addr = (ulong)nodesType.GetArrayElementValue(nodes, i); if (addr != 0) { yield return(addr); } } } if (!GetFieldObject(queueHeadType, queueHead, "Next", out queueHeadType, out queueHead)) { break; } } while (queueHead != 0); } } } queueType = mscorlib.GetTypeByName("System.Threading.ThreadPoolWorkQueue"); if (queueType != null) { ClrStaticField threadQueuesField = queueType.GetStaticFieldByName("allThreadQueues"); if (threadQueuesField != null) { foreach (ClrAppDomain domain in _runtime.AppDomains) { ulong?threadQueue = (ulong?)threadQueuesField.GetValue(domain); if (!threadQueue.HasValue || threadQueue.Value == 0) { continue; } ClrType threadQueueType = _heap.GetObjectType(threadQueue.Value); if (threadQueueType == null) { continue; } if (!GetFieldObject(threadQueueType, threadQueue.Value, "m_array", out ClrType outerArrayType, out ulong outerArray) || !outerArrayType.IsArray) { continue; } int outerLen = outerArrayType.GetArrayLength(outerArray); for (int i = 0; i < outerLen; ++i) { ulong entry = (ulong)outerArrayType.GetArrayElementValue(outerArray, i); if (entry == 0) { continue; } ClrType entryType = _heap.GetObjectType(entry); if (entryType == null) { continue; } if (!GetFieldObject(entryType, entry, "m_array", out ClrType arrayType, out ulong array) || !arrayType.IsArray) { continue; } int len = arrayType.GetArrayLength(array); for (int j = 0; j < len; ++j) { ulong addr = (ulong)arrayType.GetArrayElementValue(array, i); if (addr != 0) { yield return(addr); } } } } } } } }
public static Node FindPathToTarget(ClrHeap heap, ClrRoot root) { ClrType type = heap.GetObjectType(root.Object); if (type == null) { return(null); } List <ulong> refList = new List <ulong>(); List <int> offsetList = new List <int>(); Node curr = new Node(root.Object, type); while (curr != null) { if (curr.Children == null) { refList.Clear(); offsetList.Clear(); curr.Type.EnumerateRefsOfObject(curr.Object, delegate(ulong child, int offset) { if (child != 0) { refList.Add(child); offsetList.Add(offset); } }); curr.Children = refList.ToArray(); curr.Offsets = offsetList.ToArray(); } else { if (curr.Curr < curr.Children.Length) { ulong nextObj = curr.Children[curr.Curr]; int offset = curr.Offsets[curr.Curr]; curr.Curr++; if (m_considered.Contains(nextObj)) { continue; } m_considered.Add(nextObj); Node next = null; if (m_targets.TryGetValue(nextObj, out next)) { curr.Next = next; next.Prev = curr; next.Offset = offset; while (curr.Prev != null) { m_targets[curr.Object] = curr; curr = curr.Prev; } m_targets[curr.Object] = curr; return(curr); } type = heap.GetObjectType(nextObj); if (type != null && type.ContainsPointers) { curr = new Node(nextObj, type, curr); curr.Offset = offset; } } else { curr = curr.Prev; if (curr != null) { curr.Next = null; } } } } return(null); }
public static void PrintDict(ClrRuntime runtime, string args) { bool failed = false; ulong obj = 0; try { obj = Convert.ToUInt64(args, 16); failed = obj == 0; } catch (ArgumentException) { failed = true; } if (failed) { Console.WriteLine("Usage: !PrintDict <dictionary>"); return; } ClrHeap heap = runtime.GetHeap(); ClrType type = heap.GetObjectType(obj); if (type == null) { Console.WriteLine("Invalid object {0:X}", obj); return; } if (!type.Name.StartsWith("System.Collections.Generic.Dictionary")) { Console.WriteLine("Error: Expected object {0:X} to be a dictionary, instead it's of type '{1}'."); return; } // Get the entries field. ulong entries = type.GetFieldValue(obj, "entries"); if (entries == 0) { return; } ClrType entryArray = heap.GetObjectType(entries); ClrType arrayComponent = entryArray.ArrayComponentType; ClrInstanceField hashCodeField = arrayComponent.GetFieldByName("hashCode"); ClrInstanceField keyField = arrayComponent.GetFieldByName("key"); ClrInstanceField valueField = arrayComponent.GetFieldByName("value"); Console.WriteLine("{0,8} {1,16} : {2}", "hash", "key", "value"); int len = entryArray.GetArrayLength(entries); for (int i = 0; i < len; ++i) { ulong arrayElementAddr = entryArray.GetArrayElementAddress(entries, i); int hashCode = (int)hashCodeField.GetFieldValue(arrayElementAddr, true); object key = keyField.GetFieldValue(arrayElementAddr, true); object value = valueField.GetFieldValue(arrayElementAddr, true); key = Format(heap, key); value = Format(heap, value); bool skip = key is ulong && (ulong)key == 0 && value is ulong && (ulong)value == 0; if (!skip) { Console.WriteLine("{0,8:X} {1,16} : {2}", hashCode, key, value); } } }
public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) { if (IsNull()) { result = new ClrNullValue(m_heap); return(true); } if (m_type.IsArray) { // Populate length for bounds check. if (m_len == -1) { m_len = m_type.GetArrayLength(m_addr); } int index = GetIndexFromObjects(indexes); return(GetArrayValue(m_type, m_addr, index, out result)); } // Dictionary if (IsDictionary()) { if (indexes.Length != 1) { throw new ArgumentException("Only one index is allowed for Dictionary indexing."); } dynamic dict = ((dynamic)this); dynamic entries = dict.entries; ClrInstanceField key = ((ClrType)entries).ArrayComponentType.GetFieldByName("key"); // Two cases: The key is an object or string, denoted by "System.__Canon", or it's a primitive. // otherwise we don't support it. var keyType = key.Type; if (keyType.IsObjectReference) { Debug.Assert(keyType.Name == "System.__Canon"); if (indexes[0].GetType() == typeof(string)) { string index = (string)indexes[0]; int len = dict.count; for (int i = 0; i < len; ++i) { if ((string)entries[i].key == index) { result = entries[i].value; return(true); } } throw new KeyNotFoundException(); } else { ulong addr; if (indexes[0].GetType() == typeof(long)) { addr = (ulong)(long)indexes[0]; } else if (indexes[0].GetType() == typeof(ulong)) { addr = (ulong)indexes[0]; } else { addr = (ulong)(dynamic)indexes[0]; } int len = dict.count; for (int i = 0; i < len; ++i) { if ((ulong)entries[i].key == addr) { result = entries[i].value; return(true); } } } } else if (keyType.IsPrimitive) { object index = indexes[0]; if (index is ClrPrimitiveValue) { index = ((ClrPrimitiveValue)index).GetValue(); } Type type = index.GetType(); int len = dict.count; for (int i = 0; i < len; ++i) { ClrPrimitiveValue value = entries[i].key; if (value.GetValue().Equals(index)) { result = entries[i].value; return(true); } } throw new KeyNotFoundException(); } } if (IsList()) { int index = GetIndexFromObjects(indexes); var itemsField = m_type.GetFieldByName("_items"); ulong addr = (ulong)itemsField.GetFieldValue(m_addr); // Populate length for bounds check. if (m_len == -1) { var sizeField = m_type.GetFieldByName("_size"); m_len = (int)sizeField.GetFieldValue(m_addr); } // If type is null, then we've hit a dac bug. Attempt to work around it, // but we'll have to give up if getting the object type directly doesn't work. ClrType type = itemsField.Type; if (type == null || type.ArrayComponentType == null) { type = m_heap.GetObjectType(addr); if (type == null || type.ArrayComponentType == null) { result = new ClrNullValue(m_heap); return(true); } } return(GetArrayValue(type, addr, index, out result)); } throw new InvalidOperationException(string.Format("Object of type '{0}' is not indexable.", m_type.Name)); }
public void TypeEqualityTest() { // This test ensures that only one ClrType is created when we have a type loaded into two different AppDomains with two different // method tables. const string TypeName = "Foo"; using DataTarget dt = TestTargets.AppDomains.LoadFullDump(); using ClrRuntime runtime = dt.ClrVersions.Single().CreateRuntime(); ClrHeap heap = runtime.Heap; ClrType[] types = (from obj in heap.EnumerateObjects() let t = heap.GetObjectType(obj.Address) where t.Name == TypeName orderby t.MethodTable select t).ToArray(); Assert.Equal(2, types.Length); Assert.NotSame(types[0], types[1]); ClrType[] typesFromModule = (from module in runtime.EnumerateModules() let name = Path.GetFileNameWithoutExtension(module.Name) where name.Equals("sharedlibrary", StringComparison.OrdinalIgnoreCase) let type = module.GetTypeByName(TypeName) select type).ToArray(); Assert.Equal(2, typesFromModule.Length); Assert.NotSame(types[0], types[1]); Assert.NotEqual(types[0], types[1]); if (dt.CacheOptions.CacheTypes) { Assert.Same(types[0], typesFromModule[0]); Assert.Same(types[1], typesFromModule[1]); } else { Assert.Equal(types[0], typesFromModule[0]); Assert.Equal(types[1], typesFromModule[1]); } // Get new types runtime.FlushCachedData(); ClrType[] newTypes = (from module in runtime.EnumerateModules() let name = Path.GetFileNameWithoutExtension(module.Name) where name.Equals("sharedlibrary", StringComparison.OrdinalIgnoreCase) let type = module.GetTypeByName(TypeName) select type).ToArray(); Assert.Equal(2, newTypes.Length); for (int i = 0; i < newTypes.Length; i++) { Assert.NotSame(typesFromModule[i], newTypes[i]); Assert.Equal(typesFromModule[i], newTypes[i]); } // Even though these are the same underlying type defined in sharedlibrary's metadata, // they have different MethodTables, Parent modules, and parent domains. These do not // compare as equal. Assert.NotEqual(typesFromModule[0], typesFromModule[1]); }
public async Task <IEnumerable <object> > Execute(OperationModel model, CancellationToken token, object customeParameter) { //TODO: add support of local variables return(await DebuggerSession.Instance.ExecuteOperation(() => { var result = new List <ClrStackDump>(); foreach (var thread in DebuggerSession.Instance.Runtime.Threads) { if (token.IsCancellationRequested) { break; } var stackDetails = new ClrStackDump(); stackDetails.StackFrames = new List <object>(); stackDetails.StackObjects = new List <object>(); foreach (var stackFrame in thread.StackTrace) { stackDetails.StackBase = thread.StackBase; stackDetails.StackLimit = thread.StackLimit; stackDetails.Exception = thread.CurrentException; stackDetails.OSThreadID = thread.IsAlive ? thread.OSThreadId.ToString() : "XXX"; stackDetails.ManagedThreadId = thread.ManagedThreadId; stackDetails.StackFrames.Add( new { StackPointer = stackFrame.StackPointer, InstructionPointer = stackFrame.InstructionPointer, DisplayString = stackFrame.DisplayString, //FileAndLine = source != null ? source.FilePath + ": " + source.LineNumber : "", Method = stackFrame.Method }); } ClrHeap heap = DebuggerSession.Instance.Runtime.GetHeap(); var pointerSize = DebuggerSession.Instance.Runtime.PointerSize; // address of TEB (thread execution block) + pointer size ulong start = thread.StackBase; // address of TEB (thread execution block) + pointer size * 2 ulong stop = thread.StackLimit; // We'll walk these in pointer order. if (start > stop) { ulong tmp = start; start = stop; stop = tmp; } // ptr is a stack address. for (ulong ptr = start; ptr <= stop; ptr += (ulong)pointerSize) { HashSet <ulong> stackObjects = new HashSet <ulong>(); // fail to read the memory if (!heap.ReadPointer(ptr, out ulong obj)) { break; } // the object added already if (!stackObjects.Add(obj)) { continue; } // not an object ClrType type = heap.GetObjectType(obj); if (type == null) { continue; } // free space if (type.IsFree) { continue; } // All good, add it stackDetails.StackObjects.Add( new { Address = ptr, Object = obj, Name = type.Name, Value = new ClrObject(obj, type).Fields.Value }); } result.Add(stackDetails); } return result; })); }
static void Main(string[] args) { bool stat; string dump, dac; if (!TryParseArgs(args, out dump, out dac, out stat)) { Usage(); Environment.Exit(1); } try { // Create a ClrRuntime instance from the dump and dac location. The ClrRuntime // object represents a version of CLR loaded in the process. It contains data // such as the managed threads in the process, the AppDomains in the process, // the managed heap, and so on. ClrRuntime runtime = CreateRuntime(dump, dac); // Walk the entire heap and build heap statistics in "stats". Dictionary <ClrType, Entry> stats = new Dictionary <ClrType, Entry>(); if (!stat) { Console.WriteLine("{0,16} {1,12} {2}", "Object", "Size", "Type"); } // This is the way to walk every object on the heap: Get the ClrHeap instance // from the runtime. Walk every segment in heap.Segments, and use // ClrSegment.FirstObject and ClrSegment.NextObject to iterate through // objects on that segment. ClrHeap heap = runtime.GetHeap(); foreach (ClrSegment seg in heap.Segments) { for (ulong obj = seg.FirstObject; obj != 0; obj = seg.NextObject(obj)) { // This gets the type of the object. ClrType type = heap.GetObjectType(obj); ulong size = type.GetSize(obj); // If the user didn't request "-stat", print out the object. if (!stat) { Console.WriteLine("{0,16:X} {1,12:n0} {2}", obj, size, type.Name); } // Add an entry to the dictionary, if one doesn't already exist. Entry entry = null; if (!stats.TryGetValue(type, out entry)) { entry = new Entry(); entry.Name = type.Name; stats[type] = entry; } // Update the statistics for this object. entry.Count++; entry.Size += type.GetSize(obj); } } // Now print out statistics. if (!stat) { Console.WriteLine(); } // We'll actually let linq do the heavy lifting. var sortedStats = from entry in stats.Values orderby entry.Size select entry; Console.WriteLine("{0,12} {1,12} {2}", "Size", "Count", "Type"); foreach (var entry in sortedStats) { Console.WriteLine("{0,12:n0} {1,12:n0} {2}", entry.Size, entry.Count, entry.Name); } } catch (Exception ex) { Console.WriteLine("Unhandled exception:"); Console.WriteLine(ex); } }
public static void LogObjectFields( ClrHeap heap, ILogger logger, ulong address, ClrType objectType, ISet <string>?fieldList = null) { foreach (var clrInstanceField in objectType.Fields) { if (fieldList != null && !fieldList.Contains(clrInstanceField.Name)) { continue; } // logger.LogInformation($"{clrInstanceField.Name} ----------------------"); if (clrInstanceField.IsPrimitive) { // if (clrInstanceField.Name == "m_stateFlags") // { // var stateValue = (int) fieldValue; // logger.LogInformation($"{clrInstanceField.Name} = "); // // foreach (var statePair in TaskConstants.TASK_STATES) // { // if ((statePair.Key & stateValue) == statePair.Key) // { // logger.LogInformation($"{statePair.Value} |"); // } // } // // logger.LogInformation(); // continue; // } // var fieldValue = clrInstanceField.read<????>(address); // logger.LogInformation($"{clrInstanceField.Name} = {fieldValue}"); continue; } if (clrInstanceField.IsObjectReference) { var value = "{null}"; var fieldValue = clrInstanceField.ReadObject(address, false); if (fieldValue.Type !.IsString) { value = fieldValue.AsString(); } switch (fieldValue) { case ulong fieldAddress: if (fieldAddress != Address.Null.Value) { var fieldType = heap.GetObjectType(fieldAddress); value = $"{fieldAddress:X} {fieldType}"; } break; } logger.LogInformation($"{clrInstanceField.Name} = {value}"); // if (fieldType.Name == "System.Action") // { // var targetField = fieldType.GetFieldByName("_target"); // var targetFieldAddress = (ulong) targetField.GetValue(fieldAaddress); // if (targetFieldAddress == 0) // { // logger.LogInformation("\t_target = {null}"); // } // else // { // logger.LogInformation($"\t_target = {runtime.Heap.GetObjectType(targetFieldAddress)}"); // // // TODO if it is "System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner" then take "m_stateMachine" field // } // } continue; } if (clrInstanceField.IsValueType) { logger.LogInformation( $"{clrInstanceField.Name} {clrInstanceField.Type.GetClrTypeName()}"); continue; } logger.LogInformation($"{clrInstanceField.Name} UNKNOWN TYPE"); } }
private static string Repr(ClrHeap heap, ulong addr) { var type = heap.GetObjectType(addr); return(Escape(type?.GetValue(addr)?.ToString() ?? string.Empty)); }
GetClassValues(ClrHeap heap, ulong addr) { try { addr = Utils.RealAddress(addr); (ClrType type, ClrElementKind kind, ClrType rtType, ClrElementKind rtKind) = TypeExtractor.GetRealType(heap, addr); if (type == null) { return("Object Value Error" + Constants.HeavyGreekCrossPadded + "Cannot find an instance." + Constants.HeavyGreekCrossPadded + "Heap cannot get object type at address: " + Utils.RealAddressString(addr), null, ClrElementKind.Unknown, (null, null, null, null, null)); } var fldCnt = type.Fields.Count; var fldTypes = fldCnt == 0 ? Utils.EmptyArray <ClrType> .Value : new ClrType[fldCnt]; var fldKinds = fldCnt == 0 ? Utils.EmptyArray <ClrElementKind> .Value : new ClrElementKind[fldCnt]; var objects = fldCnt == 0 ? Utils.EmptyArray <object> .Value : new object[fldCnt]; StructValues[] structVals = null; StructFieldsInfo[] structFldInfos = null; for (int i = 0; i < fldCnt; ++i) { var fld = type.Fields[i]; var fldType = fld.Type; // returns ClrElementKind.Unknown if fld.Type is null var fldKind = TypeExtractor.GetElementKind(fldType); if (fldKind == ClrElementKind.Unknown) { objects[i] = null; continue; // nothing to do here, from MDR lib: There is // a bug in several versions of our debugging layer which causes this. } if (fldType == null || TypeExtractor.IsAmbiguousKind(fldKind)) { var fldValObj = fld.GetValue(addr, type.IsValueClass, false); if (fldValObj != null && fldValObj is ulong) { var t = heap.GetObjectType((ulong)fldValObj); if (t != null) { fldType = t; fldKind = TypeExtractor.GetElementKind(t); } } } fldTypes[i] = fldType; fldKinds[i] = fldKind; if (fldKind == ClrElementKind.Unknown) { continue; // nothing to do here, from MDR lib: There is } // a bug in several versions of our debugging layer which causes this. if (TypeExtractor.IsString(fldKind)) { objects[i] = fld.GetValue(addr, false, true); } else if (TypeExtractor.IsObjectReference(fldKind)) { object obj = fld.GetValue(addr, false, false); if (obj != null && (ulong)obj != Constants.InvalidAddress) { var t = heap.GetObjectType((ulong)obj); if (t != null) { var k = TypeExtractor.GetElementKind(t); fldTypes[i] = t; fldKinds[i] = k; } } objects[i] = obj; } else if (TypeExtractor.IsEnum(fldKind)) { objects[i] = ValueExtractor.GetEnumValueObject(addr, fld, false); } else if (fldType.IsPrimitive) { objects[i] = ValueExtractor.GetPrimitiveValueObject(addr, fld, false); } else if (TypeExtractor.IsKnownStruct(fldKind)) { switch (TypeExtractor.GetSpecialKind(fldKind)) { case ClrElementKind.DateTime: objects[i] = ValueExtractor.GetDateTimePAF(addr, fld, false); break; case ClrElementKind.Guid: objects[i] = ValueExtractor.GetGuid(addr, fld, false); break; case ClrElementKind.Decimal: objects[i] = ValueExtractor.GetDecimalPAF(addr, fld, false); break; case ClrElementKind.TimeSpan: objects[i] = ValueExtractor.TimeSpanValue(addr, fldType); break; } } else if (TypeExtractor.IsUnknownStruct(fldKind)) { //StructFields sf = StructFields.GetStructFields(fldType); //StructFieldsEx sfx = StructFieldsEx.GetStructFields(sf, fldType); if (structFldInfos == null) { structFldInfos = new StructFieldsInfo[fldCnt]; } ulong saddr = fld.GetAddress(addr, true); StructFieldsInfo sfi = StructFieldsInfo.GetStructFields(fldType, heap, saddr); structFldInfos[i] = sfi; if (sfi != null) { if (structVals == null) { structVals = new StructValues[fldCnt]; } structVals[i] = StructFieldsInfo.GetStructValues(sfi, heap, saddr); } } } return(null, type, kind, (fldTypes, fldKinds, objects, structFldInfos, structVals)); } catch (Exception ex) { return(Utils.GetExceptionErrorString(ex), null, ClrElementKind.Unknown, (null, null, null, null, null)); } }
public IClrObject ReadFromAddress(ClrHeap heap, ulong address) { var type = heap.GetObjectType(address); return(ReadFromAddress(type, address)); }
static void Main(string[] args) { bool stat, live; string dump, dac; if (!TryParseArgs(args, out dump, out dac, out stat, out live)) { Usage(); Environment.Exit(1); } try { ClrRuntime runtime = CreateRuntime(dump, dac); ClrHeap heap = runtime.GetHeap(); ObjectSet liveObjs = null; if (live) { liveObjs = GetLiveObjects(heap); } Dictionary <ClrType, Entry> stats = new Dictionary <ClrType, Entry>(); if (!stat) { Console.WriteLine("{0,16} {1,12} {2}", "Object", "Size", "Type"); } foreach (ClrSegment seg in heap.Segments) { for (ulong obj = seg.FirstObject; obj != 0; obj = seg.NextObject(obj)) { if (live && !liveObjs.Contains(obj)) { continue; } // This gets the type of the object. ClrType type = heap.GetObjectType(obj); ulong size = type.GetSize(obj); // If the user didn't request "-stat", print out the object. if (!stat) { Console.WriteLine("{0,16:X} {1,12:n0} {2}", obj, size, type.Name); } // Add an entry to the dictionary, if one doesn't already exist. Entry entry = null; if (!stats.TryGetValue(type, out entry)) { entry = new Entry(); entry.Name = type.Name; stats[type] = entry; } // Update the statistics for this object. entry.Count++; entry.Size += type.GetSize(obj); } } // Now print out statistics. if (!stat) { Console.WriteLine(); } // We'll actually let linq do the heavy lifting. var sortedStats = from entry in stats.Values orderby entry.Size select entry; ulong totalSize = 0, totalCount = 0; Console.WriteLine("{0,12} {1,12} {2}", "Size", "Count", "Type"); foreach (var entry in sortedStats) { Console.WriteLine("{0,12:n0} {1,12:n0} {2}", entry.Size, entry.Count, entry.Name); totalSize += entry.Size; totalCount += (uint)entry.Count; } Console.WriteLine(); Console.WriteLine("Total: {0:n0} bytes in {1:n0} objects", totalSize, totalCount); } catch (Exception ex) { Console.WriteLine("Unhandled exception:"); Console.WriteLine(ex); } }
private static void DumpTypes(ClrHeap heap, ulong obj, string type) { var resultItem = new ServiceDescriptionEntry((int)heap.Runtime.DataTarget.ProcessId); // Get ServiceEndpoint[] List <ulong> endpointObjs = ClrMdHelper.GetLastObjectInHierarchyAsArray(heap, obj, HIERARCHY_ServiceDescription_To_ServiceEndpoints, 0, TYPE_ServiceEndpointArray); foreach (var endpointObj in endpointObjs) { var epEntry = new ServiceEndpointEntry(); // Get Contract ulong contractObj = ClrMdHelper.GetLastObjectInHierarchy(heap, endpointObj, HIERARCHY_ServiceEndpoint_To_ContractType, 0); string contractTypeName = heap.GetObjectType(contractObj).GetRuntimeType(contractObj).Name; epEntry.Contract = contractTypeName; // Get IContractBehavior[] List <ulong> contractBehaviorObjs = ClrMdHelper.GetLastObjectInHierarchyAsArray(heap, endpointObj, HIERARCHY_ServiceEndpoint_To_ContractBehaviors, 0, TYPE_ContractBehaviorArray); foreach (var contractBehaviorObj in contractBehaviorObjs) { ClrType itemType = heap.GetObjectType(contractBehaviorObj); epEntry.ContractBehaviors.Add(itemType.Name); } // Get OperationDescription[] List <ulong> operationDescObjs = ClrMdHelper.GetLastObjectInHierarchyAsArray(heap, endpointObj, HIERARCHY_ServiceEndpoint_To_OperationDescriptions, 0, TYPE_OperationDescriptionArray); foreach (var operationDescObj in operationDescObjs) { ulong opNameObj = ClrMdHelper.GetLastObjectInHierarchy(heap, operationDescObj, HIERARCHY_OperationDescription_To_Name, 0); string opName = ClrMdHelper.GetObjectAs <string>(heap, opNameObj, FIELD_XmlName); var odEntry = new OperationDescriptionEntry(); odEntry.OperationName = opName; // Get IOperationBehavior[] List <ulong> operationBehavObjs = ClrMdHelper.GetLastObjectInHierarchyAsArray(heap, operationDescObj, HIERARCHY_OperationDescription_To_OperationBehaviors, 0, TYPE_OperationBehaviorArray); foreach (var operationBehavObj in operationBehavObjs) { ClrType itemType = heap.GetObjectType(operationBehavObj); odEntry.OperationBehaviors.Add(itemType.Name); } epEntry.ContractOperations.Add(odEntry); } // Get CallbackContract ulong cbcontractObj = ClrMdHelper.GetLastObjectInHierarchy(heap, endpointObj, HIERARCHY_ServiceEndpoint_To_CallbackContractType, 0); if (cbcontractObj != 0) { string cbcontractTypeName = heap.GetObjectType(cbcontractObj).GetRuntimeType(cbcontractObj).Name; epEntry.CallbackContract = cbcontractTypeName; } // Get EndpointAddress URI ulong uriObj = ClrMdHelper.GetLastObjectInHierarchy(heap, endpointObj, HIERARCHY_ServiceEndpoint_To_Uri, 0); string uri = ClrMdHelper.GetObjectAs <string>(heap, uriObj, FIELD_UriName); epEntry.Uri = uri; // Get IEndpointBehavior[] List <ulong> endpBehaviorObjs = ClrMdHelper.GetLastObjectInHierarchyAsArray(heap, endpointObj, HIERARCHY_ServiceEndpoint_To_EndpointBehaviors, 0, TYPE_EndpointBehaviorArray); foreach (var endpBehaviorObj in endpBehaviorObjs) { ClrType itemType = heap.GetObjectType(endpBehaviorObj); epEntry.EndpointBehaviors.Add(itemType.Name); } resultItem.ServiceEndpoints.Add(epEntry); } // Get IServiceBehavior[] List <ulong> svcBehaviorObjs = ClrMdHelper.GetLastObjectInHierarchyAsArray(heap, obj, HIERARCHY_ServiceDescription_To_ServiceBehaviors, 0, TYPE_ServiceBehaviorArray); foreach (var svcBehaviorObj in svcBehaviorObjs) { ClrType svcBehaviorType = heap.GetObjectType(svcBehaviorObj); resultItem.ServiceBehaviors.Add(svcBehaviorType.Name); } RESULTS.Add(resultItem); }
public void Execute(CommandExecutionContext context) { if (!String.IsNullOrEmpty(TypeRegex)) { try { new Regex(TypeRegex); } catch (ArgumentException) { context.WriteError("The regular expression specified for --type is not valid; did you forget to escape regex characters?"); return; } } _heap = context.Runtime.GetHeap(); if (!_heap.CanWalkHeap) { context.WriteError("The heap is not in a walkable state."); return; } var typeInfos = new Dictionary <ulong, TypeInfo>(); // MT to TypeInfo long totalObjectCount = 0; if (!StatisticsOnly) { context.WriteLine("{0,-20} {1,-20} {2}", "MT", "Address", "Size"); } foreach (var obj in _heap.EnumerateObjects()) { ulong mt = 0; context.Runtime.ReadPointer(obj, out mt); if (!FilterObject(obj, mt)) { continue; } var type = _heap.GetObjectType(obj); if (type == null || String.IsNullOrEmpty(type.Name)) { continue; } var size = type.GetSize(obj); if (!StatisticsOnly) { context.WriteLine("{0,-20:x16} {1,-20:x16} {2,-10}", mt, obj, size); } if (typeInfos.ContainsKey(mt)) { var current = typeInfos[mt]; current.Count += 1; current.Size += size; typeInfos[mt] = current; } else { var objType = _heap.GetObjectType(obj); var objTypeName = objType != null ? objType.Name : "<no name>"; typeInfos.Add(mt, new TypeInfo { Size = size, Count = 1, TypeName = objTypeName }); } ++totalObjectCount; } context.WriteLine("Statistics:"); context.WriteLine("{0,-20} {1,-10} {2,-10} {3}", "MT", "Count", "TotalSize", "Class Name"); foreach (var kvp in (from e in typeInfos orderby e.Value.Size ascending select e)) { context.WriteLine("{0,-20:x16} {1,-10} {2,-10} {3}", kvp.Key, kvp.Value.Count, kvp.Value.Size, kvp.Value.TypeName); } context.WriteLine("Total {0} objects", totalObjectCount); }
public IEnumerable <TimerInfo> EnumerateTimers(ClrRuntime runtime) { ClrHeap heap = runtime.GetHeap(); if (!heap.CanWalkHeap) { yield break; } var timerQueueType = GetMscorlib(runtime).GetTypeByName("System.Threading.TimerQueue"); if (timerQueueType == null) { yield break; } ClrStaticField staticField = timerQueueType.GetStaticFieldByName("s_queue"); if (staticField == null) { yield break; } foreach (ClrAppDomain domain in runtime.AppDomains) { ulong?timerQueue = (ulong?)staticField.GetValue(domain); if (!timerQueue.HasValue || timerQueue.Value == 0) { continue; } // m_timers is the start of the list of TimerQueueTimer var currentPointer = GetFieldValue(heap, timerQueue.Value, "m_timers"); while ((currentPointer != null) && (((ulong)currentPointer) != 0)) { // currentPointer points to a TimerQueueTimer instance ulong currentTimerQueueTimerRef = (ulong)currentPointer; TimerInfo ti = new TimerInfo() { TimerQueueTimerAddress = currentTimerQueueTimerRef }; var val = GetFieldValue(heap, currentTimerQueueTimerRef, "m_dueTime"); ti.DueTime = (uint)val; val = GetFieldValue(heap, currentTimerQueueTimerRef, "m_period"); ti.Period = (uint)val; val = GetFieldValue(heap, currentTimerQueueTimerRef, "m_canceled"); ti.Cancelled = (bool)val; val = GetFieldValue(heap, currentTimerQueueTimerRef, "m_state"); ti.StateTypeName = ""; if (val == null) { ti.StateAddress = 0; } else { ti.StateAddress = (ulong)val; var stateType = heap.GetObjectType(ti.StateAddress); if (stateType != null) { ti.StateTypeName = stateType.Name; } } // decypher the callback details val = GetFieldValue(heap, currentTimerQueueTimerRef, "m_timerCallback"); if (val != null) { ulong elementAddress = (ulong)val; if (elementAddress == 0) { continue; } var elementType = heap.GetObjectType(elementAddress); if (elementType != null) { if (elementType.Name == "System.Threading.TimerCallback") { ti.MethodName = BuildTimerCallbackMethodName(runtime, elementAddress); } else { ti.MethodName = "<" + elementType.Name + ">"; } } else { ti.MethodName = "{no callback type?}"; } } else { ti.MethodName = "???"; } yield return(ti); currentPointer = GetFieldValue(heap, currentTimerQueueTimerRef, "m_next"); } } }
static void Main(string[] args) { if (!TryParseArgs(args, out string dump, out bool dso)) { Usage(); return; } // Create the data target. This tells us the versions of CLR loaded in the target process. using DataTarget dataTarget = DataTarget.LoadDump(dump); // Now check bitness of our program/target: bool isTarget64Bit = dataTarget.DataReader.PointerSize == 8; if (Environment.Is64BitProcess != isTarget64Bit) { throw new Exception(string.Format("Architecture mismatch: Process is {0} but target is {1}", Environment.Is64BitProcess ? "64 bit" : "32 bit", isTarget64Bit ? "64 bit" : "32 bit")); } // Note I just take the first version of CLR in the process. You can loop over every loaded // CLR to handle the SxS case where both desktop CLR and .Net Core are loaded in the process. ClrInfo version = dataTarget.ClrVersions[0]; // Now that we have the DataTarget, the version of CLR, and the right dac, we create and return a // ClrRuntime instance. using ClrRuntime runtime = version.CreateRuntime(); // Walk each thread in the process. foreach (ClrThread thread in runtime.Threads) { // The ClrRuntime.Threads will also report threads which have recently died, but their // underlying datastructures have not yet been cleaned up. This can potentially be // useful in debugging (!threads displays this information with XXX displayed for their // OS thread id). You cannot walk the stack of these threads though, so we skip them // here. if (!thread.IsAlive) { continue; } Console.WriteLine("Thread {0:X}:", thread.OSThreadId); Console.WriteLine("Stack: {0:X} - {1:X}", thread.StackBase, thread.StackLimit); // Each thread tracks a "last thrown exception". This is the exception object which // !threads prints. If that exception object is present, we will display some basic // exception data here. Note that you can get the stack trace of the exception with // ClrHeapException.StackTrace (we don't do that here). ClrException?currException = thread.CurrentException; if (currException is ClrException ex) { Console.WriteLine("Exception: {0:X} ({1}), HRESULT={2:X}", ex.Address, ex.Type.Name, ex.HResult); } // Walk the stack of the thread and print output similar to !ClrStack. Console.WriteLine(); Console.WriteLine("Managed Callstack:"); foreach (ClrStackFrame frame in thread.EnumerateStackTrace()) { // Note that CLRStackFrame currently only has three pieces of data: stack pointer, // instruction pointer, and frame name (which comes from ToString). Future // versions of this API will allow you to get the type/function/module of the // method (instead of just the name). This is not yet implemented. Console.WriteLine($" {frame.StackPointer:x12} {frame.InstructionPointer:x12} {frame}"); } // Print a !DumpStackObjects equivalent. if (dso) { // We'll need heap data to find objects on the stack. ClrHeap heap = runtime.Heap; // Walk each pointer aligned address on the stack. Note that StackBase/StackLimit // is exactly what they are in the TEB. This means StackBase > StackLimit on AMD64. ulong start = thread.StackBase; ulong stop = thread.StackLimit; // We'll walk these in pointer order. if (start > stop) { ulong tmp = start; start = stop; stop = tmp; } Console.WriteLine(); Console.WriteLine("Stack objects:"); // Walk each pointer aligned address. Ptr is a stack address. for (ulong ptr = start; ptr <= stop; ptr += (uint)IntPtr.Size) { // Read the value of this pointer. If we fail to read the memory, break. The // stack region should be in the crash dump. if (!dataTarget.DataReader.ReadPointer(ptr, out ulong obj)) { break; } // 003DF2A4 // We check to see if this address is a valid object by simply calling // GetObjectType. If that returns null, it's not an object. ClrType type = heap.GetObjectType(obj); if (type == null) { continue; } // Don't print out free objects as there tends to be a lot of them on // the stack. if (!type.IsFree) { Console.WriteLine("{0,16:X} {1,16:X} {2}", ptr, obj, type.Name); } } } Console.WriteLine(); Console.WriteLine("----------------------------------"); Console.WriteLine(); } }
private IEnumerable<ulong> EnumerateManagedThreadpoolObjects() { m_heap = m_runtime.GetHeap(); ClrModule mscorlib = GetMscorlib(); if (mscorlib != null) { ClrType queueType = mscorlib.GetTypeByName("System.Threading.ThreadPoolGlobals"); if (queueType != null) { ClrStaticField workQueueField = queueType.GetStaticFieldByName("workQueue"); if (workQueueField != null) { foreach (var appDomain in m_runtime.AppDomains) { ulong workQueue = (ulong)workQueueField.GetValue(appDomain); ClrType workQueueType = m_heap.GetObjectType(workQueue); if (workQueue == 0 || workQueueType == null) continue; ulong queueHead; ClrType queueHeadType; do { if (!GetFieldObject(workQueueType, workQueue, "queueHead", out queueHeadType, out queueHead)) break; ulong nodes; ClrType nodesType; if (GetFieldObject(queueHeadType, queueHead, "nodes", out nodesType, out nodes) && nodesType.IsArray) { int len = nodesType.GetArrayLength(nodes); for (int i = 0; i < len; ++i) { ulong addr = (ulong)nodesType.GetArrayElementValue(nodes, i); if (addr != 0) yield return addr; } } if (!GetFieldObject(queueHeadType, queueHead, "Next", out queueHeadType, out queueHead)) break; } while (queueHead != 0); } } } queueType = mscorlib.GetTypeByName("System.Threading.ThreadPoolWorkQueue"); if (queueType != null) { ClrStaticField threadQueuesField = queueType.GetStaticFieldByName("allThreadQueues"); if (threadQueuesField != null) { foreach (ClrAppDomain domain in m_runtime.AppDomains) { ulong threadQueue = (ulong)threadQueuesField.GetValue(domain); if (threadQueue == 0) continue; ClrType threadQueueType = m_heap.GetObjectType(threadQueue); if (threadQueueType == null) continue; ulong outerArray = 0; ClrType outerArrayType = null; if (!GetFieldObject(threadQueueType, threadQueue, "m_array", out outerArrayType, out outerArray) || !outerArrayType.IsArray) continue; int outerLen = outerArrayType.GetArrayLength(outerArray); for (int i = 0; i < outerLen; ++i) { ulong entry = (ulong)outerArrayType.GetArrayElementValue(outerArray, i); if (entry == 0) continue; ClrType entryType = m_heap.GetObjectType(entry); if (entryType == null) continue; ulong array; ClrType arrayType; if (!GetFieldObject(entryType, entry, "m_array", out arrayType, out array) || !arrayType.IsArray) continue; int len = arrayType.GetArrayLength(array); for (int j = 0; j < len; ++j) { ulong addr = (ulong)arrayType.GetArrayElementValue(array, i); if (addr != 0) yield return addr; } } } } } } }
internal override IList <ClrStackFrame> GetExceptionStackTrace(ulong obj, ClrType type) { // TODO: Review this and if it works on v4.5, merge the two implementations back into RuntimeBase. List <ClrStackFrame> result = new List <ClrStackFrame>(); if (type == null) { return(result); } if (!GetStackTraceFromField(type, obj, out ulong _stackTrace)) { if (!ReadPointer(obj + GetStackTraceOffset(), out _stackTrace)) { return(result); } } if (_stackTrace == 0) { return(result); } ClrHeap heap = Heap; ClrType stackTraceType = heap.GetObjectType(_stackTrace); if (stackTraceType == null || !stackTraceType.IsArray) { return(result); } int len = stackTraceType.GetArrayLength(_stackTrace); if (len == 0) { return(result); } int elementSize = IntPtr.Size * 4; ulong dataPtr = _stackTrace + (ulong)(IntPtr.Size * 2); if (!ReadPointer(dataPtr, out ulong count)) { return(result); } // Skip size and header dataPtr += (ulong)(IntPtr.Size * 2); DesktopThread thread = null; for (int i = 0; i < (int)count; ++i) { if (!ReadPointer(dataPtr, out ulong ip)) { break; } if (!ReadPointer(dataPtr + (ulong)IntPtr.Size, out ulong sp)) { break; } if (!ReadPointer(dataPtr + (ulong)(2 * IntPtr.Size), out ulong md)) { break; } if (i == 0 && sp != 0) { thread = (DesktopThread)GetThreadByStackAddress(sp); } // it seems that the first frame often has 0 for IP and SP. Try the 2nd frame as well if (i == 1 && thread == null && sp != 0) { thread = (DesktopThread)GetThreadByStackAddress(sp); } result.Add(new DesktopStackFrame(this, thread, ip, sp, md)); dataPtr += (ulong)elementSize; } return(result); }
private ClrType TryBuildType(ClrHeap heap) { var runtime = heap.GetRuntime(); var domains = runtime.AppDomains; ClrType[] types = new ClrType[domains.Count]; ClrElementType elType = ElementType; if (ClrRuntime.IsPrimitive(elType) || elType == ClrElementType.String) return ((DesktopGCHeap)heap).GetBasicType(elType); int count = 0; foreach (var domain in domains) { object value = GetValue(domain); if (value != null && value is ulong && ((ulong)value != 0)) { types[count++] = heap.GetObjectType((ulong)value); } } int depth = int.MaxValue; ClrType result = null; for (int i = 0; i < count; ++i) { ClrType curr = types[i]; if (curr == result || curr == null) continue; int nextDepth = GetDepth(curr); if (nextDepth < depth) { result = curr; depth = nextDepth; } } return result; }
// Returns all the info about any Composite Keys stored in memory public static List <CompositeKeyInfo> GetCompositeKeyInfo(Process process) { List <CompositeKeyInfo> keyInfo = new List <CompositeKeyInfo>(); DataTarget dt = null; string databaseLocation = ""; try { dt = DataTarget.AttachToProcess(process.Id, 50000); if (dt.ClrVersions.Count == 0) { string err = "CLR is not loaded. Is it Keepass 1.x, perhaps?"; Logger.WriteLine(err); throw new Exception(err); } if (dt.ClrVersions.Count > 1) { Logger.WriteLine("*** Interesting... there are multiple .NET runtimes loaded in KeePass"); } ClrInfo Version = dt.ClrVersions[0]; ClrRuntime Runtime = Version.CreateRuntime(); ClrHeap Heap = Runtime.GetHeap(); if (!Heap.CanWalkHeap) { string err = "Error: Cannot walk the heap!"; Logger.WriteLine(err); throw new Exception(err); } foreach (ulong obj in Heap.EnumerateObjectAddresses()) { ClrType type = Heap.GetObjectType(obj); if (type == null || type.Name != "KeePassLib.PwDatabase") { continue; } Logger.WriteLine("************ Found a PwDatabase! **********"); List <ulong> referencedObjects = ClrMDHelper.GetReferencedObjects(Heap, obj); // First walk the referenced objects to find the database path foreach (ulong refObj in referencedObjects) { ClrType refObjType = Heap.GetObjectType(refObj); if (refObjType.Name == "KeePassLib.Serialization.IOConnectionInfo") { ClrInstanceField UrlField = refObjType.GetFieldByName("m_strUrl"); ulong UrlFieldAddr = UrlField.GetAddress(refObj); object Url = UrlField.GetValue(UrlFieldAddr, true); databaseLocation = (string)Url; } } if (databaseLocation != "") { Logger.WriteLine("*** PwDatabase location : " + databaseLocation); referencedObjects = ClrMDHelper.GetReferencedObjects(Heap, obj); // now walk the referenced objects looking for a master composite key foreach (ulong refObj in referencedObjects) { ClrType refObjType = Heap.GetObjectType(refObj); if (refObjType.Name == "KeePassLib.Keys.CompositeKey") { Logger.WriteLine("************ Found a CompositeKey! **********"); CompositeKeyInfo CompositeKey = new CompositeKeyInfo(); // Get all objects kept alive by the composite key. // (A shortcut to get references to all Key types) List <ulong> referencedObjects2 = ClrMDHelper.GetReferencedObjects(Heap, refObj); foreach (ulong refObj2 in referencedObjects2) { ClrType refObjType2 = Heap.GetObjectType(refObj2); if (refObjType2.Name == "KeePassLib.Keys.KcpPassword") { KcpPassword KcpPassword = GetKcpPasswordInfo(refObj2, refObjType2, Heap, databaseLocation); if (KcpPassword == null) { continue; } CompositeKey.AddUserKey(KcpPassword); } else if (refObjType2.Name == "KeePassLib.Keys.KcpKeyFile") { KcpKeyFile KcpKeyFile = GetKcpKeyFileInfo(refObj2, refObjType2, Heap, databaseLocation); if (KcpKeyFile == null) { continue; } CompositeKey.AddUserKey(KcpKeyFile); } else if (refObjType2.Name == "KeePassLib.Keys.KcpUserAccount") { KcpUserAccount KcpUserAccount = GetKcpUserAccountInfo(refObj2, refObjType2, Heap, databaseLocation); if (KcpUserAccount == null) { continue; } CompositeKey.AddUserKey(KcpUserAccount); } } if (CompositeKey.UserKeyCount > 0) { keyInfo.Add(CompositeKey); } } } } } Logger.Write("\n"); } catch (Exception e) { Logger.WriteLine(e.Message); throw; } finally { if (dt != null) { dt.Dispose(); } } return(keyInfo); }