Exemple #1
0
        private DesktopBlockingObject CreateRWSObject(ulong obj, ClrType type)
        {
            if (type == null)
            {
                return(new DesktopBlockingObject(obj, false, 0, null, BlockingReason.None));
            }

            ClrInstanceField field = type.GetFieldByName("writeLockOwnerId");

            if (field != null && field.ElementType == ClrElementType.Int32)
            {
                int       id     = (int)field.GetValue(obj);
                ClrThread thread = GetThreadById(id);
                if (thread != null)
                {
                    return(new DesktopBlockingObject(obj, true, 0, thread, BlockingReason.WriterAcquired));
                }
            }

            field = type.GetFieldByName("upgradeLockOwnerId");
            if (field != null && field.ElementType == ClrElementType.Int32)
            {
                int       id     = (int)field.GetValue(obj);
                ClrThread thread = GetThreadById(id);
                if (thread != null)
                {
                    return(new DesktopBlockingObject(obj, true, 0, thread, BlockingReason.WriterAcquired));
                }
            }

            field = type.GetFieldByName("rwc");
            if (field != null)
            {
                List <ClrThread> threads      = null;
                ulong            rwc          = (ulong)field.GetValue(obj);
                ClrType          rwcArrayType = _heap.GetObjectType(rwc);
                if (rwcArrayType != null && rwcArrayType.IsArray && rwcArrayType.ComponentType != null)
                {
                    ClrType          rwcType  = rwcArrayType.ComponentType;
                    ClrInstanceField threadId = rwcType.GetFieldByName("threadid");
                    ClrInstanceField next     = rwcType.GetFieldByName("next");
                    if (threadId != null && next != null)
                    {
                        int count = rwcArrayType.GetArrayLength(rwc);
                        for (int i = 0; i < count; ++i)
                        {
                            ulong entry = (ulong)rwcArrayType.GetArrayElementValue(rwc, i);
                            GetThreadEntry(ref threads, threadId, next, entry, false);
                        }
                    }
                }

                if (threads != null)
                {
                    return(new DesktopBlockingObject(obj, true, 0, BlockingReason.ReaderAcquired, threads.ToArray()));
                }
            }

            return(new DesktopBlockingObject(obj, false, 0, null, BlockingReason.None));
        }
Exemple #2
0
        public static ulong GetLastObjectInHierarchy(ClrHeap heap, ulong heapobject, string[] hierarchy, int currentIndex)
        {
            ClrType          type  = heap.GetObjectType(heapobject);
            ClrInstanceField field = type.GetFieldByName(hierarchy[currentIndex]);

            if (field == null)
            {
                Console.WriteLine($"ERROR: type '{type.Name}' does not have a field '{hierarchy[currentIndex]}'");
                return(0L);
            }

            ulong fieldValue = (ulong)field.GetValue(heapobject, false, false);

            if (fieldValue == 0)
            {
                Console.WriteLine($"ERROR: the field value for '{hierarchy[currentIndex]}' was null on type '{type.Name}'  ");
                return(0L);
            }

            currentIndex++;
            if (currentIndex == hierarchy.Length)
            {
                return(fieldValue);
            }

            return(GetLastObjectInHierarchy(heap, fieldValue, hierarchy, currentIndex));
        }
Exemple #3
0
        public static object GetFieldValue(ClrHeap heap, ulong address, string fieldName)
        {
            var type = heap.GetObjectType(address);
            ClrInstanceField field = type.GetFieldByName(fieldName);

            return(field?.GetValue(address));
        }
