このメモでは、本質的に.Netプラットフォーム用のメモリプロファイラの単純なカーネルを記述します。これは、SOHヒープからダンプを削除します(将来LOHを使用する場合)。
記事を書くには、記事のコードが必要です .Netオブジェクトへのポインターを取得し、 ストリームの手動クローン作成 (オブジェクトのサイズの測定)。
今日の目標:
- 多数の.Netを反復処理する方法を学ぶ
- .Netヒープの始まりを見つけることを学ぶ
- 別のドメインのすべてのオブジェクトを焼結してみてください。
GitHubのプロジェクトへのリンク: Dotnetex
まず、このサイクルのHabréに投稿された記事の全リスト
前回と同様に、問題が利用可能になったときに解決します。
- .Netでヒープの始まりを見つける
おそらくご存じのとおり、.Netには2種類のヒープがあります。 これは、小さなオブジェクトの束と大きなオブジェクトの束(> 85K)です。 これらは、主に、オブジェクト自体の内部の構成が異なります。 SOHでオブジェクトが次々に割り当てられる場合、LOHではすべてがリンクリストと占有セクション間の空きスペースのテーブルに基づいています。 これは私たちが知っていることです。 しかし、真実は異なります。 第一に、SOHは、仮想メモリの拡張中に使用中の仮想メモリにつまずく可能性があるため、継続できません。 2番目-実際、オブジェクトは次々とハイライトされ続けるというのは美しい前提です。移動できないピンオブジェクトがあるためです。つまり、ヒープが圧縮されると空のスペースが表示されます。 これは、SOHにもvoidテーブルが含まれることを意味します。 最後に、これは具体的に次のことを意味する必要があります:(a)ヒープの下に割り当てられる複数の仮想メモリゾーンがあり、(b)VirtualAllocを介して割り当てられる可能性が最も高い、(c)オブジェクトが連続していない、つまりアルゴリズムを意味するそれに頼ることはできません。
この例を単純化するために、ヒープからオブジェクトのみをダンプしましょう。 これを行うには、任意のオブジェクトへのポインターを取得し、WinApi VirtualQuery関数を使用して、このヒープに割り当てられた仮想メモリの領域を取得します。
public static void GetManagedHeap(out IntPtr heapsOffset, out IntPtr lastHeapByte) { // . var offset = EntityPtr.ToPointer(new object()); var memoryBasicInformation = new WinApi.MEMORY_BASIC_INFORMATION(); unsafe { WinApi.VirtualQuery(offset, ref memoryBasicInformation, (IntPtr)Marshal.SizeOf(memoryBasicInformation)); heapsOffset = (IntPtr)memoryBasicInformation.AllocationBase; lastHeapByte = (IntPtr)((long)offset + (long)memoryBasicInformation.RegionSize); } }
いいね! これで、オブジェクトを検索するメモリの一部がわかりました。
- 別のドメインのすべてのオブジェクトを焼結します
これを行うには、それらを認識する方法を理解する必要があります。 したがって、デバッガを使用してメモリを調べると、オブジェクトへの参照はオブジェクトの最初のフィールドではなく、仮想メソッドのテーブルへのポインタを示していると最終的に結論付けることができます。 実際には、仮想メソッドのテーブルだけでなく、一般的なメソッドとタイプの説明です。 結局のところ、私たちはC ++の世界ではなく、.Netの世界にいます。各オブジェクトについて、「誰になりますか?」 しかし、SyncBlockIndexについてはどうでしょうか。 そして、結局のところ、彼はオブジェクトの「前」にあり、ワードのサイズ(32ビットでは4バイト、64ビットシステムでは8バイト)を差し引いています。 したがって、オブジェクトのタイトル構造は次のとおりです。
[StructLayout(LayoutKind.Explicit)] public unsafe struct EntityInfo { [FieldOffset(0)] public int SyncBlockIndex; [FieldOffset(4)] public MethodTableInfo *MethodTable; } [StructLayout(LayoutKind.Explicit)] public struct RefTypeInfo { [FieldOffset(0)] public EntityInfo BasicInfo; [FieldOffset(8)] public byte fieldsStart; }
次に、MethodTableInfoの外観を見てみましょう。
[StructLayout(LayoutKind.Explicit)] public unsafe struct MethodTableInfo { #region Basic Type Info [FieldOffset(0)] public MethodTableFlags Flags; [FieldOffset(4)] public int Size; [FieldOffset(8)] public short AdditionalFlags; [FieldOffset(10)] public short MethodsCount; [FieldOffset(12)] public short VirtMethodsCount; [FieldOffset(14)] public short InterfacesCount; [FieldOffset(16)] public MethodTableInfo *ParentTable; #endregion [FieldOffset(20)] public ObjectTypeInfo *ModuleInfo; [FieldOffset(24)] public ObjectTypeInfo *EEClass; }
この構造からのすべての情報の一部のみを次に示します。 実際、より広範囲です。 EEClassフィールドはここで重要であり、タイプ記述の構造につながります。 私は実際には勉強しませんでした。 コンテンツは次のようになります。
[StructLayout(LayoutKind.Explicit)] public unsafe struct ObjectTypeInfo { [FieldOffset(0)] public ObjectTypeInfo *ParentClass; [FieldOffset(16)] public MethodTableInfo *MethodsTable; }
MethodsTableフィールドのみが重要なので、私はそれを見つけました。 私は残りを逃しました。 なぜ必要なのですか? これは、EETableフィールドによって参照されるMethodsTableへの参照です。
このようにして、cな方法ではなく、.Netオブジェクトを検出するための多少不正確だが完全に機能する方法が見つかりました。 次のようになります。
private static unsafe bool IsCorrectMethodsTable(IntPtr mt) { if (mt == IntPtr.Zero) return false; if (PointsToAllocated(mt)) if (PointsToAllocated((IntPtr) ((MethodTableInfo*) mt)->EEClass)) if (PointsToAllocated((IntPtr) ((MethodTableInfo*) mt)->EEClass->MethodsTable)) return ((IntPtr) ((MethodTableInfo*) mt)->EEClass->MethodsTable == mt) || ((IntPtr) ((MethodTableInfo*) mt)->ModuleInfo == MscorlibModule); return false; } private static bool PointsToAllocated(IntPtr ptr) { if (ptr == IntPtr.Zero) return false; return !WinApi.IsBadReadPtr(ptr, 32); }
各ポインタについて、割り当てられたメモリを指すかどうかがチェックされます。 これが最初の障壁です。 2番目の障壁は、理論オブジェクトの最初のフィールドが、MethodsTableとして最初に解釈するものを示している場合です。 EEClassフィールドが割り当てられたメモリを指していることを確認し、このポインターをObjectTypeInfo構造体へのポインターとして解釈し、MethodsTableへのポインターが見つかったポインターと等しいかどうかを確認します(バックリンクがありますか?)すべてが正常であれば、オブジェクトが見つかりました。
メモリのすべての領域を通り抜けて、そこにあるオブジェクトを認識しようとするだけです。 画面に見つかって表示されたものを登録するためのコードもあるため、このシートをレイアウトしません。 問題が解決したとだけ言います。プログラムを終了するためのリンクは下にあります。
完全なサンプルコードへのリンク: GitHub / DotNetEx / AdvSample
プログラムの出力、メモリダンプ00606 : System.String 00583 : System.Object 00277 : System.RuntimeType 00072 : System.Array+SZArrayEnumerator 00046 : System.Char[] 00041 : System.Int32[] 00033 : System.String[] 00032 : System.Object[] 00030 : System.Version 00029 : System.Byte[] 00024 : System.Text.StringBuilder 00023 : System.Collections.Hashtable+bucket[] 00020 : System.Security.PermissionSet 00020 : System.Collections.Hashtable 00020 : System.Reflection.AssemblyName 00014 : System.Reflection.RuntimeAssembly 00014 : System.Security.Permissions.EnvironmentPermission 00013 : System.Collections.Hashtable+SyncHashtable 00012 : System.Globalization.CompareInfo 00012 : System.Collections.Generic.Dictionary`2+Entry[[System.Type, mscorlib, Ve rsion=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Securit y.Policy.EvidenceTypeDescriptor, mscorlib, Version=4.0.0.0, Culture=neutral, Pub licKeyToken=b77a5c561934e089]][] 00011 : System.Globalization.CultureInfo 00010 : System.Collections.ArrayList 00010 : System.Int32 00010 : System.Threading.ThreadStart 00010 : System.Internal.HandleCollector+HandleType 00010 : System.Internal.HandleCollector+HandleType 00009 : System.Security.Permissions.SecurityPermission 00009 : System.EventHandler 00009 : Microsoft.Win32.SafeHandles.SafeRegistryHandle 00009 : Microsoft.Win32.RegistryKey 00008 : System.Threading.Thread 00008 : Microsoft.Win32.SafeHandles.SafeWaitHandle 00008 : System.Security.Policy.EvidenceTypeDescriptor 00008 : System.Runtime.InteropServices.HandleRef 00008 : System.UInt16 00006 : System.Runtime.Remoting.Metadata.SoapTypeAttribute[] 00005 : System.Type[] 00005 : System.Threading.ReaderWriterLock 00005 : System.Reflection.CustomAttributeRecord[] 00005 : System.Globalization.TextInfo 00005 : System.Security.Permissions.EnvironmentStringExpressionSet 00005 : System.WeakReference 00005 : System.Threading.ThreadHelper 00004 : System.Security.FrameSecurityDescriptor 00004 : System.Reflection.RuntimeModule 00004 : System.Reflection.RuntimeConstructorInfo 00004 : System.Guid 00004 : Microsoft.Win32.SafeHandles.SafePEFileHandle 00004 : System.Security.Policy.Evidence 00004 : System.Collections.Generic.Dictionary`2[[System.Type, mscorlib, Version= 4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Security.Poli cy.EvidenceTypeDescriptor, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKey Token=b77a5c561934e089]] 00004 : System.Security.Policy.AssemblyEvidenceFactory 00004 : System.Security.Policy.Evidence+EvidenceUpgradeLockHolder 00003 : System.Security.Util.TokenBasedSet 00003 : System.Globalization.CultureData 00003 : System.Reflection.MemberFilter 00003 : System.Reflection.MethodBase[] 00003 : System.RuntimeType[] 00003 : System.Runtime.Remoting.Lifetime.LeaseLifeTimeServiceProperty 00003 : System.Attribute[] 00003 : System.Threading.ManualResetEvent 00003 : System.IO.PathHelper 00003 : System.Security.Permissions.UIPermission 00002 : System.AppDomainSetup 00002 : System.Security.PermissionToken 00002 : System.Runtime.Remoting.Contexts.IContextProperty[] 00002 : System.Reflection.TypeFilter 00002 : System.Collections.Queue 00002 : System.WeakReference[] 00002 : System.Char 00002 : System.Security.Policy.StrongName[] 00002 : System.Reflection.RuntimeMethodInfo 00002 : System.Threading.SynchronizationContext 00002 : System.Internal.HandleCollector+HandleType[] 00002 : System.Globalization.NumberFormatInfo 00002 : Microsoft.Win32.SystemEvents+SystemEventInvokeInfo[] 00002 : System.Text.EncoderReplacementFallback 00002 : System.IO.UnmanagedMemoryStream 00002 : System.Security.Permissions.FileIOAccess 00002 : System.Security.Util.StringExpressionSet 00001 : System.Exception 00001 : System.OutOfMemoryException 00001 : System.StackOverflowException 00001 : System.ExecutionEngineException 00001 : System.AppDomain 00001 : System.Security.PermissionTokenFactory 00001 : System.Security.PermissionToken[] 00001 : System.Globalization.CalendarData[] 00001 : System.Globalization.CalendarData 00001 : System.__Filters 00001 : System.DefaultBinder 00001 : System.RuntimeType+RuntimeTypeCache+MemberInfoCache`1[[System.Reflection .RuntimeConstructorInfo, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyTo ken=b77a5c561934e089]] 00001 : System.Reflection.RuntimeConstructorInfo[] 00001 : System.Reflection.ConstructorInfo[] 00001 : System.Collections.Generic.List`1[[System.Reflection.MethodBase, mscorli b, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.Signature 00001 : System.Reflection.ParameterInfo[] 00001 : System.Int32[][] 00001 : System.Runtime.Remoting.Proxies.ProxyAttribute 00001 : System.Runtime.Remoting.DomainSpecificRemotingData 00001 : System.Runtime.Remoting.Channels.ChannelServicesData 00001 : System.Runtime.Remoting.Activation.LocalActivator 00001 : System.Runtime.Remoting.Activation.ActivationListener 00001 : System.Runtime.Remoting.Contexts.ContextAttribute[] 00001 : System.Runtime.Remoting.Contexts.Context 00001 : System.Runtime.Remoting.Messaging.ConstructorCallMessage 00001 : System.Runtime.Remoting.Metadata.RemotingTypeCachedData 00001 : System.Collections.Generic.Dictionary`2[[System.RuntimeType, mscorlib, V ersion=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Runtim eType, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e0 89]] 00001 : System.Collections.Generic.Dictionary`2+Entry[[System.RuntimeType, mscor lib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System. RuntimeType, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c56 1934e089]][] 00001 : System.AttributeUsageAttribute 00001 : System.Runtime.Remoting.Metadata.SoapTypeAttribute 00001 : System.Runtime.Remoting.Activation.ConstructionLevelActivator 00001 : System.Runtime.Remoting.RemotingConfigHandler+RemotingConfigInfo 00001 : System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Versio n=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Globalizati on.CultureData, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5 c561934e089]] 00001 : System.Runtime.Remoting.ObjectHandle 00001 : System.Diagnostics.TraceSwitch 00001 : System.Collections.Generic.Dictionary`2[[System.Int16, mscorlib, Version =4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.IntPtr, msco rlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.Collections.Generic.GenericEqualityComparer`1[[System.Int16, msco rlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.Collections.Generic.ObjectEqualityComparer`1[[System.IntPtr, msco rlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.Threading.Mutex 00001 : System.Runtime.CompilerServices.RuntimeHelpers+CleanupCode 00001 : System.Threading.Mutex+MutexCleanupInfo 00001 : System.Threading.Mutex+MutexTryCodeHelper 00001 : System.Threading.EventWaitHandle 00001 : System.Threading.HostExecutionContextManager 00001 : System.Collections.Generic.ObjectEqualityComparer`1[[System.Type, mscorl ib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.Security.Policy.ApplicationTrust 00001 : System.Security.Policy.PolicyStatement 00001 : System.Collections.Generic.List`1[[System.Security.Policy.StrongName, ms corlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.SZArrayHelper+SZGenericArrayEnumerator`1[[System.Security.Policy. StrongName, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561 934e089]] 00001 : System.Collections.ObjectModel.ReadOnlyCollection`1[[System.Security.Pol icy.StrongName, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5 c561934e089]] 00001 : Microsoft.Win32.Win32Native+OSVERSIONINFO 00001 : Microsoft.Win32.Win32Native+OSVERSIONINFOEX 00001 : System.OperatingSystem 00001 : System.__ComObject 00001 : System.Collections.Queue+SynchronizedQueue 00001 : System.Threading.AutoResetEvent 00001 : System.Threading.ContextCallback 00001 : System.RuntimeMethodInfoStub 00001 : System.RuntimeType+RuntimeTypeCache+MemberInfoCache`1[[System.Reflection .RuntimeMethodInfo, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b 77a5c561934e089]] 00001 : System.Reflection.RuntimeMethodInfo[] 00001 : System.Collections.Generic.List`1[[System.Attribute, mscorlib, Version=4 .0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.Drawing.SizeF 00001 : System.Drawing.Point 00001 : System.Windows.Forms.Application+ThreadContext 00001 : System.Windows.Forms.WindowsFormsSynchronizationContext 00001 : System.EventArgs 00001 : System.Windows.Forms.NativeMethods+WNDCLASS_D 00001 : Microsoft.Win32.UserPreferenceChangedEventHandler 00001 : System.Random 00001 : System.Collections.Generic.ObjectEqualityComparer`1[[System.Object, msco rlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : System.Collections.Generic.Dictionary`2[[System.String, mscorlib, Versio n=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Object[], m scorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]] 00001 : Microsoft.Win32.SystemEvents 00001 : System.Runtime.Remoting.Messaging.LogicalCallContext 00001 : System.Text.UTF8Encoding 00001 : Microsoft.Win32.NativeMethods+WNDCLASS 00001 : System.Internal.HandleCollector+HandleType[] 00001 : System.IntPtr[] 00001 : System.Security.Util.URLString 00001 : System.Security.Permissions.FileIOPermission 00001 : System.Security.Util.LocalSiteString 00001 : System.Security.Util.DirectoryString 00001 : Microsoft.Win32.SafeHandles.SafeFileHandle 00001 : System.Text.SBCSCodePageEncoding 00001 : System.Text.InternalEncoderBestFitFallback 00001 : System.Text.InternalDecoderBestFitFallback 00001 : Microsoft.Win32.SafeHandles.SafeViewOfFileHandle 00001 : Microsoft.Win32.SafeHandles.SafeFileMappingHandle 00001 : System.IO.__ConsoleStream 00001 : System.Text.EncoderNLS 00001 : System.IO.StreamWriter+MdaHelper 00001 : System.IO.TextWriter+SyncTextWriter 00001 : System.Diagnostics.Stopwatch 00001 : System.Predicate`1[[System.Int64, mscorlib, Version=4.0.0.0, Culture=neu tral, PublicKeyToken=b77a5c561934e089]] 00001 : System.DBNull Objects total: 2294. Time taken: 437
- 他の人のドメインからオブジェクトを取得できますか?
もちろん! 結局のところ、我々は仮想メモリを見ています。 今回は...そして2つ目は...ドメイン間に境界線がありません。オブジェクトはドメイン境界線を越えても次々と目立ちます。 違いはコードにあります。 したがって、たとえば、ドメイン間でシリアル化せずにオブジェクトを転送できます。