There are times when I’ve needed to find the managed thread object backing the native object output in !threads. The way I used to get it was to dump all thread objects (!dumpheap -mt <thread MT>) and match the managed thread Id field with the output from !threads. However, there’s a better way.
There’s a method on the native thread object “clr!Thread::GetExposedObjectRaw”, which is a simple 3 instruction method. Disassembling it we can see that a pointer to the managed thread object is 0x228 bytes into the native thread object. So, simply, the managed thread object is @ poi(poi(ThreadObj+0x228)).
I assume this is also there in the 2.0 CLR but I haven’t looked. Also, this offset is for the 64 bit CLR, on a 32 bit system it’s at ThreadObj+0x160. Obviously these offsets are subject to change with any CLR patch, so don’t depend on them in any production code unless you understand the risks.