Exemple #4
0
        public static KcpUserAccount GetKcpUserAccountInfo(ulong KcpUserAccountAddr, ClrType KcpUserAccountType, ClrHeap Heap, string databaseLocation)
        {
            KcpUserAccount UserAccountInfo = new KcpUserAccount();

            // Get the embedded ProtectedBinary
            ClrInstanceField KcpProtectedBinaryField   = KcpUserAccountType.GetFieldByName("m_pbKeyData");
            ulong            KcpProtectedBinaryAddr    = KcpProtectedBinaryField.GetAddress(KcpUserAccountAddr);
            ulong            KcpProtectedBinaryObjAddr = (ulong)KcpProtectedBinaryField.GetValue(KcpUserAccountAddr);

            ClrInstanceField EncDataField     = KcpProtectedBinaryField.Type.GetFieldByName("m_pbData");
            ulong            EncDataAddr      = EncDataField.GetAddress(KcpProtectedBinaryObjAddr);
            ulong            EncDataArrayAddr = (ulong)EncDataField.GetValue(KcpProtectedBinaryObjAddr);

            ClrType EncDataArrayType = Heap.GetObjectType(EncDataArrayAddr);
            int     len = EncDataField.Type.GetArrayLength(EncDataArrayAddr);

            if (len <= 0 || len % 16 != 0) // Small sanity check to make sure everything's ok
            {
                return(null);
            }

            byte[] EncData = new byte[len];
            for (int i = 0; i < len; i++)
            {
                EncData[i] = (byte)EncDataArrayType.GetArrayElementValue(EncDataArrayAddr, i);
            }

            UserAccountInfo.databaseLocation     = databaseLocation;
            UserAccountInfo.encryptedBlob        = EncData;
            UserAccountInfo.encryptedBlobAddress = (IntPtr)KcpUserAccountType.GetArrayElementAddress(EncDataArrayAddr, 0);
            UserAccountInfo.encryptedBlobLen     = len;

            return(UserAccountInfo);
        }
        private static ulong GetTaskStateFromAddress(ulong address)
        {
            var type = Runtime.Heap.GetObjectType(address);

            if ((type != null) && (type.Name.StartsWith("System.Threading.Task")))
            {
                // try to get the m_stateFlags field value
                ClrInstanceField field = type.GetFieldByName("m_stateFlags");
                if (field != null)
                {
                    var val = field.GetValue(address);
                    if (val != null)
                    {
                        try
                        {
                            return((ulong)(int)val);
                        }
                        catch (InvalidCastException)
                        {
                        }
                    }
                }
            }

            return(0);
        }
Exemple #6
0
        public override ClrType GetRuntimeType(ulong obj)
        {
            if (!IsRuntimeType)
            {
                return(null);
            }

            ClrInstanceField field = GetFieldByName("m_handle");

            if (field == null)
            {
                return(null);
            }

            ulong methodTable = 0;

            if (field.ElementType == ClrElementType.NativeInt)
            {
                methodTable = (ulong)(long)field.GetValue(obj);
            }
            else if (field.ElementType == ClrElementType.Struct)
            {
                ClrInstanceField ptrField = field.Type.GetFieldByName("m_ptr");
                methodTable = (ulong)(long)ptrField.GetValue(field.GetAddress(obj, false), true);
            }

            return(DesktopHeap.GetTypeByMethodTable(methodTable, 0, obj));
        }
Exemple #7
0
        private Dictionary <int, string> GetManagedThreadNames(ClrHeap heap)
        {
            var result = new Dictionary <int, string>();

            if (heap == null || !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;
            ClrType          threadType     = heap.GetTypeByName("System.Threading.Thread");
            ClrInstanceField nameField      = threadType.GetFieldByName("m_Name");
            ClrInstanceField managedIdField = threadType.GetFieldByName("m_ManagedThreadId");

            foreach (var threadObject in threadObjects)
            {
                string name = (string)nameField.GetValue(threadObject);
                int    id   = (int)managedIdField.GetValue(threadObject);
                result[id] = name;
            }

            return(result);
        }
Exemple #8
0
        private void GetThreadEntry(ref List <ClrThread> threads, ClrInstanceField threadId, ClrInstanceField next, ulong curr, bool interior)
        {
            if (curr == 0)
            {
                return;
            }

            int       id     = (int)threadId.GetValue(curr, interior);
            ClrThread thread = GetThreadById(id);

            if (thread != null)
            {
                if (threads == null)
                {
                    threads = new List <ClrThread>();
                }
                threads.Add(thread);
            }

            curr = (ulong)next.GetValue(curr, interior);
            if (curr != 0)
            {
                GetThreadEntry(ref threads, threadId, next, curr, false);
            }
        }
Exemple #9
0
 public static string GetDisplayValue(this ClrInstanceField self, ClrObject clrObject)
 {
     if (self.IsObjectReference)
     {
         var obj = clrObject.GetObjectField(self.Name);
         if (obj.IsNull)
         {
             return("null");
         }
         return($"0x{obj.Address:X} [{obj.Type.Name}:0x{obj.Type.MethodTable:X}]");
     }
     else if (self.HasSimpleValue)
     {
         return(self.GetValue(clrObject.Address).ToString());
     }
     else if (self.IsValueClass)
     {
         var vt = clrObject.GetValueClassField(self.Name);
         return($"0x{vt.Address:X} [struct {vt.Type.Name}:0x{vt.Type.MethodTable:X}]");
     }
     else
     {
         return("<unknown value>");
     }
 }
Exemple #10
0
        private bool FindThread(ulong start, ulong stop, out ulong threadAddr, out ClrThread target)
        {
            ClrHeap heap = _runtime.Heap;

            foreach (ulong obj in EnumerateObjectsOfType(start, stop, "System.Threading.Thread"))
            {
                ClrType          type          = heap.GetObjectType(obj);
                ClrInstanceField threadIdField = type.GetFieldByName("m_ManagedThreadId");
                if (threadIdField != null && threadIdField.ElementType == ClrElementType.Int32)
                {
                    int       id     = (int)threadIdField.GetValue(obj);
                    ClrThread thread = GetThreadById(id);
                    if (thread != null)
                    {
                        threadAddr = obj;
                        target     = thread;
                        return(true);
                    }
                }
            }

            threadAddr = 0;
            target     = null;
            return(false);
        }
        public static TimeSpan Read(ClrObject obj)
        {
            ClrInstanceField dateDataField = obj.Type.GetFieldByName("_ticks");
            var rawDateTimeData            = (long)dateDataField.GetValue(obj.Address, true);
            var ts = new TimeSpan(rawDateTimeData);

            return(ts);
        }
Exemple #12
0
        public static T GetObjectAs <T>(ClrHeap heap, ulong heapobject, string fieldName)
        {
            ClrType          type  = heap.GetObjectType(heapobject);
            ClrInstanceField field = type.GetFieldByName(fieldName);
            T fieldValue           = (T)field.GetValue(heapobject);

            return(fieldValue);
        }
Exemple #13
0
        private DesktopBlockingObject CreateRWLObject(ulong obj, ClrType type)
        {
            if (type == null)
            {
                return(new DesktopBlockingObject(obj, false, 0, null, BlockingReason.None));
            }

            ClrInstanceField writerID = type.GetFieldByName("_dwWriterID");

            if (writerID != null && writerID.ElementType == ClrElementType.Int32)
            {
                int id = (int)writerID.GetValue(obj);
                if (id > 0)
                {
                    ClrThread thread = GetThreadById(id);
                    if (thread != null)
                    {
                        return(new DesktopBlockingObject(obj, true, 0, thread, BlockingReason.ReaderAcquired));
                    }
                }
            }

            ClrInstanceField uLock = type.GetFieldByName("_dwULockID");
            ClrInstanceField lLock = type.GetFieldByName("_dwLLockID");

            if (uLock != null && uLock.ElementType == ClrElementType.Int32 && lLock != null && lLock.ElementType == ClrElementType.Int32)
            {
                int uId = (int)uLock.GetValue(obj);
                int lId = (int)lLock.GetValue(obj);


                List <ClrThread> threads = null;
                foreach (ClrThread thread in _runtime.Threads)
                {
                    foreach (IRWLockData l in _runtime.EnumerateLockData(thread.Address))
                    {
                        if (l.LLockID == lId && l.ULockID == uId && l.Level > 0)
                        {
                            if (threads == null)
                            {
                                threads = new List <ClrThread>();
                            }

                            threads.Add(thread);
                            break;
                        }
                    }
                }

                if (threads != null)
                {
                    return(new DesktopBlockingObject(obj, true, 0, BlockingReason.ReaderAcquired, threads.ToArray()));
                }
            }

            return(new DesktopBlockingObject(obj, false, 0, null, BlockingReason.None));
        }
Exemple #14
0
        public object GetFieldValue(ulong address, string fieldName)
        {
            var type = ManagedHeap.GetObjectType(address);
            ClrInstanceField field = type.GetFieldByName(fieldName);

            if (field == null)
            {
                return(null);
            }

            return(field.GetValue(address));
        }
Exemple #15
0
        private object GetFieldValue(ClrHeap heap, ulong address, string fieldName)
        {
            var type = heap.GetObjectType(address);
            ClrInstanceField field = type.GetFieldByName(fieldName);

            if (field == null)
            {
                return(null);
            }

            return(field.GetValue(address));
        }
Exemple #16
0
        public IClrObject ReadFromFieldValue(ClrInstanceField field, ulong address, bool ownerIsNested = false)
        {
            if (field.Type.IsValueClass)
            {
                throw new ArgumentException("Cannot read ClrValue from field value. Handle this case in the caller.");
            }
            if (field.Type.IsPrimitive || field.Type.IsString)
            {
                var value = field.GetValue(address, ownerIsNested);
                return(WrapPrimitiveValue(field.Type, value));
            }

            var instanceAddress = (ulong)field.GetValue(address, ownerIsNested);

            if (instanceAddress == 0)
            {
                return(null);
            }
            var actualType = field.Type.Heap.GetObjectType(instanceAddress);

            return(ReadFromNotNullAddress(actualType, instanceAddress));
        }
Exemple #17
0
        public static ulong GetLastObjectInHierarchy(ClrHeap heap, ulong heapobject, string[] hierarchy, int currentIndex)
        {
            ClrType          type       = heap.GetObjectType(heapobject);
            ClrInstanceField field      = type.GetFieldByName(hierarchy[currentIndex]);
            ulong            fieldValue = (ulong)field.GetValue(heapobject, false, false);

            currentIndex++;
            if (currentIndex == hierarchy.Length)
            {
                return(fieldValue);
            }

            return(GetLastObjectInHierarchy(heap, fieldValue, hierarchy, currentIndex));
        }
Exemple #18
0
        static void ReadExceptionHResult(ClrType type, ulong obj)
        {
            ClrInstanceField field = type.GetFieldByName("_HResult");

            Debug.Assert(field.ElementType == ClrElementType.Int32);

            int value = (int)field.GetValue(obj);

            Console.WriteLine("  Exception 0x{0:X} hresult: 0x{1:X}", obj, value);

            // get value detail
            var output = GetOutput(obj, field);

            Console.WriteLine("  " + output);
        }
Exemple #19
0
        public ClrInstanceField TestFieldNameAndValue <T>(ClrType type, ulong obj, string name, T value)
        {
            ClrInstanceField field = type.GetFieldByName(name);

            Assert.NotNull(field);
            Assert.Equal(name, field.Name);

            object v = field.GetValue(obj);

            Assert.NotNull(v);
            Assert.IsType <T>(v);

            Assert.Equal(value, (T)v);

            return(field);
        }
Exemple #20
0
        private bool GetFieldObject(ClrType type, ulong obj, string fieldName, out ClrType valueType, out ulong value)
        {
            value = 0;
            valueType = null;

            ClrInstanceField field = type.GetFieldByName(fieldName);
            if (field == null)
                return false;

            value = (ulong)field.GetValue(obj);
            if (value == 0)
                return false;

            valueType = _heap.GetObjectType(value);
            return valueType != null;
        }
        private static bool ExtractRefObject(Pair p, ClrInstanceField field, ClrObject obj, out object reff)
        {
            reff = null;

            var reference = obj.GetObjectField(field.Name);

            if (reference.IsNull)
            {
                return(true);
            }

            if (reference.Type.IsString)
            {
                var value = GetStringContents(reference);

                reff = value;
                return(true);
            }

            if (reference.Type.IsPrimitive)
            {
                reff = field.GetValue(obj.Address);
                return(true);
            }

            if (Pair.Cache.TryGetValue(reference.Address, out reff))
            {
                return(true);
            }

            Type type = null;

            if (!GetType(p, reference.Type, out type))
            {
                return(false);
            }

            var par = Export(Yield(reference), type).Single();

            reff = par.RuntimeObject;
            p.Warnings.AddRange(par.Warnings);
            p.Errors.AddRange(par.Errors);

            return(true);
        }
Exemple #22
0
        /// <summary>
        ///   Reads date from <see cref="ClrObject" /> which points on <see cref="DateTime" />.
        /// </summary>
        /// <param name="obj">The object.</param>
        /// <returns></returns>
        public static DateTime FromObjToDate(ClrObject obj)
        {
            ClrAssert.ObjectNotNullTypeNotEmpty(obj);
            DateTime result;

            if (obj.Type.Name != typeof(DateTime).FullName)
            {
                result = DateTime.MinValue;
            }
            else
            {
                ClrInstanceField dateDataField = obj.Type.GetFieldByName("dateData");
                var rawDateTimeData            = (ulong)dateDataField.GetValue(obj.Address, true);
                result = TicksToDt(rawDateTimeData);
            }

            return(result);
        }
Exemple #23
0
        static (int length, string text) ReadString(ClrType type, ulong obj)
        {
            ClrInstanceField lengthField = type.GetFieldByName("_stringLength");

            if (lengthField == null)
            {
                return(0, String.Empty);
            }

            var stringLength = (int)lengthField.GetValue(obj);

            if (stringLength <= 0)
            {
                return(stringLength, String.Empty);
            }

            var text = (string)type.GetValue(obj);

            return(stringLength, text);
        }
Exemple #24
0
        private bool GetStackTraceFromField(ClrType type, ulong obj, out ulong stackTrace)
        {
            stackTrace = 0;
            ClrInstanceField field = type.GetFieldByName("_stackTrace");

            if (field == null)
            {
                return(false);
            }

            object tmp = field.GetValue(obj);

            if (tmp == null || !(tmp is ulong))
            {
                return(false);
            }

            stackTrace = (ulong)tmp;
            return(true);
        }
Exemple #25
0
        private static KeyValuePair <ClrInstanceField, string> GetField(string fieldName, string typeName, ClrHeap Heap)
        {
            //helper function to get an instance field
            if (!Heap.CanWalkHeap)
            {
                return(new KeyValuePair <ClrInstanceField, string>(null, "[!] Unable to walk the heap"));
            }

            Console.WriteLine("[+] Walking the heap....");
            foreach (ulong obj in Heap.EnumerateObjectAddresses())
            {
                ClrType type = Heap.GetObjectType(obj);

                if (type == null || type.Name != typeName)
                {
                    continue;
                }

                try
                {
                    ClrInstanceField field = type.GetFieldByName(fieldName);
#if DEBUG
                    Console.WriteLine("[+] Found desired field: " + fieldName);
#endif
                    ulong a = field.GetAddress(obj);
                    if (field.HasSimpleValue)
                    {
                        fieldValue = field.GetValue(obj);
                    }

                    fieldAddr = new IntPtr(long.Parse(a.ToString()));
                    return(new KeyValuePair <ClrInstanceField, string>(field, "[+] Found field " + fieldName));
                }
                catch (Exception e)
                {
                    return(new KeyValuePair <ClrInstanceField, string>(null, e.ToString()));
                }
            }

            return(new KeyValuePair <ClrInstanceField, string>(null, "Unable to locate field: " + fieldName));
        }
        static void Main(string[] args)
        {
            int pid = 1234;

            DataTarget dt = DataTarget.AttachToProcess(pid, 5000, AttachFlag.Passive);

            using (dt)
            {
                //pick first CLR version
                ClrInfo    runtimeInfo = dt.ClrVersions[0];
                ClrRuntime runtime     = runtimeInfo.CreateRuntime();

                ClrType type;

                //enumerate objects on managed heap
                foreach (ulong obj in runtime.Heap.EnumerateObjectAddresses())
                {
                    type = runtime.Heap.GetObjectType(obj);

                    if (type == null)
                    {
                        continue;
                    }

                    if (type.Name == "MyProject.MyClass")
                    {
                        Console.WriteLine("Address 0x{0:X}: {1}", obj, type.Name);
                        ClrInstanceField f   = type.GetFieldByName("Foo");
                        object           val = f.GetValue(obj);
                        if (val != null)
                        {
                            Console.WriteLine(val.ToString());
                        }
                    }
                }
            }

            Console.ReadKey();
        }
Exemple #27
0
        protected string?BuildMethodNameFromDelegate(ulong action, ulong task = 0)
        {
            string?r = "[no action]";

            if (action != 0)
            {
                var target = (ulong)fieldDelegateTarget.GetValue(action);
                if (target == 0)
                {
                    r = "[no target]";
                }
                else
                {
                    r = BuildDelegateMethodName(heap.GetObjectType(target), action);
                    if (r == "System.Runtime.CompilerServices.AsyncMethodBuilderCore+MoveNextRunner.Run")
                    {
                        var fieldStateMachine = heap.GetObjectType(target).GetFieldByName("m_stateMachine");
                        var stateMachine      = (ulong)fieldStateMachine.GetValue(target);
                        var typeStateMachine  = heap.GetObjectType(stateMachine);
                        r = typeStateMachine.Name;
                    }
                    else if (task != 0)
                    {
                        // get the task scheduler if any
                        var scheduler = (ulong)fieldTaskScheduler.GetValue(task);
                        if (scheduler != 0)
                        {
                            var schedulerTypeName = heap.GetObjectType(scheduler).ToString();
                            if (schedulerTypeName != "System.Threading.Tasks.ThreadPoolTaskScheduler")
                            {
                                r = $"{r} [{schedulerTypeName}]";
                            }
                        }
                    }
                }
            }
            return(r);
        }
Exemple #28
0
 public override IEnumerable <ObjectInfo> EnumerateTimerTasks()
 {
     if (fieldSQueue.IsInitialized(domain))
     {
         var timeQueue = (ulong)fieldSQueue.GetValue(domain);
         for (ulong timer = (ulong)fieldTimers.GetValue(timeQueue); timer != 0; timer = (ulong)fieldNext.GetValue(timer))
         {
             var state = (ulong)fieldState.GetValue(timer);
             if (state != 0)
             {
                 var typeState = heap.GetObjectType(state);
                 if (typeState == typeDelayPromise)
                 {
                     var continuation = (ulong)fieldTaskContinuationObject.GetValue(state);
                     var target       = (ulong)fieldDelegateTarget.GetValue(continuation);
                     var stateMachine = (ulong)fieldStateMachine.GetValue(target);
                     yield return(new ObjectInfo {
                         Address = stateMachine, Type = heap.GetObjectType(stateMachine)
                     });
                 }
             }
         }
     }
 }
Exemple #29
0
        internal static ClrFieldValue?Create(ClrInstanceField typeField, ClrInstance instance, bool interior)
        {
            if (typeField.Type.MetadataToken == 0 && typeField.Type.Name == "ERROR")
            {
                return(null);
                // TODO: not sure about this!
                //throw new NotSupportedException();
            }

            Contract.AssertNotNull(instance.ObjectAddress);

            object?value = null;

            try
            {
                // This code could fail with NRE with no obvious reason.
                value = typeField.GetValue(instance.ObjectAddress.Value, interior);
            }
            catch (Exception)
            { }

            var type = typeField.Type;

            if (!type.IsIntrinsic() && value != null)
            {
                // instance.Heap.GetObjectType may return null. WHY?
                type = instance.Heap.GetObjectType((ulong)value) ?? type;
            }

            if (value == null)
            {
                value = instance.ObjectAddress.Value + (ulong)typeField.Offset;
            }

            return(new ClrFieldValue(typeField, new ClrInstance(instance.Heap, value, type, interior)));
        }
Exemple #30
0
        static string GetOutput(ulong obj, ClrInstanceField field)
        {
            // If we don't have a simple value, return the address of the field in hex.
            if (!field.HasSimpleValue)
            {
                return(field.GetAddress(obj).ToString("X"));
            }

            object value = field.GetValue(obj);

            if (value == null)
            {
                return("{error}");  // Memory corruption in the target process.
            }
            // Decide how to format the string based on the underlying type of the field.
            switch (field.ElementType)
            {
            case ClrElementType.String:
                // In this case, value is the actual string itself.
                return((string)value);

            case ClrElementType.Array:
            case ClrElementType.SZArray:
            case ClrElementType.Object:
            case ClrElementType.Class:
            case ClrElementType.FunctionPointer:
            case ClrElementType.NativeInt:
            case ClrElementType.NativeUInt:
                // These types are pointers.  Print as hex.
                return(string.Format("{0:X}", value));

            default:
                // Everything else will look fine by simply calling ToString.
                return(value.ToString());
            }
        }
        private void GetThreadEntry(ref List<ClrThread> threads, ClrInstanceField threadId, ClrInstanceField next, ulong curr, bool interior)
        {
            if (curr == 0)
                return;

            int id = (int)threadId.GetValue(curr, interior);
            ClrThread thread = GetThreadById(id);
            if (thread != null)
            {
                if (threads == null)
                    threads = new List<ClrThread>();
                threads.Add(thread);
            }

            curr = (ulong)next.GetValue(curr, interior);
            if (curr != 0)
                GetThreadEntry(ref threads, threadId, next, curr, false);
        }