├── Binaries ├── dbgeng.dll ├── symsrv.dll ├── PoolData.lib ├── WpfPlus.dll ├── dbghelp.dll ├── PoolViewer.exe ├── PoolViewerExt.dll └── ManagedUIKitWPF.dll ├── PoolViewer ├── unicorn.ico ├── PoolViewer.rc ├── PoolViewer.vcxproj.user ├── resource.h ├── PoolViewer.vcxproj.filters ├── Main.cpp └── PoolViewer.vcxproj ├── packages ├── WpfPlus.1.2.0 │ ├── .signature.p7s │ ├── WpfPlus.1.2.0.nupkg │ └── lib │ │ └── net40 │ │ └── WpfPlus.dll └── UnmanagedExports.1.2.7 │ ├── .signature.p7s │ ├── tools │ ├── Mono.Cecil.dll │ ├── RGiesecke.DllExport.dll │ ├── RGiesecke.DllExport.pdb │ ├── RGiesecke.DllExport.MSBuild.dll │ ├── RGiesecke.DllExport.MSBuild.pdb │ ├── init.ps1 │ ├── uninstall.ps1 │ ├── install.ps1 │ ├── DllExportCmdLets.psm1 │ └── RGiesecke.DllExport.targets │ ├── UnmanagedExports.1.2.7.nupkg │ └── lib │ └── net │ └── RGiesecke.DllExport.Metadata.dll ├── PoolViewerExt ├── exports.def ├── PoolViewerExt.vcxproj.user ├── PoolViewerExt.vcxproj.filters ├── DllMain.cpp └── PoolViewerExt.vcxproj ├── ManagedUIKitWPF ├── packages.config ├── DllConfig.xaml ├── properties │ ├── Settings.Designer.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── UIBridge.cs ├── DllConfig.xaml.cs ├── ManagedUIKitWPF.csproj ├── MainView.xaml └── MainView.xaml.cs ├── PoolData ├── PoolData.vcxproj.user ├── PoolData.vcxproj.filters ├── PoolData.vcxproj ├── PoolData.h └── PoolData.cpp ├── LICENSE.md ├── README.md └── PoolViewer.sln /Binaries/dbgeng.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/Binaries/dbgeng.dll -------------------------------------------------------------------------------- /Binaries/symsrv.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/Binaries/symsrv.dll -------------------------------------------------------------------------------- /Binaries/PoolData.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/Binaries/PoolData.lib -------------------------------------------------------------------------------- /Binaries/WpfPlus.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/Binaries/WpfPlus.dll -------------------------------------------------------------------------------- /Binaries/dbghelp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/Binaries/dbghelp.dll -------------------------------------------------------------------------------- /PoolViewer/unicorn.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/PoolViewer/unicorn.ico -------------------------------------------------------------------------------- /Binaries/PoolViewer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/Binaries/PoolViewer.exe -------------------------------------------------------------------------------- /PoolViewer/PoolViewer.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/PoolViewer/PoolViewer.rc -------------------------------------------------------------------------------- /Binaries/PoolViewerExt.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/Binaries/PoolViewerExt.dll -------------------------------------------------------------------------------- /Binaries/ManagedUIKitWPF.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/Binaries/ManagedUIKitWPF.dll -------------------------------------------------------------------------------- /packages/WpfPlus.1.2.0/.signature.p7s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/packages/WpfPlus.1.2.0/.signature.p7s -------------------------------------------------------------------------------- /packages/WpfPlus.1.2.0/WpfPlus.1.2.0.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/packages/WpfPlus.1.2.0/WpfPlus.1.2.0.nupkg -------------------------------------------------------------------------------- /packages/WpfPlus.1.2.0/lib/net40/WpfPlus.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/packages/WpfPlus.1.2.0/lib/net40/WpfPlus.dll -------------------------------------------------------------------------------- /packages/UnmanagedExports.1.2.7/.signature.p7s: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/packages/UnmanagedExports.1.2.7/.signature.p7s -------------------------------------------------------------------------------- /PoolViewerExt/exports.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | DebugExtensionInitialize 3 | DebugExtensionUninitialize 4 | DebugExtensionNotify 5 | help 6 | poolview -------------------------------------------------------------------------------- /packages/UnmanagedExports.1.2.7/tools/Mono.Cecil.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/packages/UnmanagedExports.1.2.7/tools/Mono.Cecil.dll -------------------------------------------------------------------------------- /ManagedUIKitWPF/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/UnmanagedExports.1.2.7/UnmanagedExports.1.2.7.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/packages/UnmanagedExports.1.2.7/UnmanagedExports.1.2.7.nupkg -------------------------------------------------------------------------------- /packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.dll -------------------------------------------------------------------------------- /packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.pdb -------------------------------------------------------------------------------- /packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.MSBuild.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.MSBuild.dll -------------------------------------------------------------------------------- /packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.MSBuild.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.MSBuild.pdb -------------------------------------------------------------------------------- /packages/UnmanagedExports.1.2.7/lib/net/RGiesecke.DllExport.Metadata.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yardenshafir/PoolViewer/HEAD/packages/UnmanagedExports.1.2.7/lib/net/RGiesecke.DllExport.Metadata.dll -------------------------------------------------------------------------------- /PoolData/PoolData.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /PoolViewer/PoolViewer.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /PoolViewerExt/PoolViewerExt.vcxproj.user: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /packages/UnmanagedExports.1.2.7/tools/init.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | Import-Module (Join-Path $toolsPath DllExportCmdLets.psm1) 4 | 5 | if($project) { 6 | Assert-PlatformTargetOfProject $project.FullName 7 | } 8 | else { 9 | Get-AllDllExportMsBuildProjects | % { 10 | Assert-PlatformTargetOfProject $_.FullPath 11 | } 12 | } -------------------------------------------------------------------------------- /PoolViewer/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by MainApplication.rc 4 | // 5 | #define IDI_APPICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /packages/UnmanagedExports.1.2.7/tools/uninstall.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | $targetFileName = 'RGiesecke.DllExport.targets' 4 | $targetFileName = [System.IO.Path]::Combine($toolsPath, $targetFileName) 5 | $targetUri = New-Object Uri($targetFileName, [UriKind]::Absolute) 6 | 7 | $projects = Get-DllExportMsBuildProjectsByFullName($project.FullName) 8 | 9 | return $projects | % { 10 | $currentProject = $_ 11 | 12 | $currentProject.Xml.Imports | ? { 13 | "RGiesecke.DllExport.targets" -ieq [System.IO.Path]::GetFileName($_.Project) 14 | } | % { 15 | $currentProject.Xml.RemoveChild($_) 16 | } 17 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Yarden Shafir 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PoolData/PoolData.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | 23 | 24 | Header Files 25 | 26 | 27 | -------------------------------------------------------------------------------- /ManagedUIKitWPF/DllConfig.xaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /ManagedUIKitWPF/properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:$clrversion$ 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace PoolViewer.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /PoolViewerExt/PoolViewerExt.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | 26 | 27 | Header Files 28 | 29 | 30 | -------------------------------------------------------------------------------- /PoolViewer/PoolViewer.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | 23 | 24 | Header Files 25 | 26 | 27 | 28 | 29 | Header Files\Resource Files 30 | 31 | 32 | -------------------------------------------------------------------------------- /packages/UnmanagedExports.1.2.7/tools/install.ps1: -------------------------------------------------------------------------------- 1 | param($installPath, $toolsPath, $package, $project) 2 | 3 | $targetFileName = 'RGiesecke.DllExport.targets' 4 | $targetFileName = [IO.Path]::Combine($toolsPath, $targetFileName) 5 | $targetUri = New-Object Uri -ArgumentList $targetFileName, [UriKind]::Absolute 6 | 7 | $msBuildV4Name = 'Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'; 8 | $msBuildV4 = [System.Reflection.Assembly]::LoadWithPartialName($msBuildV4Name) 9 | 10 | if(!$msBuildV4) { 11 | throw New-Object System.IO.FileNotFoundException("Could not load $msBuildV4Name."); 12 | } 13 | 14 | $projectCollection = $msBuildV4.GetType('Microsoft.Build.Evaluation.ProjectCollection') 15 | 16 | # change the reference to RGiesecke.DllExport.Metadata.dll to not be copied locally 17 | 18 | $project.Object.References | ? { 19 | $_.Name -ieq "RGiesecke.DllExport.Metadata" 20 | } | % { 21 | if($_ | Get-Member | ? {$_.Name -eq "CopyLocal"}){ 22 | $_.CopyLocal = $false 23 | } 24 | } 25 | 26 | $projects = $projectCollection::GlobalProjectCollection.GetLoadedProjects($project.FullName) 27 | $projects | % { 28 | $currentProject = $_ 29 | 30 | # remove imports of RGiesecke.DllExport.targets from this project 31 | $currentProject.Xml.Imports | ? { 32 | return ("RGiesecke.DllExport.targets" -ieq [IO.Path]::GetFileName($_.Project)) 33 | } | % { 34 | $currentProject.Xml.RemoveChild($_); 35 | } 36 | 37 | # remove the properties DllExportAttributeFullName and DllExportAttributeAssemblyName 38 | $currentProject.Xml.Properties | ? { 39 | $_.Name -eq "DllExportAttributeFullName" -or $_.Name -eq "DllExportAttributeAssemblyName" 40 | } | % { 41 | $_.Parent.RemoveChild($_) 42 | } 43 | 44 | $projectUri = New-Object Uri -ArgumentList $currentProject.FullPath, [UriKind]::Absolute 45 | $relativeUrl = $projectUri.MakeRelative($targetUri) 46 | $import = $currentProject.Xml.AddImport($relativeUrl) 47 | $import.Condition = "Exists('$relativeUrl')"; 48 | 49 | # remove the old stuff in the DllExports folder from previous versions, (will check that only known files are in it) 50 | Remove-OldDllExportFolder $project 51 | Assert-PlatformTargetOfProject $project.FullName 52 | } -------------------------------------------------------------------------------- /ManagedUIKitWPF/UIBridge.cs: -------------------------------------------------------------------------------- 1 | using RGiesecke.DllExport; 2 | using System; 3 | using System.Threading; 4 | using System.Windows.Interop; 5 | 6 | namespace PoolViewer 7 | { 8 | internal class UIBridge 9 | { 10 | //// Static Objects 11 | public static MainView mainview_ui; 12 | public static Thread gui_thread; 13 | public static IntPtr mainview_handle = IntPtr.Zero; 14 | 15 | [DllExport] 16 | public static IntPtr CreateUserInterface(IntPtr api_1_ptr, IntPtr api_2_ptr, IntPtr api_3_ptr) // Multi-Threaded Version 17 | { 18 | gui_thread = new Thread(() => 19 | { 20 | mainview_ui = new MainView(api_1_ptr, api_2_ptr, api_3_ptr) 21 | { Opacity = 0, Width = 0, Height = 0 }; 22 | mainview_ui.Show(); 23 | mainview_handle = new WindowInteropHelper(mainview_ui).Handle; 24 | System.Windows.Threading.Dispatcher.Run(); 25 | }); 26 | gui_thread.SetApartmentState(ApartmentState.STA); // STA Thread Initialization 27 | gui_thread.Start(); 28 | 29 | while (mainview_handle == IntPtr.Zero) { } 30 | return mainview_handle; 31 | } 32 | 33 | [DllExport] 34 | public static void DisplayUserInterface() // Multi-Threaded Version 35 | { 36 | try 37 | { 38 | mainview_ui.Opacity = 1; 39 | } 40 | catch // Can't Access to UI Thread , So Dispatching 41 | { 42 | mainview_ui.Dispatcher.BeginInvoke((Action)(() => 43 | { 44 | mainview_ui.Opacity = 1; 45 | })); 46 | } 47 | } 48 | 49 | [DllExport] 50 | public static void DestroyUserInterface() // Multi-Threaded Version 51 | { 52 | try 53 | { 54 | mainview_ui.Close(); 55 | } 56 | catch // Can't Access to UI Thread , So Dispatching 57 | { 58 | mainview_ui.Dispatcher.BeginInvoke((Action)(() => 59 | { 60 | mainview_ui.Close(); 61 | })); 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /PoolViewerExt/DllMain.cpp: -------------------------------------------------------------------------------- 1 | #include "engextcpp.hpp" 2 | #include "..\PoolData\PoolData.cpp" 3 | 4 | #pragma once 5 | 6 | class EXT_CLASS : public ExtExtension 7 | { 8 | public: 9 | EXT_COMMAND_METHOD(poolview); 10 | virtual HRESULT Initialize(void); 11 | virtual void Uninitialize(void); 12 | }; 13 | 14 | EXT_DECLARE_GLOBALS(); 15 | 16 | UINT g_helpMenu = 0; 17 | 18 | EXT_COMMAND(poolview, "Parses the kernel pool", "{;e64,o;address;input address}{tag;s;str;Pool tag to use}{paged;b;paged;only search paged pools}{nonpaged;b;nonpaged;only search nonpaged pools}") 19 | { 20 | ULONG64 address; 21 | PCSTR tag; 22 | POOL_VIEW_FLAGS Flags; 23 | 24 | Flags.AllFlags = 0; 25 | if (HasArg("paged")) 26 | { 27 | Flags.OnlyPaged = 1; 28 | } 29 | if (HasArg("nonpaged")) 30 | { 31 | Flags.OnlyNonPaged = 1; 32 | } 33 | if (HasArg("tag")) 34 | { 35 | tag = GetArgStr("tag", false); 36 | if (strlen(tag) > 4) 37 | { 38 | g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "Pool tag can be up to 4 bytes\n"); 39 | return; 40 | } 41 | g_DebugControl->Output(DEBUG_OUTPUT_DEBUGGEE, " Address Size (Status) Tag Type\n"); 42 | g_DebugControl->Output(DEBUG_OUTPUT_DEBUGGEE, " ---------------------------------------------------------\n"); 43 | GetAllHeaps(tag, Flags); 44 | return; 45 | } 46 | else 47 | { 48 | address = GetUnnamedArgU64(0); 49 | if (address) 50 | { 51 | g_DebugControl->Output(DEBUG_OUTPUT_DEBUGGEE, " Address Size (Status) Tag Type\n"); 52 | g_DebugControl->Output(DEBUG_OUTPUT_DEBUGGEE, " ---------------------------------------------------------\n"); 53 | GetPoolDataForAddress((PVOID)address); 54 | return; 55 | } 56 | } 57 | } 58 | 59 | HRESULT EXT_CLASS::Initialize(void) 60 | { 61 | HRESULT hResult; 62 | 63 | hResult = InitializeDebugGlobals(); 64 | 65 | if ((!SUCCEEDED(GetTypes())) || 66 | (!SUCCEEDED(GetOffsets())) || 67 | (!SUCCEEDED(GetSizes())) || 68 | (!SUCCEEDED(GetHeapGlobals()))) 69 | { 70 | return S_FALSE; 71 | } 72 | 73 | return hResult; 74 | } 75 | 76 | void EXT_CLASS::Uninitialize(void) 77 | { 78 | UninitializeDebugGlobals(); 79 | 80 | return; 81 | } -------------------------------------------------------------------------------- /ManagedUIKitWPF/DllConfig.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Runtime.InteropServices; 4 | using Microsoft.Win32; 5 | using System.IO; 6 | using System.Windows.Documents; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Windows.Controls; 12 | using System.Windows.Data; 13 | using System.Windows.Input; 14 | using System.Windows.Media; 15 | using System.Windows.Media.Imaging; 16 | using System.Windows.Navigation; 17 | using System.Collections.ObjectModel; 18 | using System.Security.Policy; 19 | using System.ComponentModel; 20 | using System.Windows.Interop; 21 | using System.Security.Principal; 22 | using System.Diagnostics; 23 | using System.Windows.Threading; 24 | 25 | namespace PoolViewer 26 | { 27 | static class NativeMethods 28 | { 29 | [DllImport("kernel32.dll")] 30 | public static extern IntPtr LoadLibrary(string dllToLoad); 31 | 32 | [DllImport("kernel32.dll")] 33 | public static extern IntPtr GetModuleHandle(string ModuleName); 34 | } 35 | 36 | /// 37 | /// Interaction logic for DllConfig.xaml 38 | /// 39 | public partial class DllConfig : Window 40 | { 41 | public DllConfig() 42 | { 43 | InitializeComponent(); 44 | } 45 | 46 | private void OK_Button_Click(object sender, RoutedEventArgs e) 47 | { 48 | var path = DllSearchPath.Text; 49 | if (File.Exists($"{path}\\DbgEng.dll") && 50 | File.Exists($"{path}\\DbgHelp.dll")) 51 | { 52 | IntPtr dbgEng = NativeMethods.GetModuleHandle("DbgEng.dll"); 53 | IntPtr dbgHelp = NativeMethods.GetModuleHandle("DbgHelp.dll"); 54 | 55 | if (dbgEng == IntPtr.Zero && dbgHelp == IntPtr.Zero) 56 | { 57 | NativeMethods.LoadLibrary($"{path}\\DbgHelp.dll"); 58 | NativeMethods.LoadLibrary($"{path}\\DbgEng.dll"); 59 | } 60 | 61 | } 62 | else 63 | { 64 | DllSearchPath.Text = ""; 65 | string message = "Invalid path!"; 66 | MessageBox.Show(message); 67 | } 68 | this.Close(); 69 | } 70 | private void Cancel_Button_Click(object sender, RoutedEventArgs e) 71 | { 72 | this.Close(); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PoolViewer 2 | An application to view and filter pool allocations from a dmp file on Windows 10 RS5+. 3 | Presents information about active heaps and all pool allocations in the system, both allocated and free, as well as some basic statistics. 4 | The information is extracted from a memory dump using Windows' Debugger API. 5 | 6 | Integration of c++ code with WPF GUI relies on DllExport package and is heavily based on the code from here: 7 | https://www.codeproject.com/Articles/5253279/Create-An-Awesome-WPF-UI-for-Your-Cplusplus-QT-App 8 | 9 | ## Features 10 | - Presents information in easy-to-use format 11 | - Read from memory dump or from a live machine 12 | - Can filter based on all fields 13 | - Right-click on any of the tables in the "General" tab to filter pool blocks based on chosen row 14 | - Export all pool blocks to csv 15 | 16 | ## Build 17 | There are 4 projects in the repository: 18 | 1. ManagedUIKitWpf - implementing the GUI for PoolViewer (required .NET 4.5 to be installed) 19 | 2. PoolData - implementing all pool analysis functionality 20 | 3. PoolViewer - using PoolData and ManagedUIKitWpf to implement GUI analysis tool 21 | 4. PoolViewExt - using PoolData to create a WinDbg extension (does not require ManagedUIKitWpf to be built) 22 | 23 | ## Usage 24 | You can either clone the repository and build it yourself or use the binaries found in Binaries folder. 25 | Notice that the app needs dbgeng.dll and dbghelp.dll to exist in the same directory. 26 | The ones in System32 are often broken, so copy the dlls from the same folder windbg.exe is in. 27 | 28 | The app parses a memory dmp of a Windows 10 RS5+ machine, which can be created with livekd: 29 | `livekd.exe -ml -k -o c:\temp\live.dmp` 30 | 31 | Another option is analyzing the live machine - this is done by creating a temporary dmp file of the live machine and analyzing it. This option requires running PoolViewer as admin. 32 | 33 | ## PoolViewerExt 34 | WinDbg extension to print information about a specific pool address or a pool tag. 35 | Options: 36 | 1. !poolview [address] 37 | ```!poolview ffffcc8b7d8840c0 38 | Address Size (Status) Tag Type 39 | --------------------------------------------------------- 40 | * 0xffffcc8b7d884050 0xe70 (Allocated) Proc Vs 41 | 0xffffcc8b7d884ee0 0x100 (Free) Vs 42 | ``` 43 | 2. !poolview -tag [tag] 44 | ```!poolview -tag Even 45 | Address Size (Status) Tag Type 46 | --------------------------------------------------------- 47 | 0xffffcc8b460f6580 0x80 (Allocated) Even Lfh 48 | 0xffffcc8b460f6700 0x80 (Allocated) Even Lfh 49 | 0xffffcc8b460f6c80 0x80 (Allocated) Even Lfh 50 | 0xffffcc8b460f6d00 0x80 (Allocated) Even Lfh 51 | 0xffffcc8b497fc190 0xa0 (Allocated) Even Lfh 52 | 0xffffcc8b497fc9b0 0xa0 (Allocated) Even Lfh 53 | 0xffffcc8b4aafedd0 0x60 (Allocated) Even Lfh 54 | 0xffffcc8b4aafef50 0x60 (Allocated) Even Lfh 55 | ``` 56 | -------------------------------------------------------------------------------- /ManagedUIKitWPF/properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ManagedUIKitWPF.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ManagedUIKitWPF.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /PoolViewer.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29503.13 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PoolViewer", "PoolViewer\PoolViewer.vcxproj", "{911A68FB-82ED-4424-A8B4-5C8538AFB14F}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagedUIKitWPF", "ManagedUIKitWPF\ManagedUIKitWPF.csproj", "{2BDAFA06-AFD9-4C7D-8B5F-0A2F418FED5F}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PoolData", "PoolData\PoolData.vcxproj", "{C18584A1-362A-4E91-92B0-6CCA0C17F1FF}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PoolViewerExt", "PoolViewerExt\PoolViewerExt.vcxproj", "{F75DBF2D-3D6A-496F-BAC8-181E96C25F0D}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|x64 = Debug|x64 17 | Debug|x86 = Debug|x86 18 | Release|x64 = Release|x64 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {911A68FB-82ED-4424-A8B4-5C8538AFB14F}.Debug|x64.ActiveCfg = Debug|x64 23 | {911A68FB-82ED-4424-A8B4-5C8538AFB14F}.Debug|x64.Build.0 = Debug|x64 24 | {911A68FB-82ED-4424-A8B4-5C8538AFB14F}.Debug|x86.ActiveCfg = Debug|Win32 25 | {911A68FB-82ED-4424-A8B4-5C8538AFB14F}.Debug|x86.Build.0 = Debug|Win32 26 | {911A68FB-82ED-4424-A8B4-5C8538AFB14F}.Release|x64.ActiveCfg = Release|x64 27 | {911A68FB-82ED-4424-A8B4-5C8538AFB14F}.Release|x64.Build.0 = Release|x64 28 | {911A68FB-82ED-4424-A8B4-5C8538AFB14F}.Release|x86.ActiveCfg = Release|Win32 29 | {911A68FB-82ED-4424-A8B4-5C8538AFB14F}.Release|x86.Build.0 = Release|Win32 30 | {2BDAFA06-AFD9-4C7D-8B5F-0A2F418FED5F}.Debug|x64.ActiveCfg = Debug|x64 31 | {2BDAFA06-AFD9-4C7D-8B5F-0A2F418FED5F}.Debug|x64.Build.0 = Debug|x64 32 | {2BDAFA06-AFD9-4C7D-8B5F-0A2F418FED5F}.Debug|x86.ActiveCfg = Debug|x64 33 | {2BDAFA06-AFD9-4C7D-8B5F-0A2F418FED5F}.Release|x64.ActiveCfg = Release|x64 34 | {2BDAFA06-AFD9-4C7D-8B5F-0A2F418FED5F}.Release|x64.Build.0 = Release|x64 35 | {2BDAFA06-AFD9-4C7D-8B5F-0A2F418FED5F}.Release|x86.ActiveCfg = Release|x64 36 | {C18584A1-362A-4E91-92B0-6CCA0C17F1FF}.Debug|x64.ActiveCfg = Debug|x64 37 | {C18584A1-362A-4E91-92B0-6CCA0C17F1FF}.Debug|x64.Build.0 = Debug|x64 38 | {C18584A1-362A-4E91-92B0-6CCA0C17F1FF}.Debug|x86.ActiveCfg = Debug|Win32 39 | {C18584A1-362A-4E91-92B0-6CCA0C17F1FF}.Debug|x86.Build.0 = Debug|Win32 40 | {C18584A1-362A-4E91-92B0-6CCA0C17F1FF}.Release|x64.ActiveCfg = Release|x64 41 | {C18584A1-362A-4E91-92B0-6CCA0C17F1FF}.Release|x64.Build.0 = Release|x64 42 | {C18584A1-362A-4E91-92B0-6CCA0C17F1FF}.Release|x86.ActiveCfg = Release|Win32 43 | {C18584A1-362A-4E91-92B0-6CCA0C17F1FF}.Release|x86.Build.0 = Release|Win32 44 | {F75DBF2D-3D6A-496F-BAC8-181E96C25F0D}.Debug|x64.ActiveCfg = Debug|x64 45 | {F75DBF2D-3D6A-496F-BAC8-181E96C25F0D}.Debug|x64.Build.0 = Debug|x64 46 | {F75DBF2D-3D6A-496F-BAC8-181E96C25F0D}.Debug|x86.ActiveCfg = Debug|Win32 47 | {F75DBF2D-3D6A-496F-BAC8-181E96C25F0D}.Debug|x86.Build.0 = Debug|Win32 48 | {F75DBF2D-3D6A-496F-BAC8-181E96C25F0D}.Release|x64.ActiveCfg = Release|x64 49 | {F75DBF2D-3D6A-496F-BAC8-181E96C25F0D}.Release|x64.Build.0 = Release|x64 50 | {F75DBF2D-3D6A-496F-BAC8-181E96C25F0D}.Release|x86.ActiveCfg = Release|Win32 51 | {F75DBF2D-3D6A-496F-BAC8-181E96C25F0D}.Release|x86.Build.0 = Release|Win32 52 | EndGlobalSection 53 | GlobalSection(SolutionProperties) = preSolution 54 | HideSolutionNode = FALSE 55 | EndGlobalSection 56 | GlobalSection(ExtensibilityGlobals) = postSolution 57 | SolutionGuid = {41CA2BFF-5CD6-4BFB-9A77-A0E11C418938} 58 | EndGlobalSection 59 | EndGlobal 60 | -------------------------------------------------------------------------------- /packages/UnmanagedExports.1.2.7/tools/DllExportCmdLets.psm1: -------------------------------------------------------------------------------- 1 | function Remove-OldDllExportFolder { 2 | param($project) 3 | $defaultFiles = ('DllExportAttribute.cs', 4 | 'Mono.Cecil.dll', 5 | 'RGiesecke.DllExport.dll', 6 | 'RGiesecke.DllExport.pdb', 7 | 'RGiesecke.DllExport.MSBuild.dll', 8 | 'RGiesecke.DllExport.MSBuild.pdb', 9 | 'RGiesecke.DllExport.targets') 10 | 11 | $projectFile = New-Object 'System.IO.FileInfo'($project.FullName) 12 | 13 | $projectFile.Directory.GetDirectories("DllExport") | Select-Object -First 1 | % { 14 | $dllExportDir = $_ 15 | 16 | if($dllExportDir.GetDirectories().Count -eq 0){ 17 | $unknownFiles = $dllExportDir.GetFiles() | Select -ExpandProperty Name | ? { -not $defaultFiles -contains $_ } 18 | 19 | if(-not $unknownFiles){ 20 | Write-Host "Removing 'DllExport' from " $project.Name 21 | $project.ProjectItems | ? { $_.Name -ieq 'DllExport' } | % { 22 | $_.Remove() 23 | } 24 | 25 | Write-Host "Deleting " $dllExportDir.FullName " ..." 26 | $dllExportDir.Delete($true) 27 | } 28 | } 29 | } 30 | } 31 | 32 | function Remove-OldDllExportFolders { 33 | Get-Project -all | % { 34 | Remove-OldDllExportFolder $_ 35 | } 36 | } 37 | 38 | function Get-DllExportMsBuildProjectsByFullName([String] $fullName) { 39 | $msBuildV4Name = 'Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'; 40 | $msBuildV4 = [System.Reflection.Assembly]::LoadWithPartialName($msBuildV4Name) 41 | 42 | if(!$msBuildV4) { 43 | throw New-Object 'System.IO.FileNotFoundException'("Could not load $msBuildV4Name.") 44 | } 45 | 46 | $projectCollection = $msBuildV4.GetType('Microsoft.Build.Evaluation.ProjectCollection') 47 | 48 | return $projectCollection::GlobalProjectCollection.GetLoadedProjects($fullName) 49 | } 50 | 51 | function Get-AllDllExportMsBuildProjects { 52 | (Get-Project -all | % { 53 | Get-DllExportMsBuildProjectsByFullName $_.FullName 54 | }) | ? { 55 | return ($_.Xml.Imports | ? { 56 | "RGiesecke.DllExport.targets" -ieq [System.IO.Path]::GetFileName($_.Project); 57 | }).Length -gt 0; 58 | } 59 | } 60 | 61 | function Assert-PlatformTargetOfProject([String] $fullName) { 62 | $proj = Get-DllExportMsBuildProjectsByFullName $fullName 63 | 64 | if(!$proj) { 65 | return; 66 | } 67 | 68 | $platformTarget = $proj.GetPropertyValue('PlatformTarget'); 69 | 70 | if(!$platformTarget -or ($platformTarget -ine 'x86' -and $platformTarget -ine 'x64')) { 71 | $projectName = [IO.Path]::GetFileNameWithoutExtension($fullName); 72 | if(!$platformTarget) { 73 | $platformTarget = "has no platform target"; 74 | } else { 75 | $platformTarget = "has a platform target of '$platformTarget'"; 76 | } 77 | Write-Warning "The project '$projectName' $platformTarget. Only x86 or x64 assemblies can export functions." 78 | Write-Host "" 79 | } 80 | } 81 | 82 | function Set-NoDllExportsForAnyCpu([String] $projectName, [System.Nullable[bool]] $value) { 83 | $projects = Get-AllDllExportMsBuildProjects; 84 | 85 | [String] $asString = $value; 86 | 87 | if($projectName) { 88 | $projects = $projects | where { $_.Name -ieq $projectName }; 89 | } 90 | $propertyName = 'NoDllExportsForAnyCpu'; 91 | 92 | $projects = $projects | where { 93 | $_.GetPropertyValue($propertyName) -ine $asString 94 | } | % { 95 | $_.SetProperty($propertyName, $asString); 96 | } 97 | } 98 | 99 | Export-ModuleMember Set-NoDllExportsForAnyCpu 100 | 101 | Export-ModuleMember Remove-OldDllExportFolder 102 | Export-ModuleMember Remove-OldDllExportFolders 103 | Export-ModuleMember Get-DllExportMsBuildProjectsByFullName 104 | Export-ModuleMember Get-AllDllExportMsBuildProjects 105 | Export-ModuleMember Assert-PlatformTargetOfProject -------------------------------------------------------------------------------- /packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | $(PostBuildEventDependsOn); 7 | RGieseckeDllExport 8 | 9 | 10 | 11 | 12 | 13 | $(BuildDependsOn); 14 | RGieseckeDllExport 15 | 16 | 17 | 18 | 20 | 21 | 23 | 24 | 34 | 35 | 36 | 37 | RGiesecke.DllExport.DllExportAttribute 38 | RGiesecke.DllExport.Metadata 39 | 40 | $(Platform) 41 | $(PlatformTarget) 42 | $(CpuType) 43 | $(DebugSymbols) 44 | false 45 | $(DllExportTimeout) 46 | $(KeyContainerName)$(AssemblyKeyContainerName) 47 | $(KeyOriginatorFile) 48 | $(MSBuildProjectDirectory) 49 | $(TargetPath) 50 | $(TargetedFrameworkDir);$(TargetFrameworkDirectory) 51 | $(DevEnvDir)\..\..\VC\bin 52 | $(DevEnvDir) 53 | $(TargetFrameworkVersion) 54 | $(TargetFrameworkSDKToolsDirectory) 55 | $(NoDllExportsForAnyCpu) 56 | 57 | 58 | 74 | 75 | -------------------------------------------------------------------------------- /ManagedUIKitWPF/ManagedUIKitWPF.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2BDAFA06-AFD9-4C7D-8B5F-0A2F418FED5F} 8 | Library 9 | ManagedUIKitWPF 10 | ManagedUIKitWPF 11 | v4.5 12 | 512 13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 4 15 | true 16 | 17 | 18 | 19 | ..\Binaries\ 20 | TRACE 21 | true 22 | none 23 | x64 24 | 7.3 25 | prompt 26 | MinimumRecommendedRules.ruleset 27 | true 28 | 1024 29 | 30 | 31 | 32 | 33 | 34 | ..\Binaries\ 35 | AnyCPU 36 | true 37 | full 38 | false 39 | DEBUG;TRACE 40 | prompt 41 | 4 42 | false 43 | 44 | 45 | false 46 | 47 | 48 | 49 | ..\packages\UnmanagedExports.1.2.7\lib\net\RGiesecke.DllExport.Metadata.dll 50 | False 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 4.0 63 | 64 | 65 | 66 | 67 | 68 | ..\packages\WpfPlus.1.2.0\lib\net40\WpfPlus.dll 69 | 70 | 71 | 72 | 73 | DllConfig.xaml 74 | 75 | 76 | MainView.xaml 77 | 78 | 79 | True 80 | True 81 | Resources.resx 82 | 83 | 84 | True 85 | Settings.settings 86 | True 87 | 88 | 89 | 90 | ResXFileCodeGenerator 91 | Resources.Designer.cs 92 | 93 | 94 | 95 | SettingsSingleFileGenerator 96 | Settings.Designer.cs 97 | 98 | 99 | 100 | 101 | Designer 102 | MSBuild:Compile 103 | 104 | 105 | Designer 106 | MSBuild:Compile 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /ManagedUIKitWPF/properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /PoolData/PoolData.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {c18584a1-362a-4e91-92b0-6cca0c17f1ff} 25 | PoolData 26 | 10.0 27 | 28 | 29 | 30 | StaticLibrary 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | StaticLibrary 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | StaticLibrary 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | StaticLibrary 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | ..\Binaries 76 | 77 | 78 | false 79 | ..\Binaries 80 | 81 | 82 | true 83 | ..\Binaries 84 | 85 | 86 | false 87 | ..\Binaries 88 | 89 | 90 | 91 | Level3 92 | true 93 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 94 | true 95 | 96 | 97 | Console 98 | true 99 | 100 | 101 | 102 | 103 | Level3 104 | true 105 | true 106 | true 107 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 108 | true 109 | 110 | 111 | Console 112 | true 113 | true 114 | true 115 | 116 | 117 | 118 | 119 | Level3 120 | true 121 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 122 | true 123 | 124 | 125 | Console 126 | true 127 | 128 | 129 | 130 | 131 | Level3 132 | true 133 | true 134 | true 135 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 136 | true 137 | 138 | 139 | Console 140 | true 141 | true 142 | true 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /PoolViewer/Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "resource.h" 4 | #include "..\PoolData\PoolData.h" 5 | 6 | #pragma once 7 | 8 | // 9 | // Global Objects 10 | // 11 | WNDCLASSEX HostWindowClass; 12 | MSG loop_message; 13 | HINSTANCE hInstance = GetModuleHandle(NULL); 14 | HWND cpphwin_hwnd; 15 | HWND wpf_hwnd; 16 | typedef void (*GetPoolInformation_Ptr)(void); 17 | typedef void (*GetNextHeapInformation_Ptr)(void); 18 | typedef void (*GetNextAllocation_Ptr)(void); 19 | typedef HWND(*CreateUserInterfaceFunc)(GetPoolInformation_Ptr, GetNextHeapInformation_Ptr, GetNextAllocation_Ptr); 20 | CreateUserInterfaceFunc CreateUserInterface; 21 | typedef void(*DisplayUserInterfaceFunc)(void); 22 | DisplayUserInterfaceFunc DisplayUserInterface; 23 | typedef void(*DestroyUserInterfaceFunc)(void); 24 | DestroyUserInterfaceFunc DestroyUserInterface; 25 | HMODULE dotNetGUILibrary; 26 | RECT hwin_rect; 27 | 28 | // 29 | // Global Configs 30 | // 31 | const wchar_t cpphwinCN[] = L"CppMAppHostWinClass"; 32 | bool isHWindowRunning = false; 33 | #define FIXED_WINDOW false 34 | #define HWIN_TITLE L"PoolViewer" 35 | 36 | /* 37 | Host Window Callback 38 | */ 39 | LRESULT 40 | CALLBACK 41 | HostWindowProc ( 42 | _In_ HWND hwnd, 43 | _In_ UINT msg, 44 | _In_ WPARAM wParam, 45 | _In_ LPARAM lParam 46 | ) 47 | { 48 | switch (msg) 49 | { 50 | case WM_CLOSE: 51 | // 52 | // Destroy WPF Control before Destorying Host Window 53 | // 54 | DestroyUserInterface(); 55 | DestroyWindow(hwnd); 56 | break; 57 | case WM_DESTROY: 58 | isHWindowRunning = false; 59 | break; 60 | case WM_SIZE: 61 | // 62 | // Resize WPF Control on Host Window Resizing 63 | // 64 | if (wpf_hwnd != nullptr) { 65 | GetClientRect(cpphwin_hwnd, &hwin_rect); 66 | MoveWindow(wpf_hwnd, 0, 0, hwin_rect.right - hwin_rect.left, hwin_rect.bottom - hwin_rect.top, TRUE); 67 | } 68 | break; 69 | default: 70 | return DefWindowProc(hwnd, msg, wParam, lParam); 71 | } 72 | return 0; 73 | } 74 | 75 | int 76 | WINAPI 77 | wWinMain ( 78 | _In_ HINSTANCE hInstance, 79 | _In_ HINSTANCE hPrevInstance, 80 | _In_ LPWSTR lpCmdLine, 81 | _In_ int nShowCmd 82 | ) 83 | { 84 | // 85 | // Create Icon Object From Resources 86 | // 87 | HICON app_icon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_APPICON)); 88 | 89 | // 90 | // Define Our Host Window Class 91 | // 92 | HostWindowClass.cbSize = sizeof(WNDCLASSEX); 93 | HostWindowClass.lpfnWndProc = HostWindowProc; 94 | HostWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW); 95 | HostWindowClass.cbClsExtra = 0; 96 | HostWindowClass.style = 0; 97 | HostWindowClass.cbWndExtra = 0; 98 | HostWindowClass.hInstance = hInstance; 99 | HostWindowClass.hIcon = app_icon; 100 | HostWindowClass.hIconSm = app_icon; 101 | HostWindowClass.lpszClassName = cpphwinCN; 102 | HostWindowClass.lpszMenuName = NULL; 103 | printf("Starting\n"); 104 | // 105 | // Register Window 106 | // 107 | if (!RegisterClassEx(&HostWindowClass)) 108 | { 109 | printf("Error %d\n", GetLastError()); 110 | return 0; 111 | } 112 | 113 | /// Creating Unmanaged Host Window 114 | cpphwin_hwnd = CreateWindowEx(WS_EX_CLIENTEDGE, 115 | cpphwinCN, 116 | HWIN_TITLE, 117 | WS_THICKFRAME | WS_OVERLAPPEDWINDOW, 118 | CW_USEDEFAULT, 119 | CW_USEDEFAULT, 120 | 1800, 121 | 1000, 122 | NULL, 123 | NULL, 124 | hInstance, 125 | NULL); 126 | 127 | // 128 | // Check if Window is valid 129 | // 130 | if (cpphwin_hwnd == NULL) 131 | { 132 | printf("Error %d\n", GetLastError()); 133 | return 0; 134 | } 135 | 136 | // 137 | // Making Window Fixed Size 138 | // 139 | if (FIXED_WINDOW) 140 | { 141 | ::SetWindowLong(cpphwin_hwnd, 142 | GWL_STYLE, 143 | GetWindowLong(cpphwin_hwnd, GWL_STYLE) & ~WS_SIZEBOX); 144 | } 145 | 146 | // 147 | // Center Host Window 148 | // 149 | RECT window_r; 150 | RECT desktop_r; 151 | GetWindowRect(cpphwin_hwnd, &window_r); 152 | GetWindowRect(GetDesktopWindow(), &desktop_r); 153 | int xPos = (desktop_r.right - (window_r.right - window_r.left)) / 2; 154 | int yPos = (desktop_r.bottom - (window_r.bottom - window_r.top)) / 2; 155 | 156 | // 157 | // Set Window Position 158 | // 159 | ::SetWindowPos(cpphwin_hwnd, 0, xPos, yPos, 0, 0, SWP_NOZORDER | SWP_NOSIZE); 160 | 161 | // 162 | // Load .Net UI Library 163 | // 164 | dotNetGUILibrary = LoadLibrary(L"ManagedUIKitWPF.dll"); 165 | CreateUserInterface = (CreateUserInterfaceFunc)GetProcAddress(dotNetGUILibrary, "CreateUserInterface"); 166 | DisplayUserInterface = (DisplayUserInterfaceFunc)GetProcAddress(dotNetGUILibrary, "DisplayUserInterface"); 167 | DestroyUserInterface = (DestroyUserInterfaceFunc)GetProcAddress(dotNetGUILibrary, "DestroyUserInterface"); 168 | 169 | // 170 | // Create .Net GUI 171 | // 172 | wpf_hwnd = CreateUserInterface( 173 | (GetPoolInformation_Ptr)&GetPoolInformation, 174 | (GetNextHeapInformation_Ptr)&GetNextHeapInformation, 175 | (GetNextAllocation_Ptr)&GetNextAllocation); 176 | 177 | // 178 | // Set Thread to STA 179 | // 180 | CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 181 | 182 | // 183 | // Check if WPF Window is valid 184 | // 185 | if (wpf_hwnd != nullptr) { 186 | // 187 | // Disable Host Window Updates & Draws 188 | // 189 | SendMessage(cpphwin_hwnd, WM_SETREDRAW, FALSE, 0); 190 | 191 | // 192 | // Disable Host Window Double Buffering 193 | // 194 | long dwExStyle = GetWindowLong(cpphwin_hwnd, GWL_EXSTYLE); 195 | dwExStyle &= ~WS_EX_COMPOSITED; 196 | SetWindowLong(cpphwin_hwnd, GWL_EXSTYLE, dwExStyle); 197 | 198 | // 199 | // Set WPF Window to a Child Control 200 | // 201 | SetWindowLong(wpf_hwnd, GWL_STYLE, WS_CHILD); 202 | 203 | // 204 | // Get host client area rect 205 | // 206 | GetClientRect(cpphwin_hwnd, &hwin_rect); 207 | 208 | // 209 | // Set WPF Control Order, Size and Position 210 | // 211 | MoveWindow(wpf_hwnd, 0, 0, hwin_rect.right - hwin_rect.left, hwin_rect.bottom - hwin_rect.top, TRUE); 212 | SetWindowPos(wpf_hwnd, HWND_TOP, 0, 0, hwin_rect.right - hwin_rect.left, hwin_rect.bottom - hwin_rect.top, SWP_NOMOVE); 213 | 214 | // 215 | // Set WPF as A Child to Host Window... 216 | // 217 | SetParent(wpf_hwnd, cpphwin_hwnd); 218 | 219 | // 220 | // Show window 221 | // 222 | ShowWindow(wpf_hwnd, SW_RESTORE); 223 | 224 | // 225 | // Display WPF Control by Reseting its Opacity 226 | // 227 | DisplayUserInterface(); 228 | } 229 | 230 | // 231 | // Display Window 232 | // 233 | ShowWindow(cpphwin_hwnd, SW_SHOW); 234 | UpdateWindow(cpphwin_hwnd); 235 | BringWindowToTop(cpphwin_hwnd); 236 | isHWindowRunning = true; 237 | 238 | // 239 | // Add Message Loop 240 | // 241 | while (GetMessage(&loop_message, NULL, 0, 0) > 0 && isHWindowRunning) 242 | { 243 | TranslateMessage(&loop_message); 244 | DispatchMessage(&loop_message); 245 | } 246 | 247 | // 248 | // Clean Up 249 | // 250 | printf("C++ Main App Finished.\n"); 251 | FreeLibrary(dotNetGUILibrary); 252 | Sleep(500); 253 | return 0; 254 | } -------------------------------------------------------------------------------- /PoolData/PoolData.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #pragma once 9 | 10 | using namespace std; 11 | 12 | // 13 | // Enums 14 | // 15 | 16 | extern PDEBUG_CLIENT g_DebugClient; 17 | extern PDEBUG_SYMBOLS g_DebugSymbols; 18 | extern PDEBUG_DATA_SPACES4 g_DataSpaces; 19 | extern PDEBUG_CONTROL g_DebugContro; 20 | 21 | enum class RTLP_CSPARSE_BITMAP_STATE 22 | { 23 | CommitBitmapInvalid = 0x0, 24 | UserBitmapInvalid = 0x1, 25 | UserBitmapValid = 0x2, 26 | }; 27 | 28 | enum class ALLOCATION_TYPE 29 | { 30 | Lfh = 0x0, 31 | Vs = 0x1, 32 | Large = 0x2, 33 | Big = 0x3 34 | }; 35 | 36 | typedef _Enum_is_bitflag_ enum _POOL_TYPE { 37 | NonPagedPool, 38 | NonPagedPoolExecute = NonPagedPool, 39 | PagedPool, 40 | NonPagedPoolMustSucceed = NonPagedPool + 2, 41 | DontUseThisType, 42 | NonPagedPoolCacheAligned = NonPagedPool + 4, 43 | PagedPoolCacheAligned, 44 | NonPagedPoolCacheAlignedMustS = NonPagedPool + 6, 45 | MaxPoolType, 46 | 47 | // 48 | // Define base types for NonPaged (versus Paged) pool, for use in cracking 49 | // the underlying pool type. 50 | // 51 | 52 | NonPagedPoolBase = 0, 53 | NonPagedPoolBaseMustSucceed = NonPagedPoolBase + 2, 54 | NonPagedPoolBaseCacheAligned = NonPagedPoolBase + 4, 55 | NonPagedPoolBaseCacheAlignedMustS = NonPagedPoolBase + 6, 56 | 57 | // 58 | // Note these per session types are carefully chosen so that the appropriate 59 | // masking still applies as well as MaxPoolType above. 60 | // 61 | 62 | NonPagedPoolSession = 32, 63 | PagedPoolSession = NonPagedPoolSession + 1, 64 | NonPagedPoolMustSucceedSession = PagedPoolSession + 1, 65 | DontUseThisTypeSession = NonPagedPoolMustSucceedSession + 1, 66 | NonPagedPoolCacheAlignedSession = DontUseThisTypeSession + 1, 67 | PagedPoolCacheAlignedSession = NonPagedPoolCacheAlignedSession + 1, 68 | NonPagedPoolCacheAlignedMustSSession = PagedPoolCacheAlignedSession + 1, 69 | 70 | NonPagedPoolNx = 512, 71 | NonPagedPoolNxCacheAligned = NonPagedPoolNx + 4, 72 | NonPagedPoolSessionNx = NonPagedPoolNx + 32, 73 | 74 | } _Enum_is_bitflag_ POOL_TYPE; 75 | 76 | // 77 | // Needed offsets inside structures 78 | // 79 | 80 | struct HEAP_MANAGER_STATE_OFFSETS 81 | { 82 | ULONG HeapManagerOffset; 83 | ULONG PoolNodeOffset; 84 | ULONG NumberOfPoolsOffset; 85 | ULONG SpecialHeapsOffset; 86 | }; 87 | 88 | struct RTLP_HP_HEAP_MANAGER_OFFSETS 89 | { 90 | ULONG AllocTrackerOffset; 91 | ULONG VaMgrOffset; 92 | }; 93 | 94 | struct RTLP_HP_ALLOC_TRACKER_OFFSETS 95 | { 96 | ULONG AllocTrackerBitmapOffset; 97 | ULONG BaseAddressOffset; 98 | }; 99 | 100 | struct RTL_CSPARSE_BITMAP_OFFSETS 101 | { 102 | ULONG CommitDirectoryOffset; 103 | ULONG CommitBitmapOffset; 104 | ULONG UserBitmapOffset; 105 | }; 106 | 107 | struct HEAP_VAMGR_CTX_OFFSETS 108 | { 109 | ULONG VaSpaceOffset; 110 | }; 111 | 112 | struct HEAP_VAMGR_VASPACE_OFFSETS 113 | { 114 | ULONG VaRangeArrayOffset; 115 | ULONG BaseAddressOffset; 116 | }; 117 | 118 | struct RTL_SPARSE_ARRAY_OFFSETS 119 | { 120 | ULONG ElementSizeShiftOffset; 121 | ULONG BitmapOffset; 122 | }; 123 | 124 | struct HEAP_VAMGR_RANGE_OFFSETS 125 | { 126 | ULONG AllocatedOffset; 127 | }; 128 | 129 | struct HEAP_SEG_CONTEXT_OFFSETS 130 | { 131 | ULONG SegmentListHeadOffset; 132 | ULONG FirstDescriptorIndexOffset; 133 | ULONG SegmentCountOffset; 134 | ULONG UnitShiftOffset; 135 | }; 136 | 137 | struct HEAP_PAGE_SEGMENT_OFFSETS 138 | { 139 | ULONG ListEntryOffset; 140 | ULONG SignatureOffset; 141 | ULONG SegmentCommitStateOffset; 142 | ULONG DescArrayOffset; 143 | }; 144 | 145 | struct HEAP_PAGE_RANGE_DESCRIPTOR_OFFSET 146 | { 147 | ULONG TreeSignatureOffset; 148 | ULONG RangeFlagsOffset; 149 | ULONG UnitSizeOffset; 150 | }; 151 | 152 | struct SEGMENT_HEAP_OFFSETS 153 | { 154 | ULONG LargeAllocMetadataOffset; 155 | ULONG SegContextsOffsets; 156 | }; 157 | 158 | struct EX_HEAP_POOL_NODE_OFFSETS 159 | { 160 | ULONG HeapsOffset; 161 | }; 162 | 163 | struct HEAP_VS_SUBSEGMENT_OFFSETS 164 | { 165 | ULONG SignatureOffset; 166 | ULONG SizeOffset; 167 | }; 168 | 169 | struct HEAP_VS_CHUNK_HEADER_OFFSETS 170 | { 171 | ULONG SizesOffset; 172 | }; 173 | 174 | struct HEAP_VS_CHUNK_HEADER_SIZE_OFFSETS 175 | { 176 | ULONG HeaderBitsOffset; 177 | ULONG UnsafeSizeOffset; 178 | ULONG AllocatedOffset; 179 | }; 180 | 181 | struct POOL_HEADER_OFFSETS 182 | { 183 | ULONG PoolTagOffset; 184 | }; 185 | 186 | struct HEAP_LFH_SUBSEGMENT_OFFSETS 187 | { 188 | ULONG BlockOffsets; 189 | ULONG BlockBitmap; 190 | }; 191 | 192 | struct HEAP_LFH_SUBSEGMENT_ENCODED_OFFSETS_OFFSETS 193 | { 194 | ULONG EncodedData; 195 | ULONG BlockSize; 196 | ULONG FirstBlockOffset; 197 | }; 198 | 199 | struct POOL_TRACKER_BIG_PAGES_OFFSETS 200 | { 201 | ULONG Va; 202 | ULONG Key; 203 | ULONG NumberOfBytes; 204 | }; 205 | 206 | struct HEAP_LARGE_ALLOC_DATA_OFFSETS 207 | { 208 | ULONG TreeNodeOffset; 209 | ULONG VirtualAddressOffset; 210 | }; 211 | 212 | struct RTL_RB_TREE_OFFSETS 213 | { 214 | ULONG RootOffset; 215 | ULONG EncodedOffset; 216 | }; 217 | 218 | struct RTL_BALANCED_NODE_OFFSETS 219 | { 220 | ULONG LeftOffset; 221 | ULONG RightOffset; 222 | }; 223 | 224 | struct ALLOC 225 | { 226 | ULONG Size; 227 | string PoolTag; 228 | BOOLEAN Allocated; 229 | ULONG64 Address; 230 | ALLOCATION_TYPE Type; 231 | }; 232 | 233 | struct HEAP 234 | { 235 | ULONG NodeNumber; 236 | BOOLEAN Special; 237 | POOL_TYPE PoolType; 238 | ULONG64 NumberOfAllocations; 239 | list Allocations; 240 | ULONG64 Address; 241 | }; 242 | 243 | struct POOL_VIEW_FLAGS { 244 | union { 245 | ULONG AllFlags; 246 | struct { 247 | ULONG OnlyPaged : 1; 248 | ULONG OnlyNonPaged : 1; 249 | }; 250 | }; 251 | }; 252 | 253 | // 254 | // structs needed to create live dmp 255 | // 256 | 257 | typedef union _SYSDBG_LIVEDUMP_CONTROL_FLAGS 258 | { 259 | struct 260 | { 261 | ULONG UseDumpStorageStack : 1; 262 | ULONG CompressMemoryPagesData : 1; 263 | ULONG IncludeUserSpaceMemoryPages : 1; 264 | ULONG Reserved : 29; 265 | }; 266 | ULONG AsUlong; 267 | } SYSDBG_LIVEDUMP_CONTROL_FLAGS; 268 | 269 | typedef union _SYSDBG_LIVEDUMP_CONTROL_ADDPAGES 270 | { 271 | struct 272 | { 273 | ULONG HypervisorPages : 1; 274 | ULONG Reserved : 31; 275 | }; 276 | ULONG AsUlong; 277 | } SYSDBG_LIVEDUMP_CONTROL_ADDPAGES; 278 | 279 | typedef struct _SYSDBG_LIVEDUMP_CONTROL 280 | { 281 | ULONG Version; 282 | ULONG BugCheckCode; 283 | ULONG_PTR BugCheckParam1; 284 | ULONG_PTR BugCheckParam2; 285 | ULONG_PTR BugCheckParam3; 286 | ULONG_PTR BugCheckParam4; 287 | PVOID DumpFileHandle; 288 | PVOID CancelEventHandle; 289 | SYSDBG_LIVEDUMP_CONTROL_FLAGS Flags; 290 | SYSDBG_LIVEDUMP_CONTROL_ADDPAGES AddPagesControl; 291 | } SYSDBG_LIVEDUMP_CONTROL, * PSYSDBG_LIVEDUMP_CONTROL; 292 | 293 | typedef 294 | NTSTATUS 295 | (__stdcall* 296 | NtSystemDebugControl) ( 297 | ULONG ControlCode, 298 | PVOID InputBuffer, 299 | ULONG InputBufferLength, 300 | PVOID OutputBuffer, 301 | ULONG OutputBufferLength, 302 | PULONG ReturnLength 303 | ); 304 | 305 | // 306 | // Externally-used functions 307 | // 308 | 309 | char* 310 | GetNextAllocation ( 311 | _Outptr_ ULONG64* Address, 312 | _Outptr_ int* Size, 313 | _Outptr_ bool* Allocated, 314 | _Outptr_ int* Type 315 | ); 316 | 317 | bool 318 | GetNextHeapInformation ( 319 | _Outptr_ ULONG64* Address, 320 | _Outptr_ int* NodeNumber, 321 | _Outptr_ long* NumberOfAllocations, 322 | _Outptr_ int* PoolType, 323 | _Outptr_ bool* Special 324 | ); 325 | 326 | HRESULT 327 | GetPoolInformation ( 328 | _In_ char* FilePath, 329 | _In_ bool CreateLiveDump, 330 | _Outptr_ int* numberOfHeaps 331 | ); 332 | 333 | HRESULT 334 | InitializeDebugGlobals( 335 | void 336 | ); 337 | 338 | VOID 339 | UninitializeDebugGlobals( 340 | void 341 | ); 342 | 343 | std::list 344 | GetAllHeaps( 345 | _In_opt_ PCSTR Tag, 346 | _In_opt_ POOL_VIEW_FLAGS Flags 347 | ); 348 | 349 | VOID 350 | GetPoolDataForAddress( 351 | _In_ PVOID Address 352 | ); 353 | 354 | HRESULT 355 | GetTypes( 356 | void 357 | ); 358 | 359 | HRESULT 360 | GetOffsets( 361 | void 362 | ); 363 | 364 | HRESULT 365 | GetSizes( 366 | void 367 | ); 368 | 369 | HRESULT 370 | GetHeapGlobals( 371 | void 372 | ); 373 | 374 | typedef HRESULT(*DebugCreateFunc)(_In_ REFIID, _Out_ PVOID*); -------------------------------------------------------------------------------- /PoolViewerExt/PoolViewerExt.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 19.0 23 | Win32Proj 24 | {f75dbf2d-3d6a-496f-bac8-181e96c25f0d} 25 | PoolViewerExt 26 | 10.0 27 | 28 | 29 | 30 | DynamicLibrary 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | DynamicLibrary 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | DynamicLibrary 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | ..\Binaries 76 | 77 | 78 | false 79 | ..\Binaries 80 | 81 | 82 | true 83 | ..\Binaries 84 | 85 | 86 | false 87 | ..\Binaries 88 | 89 | 90 | 91 | Level3 92 | true 93 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 94 | true 95 | 96 | 97 | Console 98 | true 99 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dbgeng.lib;pooldata.lib;%(AdditionalDependencies) 100 | exports.def 101 | ..\Binaries 102 | 103 | 104 | 105 | 106 | Level3 107 | true 108 | true 109 | true 110 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 111 | true 112 | 113 | 114 | Console 115 | true 116 | true 117 | true 118 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dbgeng.lib;pooldata.lib;%(AdditionalDependencies) 119 | exports.def 120 | ..\Binaries 121 | 122 | 123 | 124 | 125 | Level3 126 | true 127 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 128 | true 129 | 130 | 131 | Console 132 | true 133 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dbgeng.lib;pooldata.lib;PoolData.lib;%(AdditionalDependencies) 134 | exports.def 135 | ..\Binaries 136 | 137 | 138 | 139 | 140 | Level3 141 | true 142 | true 143 | true 144 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 145 | true 146 | 147 | 148 | Console 149 | true 150 | true 151 | true 152 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dbgeng.lib;pooldata.lib;%(AdditionalDependencies) 153 | exports.def 154 | ..\Binaries 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /PoolViewer/PoolViewer.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | {911A68FB-82ED-4424-A8B4-5C8538AFB14F} 24 | Win32Proj 25 | PoolViewer 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | ..\Binaries 76 | 77 | 78 | true 79 | ..\Binaries 80 | 81 | 82 | false 83 | ..\Binaries 84 | 85 | 86 | false 87 | ..\Binaries 88 | 89 | 90 | 91 | 92 | 93 | Level3 94 | true 95 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 96 | true 97 | ProgramDatabase 98 | 99 | 100 | Windows 101 | true 102 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 103 | ..\Binaries 104 | 105 | 106 | 107 | 108 | 109 | 110 | Level3 111 | true 112 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 113 | true 114 | ProgramDatabase 115 | 116 | 117 | Windows 118 | true 119 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;PoolData.lib;%(AdditionalDependencies) 120 | /DEBUG:FULL %(AdditionalOptions) 121 | ..\Binaries 122 | 123 | 124 | 125 | 126 | 127 | 128 | Level3 129 | true 130 | true 131 | true 132 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 133 | true 134 | None 135 | 136 | 137 | Windows 138 | true 139 | true 140 | false 141 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 142 | ..\Binaries 143 | 144 | 145 | 146 | 147 | 148 | 149 | Level3 150 | true 151 | true 152 | true 153 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 154 | true 155 | None 156 | 157 | 158 | Windows 159 | true 160 | true 161 | kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) 162 | false 163 | ..\Binaries 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /ManagedUIKitWPF/MainView.xaml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /ManagedUIKitWPF/MainView.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows; 3 | using System.Runtime.InteropServices; 4 | using Microsoft.Win32; 5 | using System.IO; 6 | using System.Windows.Documents; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Windows.Controls; 12 | using System.Windows.Data; 13 | using System.Windows.Input; 14 | using System.Windows.Media; 15 | using System.Windows.Media.Imaging; 16 | using System.Windows.Navigation; 17 | using System.Collections.ObjectModel; 18 | using System.Security.Policy; 19 | using System.ComponentModel; 20 | using System.Windows.Interop; 21 | using System.Security.Principal; 22 | using System.Diagnostics; 23 | using System.Windows.Threading; 24 | 25 | namespace PoolViewer 26 | { 27 | public partial class MainView : Window 28 | { 29 | public List heaps = new List { }; 30 | public List poolBlocks = new List { }; 31 | public Dictionary tags = new Dictionary(); 32 | public Dictionary subsegs = new Dictionary(); 33 | 34 | //// API Delegates 35 | delegate long GetPoolInformation_Ptr(string FilePath, bool CreateLiveDump, out int res); 36 | delegate bool GetNextHeapInformation_Ptr(out Int64 Address, out int NodeNumber, out long NumberOfAllocations, out int PoolType, out bool Special); 37 | delegate string GetNextAllocation_Ptr(out Int64 Address, out int Size, out bool Allocated, out int Type); 38 | 39 | 40 | //// Ported Functions 41 | GetPoolInformation_Ptr GetPoolInformation; 42 | GetNextHeapInformation_Ptr GetNextHeapInformation; 43 | GetNextAllocation_Ptr GetNextAllocation; 44 | 45 | public class Heap 46 | { 47 | public Int64 Address { get; set; } 48 | public int NodeNumber { get; set; } 49 | public string Special { get; set; } 50 | public string PoolType { get; set; } 51 | public Int64 NumberOfAllocs { get; set; } 52 | public override string ToString() 53 | { 54 | return this.Address.ToString("X") + "," + 55 | this.NodeNumber.ToString() + "," + 56 | this.Special + "," + 57 | this.PoolType + "," + 58 | this.NumberOfAllocs.ToString(); 59 | } 60 | } 61 | public class PoolBlock 62 | { 63 | public Int64 Address { get; set; } 64 | public Int64 Heap { get; set; } 65 | public string PoolType { get; set; } 66 | public long Size { get; set; } 67 | public string Tag { get; set; } 68 | public string Allocated { get; set; } 69 | public string SubsegType { get; set; } 70 | public string Special { get; set; } 71 | public override string ToString() 72 | { 73 | return this.Address.ToString("X") + "," + 74 | this.Heap.ToString("X") + "," + 75 | this.Size.ToString() + "," + 76 | this.Tag + "," + 77 | this.Allocated + "," + 78 | this.SubsegType + "," + 79 | this.PoolType + "," + 80 | this.Special; 81 | } 82 | } 83 | 84 | void PrintLog(string log_input) 85 | { 86 | LogView.Text += $"[{DateTime.Now.ToShortTimeString()}] :: {log_input}\r\n"; 87 | Dispatcher.Invoke(() => { LogView.ScrollToEnd(); }, DispatcherPriority.ContextIdle); 88 | } 89 | 90 | public MainView(IntPtr api_1_ptr, IntPtr api_2_ptr, IntPtr api_3_ptr) 91 | { 92 | InitializeComponent(); 93 | PrintLog("Initializing ..."); 94 | 95 | //// Recovering Native Functions 96 | if (api_1_ptr != IntPtr.Zero && api_2_ptr != IntPtr.Zero && api_3_ptr != IntPtr.Zero) 97 | { 98 | GetPoolInformation = (GetPoolInformation_Ptr)Marshal.GetDelegateForFunctionPointer(api_1_ptr, typeof(GetPoolInformation_Ptr)); 99 | GetNextHeapInformation = (GetNextHeapInformation_Ptr)Marshal.GetDelegateForFunctionPointer(api_2_ptr, typeof(GetNextHeapInformation_Ptr)); 100 | GetNextAllocation = (GetNextAllocation_Ptr)Marshal.GetDelegateForFunctionPointer(api_3_ptr, typeof(GetNextAllocation_Ptr)); 101 | } 102 | PrintLog("Successfully initialized"); 103 | } 104 | 105 | private void FilterPoolBlocks() 106 | { 107 | var _itemSourceList = new CollectionViewSource() { Source = poolBlocks }; 108 | _itemSourceList.Filter += new FilterEventHandler(DataFilter); 109 | ICollectionView Itemlist = _itemSourceList.View; 110 | Blocks.ItemsSource = Itemlist; 111 | } 112 | private void Search_Button_Click(object sender, RoutedEventArgs e) 113 | { 114 | FilterPoolBlocks(); 115 | } 116 | private void MenuItem_Click(object sender, RoutedEventArgs e) 117 | { 118 | // Get the clicked MenuItem 119 | var menuItem = (MenuItem)sender; 120 | 121 | // Get the ContextMenu to which the menuItem belongs 122 | var contextMenu = (ContextMenu)menuItem.Parent; 123 | 124 | // Find the placementTarget 125 | var item = (DataGrid)contextMenu.PlacementTarget; 126 | 127 | // Get the underlying item, that you cast to your object that is bound 128 | // to the DataGrid (and has subject and state as property) 129 | var heap = (Heap)item.SelectedCells[0].Item; 130 | 131 | HeapAddress.Text = heap.Address.ToString("X"); 132 | PoolType.SelectedIndex = -1; 133 | MinSize.Clear(); 134 | MaxSize.Clear(); 135 | Allocated.SelectedIndex = -1; 136 | Special.SelectedIndex = -1; 137 | Subseg.SelectedIndex = -1; 138 | Tag.Clear(); 139 | FilterPoolBlocks(); 140 | if (Pools.SelectedIndex == 0) 141 | { 142 | Pools.SelectedIndex++; 143 | } 144 | } 145 | 146 | private void MenuItem_Click_1(object sender, RoutedEventArgs e) 147 | { 148 | System.Text.StringBuilder sb = new System.Text.StringBuilder(); 149 | sb.AppendLine("Address,Heap,Size,Tag,Allocated,Subsegment,Pool Type,Special Pool"); 150 | foreach (var item in poolBlocks) 151 | { 152 | sb.AppendLine(item.ToString()); 153 | } 154 | System.IO.File.WriteAllText( 155 | System.IO.Path.Combine( 156 | AppDomain.CurrentDomain.BaseDirectory, "export.csv"), 157 | sb.ToString()); 158 | PrintLog($"Exported pool blocks to {System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "export.csv")}."); 159 | } 160 | 161 | private void DisplayInfoFromDmp(int NumberOfHeaps) 162 | { 163 | long address; 164 | int nodeNum; 165 | long allocNum; 166 | int poolType; 167 | bool special; 168 | string tag; 169 | bool allocated; 170 | int allocSize; 171 | long blockAddress; 172 | int allocType; 173 | string poolTypeStr = "Unknown"; 174 | string specialStr = "No"; 175 | 176 | heaps.Clear(); 177 | poolBlocks.Clear(); 178 | subsegs.Clear(); 179 | tags.Clear(); 180 | 181 | subsegs.Add("Lfh", 0); 182 | subsegs.Add("Vs", 0); 183 | subsegs.Add("Large", 0); 184 | subsegs.Add("Big", 0); 185 | subsegs.Add("Unknown", 0); 186 | 187 | for (int i = 0; i < NumberOfHeaps; i++) 188 | { 189 | GetNextHeapInformation(out address, out nodeNum, out allocNum, out poolType, out special); 190 | if (special) 191 | { 192 | specialStr = "Yes"; 193 | } 194 | if (poolType == 0) 195 | { 196 | poolTypeStr = "NonPagedPool"; 197 | } 198 | else if (poolType == 1) 199 | { 200 | poolTypeStr = "PagedPool"; 201 | } 202 | else if (poolType == 512) 203 | { 204 | poolTypeStr = "NonPagedPoolNx"; 205 | } 206 | Heap h = new Heap 207 | { 208 | Address = address, 209 | PoolType = poolTypeStr, 210 | NodeNumber = nodeNum, 211 | Special = specialStr, 212 | NumberOfAllocs = allocNum 213 | }; 214 | heaps.Add(h); 215 | 216 | // 217 | // Get all pool blocks for heap 218 | // 219 | for (int j = 0; j < allocNum; j++) 220 | { 221 | tag = GetNextAllocation(out blockAddress, out allocSize, out allocated, out allocType); 222 | if (allocSize == 0) 223 | { 224 | continue; 225 | } 226 | string subsegType = "Unknown"; 227 | string isAllocated = "No"; 228 | if (allocType == 0) 229 | { 230 | subsegType = "Lfh"; 231 | } 232 | else if (allocType == 1) 233 | { 234 | subsegType = "Vs"; 235 | } 236 | else if (allocType == 2) 237 | { 238 | subsegType = "Large"; 239 | } 240 | else if (allocType == 3) 241 | { 242 | subsegType = "Big"; 243 | } 244 | if (allocated == true) 245 | { 246 | isAllocated = "Yes"; 247 | } 248 | PoolBlock p = new PoolBlock 249 | { 250 | Address = blockAddress, 251 | Size = allocSize, 252 | SubsegType = subsegType, 253 | Tag = tag, 254 | Heap = h.Address, 255 | PoolType = h.PoolType, 256 | Special = h.Special, 257 | Allocated = isAllocated 258 | }; 259 | poolBlocks.Add(p); 260 | if (!tags.ContainsKey(tag)) 261 | { 262 | tags.Add(tag, 1); 263 | } 264 | else 265 | { 266 | tags[tag] += 1; 267 | } 268 | subsegs[subsegType] += 1; 269 | } 270 | } 271 | Heaps.ItemsSource = heaps; 272 | Heaps.Visibility = Visibility.Visible; 273 | heapsText.Visibility = Visibility.Visible; 274 | Blocks.ItemsSource = poolBlocks; 275 | Blocks.Visibility = Visibility.Visible; 276 | 277 | Dictionary tags2 = new Dictionary(); 278 | var maxTags = tags.OrderByDescending(x => x.Value); 279 | 280 | int n = 0; 281 | foreach (KeyValuePair entry in maxTags) 282 | { 283 | tags2.Add(entry.Key, entry.Value); 284 | n++; 285 | if (n == 10) 286 | { 287 | break; 288 | } 289 | } 290 | Tags.ItemsSource = tags2; 291 | Tags.Visibility = Visibility.Visible; 292 | tagsText.Visibility = Visibility.Visible; 293 | Subsegments.ItemsSource = subsegs; 294 | Subsegments.Visibility = Visibility.Visible; 295 | SubsegText.Visibility = Visibility.Visible; 296 | } 297 | 298 | private bool LoadDbgDlls() 299 | { 300 | IntPtr dbgEng = NativeMethods.GetModuleHandle("DbgEng.dll"); 301 | if (dbgEng != IntPtr.Zero) 302 | { 303 | return true; 304 | } 305 | 306 | string currentDir = Directory.GetCurrentDirectory(); 307 | if (File.Exists($"{currentDir}\\DbgEng.dll") && 308 | File.Exists($"{currentDir}\\DbgHelp.dll")) 309 | { 310 | // 311 | // Load DLLs from current directory if they exist. 312 | // Loading DbgEng.dll will load DbgHelp.dll as a dependency 313 | // 314 | NativeMethods.LoadLibrary($"{currentDir}\\DbgEng.dll"); 315 | } 316 | 317 | dbgEng = NativeMethods.GetModuleHandle("DbgEng.dll"); 318 | if (dbgEng == IntPtr.Zero) 319 | { 320 | string message = "No DbgEng.dll! Configure load path"; 321 | MessageBox.Show(message); 322 | return false; 323 | } 324 | return true; 325 | } 326 | 327 | private void MenuItem_Click_2(object sender, RoutedEventArgs e) 328 | { 329 | int heapNum; 330 | long result; 331 | 332 | if (!LoadDbgDlls()) 333 | { 334 | return; 335 | } 336 | 337 | OpenFileDialog openFileDialog = new OpenFileDialog(); 338 | if (openFileDialog.ShowDialog() == true) 339 | { 340 | PrintLog($"Opening file '{Path.GetFileName(openFileDialog.FileName)}'."); 341 | result = GetPoolInformation(openFileDialog.FileName.ToString(), false, out heapNum); 342 | if (result != 0) 343 | { 344 | PrintLog($"Opening dmp file failed with error {result}"); 345 | return; 346 | } 347 | PrintLog($"File '{Path.GetFileName(openFileDialog.FileName)}' has been opened."); 348 | DisplayInfoFromDmp(heapNum); 349 | } 350 | } 351 | 352 | private void DataFilter(object sender, FilterEventArgs e) 353 | { 354 | Int64 heap = 0; 355 | string poolType = ""; 356 | long minSize = 0; 357 | long maxSize = 0xffffffff; 358 | var tag = Tag.Text; 359 | bool filterByAllocated = false; 360 | bool allocated = false; 361 | bool special = false; 362 | bool filterBySpecial = false; 363 | string subseg = ""; 364 | ComboBoxItem typeItem = (ComboBoxItem)PoolType.SelectedItem; 365 | if (typeItem != null) 366 | { 367 | poolType = typeItem.Content.ToString(); 368 | } 369 | if (HeapAddress.Text != "") 370 | { 371 | heap = Convert.ToInt64(HeapAddress.Text, 16); 372 | } 373 | if (MinSize.Text != "") 374 | { 375 | if (MinSize.Text.StartsWith("0x") || MinSize.Text.StartsWith("0X")) 376 | { 377 | minSize = Convert.ToInt64(MinSize.Text, 16); 378 | } 379 | else 380 | { 381 | minSize = Convert.ToInt64(MinSize.Text, 10); 382 | } 383 | } 384 | if (MaxSize.Text != "") 385 | { 386 | if (MaxSize.Text.StartsWith("0x") || MaxSize.Text.StartsWith("0X")) 387 | { 388 | maxSize = Convert.ToInt64(MaxSize.Text, 16); 389 | } 390 | else 391 | { 392 | maxSize = Convert.ToInt64(MaxSize.Text, 10); 393 | } 394 | } 395 | if (Allocated.SelectedItem != null) 396 | { 397 | if (((ComboBoxItem)(Allocated.SelectedItem)).Tag.ToString() != "Both") 398 | { 399 | allocated = ((ComboBoxItem)(Allocated.SelectedItem)).Tag.ToString() == "True"; 400 | filterByAllocated = true; 401 | } 402 | } 403 | 404 | if (Special.SelectedItem != null) 405 | { 406 | if (((ComboBoxItem)(Special.SelectedItem)).Tag.ToString() != "Both") 407 | { 408 | special = ((ComboBoxItem)(Special.SelectedItem)).Tag.ToString() == "True"; 409 | filterBySpecial = true; 410 | } 411 | } 412 | 413 | if (Subseg.SelectedItem != null) 414 | { 415 | if (((ComboBoxItem)(Subseg.SelectedItem)).Tag.ToString() != "All") 416 | { 417 | subseg = ((ComboBoxItem)(Subseg.SelectedItem)).Tag.ToString(); 418 | } 419 | } 420 | 421 | var obj = e.Item as PoolBlock; 422 | if (obj != null) 423 | { 424 | if (((heap == 0) || (obj.Heap == heap)) && 425 | ((obj.Size >= minSize) && (obj.Size <= maxSize)) && 426 | ((poolType == "") || (poolType == "All") || (obj.PoolType == poolType)) && 427 | ((tag == "" || obj.Tag == tag)) && 428 | ((filterByAllocated == false) || ((obj.Allocated == "Yes") == allocated)) && 429 | ((filterBySpecial == false) || ((obj.Special == "Yes") == special)) && 430 | ((subseg == "") || (obj.SubsegType == subseg))) 431 | { 432 | e.Accepted = true; 433 | } 434 | else 435 | e.Accepted = false; 436 | } 437 | } 438 | 439 | private void Window_Loaded(object sender, RoutedEventArgs e) 440 | { 441 | PrintLog("WPF UI has been initialized successfully."); 442 | } 443 | 444 | private void MenuItem_Click_3(object sender, RoutedEventArgs e) 445 | { 446 | // Get the clicked MenuItem 447 | var menuItem = (MenuItem)sender; 448 | 449 | // Get the ContextMenu to which the menuItem belongs 450 | var contextMenu = (ContextMenu)menuItem.Parent; 451 | 452 | // Find the placementTarget 453 | var item = (DataGrid)contextMenu.PlacementTarget; 454 | 455 | // Get the underlying item, that you cast to your object that is bound 456 | // to the DataGrid (and has subject and state as property) 457 | KeyValuePair tag = (KeyValuePair)item.SelectedCells[0].Item; 458 | 459 | HeapAddress.Clear(); 460 | PoolType.SelectedIndex = -1; 461 | MinSize.Clear(); 462 | MaxSize.Clear(); 463 | Allocated.SelectedIndex = -1; 464 | Special.SelectedIndex = -1; 465 | Subseg.SelectedIndex = -1; 466 | Tag.Text = tag.Key; 467 | FilterPoolBlocks(); 468 | if (Pools.SelectedIndex == 0) 469 | { 470 | Pools.SelectedIndex++; 471 | } 472 | } 473 | 474 | private void MenuItem_Click_4(object sender, RoutedEventArgs e) 475 | { 476 | // Get the clicked MenuItem 477 | var menuItem = (MenuItem)sender; 478 | 479 | // Get the ContextMenu to which the menuItem belongs 480 | var contextMenu = (ContextMenu)menuItem.Parent; 481 | 482 | // Find the placementTarget 483 | var item = (DataGrid)contextMenu.PlacementTarget; 484 | 485 | // Get the underlying item, that you cast to your object that is bound 486 | // to the DataGrid (and has subject and state as property) 487 | KeyValuePair subseg = (KeyValuePair)item.SelectedCells[0].Item; 488 | 489 | HeapAddress.Clear(); 490 | PoolType.SelectedIndex = -1; 491 | MinSize.Clear(); 492 | MaxSize.Clear(); 493 | Allocated.SelectedIndex = -1; 494 | Special.SelectedIndex = -1; 495 | Tag.Clear(); 496 | 497 | if (subseg.Key == "Lfh") 498 | { 499 | Subseg.SelectedIndex = 0; 500 | } 501 | else if (subseg.Key == "Vs") 502 | { 503 | Subseg.SelectedIndex = 1; 504 | } 505 | else if (subseg.Key == "Large") 506 | { 507 | Subseg.SelectedIndex = 2; 508 | } 509 | else 510 | { 511 | PrintLog("Unknown subsegment"); 512 | return; 513 | } 514 | 515 | FilterPoolBlocks(); 516 | if (Pools.SelectedIndex == 0) 517 | { 518 | Pools.SelectedIndex++; 519 | } 520 | } 521 | private void MenuItem_Click_5(object sender, RoutedEventArgs e) 522 | { 523 | Environment.Exit(0); 524 | } 525 | 526 | private void DllPath_Click(object sender, RoutedEventArgs e) 527 | { 528 | DllConfig dllConfig = new DllConfig(); 529 | dllConfig.Show(); 530 | } 531 | 532 | private void Window_Closing(object sender, CancelEventArgs e) 533 | { 534 | Environment.Exit(0); 535 | } 536 | 537 | [Serializable] 538 | public struct ShellExecuteInfo 539 | { 540 | public int Size; 541 | public uint Mask; 542 | public IntPtr hwnd; 543 | public string Verb; 544 | public string File; 545 | public string Parameters; 546 | public string Directory; 547 | public uint Show; 548 | public IntPtr InstApp; 549 | public IntPtr IDList; 550 | public string Class; 551 | public IntPtr hkeyClass; 552 | public uint HotKey; 553 | public IntPtr Icon; 554 | public IntPtr Monitor; 555 | } 556 | 557 | [DllImport("shell32.dll", SetLastError = true)] 558 | extern public static bool ShellExecuteEx(ref ShellExecuteInfo lpExecInfo); 559 | public void RunAsAdmin() 560 | { 561 | const uint SW_NORMAL = 1; 562 | ShellExecuteInfo sei = new ShellExecuteInfo(); 563 | sei.Size = Marshal.SizeOf(sei); 564 | sei.Verb = "runas"; 565 | sei.File = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName); 566 | sei.Show = SW_NORMAL; 567 | if (!ShellExecuteEx(ref sei)) 568 | { 569 | return; 570 | } 571 | Environment.Exit(0); 572 | } 573 | public bool IsUserAdministrator() 574 | { 575 | bool isAdmin; 576 | try 577 | { 578 | WindowsIdentity user = WindowsIdentity.GetCurrent(); 579 | WindowsPrincipal principal = new WindowsPrincipal(user); 580 | isAdmin = principal.IsInRole(WindowsBuiltInRole.Administrator); 581 | } 582 | catch (Exception ex) 583 | { 584 | isAdmin = false; 585 | } 586 | return isAdmin; 587 | } 588 | private void MenuItem_Click_6(object sender, RoutedEventArgs e) 589 | { 590 | int heapNum; 591 | long res; 592 | var isAdmin = IsUserAdministrator(); 593 | if (!isAdmin) 594 | { 595 | RunAsAdmin(); 596 | PrintLog("Failed to open PoolViewer as admin. Can't analyze machine."); 597 | return; 598 | } 599 | 600 | if (!LoadDbgDlls()) 601 | { 602 | return; 603 | } 604 | 605 | var filePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "live.dmp"); 606 | PrintLog("Creating dump file of the current machine. This can take a few minutes."); 607 | res = GetPoolInformation(filePath, true, out heapNum); 608 | if (res != 0) 609 | { 610 | PrintLog($"Failed opening dmp file with error {res}"); 611 | return; 612 | } 613 | PrintLog($"File '{filePath}' has been opened."); 614 | try 615 | { 616 | DisplayInfoFromDmp(heapNum); 617 | } 618 | catch (Exception ex) 619 | { 620 | PrintLog($"Exception {ex.Message}"); 621 | } 622 | } 623 | 624 | private void dg_Sorting(object sender, DataGridSortingEventArgs e) 625 | { 626 | FilterPoolBlocks(); 627 | } 628 | } 629 | } -------------------------------------------------------------------------------- /PoolData/PoolData.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "PoolData.h" 6 | 7 | using namespace std; 8 | 9 | PDEBUG_CLIENT g_DebugClient = nullptr; 10 | PDEBUG_SYMBOLS g_DebugSymbols = nullptr; 11 | PDEBUG_DATA_SPACES4 g_DataSpaces = nullptr; 12 | PDEBUG_CONTROL g_DebugControl = nullptr; 13 | 14 | #define ALIGN_DOWN_POINTER_BY(address, alignment) \ 15 | ((PVOID)((ULONG_PTR)(address) & ~((ULONG_PTR)(alignment) - 1))) 16 | 17 | #define ALIGN_UP_POINTER_BY(address, alignment) \ 18 | (ALIGN_DOWN_POINTER_BY(((ULONG_PTR)(address) + (alignment) - 1), alignment)) 19 | 20 | #define CONTROL_KERNEL_DUMP 37 21 | static NtSystemDebugControl g_NtSystemDebugControl = NULL; 22 | 23 | ULONG64 g_HeapKey; 24 | ULONG64 g_LfhKey; 25 | ULONG64 g_KernBase; 26 | list g_Heaps; 27 | list g_CurrentAllocs; 28 | 29 | ULONG64 g_PoolState; 30 | ULONG64 g_PoolBigPageTableOffset; 31 | ULONG64 g_PoolBigPageTable; 32 | ULONG64 g_PoolBigPageTableSizeOffset; 33 | ULONG64 g_PoolBigPageTableSize; 34 | HEAP_MANAGER_STATE_OFFSETS g_HeapManagerStateOffsets; 35 | RTLP_HP_HEAP_MANAGER_OFFSETS g_HpHeapManagerOffsets; 36 | RTLP_HP_ALLOC_TRACKER_OFFSETS g_RtlpHpAllocTrackerOffsets; 37 | RTL_CSPARSE_BITMAP_OFFSETS g_RtlCsparseBitmapOffsets; 38 | HEAP_VAMGR_CTX_OFFSETS g_VamgrCtxOffsets; 39 | HEAP_VAMGR_VASPACE_OFFSETS g_VamgrVaspaceOffsets; 40 | RTL_SPARSE_ARRAY_OFFSETS g_RtlSparseArrayOffsets; 41 | HEAP_VAMGR_RANGE_OFFSETS g_VamgrRangeOffsets; 42 | HEAP_SEG_CONTEXT_OFFSETS g_SegContextOffsets; 43 | HEAP_PAGE_SEGMENT_OFFSETS g_PageSegmentOffsets; 44 | HEAP_PAGE_RANGE_DESCRIPTOR_OFFSET g_PageRangeDescriptorOffsets; 45 | SEGMENT_HEAP_OFFSETS g_SegmentHeapOffsets; 46 | EX_HEAP_POOL_NODE_OFFSETS g_PoolNodeOffsets; 47 | HEAP_VS_SUBSEGMENT_OFFSETS g_VsSubsegmentOffsets; 48 | HEAP_VS_CHUNK_HEADER_OFFSETS g_VsChunkHeaderOffsets; 49 | HEAP_VS_CHUNK_HEADER_SIZE_OFFSETS g_VsChunkHeaderSizeOffsets; 50 | POOL_HEADER_OFFSETS g_PoolHeaderOffsets; 51 | HEAP_LFH_SUBSEGMENT_OFFSETS g_lfhSubsegmentOffsets; 52 | HEAP_LFH_SUBSEGMENT_ENCODED_OFFSETS_OFFSETS g_lfhSubsegmentEncodedEffsets; 53 | POOL_TRACKER_BIG_PAGES_OFFSETS g_PoolTrackerBigPagesOffsets; 54 | HEAP_LARGE_ALLOC_DATA_OFFSETS g_HeapLargeAllocDataOffsets; 55 | RTL_BALANCED_NODE_OFFSETS g_RtlBalancedNodeOffsets; 56 | RTL_RB_TREE_OFFSETS g_RtlRbTreeOffsets; 57 | 58 | // 59 | // Types 60 | // 61 | ULONG RTLP_HP_HEAP_GLOBALS; 62 | ULONG EX_POOL_HEAP_MANAGER_STATE; 63 | ULONG RTLP_HP_HEAP_MANAGER; 64 | ULONG RTLP_HP_ALLOC_TRACKER; 65 | ULONG RTL_CSPARSE_BITMAP; 66 | ULONG EX_HEAP_POOL_NODE; 67 | ULONG SEGMENT_HEAP; 68 | ULONG HEAP_SEG_CONTEXT; 69 | ULONG HEAP_PAGE_SEGMENT; 70 | ULONG HEAP_PAGE_RANGE_DESCRIPTOR; 71 | ULONG HEAP_VAMGR_CTX; 72 | ULONG HEAP_VAMGR_VASPACE; 73 | ULONG RTL_SPARSE_ARRAY; 74 | ULONG HEAP_VAMGR_RANGE; 75 | ULONG HEAP_VS_SUBSEGMENT; 76 | ULONG HEAP_VS_CHUNK_HEADER; 77 | ULONG HEAP_VS_CHUNK_HEADER_SIZE; 78 | ULONG POOL_HEADER; 79 | ULONG HEAP_LFH_SUBSEGMENT; 80 | ULONG HEAP_LFH_SUBSEGMENT_ENCODED_OFFSETS; 81 | ULONG POOL_TRACKER_BIG_PAGES; 82 | ULONG HEAP_LARGE_ALLOC_DATA; 83 | ULONG RTL_BALANCED_NODE; 84 | ULONG RTL_RB_TREE; 85 | 86 | // 87 | // Sizes 88 | // 89 | ULONG g_HeapPageSegSize; 90 | ULONG g_RangeDescriptorSize; 91 | ULONG g_VamgrRangeSize; 92 | ULONG g_SegContextSize; 93 | ULONG g_PoolNodeSize; 94 | ULONG g_vsSubsegmentSize; 95 | ULONG g_vsChunkHeaderSize; 96 | ULONG g_poolHeaderSize; 97 | ULONG g_poolTrackerBigPagesSize; 98 | ULONG g_HeapLargeAllocDataSize; 99 | ULONG g_RtlRbTreeSize; 100 | 101 | /* 102 | Initializes all types required by this tool as globals so we don't have to 103 | find the necessary types in each function. 104 | */ 105 | HRESULT 106 | GetTypes ( 107 | void 108 | ) 109 | { 110 | if ((!SUCCEEDED(g_DataSpaces->ReadDebuggerData(DEBUG_DATA_KernBase, &g_KernBase, sizeof(g_KernBase), nullptr))) || 111 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_RTLP_HP_HEAP_GLOBALS", &RTLP_HP_HEAP_GLOBALS))) || 112 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_EX_POOL_HEAP_MANAGER_STATE", &EX_POOL_HEAP_MANAGER_STATE))) || 113 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_RTLP_HP_HEAP_MANAGER", &RTLP_HP_HEAP_MANAGER))) || 114 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_RTLP_HP_ALLOC_TRACKER", &RTLP_HP_ALLOC_TRACKER))) || 115 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_RTL_CSPARSE_BITMAP", &RTL_CSPARSE_BITMAP))) || 116 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_EX_HEAP_POOL_NODE", &EX_HEAP_POOL_NODE))) || 117 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_SEGMENT_HEAP", &SEGMENT_HEAP))) || 118 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_HEAP_SEG_CONTEXT", &HEAP_SEG_CONTEXT))) || 119 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_HEAP_PAGE_SEGMENT", &HEAP_PAGE_SEGMENT))) || 120 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_HEAP_PAGE_RANGE_DESCRIPTOR", &HEAP_PAGE_RANGE_DESCRIPTOR))) || 121 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_HEAP_VAMGR_CTX", &HEAP_VAMGR_CTX))) || 122 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_HEAP_VAMGR_VASPACE", &HEAP_VAMGR_VASPACE))) || 123 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_RTL_SPARSE_ARRAY", &RTL_SPARSE_ARRAY))) || 124 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_HEAP_VAMGR_RANGE", &HEAP_VAMGR_RANGE))) || 125 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_HEAP_VS_SUBSEGMENT", &HEAP_VS_SUBSEGMENT))) || 126 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_HEAP_VS_CHUNK_HEADER", &HEAP_VS_CHUNK_HEADER))) || 127 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_HEAP_VS_CHUNK_HEADER_SIZE", &HEAP_VS_CHUNK_HEADER_SIZE))) || 128 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_POOL_HEADER", &POOL_HEADER))) || 129 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_HEAP_LFH_SUBSEGMENT", &HEAP_LFH_SUBSEGMENT))) || 130 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_HEAP_LFH_SUBSEGMENT_ENCODED_OFFSETS", &HEAP_LFH_SUBSEGMENT_ENCODED_OFFSETS))) || 131 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_POOL_TRACKER_BIG_PAGES", &POOL_TRACKER_BIG_PAGES))) || 132 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_HEAP_LARGE_ALLOC_DATA", &HEAP_LARGE_ALLOC_DATA))) || 133 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_RTL_BALANCED_NODE", &RTL_BALANCED_NODE))) || 134 | (!SUCCEEDED(g_DebugSymbols->GetTypeId(g_KernBase, "_RTL_RB_TREE", &RTL_RB_TREE)))) 135 | { 136 | return S_FALSE; 137 | } 138 | 139 | return S_OK; 140 | } 141 | 142 | /* 143 | Initializes all the sizes of structures that this tool uses as globals. 144 | */ 145 | HRESULT 146 | GetSizes ( 147 | void 148 | ) 149 | { 150 | if ((!SUCCEEDED(g_DebugSymbols->GetTypeSize(g_KernBase, HEAP_PAGE_SEGMENT, &g_HeapPageSegSize))) || 151 | (!SUCCEEDED(g_DebugSymbols->GetTypeSize(g_KernBase, HEAP_PAGE_RANGE_DESCRIPTOR, &g_RangeDescriptorSize))) || 152 | (!SUCCEEDED(g_DebugSymbols->GetTypeSize(g_KernBase, HEAP_VAMGR_RANGE, &g_VamgrRangeSize))) || 153 | (!SUCCEEDED(g_DebugSymbols->GetTypeSize(g_KernBase, HEAP_SEG_CONTEXT, &g_SegContextSize))) || 154 | (!SUCCEEDED(g_DebugSymbols->GetTypeSize(g_KernBase, EX_HEAP_POOL_NODE, &g_PoolNodeSize))) || 155 | (!SUCCEEDED(g_DebugSymbols->GetTypeSize(g_KernBase, HEAP_VS_SUBSEGMENT, &g_vsSubsegmentSize))) || 156 | (!SUCCEEDED(g_DebugSymbols->GetTypeSize(g_KernBase, HEAP_VS_CHUNK_HEADER, &g_vsChunkHeaderSize))) || 157 | (!SUCCEEDED(g_DebugSymbols->GetTypeSize(g_KernBase, POOL_HEADER, &g_poolHeaderSize))) || 158 | (!SUCCEEDED(g_DebugSymbols->GetTypeSize(g_KernBase, POOL_TRACKER_BIG_PAGES, &g_poolTrackerBigPagesSize))) || 159 | (!SUCCEEDED(g_DebugSymbols->GetTypeSize(g_KernBase, RTL_RB_TREE, &g_RtlRbTreeSize))) || 160 | (!SUCCEEDED(g_DebugSymbols->GetTypeSize(g_KernBase, HEAP_LARGE_ALLOC_DATA, &g_HeapLargeAllocDataSize))) 161 | ) 162 | { 163 | printf("GetTypeSize failed\n"); 164 | return S_FALSE; 165 | } 166 | return S_OK; 167 | } 168 | 169 | /* 170 | Initialize HeapKey and LfhKey. 171 | */ 172 | HRESULT 173 | GetHeapGlobals ( 174 | void 175 | ) 176 | { 177 | HRESULT result; 178 | ULONG64 heapGlobals; 179 | ULONG lfhKeyOffset; 180 | ULONG64 lfhKeyAddress; 181 | ULONG64 lfhKey; 182 | ULONG heapKeyOffset; 183 | ULONG64 heapKeyAddress; 184 | ULONG64 heapKey; 185 | 186 | if ((!SUCCEEDED(g_DebugSymbols->GetOffsetByName("nt!RtlpHpHeapGlobals", &heapGlobals))) || 187 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, RTLP_HP_HEAP_GLOBALS, "LfhKey", &lfhKeyOffset)))) 188 | { 189 | result = S_FALSE; 190 | goto Exit; 191 | } 192 | 193 | lfhKeyAddress = heapGlobals + lfhKeyOffset; 194 | 195 | result = g_DataSpaces->ReadVirtual(lfhKeyAddress, &lfhKey, sizeof(lfhKey), nullptr); 196 | if (!SUCCEEDED(result)) 197 | { 198 | goto Exit; 199 | } 200 | g_LfhKey = lfhKey; 201 | 202 | result = g_DebugSymbols->GetFieldOffset(g_KernBase, RTLP_HP_HEAP_GLOBALS, "HeapKey", &heapKeyOffset); 203 | if (!SUCCEEDED(result)) 204 | { 205 | goto Exit; 206 | } 207 | 208 | heapKeyAddress = heapGlobals + heapKeyOffset; 209 | 210 | result = g_DataSpaces->ReadVirtual(heapKeyAddress, &heapKey, sizeof(heapKey), nullptr); 211 | if (!SUCCEEDED(result)) 212 | { 213 | goto Exit; 214 | } 215 | g_HeapKey = heapKey; 216 | 217 | Exit: 218 | return result; 219 | } 220 | 221 | /* 222 | Get offsets for all the structures that this tool uses. 223 | */ 224 | HRESULT 225 | GetOffsets ( 226 | void 227 | ) 228 | { 229 | if ((!SUCCEEDED(g_DebugSymbols->GetOffsetByName("nt!ExPoolState", &g_PoolState))) || 230 | (!SUCCEEDED(g_DebugSymbols->GetOffsetByName("nt!PoolBigPageTable", &g_PoolBigPageTableOffset))) || 231 | (!SUCCEEDED(g_DataSpaces->ReadVirtual(g_PoolBigPageTableOffset, &g_PoolBigPageTable, sizeof(g_PoolBigPageTable), nullptr))) || 232 | (!SUCCEEDED(g_DebugSymbols->GetOffsetByName("nt!PoolBigPageTableSize", &g_PoolBigPageTableSizeOffset))) || 233 | (!SUCCEEDED(g_DataSpaces->ReadVirtual(g_PoolBigPageTableSizeOffset, &g_PoolBigPageTableSize, sizeof(g_PoolBigPageTableSize), nullptr))) || 234 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, EX_POOL_HEAP_MANAGER_STATE, "HeapManager", &g_HeapManagerStateOffsets.HeapManagerOffset))) || 235 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, EX_POOL_HEAP_MANAGER_STATE, "PoolNode", &g_HeapManagerStateOffsets.PoolNodeOffset))) || 236 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, EX_POOL_HEAP_MANAGER_STATE, "NumberOfPools", &g_HeapManagerStateOffsets.NumberOfPoolsOffset))) || 237 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, EX_POOL_HEAP_MANAGER_STATE, "SpecialHeaps", &g_HeapManagerStateOffsets.SpecialHeapsOffset))) || 238 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, RTLP_HP_HEAP_MANAGER, "AllocTracker", &g_HpHeapManagerOffsets.AllocTrackerOffset))) || 239 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, RTLP_HP_HEAP_MANAGER, "VaMgr", &g_HpHeapManagerOffsets.VaMgrOffset))) || 240 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, RTLP_HP_ALLOC_TRACKER, "AllocTrackerBitmap", &g_RtlpHpAllocTrackerOffsets.AllocTrackerBitmapOffset))) || 241 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, RTLP_HP_ALLOC_TRACKER, "BaseAddress", &g_RtlpHpAllocTrackerOffsets.BaseAddressOffset))) || 242 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, RTL_CSPARSE_BITMAP, "CommitDirectory", &g_RtlCsparseBitmapOffsets.CommitDirectoryOffset))) || 243 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, RTL_CSPARSE_BITMAP, "CommitBitmap", &g_RtlCsparseBitmapOffsets.CommitBitmapOffset))) || 244 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, RTL_CSPARSE_BITMAP, "UserBitmap", &g_RtlCsparseBitmapOffsets.UserBitmapOffset))) || 245 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_VAMGR_CTX, "VaSpace", &g_VamgrCtxOffsets.VaSpaceOffset))) || 246 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_VAMGR_VASPACE, "VaRangeArray", &g_VamgrVaspaceOffsets.VaRangeArrayOffset))) || 247 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_VAMGR_VASPACE, "BaseAddress", &g_VamgrVaspaceOffsets.BaseAddressOffset))) || 248 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, RTL_SPARSE_ARRAY, "ElementSizeShift", &g_RtlSparseArrayOffsets.ElementSizeShiftOffset))) || 249 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, RTL_SPARSE_ARRAY, "Bitmap", &g_RtlSparseArrayOffsets.BitmapOffset))) || 250 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_VAMGR_RANGE, "Allocated", &g_VamgrRangeOffsets.AllocatedOffset))) || 251 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_SEG_CONTEXT, "SegmentListHead", &g_SegContextOffsets.SegmentListHeadOffset))) || 252 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_SEG_CONTEXT, "FirstDescriptorIndex", &g_SegContextOffsets.FirstDescriptorIndexOffset))) || 253 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_SEG_CONTEXT, "SegmentCount", &g_SegContextOffsets.SegmentCountOffset))) || 254 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_SEG_CONTEXT, "UnitShift", &g_SegContextOffsets.UnitShiftOffset))) || 255 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_PAGE_SEGMENT, "ListEntry", &g_PageSegmentOffsets.ListEntryOffset))) || 256 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_PAGE_SEGMENT, "Signature", &g_PageSegmentOffsets.SignatureOffset))) || 257 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_PAGE_SEGMENT, "DescArray", &g_PageSegmentOffsets.DescArrayOffset))) || 258 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_PAGE_SEGMENT, "SegmentCommitState", &g_PageSegmentOffsets.SegmentCommitStateOffset))) || 259 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_PAGE_RANGE_DESCRIPTOR, "RangeFlags", &g_PageRangeDescriptorOffsets.RangeFlagsOffset))) || 260 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_PAGE_RANGE_DESCRIPTOR, "TreeSignature", &g_PageRangeDescriptorOffsets.TreeSignatureOffset))) || 261 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_PAGE_RANGE_DESCRIPTOR, "UnitSize", &g_PageRangeDescriptorOffsets.UnitSizeOffset))) || 262 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, SEGMENT_HEAP, "SegContexts", &g_SegmentHeapOffsets.SegContextsOffsets))) || 263 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, SEGMENT_HEAP, "LargeAllocMetadata", &g_SegmentHeapOffsets.LargeAllocMetadataOffset))) || 264 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, EX_HEAP_POOL_NODE, "Heaps", &g_PoolNodeOffsets.HeapsOffset))) || 265 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_VS_SUBSEGMENT, "Signature", &g_VsSubsegmentOffsets.SignatureOffset))) || 266 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_VS_SUBSEGMENT, "Size", &g_VsSubsegmentOffsets.SizeOffset))) || 267 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_VS_CHUNK_HEADER, "Sizes", &g_VsChunkHeaderOffsets.SizesOffset))) || 268 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_VS_CHUNK_HEADER_SIZE, "HeaderBits", &g_VsChunkHeaderSizeOffsets.HeaderBitsOffset))) || 269 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_VS_CHUNK_HEADER_SIZE, "UnsafeSize", &g_VsChunkHeaderSizeOffsets.UnsafeSizeOffset))) || 270 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_VS_CHUNK_HEADER_SIZE, "Allocated", &g_VsChunkHeaderSizeOffsets.AllocatedOffset))) || 271 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, POOL_HEADER, "PoolTag", &g_PoolHeaderOffsets.PoolTagOffset))) || 272 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_LFH_SUBSEGMENT, "BlockOffsets", &g_lfhSubsegmentOffsets.BlockOffsets))) || 273 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_LFH_SUBSEGMENT, "BlockBitmap", &g_lfhSubsegmentOffsets.BlockBitmap))) || 274 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_LFH_SUBSEGMENT_ENCODED_OFFSETS, "EncodedData", &g_lfhSubsegmentEncodedEffsets.EncodedData))) || 275 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_LFH_SUBSEGMENT_ENCODED_OFFSETS, "BlockSize", &g_lfhSubsegmentEncodedEffsets.BlockSize))) || 276 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_LFH_SUBSEGMENT_ENCODED_OFFSETS, "FirstBlockOffset", &g_lfhSubsegmentEncodedEffsets.FirstBlockOffset))) || 277 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, POOL_TRACKER_BIG_PAGES, "Va", &g_PoolTrackerBigPagesOffsets.Va))) || 278 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, POOL_TRACKER_BIG_PAGES, "Key", &g_PoolTrackerBigPagesOffsets.Key))) || 279 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, POOL_TRACKER_BIG_PAGES, "NumberOfBytes", &g_PoolTrackerBigPagesOffsets.NumberOfBytes))) || 280 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_LARGE_ALLOC_DATA, "TreeNode", &g_HeapLargeAllocDataOffsets.TreeNodeOffset))) || 281 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, HEAP_LARGE_ALLOC_DATA, "VirtualAddress", &g_HeapLargeAllocDataOffsets.VirtualAddressOffset))) || 282 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, RTL_RB_TREE, "Root", &g_RtlRbTreeOffsets.RootOffset))) || 283 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, RTL_RB_TREE, "Encoded", &g_RtlRbTreeOffsets.EncodedOffset))) || 284 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, RTL_BALANCED_NODE, "Left", &g_RtlBalancedNodeOffsets.LeftOffset))) || 285 | (!SUCCEEDED(g_DebugSymbols->GetFieldOffset(g_KernBase, RTL_BALANCED_NODE, "Right", &g_RtlBalancedNodeOffsets.RightOffset)))) 286 | { 287 | return S_FALSE; 288 | } 289 | 290 | return S_OK; 291 | } 292 | 293 | /* 294 | Validates the region that is about to be read and reads it. 295 | */ 296 | HRESULT 297 | ReadData ( 298 | _In_ ULONG64 Address, 299 | _In_ ULONG Size, 300 | _Out_ PVOID Buffer 301 | ) 302 | { 303 | HRESULT result; 304 | ULONG64 validBase; 305 | ULONG validSize; 306 | 307 | result = g_DataSpaces->GetValidRegionVirtual(Address, 308 | Size, 309 | &validBase, 310 | &validSize); 311 | if (result != S_OK) 312 | { 313 | return result; 314 | } 315 | if (validBase != Address) 316 | { 317 | return S_FALSE; 318 | } 319 | return g_DataSpaces->ReadVirtual(Address, 320 | Buffer, 321 | Size, 322 | nullptr); 323 | } 324 | 325 | /* 326 | Check the bitmap value for an address. 327 | */ 328 | ULONG 329 | BitmapBitmaskRead ( 330 | _In_ PVOID Address 331 | ) 332 | { 333 | RTLP_CSPARSE_BITMAP_STATE state; 334 | USHORT bitmapResult; 335 | HRESULT result; 336 | ULONG64 heapManagaerAddress; 337 | ULONG64 allocTrackerAddress; 338 | ULONG64 allocTrackerBitmapAddress; 339 | ULONG64 baseAddressAddress; 340 | ULONG64 baseAddress; 341 | ULONG bitIndex; 342 | CHAR SBitmap[256]; 343 | ULONG64 commitBitmap; 344 | ULONG64* userBitmap; 345 | LONG64 commitBitmapValue; 346 | ULONG64 userBitmapValue; 347 | 348 | bitmapResult = 0; 349 | 350 | heapManagaerAddress = g_PoolState + g_HeapManagerStateOffsets.HeapManagerOffset; 351 | allocTrackerAddress = heapManagaerAddress + g_HpHeapManagerOffsets.AllocTrackerOffset; 352 | allocTrackerBitmapAddress = allocTrackerAddress + g_RtlpHpAllocTrackerOffsets.AllocTrackerBitmapOffset; 353 | baseAddressAddress = allocTrackerAddress + g_RtlpHpAllocTrackerOffsets.BaseAddressOffset; 354 | 355 | // 356 | // Get BaseAddress and calculate bitIndex 357 | // 358 | if ((!SUCCEEDED(g_DataSpaces->ReadVirtual( 359 | baseAddressAddress, &baseAddress, sizeof(baseAddress), nullptr))) || 360 | (!SUCCEEDED(g_DebugSymbols->ReadTypedDataVirtual( 361 | allocTrackerBitmapAddress, g_KernBase, RTL_CSPARSE_BITMAP, (PVOID)SBitmap, sizeof(SBitmap), NULL)))) 362 | { 363 | g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "[%ws::%d] Failed reading bitmap base\n", __FUNCTIONW__, __LINE__); 364 | result = S_FALSE; 365 | goto Exit; 366 | } 367 | bitIndex = 2 * (((ULONG64)Address - baseAddress) >> 20); 368 | 369 | // 370 | // First check the CommitDirectory - this has a bit for each GB of user pages. 371 | // The bit marks whether there are any pages in the GB that are heap pages. If there aren't, 372 | // no memory is allocated in the UserBitmap for these pages. 373 | // 374 | 375 | if (_bittest64( 376 | (LONG64*)(&SBitmap[g_RtlCsparseBitmapOffsets.CommitDirectoryOffset]), 377 | bitIndex >> 30)) 378 | { 379 | commitBitmap = *(ULONG64*)((ULONG64)SBitmap + g_RtlCsparseBitmapOffsets.CommitBitmapOffset); 380 | // 381 | // Divide by 8 to get the right byte to read 382 | // 383 | if (!SUCCEEDED(g_DataSpaces->ReadVirtual( 384 | commitBitmap + ((bitIndex >> 15) / 8), 385 | &commitBitmapValue, 386 | sizeof(commitBitmapValue), 387 | nullptr))) 388 | { 389 | g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "[%ws::%d] Failed reading commit bitmap\n", __FUNCTIONW__, __LINE__); 390 | result = S_FALSE; 391 | goto Exit; 392 | } 393 | // 394 | // Test bitIndex % 8 to know which bit we should read in the first byte 395 | // 396 | if (_bittest64(&commitBitmapValue, (bitIndex >> 15) % 8)) 397 | { 398 | state = RTLP_CSPARSE_BITMAP_STATE::UserBitmapValid; 399 | } 400 | else 401 | { 402 | state = RTLP_CSPARSE_BITMAP_STATE::UserBitmapInvalid; 403 | } 404 | } 405 | else 406 | { 407 | state = RTLP_CSPARSE_BITMAP_STATE::CommitBitmapInvalid; 408 | } 409 | if (state == RTLP_CSPARSE_BITMAP_STATE::UserBitmapValid) 410 | { 411 | // 412 | // We need to read the address in SBitmap->UserBitmap and then we need 413 | // userBitmap to be a pointer so the pointer arythmetic on the next line works. 414 | // This comes out very ugly, there must be a nicer way of getting it to work. 415 | // 416 | userBitmap = (ULONG64*)*(ULONG64*)((ULONG64)SBitmap + g_RtlCsparseBitmapOffsets.UserBitmapOffset); 417 | if (!SUCCEEDED(g_DataSpaces->ReadVirtual( 418 | (ULONG64)(userBitmap + (bitIndex >> 6)), 419 | &userBitmapValue, 420 | sizeof(userBitmapValue), 421 | nullptr))) 422 | { 423 | g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "[%ws::%d] Failed reading user bitmap\n", __FUNCTIONW__, __LINE__); 424 | result = S_FALSE; 425 | goto Exit; 426 | } 427 | bitmapResult = (userBitmapValue >> (bitIndex & 0x3F)) & 3; 428 | } 429 | else 430 | { 431 | bitmapResult = 0; 432 | } 433 | 434 | Exit: 435 | return bitmapResult; 436 | } 437 | 438 | /* 439 | Retrieves a pool tag for a big pool allocation from the big pages table. 440 | */ 441 | ULONG 442 | GetTagFromBigPagesTable ( 443 | _In_ ULONG64 Address, 444 | _Out_ std::string *Tag 445 | ) 446 | { 447 | PVOID poolTrackerBigPages; 448 | ULONG64 hash; 449 | UCHAR* key; 450 | HRESULT result; 451 | ULONG64 va; 452 | ULONG64 numberOfBytes; 453 | std::string tag; 454 | 455 | poolTrackerBigPages = nullptr; 456 | tag = ""; 457 | numberOfBytes = 0; 458 | 459 | poolTrackerBigPages = VirtualAlloc(NULL, g_poolTrackerBigPagesSize, MEM_COMMIT, PAGE_READWRITE); 460 | if (poolTrackerBigPages == nullptr) 461 | { 462 | goto Exit; 463 | } 464 | // 465 | // Find the tag in PoolBigPageTable. 466 | // 467 | 468 | // 469 | // Calculate the hash to find the allocation in the table. 470 | // 471 | hash = ((ULONG64)((ULONG)(Address >> 0xc))) * 0x9E5F; 472 | hash = hash ^ (hash >> 0x20); 473 | result = g_DataSpaces->ReadVirtual( 474 | g_PoolBigPageTable + ((hash & (g_PoolBigPageTableSize - 1)) * g_poolTrackerBigPagesSize), 475 | poolTrackerBigPages, 476 | g_poolTrackerBigPagesSize, 477 | nullptr); 478 | if (SUCCEEDED(result)) 479 | { 480 | // 481 | // Build the tag string and get the size of the allocation. 482 | // 483 | va = *(ULONG64*)(((ULONG64)poolTrackerBigPages) + g_PoolTrackerBigPagesOffsets.Va); 484 | if (va != Address) 485 | { 486 | // 487 | // This means that we found the wrong entry. 488 | // There is a bug that I couldn't figure out yet where 489 | // sometimes we get an off-by-one error and the correct entry 490 | // is the next one. So try the next entry and if that one is wrong 491 | // too, just bail and return empty values for this allocation. 492 | // 493 | hash += 1; 494 | result = g_DataSpaces->ReadVirtual( 495 | g_PoolBigPageTable + ((hash & (g_PoolBigPageTableSize - 1)) * g_poolTrackerBigPagesSize), 496 | poolTrackerBigPages, 497 | g_poolTrackerBigPagesSize, 498 | nullptr); 499 | if (SUCCEEDED(result)) 500 | { 501 | va = *(ULONG64*)(((ULONG64)poolTrackerBigPages) + g_PoolTrackerBigPagesOffsets.Va); 502 | if (va != Address) 503 | { 504 | goto Exit; 505 | } 506 | } 507 | } 508 | key = (UCHAR*)(((ULONG64)poolTrackerBigPages) + g_PoolTrackerBigPagesOffsets.Key); 509 | numberOfBytes = *(ULONG*)(((ULONG64)poolTrackerBigPages) + g_PoolTrackerBigPagesOffsets.NumberOfBytes); 510 | 511 | tag += *key; 512 | tag += *(key + 1); 513 | tag += *(key + 2); 514 | tag += *(key + 3); 515 | tag += '\x0'; 516 | } 517 | Exit: 518 | *Tag = tag; 519 | return numberOfBytes; 520 | } 521 | 522 | /* 523 | Prints information about a pool block to the debugger console. 524 | 525 | @param[in] Address - Base address of the block 526 | @param[in] Size - Size of the block 527 | @param[in] Allocated - Is the block allocated or free 528 | @param[in] Type - Subsegment type (Vs, Lfh, Big, Large) 529 | @param[in,opt] PoolHeaderAddress - Address to search for the pool header. 530 | Only needed for blocks smaller than 0xFF8. 531 | Needs to be sent by the caller because it can slightly change based on the 532 | type and alignment of the pool block. 533 | @param[in] Highlight - If set to TRUE function will mark the allocation with * 534 | @param[in,opt] - Optional pool tag. Block will only be printed if it has a matching pool tag. 535 | */ 536 | HRESULT PrintInfoForSingleBlock ( 537 | _In_ ULONG64 Address, 538 | _In_ ULONG Size, 539 | _In_ BOOLEAN Allocated, 540 | _In_ ALLOCATION_TYPE Type, 541 | _In_opt_ ULONG64 PoolHeaderAddress, 542 | _In_ BOOLEAN Highlight, 543 | _In_opt_ PCSTR Tag, 544 | _Out_opt_ std::list* Allocations 545 | ) 546 | { 547 | HRESULT result; 548 | ALLOC allocation; 549 | ULONG numberOfBytes; 550 | UCHAR* poolTag; 551 | ULONG64 poolHeaderAddress; 552 | PVOID poolHeader = nullptr; 553 | std::string spaces; 554 | std::string type; 555 | result = S_OK; 556 | 557 | if (Tag && !Allocated) 558 | { 559 | // 560 | // If a block isn't allocated we don't want to include it in our tag search 561 | // 562 | return S_OK; 563 | } 564 | 565 | poolHeader = VirtualAlloc(NULL, g_poolHeaderSize, MEM_COMMIT, PAGE_READWRITE); 566 | if (poolHeader == nullptr) 567 | { 568 | g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "[%ws::%d] Failed to allocate pool header\n", __FUNCTIONW__, __LINE__); 569 | result = S_FALSE; 570 | goto Exit; 571 | } 572 | // 573 | // Collect information for the block. 574 | // 575 | allocation.Address = Address; 576 | allocation.Size = Size; 577 | allocation.Allocated = Allocated; 578 | allocation.PoolTag = ""; 579 | allocation.Type = Type; 580 | 581 | allocation.PoolTag = " "; 582 | allocation.PoolTag += '\x0'; 583 | 584 | if (Size >= 0xFF8) 585 | { 586 | allocation.PoolTag = ""; 587 | numberOfBytes = GetTagFromBigPagesTable(allocation.Address, &allocation.PoolTag); 588 | } 589 | // 590 | // Get pool header 591 | // 592 | else if (PoolHeaderAddress != 0) 593 | { 594 | // 595 | // The pool header is never on the last 0x10 bytes of a page. 596 | // If the chunk header ends at 0x...ff0, the pool header will be in the beginning 597 | // of the next page. 598 | // 599 | poolHeaderAddress = PoolHeaderAddress; 600 | if (0x1000 - (poolHeaderAddress & 0xfff) == 0x10) 601 | { 602 | poolHeaderAddress += 0x10; 603 | allocation.Size -= 0x10; 604 | allocation.Address += 0x10; 605 | } 606 | if (SUCCEEDED(g_DataSpaces->ReadVirtual(poolHeaderAddress, poolHeader, g_poolHeaderSize, nullptr))) 607 | { 608 | poolTag = (UCHAR*)((ULONG64)poolHeader + g_PoolHeaderOffsets.PoolTagOffset); 609 | // 610 | // Get the Tag from the pool header and build the tag string. 611 | //If pool tag has 0xffff..., this has a pointer instead of a tag, ignore. 612 | // 613 | if ((*(poolTag + 2) != 0xff) && (*(poolTag + 3) != 0xff) && (*(poolTag + 2) != 0) && (*(poolTag + 3) != 0)) 614 | { 615 | allocation.PoolTag = ""; 616 | allocation.PoolTag += *poolTag; 617 | allocation.PoolTag += *(poolTag + 1); 618 | allocation.PoolTag += *(poolTag + 2); 619 | allocation.PoolTag += *(poolTag + 3); 620 | allocation.PoolTag += '\x0'; 621 | } 622 | } 623 | } 624 | 625 | if (Tag && Allocated) 626 | { 627 | for (int i = 0; i < strlen(Tag); i++) 628 | { 629 | if (allocation.PoolTag[i] != Tag[i]) 630 | { 631 | goto Exit; 632 | } 633 | } 634 | } 635 | 636 | spaces = ""; 637 | for (int i = allocation.Size; i < 0x10000; i *= 0x10) 638 | { 639 | spaces += " "; 640 | } 641 | spaces += '\x0'; 642 | 643 | type = ""; 644 | if (Type == ALLOCATION_TYPE::Vs) 645 | { 646 | type = "Vs"; 647 | } 648 | else if (Type == ALLOCATION_TYPE::Lfh) 649 | { 650 | type = "Lfh"; 651 | } 652 | else if (Type == ALLOCATION_TYPE::Big) 653 | { 654 | type = "Big"; 655 | } 656 | else if (Type == ALLOCATION_TYPE::Large) 657 | { 658 | type = "Large"; 659 | } 660 | else 661 | { 662 | type = "Unknown"; 663 | } 664 | type += '\x0'; 665 | 666 | g_DebugControl->Output(DEBUG_OUTPUT_DEBUGGEE, "%s 0x%p 0x%x %s %s %s %s\n", 667 | Highlight ? "*" : " ", 668 | allocation.Address, 669 | allocation.Size, 670 | spaces.c_str(), 671 | allocation.Allocated ? "(Allocated)" : "(Free) ", 672 | allocation.PoolTag.c_str(), 673 | type.c_str()); 674 | if (Allocations) 675 | { 676 | Allocations->push_back(allocation); 677 | } 678 | 679 | Exit: 680 | if (poolHeader != nullptr) 681 | { 682 | VirtualFree(poolHeader, NULL, MEM_RELEASE); 683 | } 684 | return result; 685 | } 686 | 687 | /* 688 | Parse the dump for information about a large pool allocation 689 | and add its information to the provided allocations list. 690 | */ 691 | BOOLEAN 692 | ParseLargePoolAlloc ( 693 | _In_ ULONG64 Address, 694 | _In_ BOOLEAN LargePool, 695 | _Out_opt_ std::list* Allocations, 696 | _In_opt_ PCSTR Tag 697 | ) 698 | { 699 | HRESULT result; 700 | PVOID heapVamgrRange; 701 | BOOLEAN bitmapResult; 702 | ULONG elementSizeShift; 703 | ULONG64 baseAddress; 704 | ULONG64 bitIndex; 705 | ULONG64 userBitmapValue; 706 | ULONG64 elementSizeShiftAddress; 707 | ULONG64 baseAddressAddress; 708 | ULONG64 userBitmapAddress; 709 | ULONG64 numberOfBytes; 710 | ALLOC allocation; 711 | 712 | heapVamgrRange = nullptr; 713 | bitmapResult = FALSE; 714 | numberOfBytes = 0; 715 | 716 | // 717 | // Allocate memoryfor the VaMgrRange and for the poolTrackerBigPages structures. 718 | // 719 | heapVamgrRange = VirtualAlloc(NULL, g_VamgrRangeSize, MEM_COMMIT, PAGE_READWRITE); 720 | if (heapVamgrRange == nullptr) 721 | { 722 | g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "Failed to allocate memory\n"); 723 | result = S_FALSE; 724 | goto Exit; 725 | } 726 | 727 | // 728 | // Read the values we need 729 | // 730 | elementSizeShiftAddress = g_PoolState + 731 | g_HeapManagerStateOffsets.HeapManagerOffset + 732 | g_HpHeapManagerOffsets.VaMgrOffset + 733 | g_VamgrCtxOffsets.VaSpaceOffset + 734 | g_VamgrVaspaceOffsets.VaRangeArrayOffset + 735 | g_RtlSparseArrayOffsets.ElementSizeShiftOffset; 736 | baseAddressAddress = g_PoolState + 737 | g_HeapManagerStateOffsets.HeapManagerOffset 738 | + g_HpHeapManagerOffsets.VaMgrOffset + 739 | g_VamgrCtxOffsets.VaSpaceOffset + 740 | g_VamgrVaspaceOffsets.BaseAddressOffset; 741 | userBitmapAddress = g_PoolState + 742 | g_HeapManagerStateOffsets.HeapManagerOffset + 743 | g_HpHeapManagerOffsets.VaMgrOffset + 744 | g_VamgrCtxOffsets.VaSpaceOffset + 745 | g_VamgrVaspaceOffsets.VaRangeArrayOffset + 746 | g_RtlSparseArrayOffsets.BitmapOffset + 747 | g_RtlCsparseBitmapOffsets.UserBitmapOffset; 748 | 749 | if ((!SUCCEEDED(g_DataSpaces->ReadVirtual(elementSizeShiftAddress, &elementSizeShift, sizeof(elementSizeShift), nullptr))) || 750 | (!SUCCEEDED(g_DataSpaces->ReadVirtual(baseAddressAddress, &baseAddress, sizeof(baseAddress), nullptr))) || 751 | (!SUCCEEDED(g_DataSpaces->ReadVirtual(userBitmapAddress, &userBitmapValue, sizeof(userBitmapValue), nullptr)))) 752 | { 753 | result = S_FALSE; 754 | goto Exit; 755 | } 756 | 757 | // 758 | // Calculate the bitIndex for the address and read the VaMgrRange structure for it. 759 | // 760 | bitIndex = ((Address - baseAddress) >> 20) << elementSizeShift; 761 | result = g_DataSpaces->ReadVirtual(userBitmapValue + bitIndex, 762 | heapVamgrRange, 763 | g_VamgrRangeSize, 764 | nullptr); 765 | 766 | if ((!SUCCEEDED(result)) || (heapVamgrRange == nullptr)) 767 | { 768 | goto Exit; 769 | } 770 | 771 | // 772 | // Check if the block is marked as allocated. 773 | // 774 | if (_bittest64((LONG64*)((ULONG64)heapVamgrRange + g_VamgrRangeOffsets.AllocatedOffset), 0) == 1) 775 | { 776 | bitmapResult = TRUE; 777 | } 778 | 779 | // 780 | // Find the tag in PoolBigPageTable. 781 | // 782 | 783 | numberOfBytes = GetTagFromBigPagesTable(Address, &allocation.PoolTag); 784 | // 785 | // Either the read operation failed or the entry for this block is empty. 786 | // In both cases, this doesn't seem like a valid block so don't add it. 787 | // 788 | if (numberOfBytes == 0) 789 | { 790 | goto Exit; 791 | } 792 | 793 | PrintInfoForSingleBlock(Address, 794 | numberOfBytes, 795 | bitmapResult, 796 | LargePool ? ALLOCATION_TYPE::Large : ALLOCATION_TYPE::Big, 797 | 0, 798 | FALSE, 799 | Tag, 800 | Allocations); 801 | 802 | // 803 | // Add this allocation to the list. 804 | // 805 | if (Allocations != nullptr) 806 | { 807 | allocation.Address = Address; 808 | allocation.Size = numberOfBytes; 809 | if (LargePool) 810 | { 811 | allocation.Type = ALLOCATION_TYPE::Large; 812 | } 813 | else 814 | { 815 | allocation.Type = ALLOCATION_TYPE::Big; 816 | } 817 | allocation.Allocated = bitmapResult; 818 | Allocations->push_back(allocation); 819 | } 820 | 821 | Exit: 822 | if (heapVamgrRange != nullptr) 823 | { 824 | VirtualFree(heapVamgrRange, NULL, MEM_RELEASE); 825 | } 826 | return bitmapResult; 827 | } 828 | 829 | /* 830 | Iterates over the tree of large pool allocations and prints them. 831 | Can receive a pool tag and will only print blocks with a matching tag. 832 | 833 | Allocations remained from PoolViewer and is not used. Can be ignored for now. 834 | */ 835 | VOID IterateOverLargeAllocTree ( 836 | _In_ ULONG64 CurrentEntryAddr, 837 | _In_ ULONG64 RootAddrForDecode, 838 | _In_ BOOLEAN Encoded, 839 | _In_ std::list* Allocations, 840 | _In_opt_ PCSTR Tag 841 | ) 842 | { 843 | PVOID heapLargeAlloc; 844 | ULONG64 right; 845 | ULONG64 left; 846 | ULONG64 va; 847 | heapLargeAlloc = nullptr; 848 | heapLargeAlloc = VirtualAlloc(NULL, g_HeapLargeAllocDataSize, MEM_COMMIT, PAGE_READWRITE); 849 | if (heapLargeAlloc == nullptr) 850 | { 851 | goto Exit; 852 | } 853 | 854 | if (Encoded) 855 | { 856 | // 857 | // In some cases the metadata is encoded. 858 | // If it is, we need to decode each entry by XORing its address with the tree base. 859 | // 860 | CurrentEntryAddr = CurrentEntryAddr ^ RootAddrForDecode; 861 | } 862 | 863 | // 864 | // Read the HEAP_LARGE_ALLOC_DATA structure that root points to. 865 | // If points to the TreeNode field of the structure so we have to do a sort 866 | // of CONTAINING_RECORD. 867 | // 868 | if (!SUCCEEDED(g_DataSpaces->ReadVirtual(CurrentEntryAddr, heapLargeAlloc, g_HeapLargeAllocDataSize, nullptr))) 869 | { 870 | goto Exit; 871 | } 872 | // 873 | // Get information about current entry and add to Allocations list 874 | // 875 | va = *(ULONG64*)((ULONG64)heapLargeAlloc + g_HeapLargeAllocDataOffsets.VirtualAddressOffset); 876 | ParseLargePoolAlloc(va, true, Allocations, Tag); 877 | 878 | // 879 | // Get children 880 | // 881 | right = *(ULONG64*)((ULONG64)heapLargeAlloc + g_HeapLargeAllocDataOffsets.TreeNodeOffset + g_RtlBalancedNodeOffsets.RightOffset); 882 | left = *(ULONG64*)((ULONG64)heapLargeAlloc + g_HeapLargeAllocDataOffsets.TreeNodeOffset + g_RtlBalancedNodeOffsets.LeftOffset); 883 | 884 | if (left != 0) 885 | { 886 | IterateOverLargeAllocTree(left, RootAddrForDecode, Encoded, Allocations, Tag); 887 | } 888 | if (right != 0) 889 | { 890 | IterateOverLargeAllocTree(right, RootAddrForDecode, Encoded, Allocations, Tag); 891 | } 892 | Exit: 893 | if (heapLargeAlloc != nullptr) 894 | { 895 | VirtualFree(heapLargeAlloc, NULL, MEM_RELEASE); 896 | } 897 | } 898 | 899 | /* 900 | Finds all large pool blocks and prints them. 901 | Can receive a pool tag and will only print blocks with a matching tag. 902 | 903 | Allocations remained from PoolViewer and is not used. Can be ignored for now. 904 | */ 905 | VOID ParseLargePages ( 906 | _In_ ULONG64 Heap, 907 | _In_ std::list* Allocations, 908 | _In_opt_ PCSTR Tag 909 | ) 910 | { 911 | PVOID largeAllocMetadata; 912 | ULONG64 largeAllocMedatadaAddr; 913 | ULONG64 heapLargeAllocAddr; 914 | ULONG64 root; 915 | CHAR encoded; 916 | largeAllocMetadata = nullptr; 917 | // 918 | // Read LargeAllocMetadata field from heap. 919 | // It contains an RTL_RB_TREE so allocate memory for that. 920 | // 921 | largeAllocMetadata = VirtualAlloc(NULL, g_RtlRbTreeSize, MEM_COMMIT, PAGE_READWRITE); 922 | if (largeAllocMetadata == nullptr) 923 | { 924 | goto Exit; 925 | } 926 | largeAllocMedatadaAddr = Heap + g_SegmentHeapOffsets.LargeAllocMetadataOffset; 927 | if (!SUCCEEDED(g_DataSpaces->ReadVirtual(largeAllocMedatadaAddr, largeAllocMetadata, g_RtlRbTreeSize, nullptr))) 928 | { 929 | goto Exit; 930 | } 931 | // 932 | // If root is empty that means there are no large allocations in this heap. 933 | // 934 | root = *(ULONG64*)((ULONG64)largeAllocMetadata + g_RtlRbTreeOffsets.RootOffset); 935 | if (root == 0) 936 | { 937 | goto Exit; 938 | } 939 | 940 | // 941 | // Check if the tree is encoded 942 | // 943 | heapLargeAllocAddr = root - g_HeapLargeAllocDataOffsets.TreeNodeOffset; 944 | encoded = _bittest64((LONG64*)((ULONG64)largeAllocMetadata + g_RtlRbTreeOffsets.EncodedOffset), 0); 945 | IterateOverLargeAllocTree(heapLargeAllocAddr, largeAllocMedatadaAddr, encoded, Allocations, Tag); 946 | 947 | Exit: 948 | if (largeAllocMetadata != nullptr) 949 | { 950 | VirtualFree(largeAllocMetadata, NULL, MEM_RELEASE); 951 | } 952 | } 953 | 954 | /* 955 | Parse the supplied LFH subsegment to get all the pool blocks in it 956 | and print them as needed. 957 | 958 | @param[in] LfhSubsegment - Beginning of an LFH subsegment 959 | @param[in] SubsegmentEnd - End of an LFH subsegment 960 | @param[in] Allocations - Left over from PoolViewer, can be ignored for now. 961 | @param[in,opt] Address - A specific address to search for. 962 | If supplied, only blocks in the page containing Address will be printed 963 | and the block containing the requested address will be marked with *. 964 | @param[in,opt] Tag - Optional pool tag. If supplied, only blocks with a matching 965 | pool tag will be printed. 966 | */ 967 | HRESULT 968 | FindPoolBlocksInLfhSubsegment ( 969 | _In_ PVOID LfhSubsegment, 970 | _In_ PVOID SubsegmentEnd, 971 | _In_opt_ std::list* Allocations, 972 | _In_opt_ PVOID Address, 973 | _In_opt_ PCSTR Tag 974 | ) 975 | { 976 | HRESULT result; 977 | ULONG encodedData; 978 | ULONG lfhSubsegmentEncodedData; 979 | USHORT lfhBlockSize; 980 | USHORT lfhFirstEntryOffset; 981 | ULONG64 currentAddress; 982 | UCHAR bitmapResult; 983 | ULONG offsetInSubseg; 984 | ULONG indexInBitmap; 985 | PVOID bitmap; 986 | ALLOC allocation; 987 | ULONG64 encodedDataAddress; 988 | ULONG64 validBase; 989 | ULONG validSize; 990 | BOOLEAN highlight; 991 | 992 | bitmap = nullptr; 993 | result = S_OK; 994 | 995 | // 996 | // It is possible that only some pages in the subsegment are used, 997 | // so check that actual used size and don't try to access invalid pages. 998 | // 999 | result = g_DataSpaces->GetValidRegionVirtual( 1000 | (ULONG64)LfhSubsegment, 1001 | (ULONG)((ULONG64)SubsegmentEnd - (ULONG64)LfhSubsegment), 1002 | &validBase, 1003 | &validSize); 1004 | 1005 | if ((ULONG64)LfhSubsegment != validBase) 1006 | { 1007 | result = S_FALSE; 1008 | goto Exit; 1009 | } 1010 | 1011 | if ((ULONG64)SubsegmentEnd - (ULONG64)LfhSubsegment > validSize) 1012 | { 1013 | SubsegmentEnd = (PVOID)(validBase + validSize); 1014 | } 1015 | 1016 | // 1017 | // Read the HEAP_PAGE_SUBSEGMENT encodedData. 1018 | // 1019 | encodedDataAddress = (ULONG64)LfhSubsegment + g_lfhSubsegmentOffsets.BlockOffsets + g_lfhSubsegmentEncodedEffsets.EncodedData; 1020 | if (!SUCCEEDED(ReadData(encodedDataAddress, sizeof(encodedData), (PVOID)&encodedData))) 1021 | { 1022 | result = S_FALSE; 1023 | goto Exit; 1024 | } 1025 | 1026 | // 1027 | // Decode encodedData and get BlockSize and FirstEntryOffset 1028 | // that we need to parse the subsegment. 1029 | // 1030 | lfhSubsegmentEncodedData = encodedData ^ (ULONG)g_LfhKey ^ ((ULONG)(ULONG_PTR)LfhSubsegment >> 0xc); 1031 | lfhBlockSize = (lfhSubsegmentEncodedData + g_lfhSubsegmentEncodedEffsets.BlockSize) & 0x0000ffff; 1032 | lfhFirstEntryOffset = *((USHORT*)((ULONG64)&lfhSubsegmentEncodedData + g_lfhSubsegmentEncodedEffsets.FirstBlockOffset)) & 0xffff; 1033 | 1034 | // 1035 | // We have a start address and the size of the allocations, we can 1036 | // iterate over them until we find the start block of our allocation 1037 | // 1038 | currentAddress = (ULONG64)LfhSubsegment + lfhFirstEntryOffset; 1039 | bitmap = VirtualAlloc(NULL, lfhFirstEntryOffset - g_lfhSubsegmentOffsets.BlockBitmap, MEM_COMMIT, PAGE_READWRITE); 1040 | if (bitmap == nullptr) 1041 | { 1042 | result = S_FALSE; 1043 | goto Exit; 1044 | } 1045 | 1046 | // 1047 | // Read the bitmap here so we don't need to read a part of it for every allocation 1048 | // 1049 | if (!SUCCEEDED(ReadData((ULONG64)LfhSubsegment + g_lfhSubsegmentOffsets.BlockBitmap, 1050 | lfhFirstEntryOffset - g_lfhSubsegmentOffsets.BlockBitmap, 1051 | bitmap))) 1052 | { 1053 | result = S_FALSE; 1054 | goto Exit; 1055 | } 1056 | 1057 | while (currentAddress + lfhBlockSize <= (ULONG_PTR)SubsegmentEnd) 1058 | { 1059 | highlight = FALSE; 1060 | // 1061 | // It looks like blocks actually don't cross page boundaries, not really sure why 1062 | // 1063 | if (0x1000 - (currentAddress & 0xfff) >= lfhBlockSize) 1064 | { 1065 | if ((!Address) || (currentAddress > (ULONG64)ALIGN_DOWN_POINTER_BY(Address, 0x1000))) 1066 | { 1067 | // 1068 | // The LFH subsegment contains a bitmap where each block is marked by 2 bits stating its allocation status. 1069 | // To check that bit: 1070 | // 1. Get the allocation offset in the subsegment (remember to include FirstEntryOffset) 1071 | // 2. Get the index by dividing the offset with the block size and multiplying by 2 (2 bits for each block) 1072 | // 3. Read the correct byte from the bitmap (byte = index / 8) and check the correct bit (index % 8) 1073 | // 1074 | offsetInSubseg = currentAddress - lfhFirstEntryOffset - (ULONG64)LfhSubsegment; 1075 | indexInBitmap = (offsetInSubseg / lfhBlockSize) * 2; 1076 | bitmapResult = *(CHAR*)((ULONG64)bitmap + (indexInBitmap / 8)); 1077 | 1078 | if (Address && (currentAddress > (ULONG64)ALIGN_UP_POINTER_BY(Address, 0x1000))) 1079 | { 1080 | break; 1081 | } 1082 | if (Address) 1083 | { 1084 | if ((currentAddress <= (ULONG64)Address) && 1085 | (currentAddress + lfhBlockSize > (ULONG64)Address)) 1086 | { 1087 | highlight = TRUE; 1088 | } 1089 | } 1090 | PrintInfoForSingleBlock( 1091 | currentAddress, 1092 | lfhBlockSize, 1093 | _bittest64((LONG64*)(&bitmapResult), indexInBitmap % 8), 1094 | ALLOCATION_TYPE::Lfh, 1095 | currentAddress, 1096 | highlight, 1097 | Address ? 0 : Tag, 1098 | Allocations); 1099 | } 1100 | } 1101 | // 1102 | // Advance to the next block. 1103 | // 1104 | currentAddress = currentAddress + lfhBlockSize; 1105 | } 1106 | Exit: 1107 | if (bitmap != nullptr) 1108 | { 1109 | VirtualFree(bitmap, NULL, MEM_RELEASE); 1110 | } 1111 | return result; 1112 | } 1113 | 1114 | /* 1115 | Prints information for a VS pool block, optionally filtered by tag. 1116 | 1117 | @param[in] ChunkHeader - Address of the chunk header belonging to the pool block 1118 | @param[in] BlockSize - Size of the pool block 1119 | @param[in] Allocated- Is the block allocated or free 1120 | @param[in,opt] Tag - Optional pool tag. If supplied, only blocks with a matching 1121 | pool tag will be printed. 1122 | */ 1123 | VOID 1124 | PrintInfoForAllVsAllocs ( 1125 | _In_ ULONG64 ChunkHeader, 1126 | _In_ ULONG BlockSize, 1127 | _In_ BOOLEAN Allocated, 1128 | _In_opt_ PCSTR Tag, 1129 | _In_opt_ std::list* Allocations 1130 | ) 1131 | { 1132 | ULONG64 currentAddress; 1133 | ULONG currentSize; 1134 | 1135 | if (!Allocated) 1136 | { 1137 | return; 1138 | } 1139 | if (BlockSize > 0x1000) 1140 | { 1141 | // 1142 | // No pool header 1143 | // 1144 | currentAddress = ChunkHeader + g_vsChunkHeaderSize; 1145 | currentSize = BlockSize - g_vsChunkHeaderSize; 1146 | } 1147 | else if ((0x1000 - (ChunkHeader & 0xfff) >= BlockSize) || 1148 | (0x1000 - (ChunkHeader & 0xfff) <= (g_vsChunkHeaderSize + g_poolHeaderSize))) 1149 | { 1150 | // 1151 | // Allocation data doesn't cross page boundaries, so if UnsafeSize is 1152 | // larger than the space that's left until the end of the page, this is 1153 | // not an actual allocation so move on to the next one. 1154 | // But there are blocks where the chunk header + pool header are right 1155 | // before page break and the data is immediately after and those seem fine. 1156 | // 1157 | currentAddress = ChunkHeader + g_vsChunkHeaderSize + g_poolHeaderSize; 1158 | currentSize = BlockSize - g_vsChunkHeaderSize - g_poolHeaderSize; 1159 | } 1160 | else 1161 | { 1162 | return; 1163 | } 1164 | PrintInfoForSingleBlock( 1165 | currentAddress, 1166 | currentSize, 1167 | Allocated, 1168 | ALLOCATION_TYPE::Vs, 1169 | ChunkHeader + g_vsChunkHeaderSize, 1170 | FALSE, 1171 | Tag, 1172 | Allocations); 1173 | } 1174 | 1175 | /* 1176 | Prints information for a VS pool block only if matching the requested address. 1177 | 1178 | @param[in] ChunkHeader - Address of the chunk header belonging to the pool block 1179 | @param[in] BlockSize - Size of the pool block 1180 | @param[in] Allocated- Is the block allocated or free 1181 | @param[in] Address - Address requested by the user. 1182 | Pool block will only be printed if it's in the page containing Address. 1183 | The pool block that Address belongs to will be marked with *. 1184 | */ 1185 | VOID 1186 | PrintInfoForVSAddress ( 1187 | _In_ ULONG64 ChunkHeader, 1188 | _In_ ULONG BlockSize, 1189 | _In_ BOOLEAN Allocated, 1190 | _In_ ULONG64 Address, 1191 | _In_opt_ std::list* Allocations 1192 | ) 1193 | { 1194 | BOOLEAN highlight; 1195 | 1196 | // 1197 | // Print all pool blocks in the page the allocation is in 1198 | // 1199 | if (((ChunkHeader + g_vsChunkHeaderSize + g_poolHeaderSize) & ~0xfff) == (Address & ~0xfff)) 1200 | { 1201 | highlight = FALSE; 1202 | if ((ChunkHeader <= Address) && 1203 | (ChunkHeader + BlockSize > Address)) 1204 | { 1205 | highlight = TRUE; 1206 | } 1207 | PrintInfoForSingleBlock( 1208 | ChunkHeader + g_vsChunkHeaderSize + g_poolHeaderSize, 1209 | BlockSize - g_vsChunkHeaderSize - g_poolHeaderSize, 1210 | Allocated, 1211 | ALLOCATION_TYPE::Vs, 1212 | ChunkHeader + g_vsChunkHeaderSize, 1213 | highlight, 1214 | 0, 1215 | Allocations); 1216 | } 1217 | } 1218 | 1219 | /* 1220 | Prints information for pool blocks found in a VS subsegment. 1221 | 1222 | @param[in] StartAddress - Start address of the subsegment 1223 | @param[in] EndAddress - End address of the subsegment 1224 | @param[in,opt] Address- Optional address to search for 1225 | @param[in,opt] Tag - Optional pool tag. If supplied, only blocks with a matching 1226 | pool tag will be printed. 1227 | */ 1228 | VOID 1229 | PrintInfoForVsSubsegment ( 1230 | _In_ ULONG64 StartAddress, 1231 | _In_ ULONG64 EndAddress, 1232 | _In_opt_ PVOID Address, 1233 | _In_opt_ PCSTR Tag, 1234 | _In_opt_ std::list* Allocations 1235 | ) 1236 | { 1237 | ALLOC allocation; 1238 | ULONG64 chunkHeaderKernelAddress; 1239 | PVOID chunkHeader = nullptr; 1240 | ULONG64 vsChunkHeaderSizes; 1241 | ULONG64 headerBits; 1242 | ULONG unsafeSize; 1243 | BYTE allocated; 1244 | ULONG64 currentAddress; 1245 | ULONG currentSize; 1246 | 1247 | ULONG64 validBase; 1248 | ULONG validSize; 1249 | 1250 | chunkHeaderKernelAddress = StartAddress; 1251 | currentAddress = 0; 1252 | currentSize = 0; 1253 | 1254 | chunkHeader = VirtualAlloc(NULL, g_vsChunkHeaderSize, MEM_COMMIT, PAGE_READWRITE); 1255 | if (chunkHeader == nullptr) 1256 | { 1257 | goto Exit; 1258 | } 1259 | // 1260 | // Walk the pool chunks in the subsegment and get information for each one. 1261 | // 1262 | do 1263 | { 1264 | // 1265 | // Read the current chunk header. 1266 | // 1267 | if (!SUCCEEDED(ReadData((ULONG64)chunkHeaderKernelAddress, g_vsChunkHeaderSize, chunkHeader))) 1268 | { 1269 | goto Exit; 1270 | } 1271 | 1272 | // 1273 | // Get the VS_CHUNK_HEADER_SIZES from the chunk header. 1274 | // 1275 | vsChunkHeaderSizes = *(ULONG64*)((ULONG64)chunkHeader + g_VsChunkHeaderOffsets.SizesOffset); 1276 | headerBits = *(ULONG64*)(&vsChunkHeaderSizes + g_VsChunkHeaderSizeOffsets.HeaderBitsOffset); 1277 | if (headerBits == 0) 1278 | { 1279 | goto Exit; 1280 | } 1281 | 1282 | // 1283 | // Decode the VS_CHUNK_HEADER_SIZES structure and get UnsafeSize 1284 | // and Allocated fields from it. 1285 | // 1286 | headerBits ^= (ULONG_PTR)chunkHeaderKernelAddress ^ g_HeapKey; 1287 | unsafeSize = (USHORT)((*(ULONG64*)((ULONG64)&headerBits 1288 | + g_VsChunkHeaderSizeOffsets.UnsafeSizeOffset)) >> 16) * 0x10; /* bit position: 16 */ 1289 | allocated = (BYTE)((*(ULONG64*)((ULONG64)&headerBits 1290 | + g_VsChunkHeaderSizeOffsets.AllocatedOffset)) >> 16); /* bit position: 16 */ 1291 | 1292 | if (Address) 1293 | { 1294 | PrintInfoForVSAddress(chunkHeaderKernelAddress, unsafeSize, allocated, (ULONG64)Address, Allocations); 1295 | } 1296 | else 1297 | { 1298 | PrintInfoForAllVsAllocs(chunkHeaderKernelAddress, unsafeSize, allocated, Tag, Allocations); 1299 | } 1300 | 1301 | Next: 1302 | // 1303 | // Advance to the next chunk header. 1304 | // 1305 | chunkHeaderKernelAddress = chunkHeaderKernelAddress + unsafeSize; 1306 | } while ((ULONG_PTR)chunkHeaderKernelAddress < EndAddress); 1307 | 1308 | Exit: 1309 | if (chunkHeader != nullptr) 1310 | { 1311 | VirtualFree(chunkHeader, NULL, MEM_RELEASE); 1312 | } 1313 | return; 1314 | } 1315 | 1316 | /* 1317 | Parses the supplied VS subsegment to get all the pool blocks in it 1318 | and add them to the Allocations list. 1319 | */ 1320 | HRESULT 1321 | FindPoolBlocksInVsSubsegment ( 1322 | _In_ PVOID VsSubsegment, 1323 | _In_ PVOID SubsegmentEnd, 1324 | _In_opt_ std::list* Allocations, 1325 | _In_opt_ PVOID Address, 1326 | _In_opt_ PCSTR Tag 1327 | ) 1328 | { 1329 | HRESULT result; 1330 | USHORT signature; 1331 | USHORT size; 1332 | PVOID chunkHeader; 1333 | ULONG64 chunkHeaderKernelAddress; 1334 | PVOID poolHeader; 1335 | ALLOC allocation; 1336 | 1337 | ULONG64 validBase; 1338 | ULONG validSize; 1339 | 1340 | chunkHeader = nullptr; 1341 | poolHeader = nullptr; 1342 | result = S_OK; 1343 | 1344 | // 1345 | // First validate that this really is a VS subsegment by checking if 1346 | // vsSubsegment->Signature ^ 0x2BED == vsSubsegment.Size 1347 | // 1348 | result = g_DataSpaces->GetValidRegionVirtual((ULONG64)VsSubsegment, g_vsSubsegmentSize, &validBase, &validSize); 1349 | if ((!SUCCEEDED(result)) || (validBase != (ULONG64)VsSubsegment)) 1350 | { 1351 | g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "[%ws::%d] Invalid region at subsegment 0x%p\n", __FUNCTIONW__, __LINE__, VsSubsegment); 1352 | result = S_FALSE; 1353 | goto Exit; 1354 | } 1355 | 1356 | if ((!SUCCEEDED(g_DataSpaces->ReadVirtual((ULONG64)VsSubsegment + g_VsSubsegmentOffsets.SignatureOffset, &signature, sizeof(signature), nullptr))) || 1357 | (!SUCCEEDED(g_DataSpaces->ReadVirtual((ULONG64)VsSubsegment + g_VsSubsegmentOffsets.SizeOffset, &size, sizeof(size), nullptr)))) 1358 | { 1359 | g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "[%ws::%d] Failed to read data\n", __FUNCTIONW__, __LINE__); 1360 | result = S_FALSE; 1361 | goto Exit; 1362 | } 1363 | 1364 | if ((signature ^ 0x2BED) != size) 1365 | { 1366 | g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "[%ws::%d] Incorrect signature at subsegment 0x%p\n", __FUNCTIONW__, __LINE__, VsSubsegment); 1367 | result = S_FALSE; 1368 | goto Exit; 1369 | } 1370 | 1371 | // 1372 | // Get the VS chunk header that is right after the HEAP_VS_SUBSEGMENT structure. 1373 | // 1374 | chunkHeaderKernelAddress = (ULONG64)VsSubsegment + g_vsSubsegmentSize; 1375 | chunkHeaderKernelAddress = (ULONG64)ALIGN_UP_POINTER_BY((PVOID)chunkHeaderKernelAddress, 0x10); 1376 | 1377 | PrintInfoForVsSubsegment(chunkHeaderKernelAddress, (ULONG_PTR)SubsegmentEnd - 0x1000, Address, Tag, Allocations); 1378 | 1379 | Exit: 1380 | return result; 1381 | } 1382 | 1383 | /* 1384 | Parses a pool descriptor and prints the requested pool blocks in it. 1385 | Finds the type for the descriptor and calls an appropriate function 1386 | for the type. 1387 | 1388 | @param[in] SegmentAddress - Kernel address of the HEAP_PAGE_SEGMENT being parsed 1389 | @param[in] HeapPageSeg - Local buffer containing the data from SegmentAddress 1390 | HEAP_PAGE_SEGMENT is a large structure to we read it once and pass the local copy around 1391 | for each descriptor. 1392 | @param[in] CurrentDescriptorIndex - Index of the descriptor to parse 1393 | @param[in] UnitShift - Number of shifts needed to find the basic unit handled by this segment 1394 | Can be 0xC of 0x10 depending on segment type. 1395 | @param[in,opt] Allocations - Left over from PoolViewer, can be ignored for now 1396 | @param[in,opt] Address- Optional address to search for 1397 | @param[in,opt] Tag - Optional pool tag. If supplied, only blocks with a matching 1398 | pool tag will be printed. 1399 | */ 1400 | ULONG 1401 | GetDataForDescriptor ( 1402 | _In_ ULONG64 SegmentAddress, 1403 | _In_ PVOID HeapPageSeg, 1404 | _In_ ULONG CurrentDescriptorIndex, 1405 | _In_ UCHAR UnitShift, 1406 | _In_opt_ std::list* Allocations, 1407 | _In_opt_ PVOID Address, 1408 | _In_opt_ PCSTR Tag 1409 | ) 1410 | { 1411 | HRESULT result; 1412 | UCHAR rangeFlags; 1413 | BOOLEAN allocated; 1414 | ULONG64 descArrayEntry; 1415 | UCHAR unitSize; 1416 | ULONG64 segmentStart; 1417 | ULONG64 segmentEnd; 1418 | 1419 | result = S_OK; 1420 | // 1421 | // Get the current DescArray entry and get the UnitSize and RangeFlags fields from it. 1422 | // 1423 | descArrayEntry = (ULONG64)HeapPageSeg + g_PageSegmentOffsets.DescArrayOffset + (g_RangeDescriptorSize * CurrentDescriptorIndex); 1424 | unitSize = *(UCHAR*)(descArrayEntry + g_PageRangeDescriptorOffsets.UnitSizeOffset); 1425 | if (unitSize == 0) 1426 | { 1427 | g_DebugControl->Output(DEBUG_OUTPUT_DEBUGGEE, "Unit size is 0!\n"); 1428 | return 1; 1429 | } 1430 | rangeFlags = *(UCHAR*)(descArrayEntry + g_PageRangeDescriptorOffsets.RangeFlagsOffset); 1431 | 1432 | // 1433 | // Calculate the start and end addresses of this subsegment. 1434 | // 1435 | segmentStart = (ULONG_PTR)SegmentAddress + ((ULONG64)CurrentDescriptorIndex * 1 << UnitShift); 1436 | segmentEnd = (ULONG_PTR)SegmentAddress + (((ULONG64)CurrentDescriptorIndex + unitSize) * 1 << UnitShift); 1437 | 1438 | // 1439 | // RangeFlags 0 / 2 = area not currently used by any allocations (bottom bit not set) 1440 | // If we requested information for a specific address and it's in the middle of a subsegment, 1441 | // call this function again with the index of the beginning of the subsegment. 1442 | // If we do not care about a specific address we will iterate all subsegments anyway 1443 | // so we'll ignore descriptors that are in the middle of a subsegment. 1444 | // 1445 | if (Address && (rangeFlags == 1)) 1446 | { 1447 | GetDataForDescriptor(SegmentAddress, 1448 | HeapPageSeg, 1449 | CurrentDescriptorIndex - unitSize, 1450 | UnitShift, 1451 | Allocations, 1452 | Address, 1453 | Tag); 1454 | } 1455 | else if ((rangeFlags != 0) && (rangeFlags != 2)) 1456 | { 1457 | // 1458 | // Check if descriptor is valid by comparing the signature with the magic signature value 1459 | // 1460 | if (*(ULONG*)(descArrayEntry + g_PageRangeDescriptorOffsets.TreeSignatureOffset) != 0xccddccdd) 1461 | { 1462 | g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "[%ws::%d] Invalid descriptor signature\n", __FUNCTIONW__, __LINE__); 1463 | return 1; 1464 | } 1465 | if (rangeFlags == 3) 1466 | { 1467 | // 1468 | // Large pool 1469 | // 1470 | allocated = ParseLargePoolAlloc( 1471 | segmentStart, 1472 | false, 1473 | Allocations, 1474 | Tag); 1475 | } 1476 | else if (rangeFlags == 0xf) 1477 | { 1478 | // 1479 | // VS subsegment 1480 | // 1481 | result = FindPoolBlocksInVsSubsegment( 1482 | (PVOID)segmentStart, 1483 | (PVOID)segmentEnd, 1484 | Allocations, 1485 | Address, 1486 | Tag); 1487 | } 1488 | else if (rangeFlags == 0xb) 1489 | { 1490 | // 1491 | // Lfh subsegment 1492 | // 1493 | result = FindPoolBlocksInLfhSubsegment( 1494 | (PVOID)segmentStart, 1495 | (PVOID)segmentEnd, 1496 | Allocations, 1497 | Address, 1498 | Tag); 1499 | } 1500 | } 1501 | return unitSize; 1502 | } 1503 | 1504 | /* 1505 | Get all the allocations inside a HEAP_SEG_CONTEXT strcture. 1506 | */ 1507 | VOID 1508 | ParseSegContext ( 1509 | _In_ ULONG64 SegContext, 1510 | _In_ std::list* Allocations, 1511 | _In_opt_ PCSTR Tag 1512 | ) 1513 | { 1514 | LIST_ENTRY listHead; 1515 | PLIST_ENTRY nextEntry; 1516 | LIST_ENTRY listEntry; 1517 | PVOID heapPageSeg; 1518 | CHAR firstDescriptorIndex; 1519 | UCHAR unitSize; 1520 | UCHAR unitShift; 1521 | ULONG64 segment; 1522 | 1523 | heapPageSeg = nullptr; 1524 | 1525 | // 1526 | // Read SegmentListHead, UnitShift and FirstDescriptorIndex fields from the segment. 1527 | // 1528 | if (!SUCCEEDED(g_DataSpaces->ReadVirtual(SegContext + g_SegContextOffsets.SegmentListHeadOffset, &listHead, sizeof(listHead), nullptr)) || 1529 | !SUCCEEDED(g_DataSpaces->ReadVirtual(SegContext + g_SegContextOffsets.UnitShiftOffset, &unitShift, sizeof(unitShift), nullptr)) || 1530 | !SUCCEEDED(g_DataSpaces->ReadVirtual(SegContext + g_SegContextOffsets.FirstDescriptorIndexOffset, &firstDescriptorIndex, sizeof(firstDescriptorIndex), nullptr))) 1531 | { 1532 | goto Exit; 1533 | } 1534 | nextEntry = listHead.Flink; 1535 | listEntry.Flink = 0; /* This value will be overwritten */ 1536 | 1537 | // 1538 | // Allocate memory for the HEAP_PAGE_SEGMENT. 1539 | // 1540 | heapPageSeg = VirtualAlloc(NULL, g_HeapPageSegSize, MEM_COMMIT, PAGE_READWRITE); 1541 | if (heapPageSeg == nullptr) 1542 | { 1543 | goto Exit; 1544 | } 1545 | 1546 | // 1547 | // Iterate over all the segments in this SegContext and get the pool blocks in them. 1548 | // 1549 | while (listEntry.Flink != listHead.Flink) 1550 | { 1551 | if ((ULONG64)nextEntry == SegContext + g_SegContextOffsets.SegmentListHeadOffset) 1552 | { 1553 | // 1554 | // This means we hit the end of the list, no need for an error 1555 | // 1556 | goto Next; 1557 | } 1558 | 1559 | // 1560 | // We want to read the HEAP_PAGE_SEGMENT structures linked in this segContext 1561 | // We basically need get the CONTAINING_RECORD for each list entry 1562 | // 1563 | segment = (ULONG64)nextEntry - g_PageSegmentOffsets.ListEntryOffset; 1564 | if (!SUCCEEDED(g_DataSpaces->ReadVirtual(segment, heapPageSeg, g_HeapPageSegSize, nullptr))) 1565 | { 1566 | goto Next; 1567 | } 1568 | 1569 | // 1570 | // Each HEAP_PAGE_SEGMENT has 256 HEAP_PAGE_RANGE_DESCRIPTORs. 1571 | // Walk over them and handle each separately. 1572 | // 1573 | for (int i = firstDescriptorIndex; i < 256; i++) 1574 | { 1575 | unitSize = GetDataForDescriptor(segment, heapPageSeg, i, unitShift, Allocations, 0, Tag); 1576 | i += unitSize - 1; 1577 | } 1578 | Next: 1579 | // 1580 | // Move to the next HEAP_PAGE_SUBSEGMENT in the list. 1581 | // 1582 | if (!SUCCEEDED(g_DataSpaces->ReadVirtual((ULONG64)nextEntry, &listEntry, sizeof(listEntry), nullptr))) 1583 | { 1584 | goto Exit; 1585 | } 1586 | nextEntry = listEntry.Flink; 1587 | } 1588 | Exit: 1589 | if (heapPageSeg != nullptr) 1590 | { 1591 | VirtualFree(heapPageSeg, NULL, MEM_RELEASE); 1592 | } 1593 | return; 1594 | } 1595 | 1596 | /* 1597 | Parse a SEGMENT_HEAP structure and get all the pool blocks in it. 1598 | */ 1599 | VOID 1600 | GetInformationForHeap ( 1601 | _In_ ULONG64 HeapAddress, 1602 | _In_ std::list* Allocations, 1603 | _In_opt_ PCSTR Tag 1604 | ) 1605 | { 1606 | // 1607 | // Parse SegContexts[0]. 1608 | // 1609 | ParseSegContext( 1610 | HeapAddress + g_SegmentHeapOffsets.SegContextsOffsets, 1611 | Allocations, 1612 | Tag); 1613 | 1614 | // 1615 | // Parse SegContexts[1]. 1616 | // 1617 | ParseSegContext( 1618 | HeapAddress + g_SegmentHeapOffsets.SegContextsOffsets + g_SegContextSize, 1619 | Allocations, 1620 | Tag); 1621 | 1622 | // 1623 | // Parse LargeAllocMetadata 1624 | // 1625 | ParseLargePages(HeapAddress, Allocations, Tag); 1626 | } 1627 | 1628 | /* 1629 | Find all SEGMENT_HEAP structures and parse them to get the pool blocks they manage. 1630 | */ 1631 | std::list 1632 | GetAllHeaps ( 1633 | _In_opt_ PCSTR Tag, 1634 | _In_opt_ POOL_VIEW_FLAGS Flags 1635 | ) 1636 | { 1637 | HRESULT result; 1638 | ULONG numberOfPools; 1639 | ULONG64 heaps[4]; 1640 | ULONG64 specialHeaps[3]; 1641 | HEAP heap; 1642 | POOL_TYPE heapNumberToPoolType[4]; 1643 | 1644 | // 1645 | // Get the number of pools. 1646 | // 1647 | result = g_DataSpaces->ReadVirtual( 1648 | g_PoolState + g_HeapManagerStateOffsets.NumberOfPoolsOffset, 1649 | &numberOfPools, 1650 | sizeof(numberOfPools), 1651 | nullptr); 1652 | if (!SUCCEEDED(result)) 1653 | { 1654 | goto Exit; 1655 | } 1656 | 1657 | // 1658 | // Iterate over all pool nodes, print all heaps (up to 4) in each 1659 | // 1660 | heapNumberToPoolType[0] = NonPagedPool; 1661 | heapNumberToPoolType[1] = NonPagedPoolNx; 1662 | heapNumberToPoolType[2] = PagedPool; 1663 | heapNumberToPoolType[3] = PagedPool; 1664 | 1665 | // 1666 | // Iterate over all pools and get the heaps from each. 1667 | // 1668 | for (ULONG j = 0; j < numberOfPools; j++) 1669 | { 1670 | // 1671 | // Every POOL_NODE structure potentially contains 4 heaps. 1672 | // First zero out our heaps array to not carry any information 1673 | // over from a previous iteration. 1674 | // 1675 | for (int i = 0; i < 4; i++) 1676 | { 1677 | heaps[i] = 0; 1678 | } 1679 | 1680 | // 1681 | // The pool nodes are embedded inside PoolState. 1682 | // Get to the offset of the current one we are looking at and read the addresses of the 4 heaps. 1683 | // 1684 | if (!SUCCEEDED(g_DataSpaces->ReadVirtual( 1685 | g_PoolState + g_HeapManagerStateOffsets.PoolNodeOffset + (g_PoolNodeSize * j) + g_PoolNodeOffsets.HeapsOffset, 1686 | &heaps, 1687 | sizeof(heaps), 1688 | nullptr))) 1689 | { 1690 | goto Exit; 1691 | } 1692 | // 1693 | // Get basic information for each heap and parse it to 1694 | // get information about the pool blocks it manages. 1695 | // 1696 | for (int i = 0; i < 4; i++) 1697 | { 1698 | // 1699 | // This ugly if statement basically means: 1700 | // If we requested only a specific pool type and this is the wrong pool type, skip this 1701 | // 1702 | if (((Flags.OnlyPaged) && (heapNumberToPoolType[i] != PagedPool)) || 1703 | (Flags.OnlyNonPaged) && ( 1704 | (heapNumberToPoolType[i] != NonPagedPool) && heapNumberToPoolType[i] != NonPagedPoolNx)) 1705 | { 1706 | continue; 1707 | } 1708 | if (i == 0) 1709 | { 1710 | g_DebugControl->Output(DEBUG_OUTPUT_DEBUGGEE, "*** Printing Data for NonPagedPool ***\n\n"); 1711 | } 1712 | else if (i == 1) 1713 | { 1714 | g_DebugControl->Output(DEBUG_OUTPUT_DEBUGGEE, "*** Printing Data for NonPagedPoolNx ***\n\n"); 1715 | } 1716 | else if (i == 2) 1717 | { 1718 | g_DebugControl->Output(DEBUG_OUTPUT_DEBUGGEE, "*** Printing Data for PagedPool ***\n\n"); 1719 | } 1720 | else if (i == 3) 1721 | { 1722 | g_DebugControl->Output(DEBUG_OUTPUT_DEBUGGEE, "*** Printing Data for Prototype PagedPool ***\n\n"); 1723 | } 1724 | 1725 | heap = {}; 1726 | GetInformationForHeap(heaps[i], &heap.Allocations, Tag); 1727 | heap.Address = heaps[i]; 1728 | heap.NodeNumber = j; 1729 | heap.Special = FALSE; 1730 | heap.PoolType = heapNumberToPoolType[i]; 1731 | heap.NumberOfAllocations = heap.Allocations.size(); 1732 | g_Heaps.push_back(heap); 1733 | } 1734 | } 1735 | // 1736 | // Get special heaps from nt!ExPoolState->SpecialHeaps 1737 | // 1738 | if (!SUCCEEDED(g_DataSpaces->ReadVirtual( 1739 | g_PoolState + g_HeapManagerStateOffsets.SpecialHeapsOffset, 1740 | &specialHeaps, 1741 | sizeof(specialHeaps), 1742 | nullptr))) 1743 | { 1744 | goto Exit; 1745 | } 1746 | for (int i = 0; i < 3; i++) 1747 | { 1748 | heap = {}; 1749 | if (i == 0) 1750 | { 1751 | g_DebugControl->Output(DEBUG_OUTPUT_DEBUGGEE, "*** Printing Data for Special NonPagedPool ***\n\n"); 1752 | } 1753 | else if (i == 1) 1754 | { 1755 | g_DebugControl->Output(DEBUG_OUTPUT_DEBUGGEE, "*** Printing Data for Special NonPagedPoolNx ***\n\n"); 1756 | } 1757 | if (i == 2) 1758 | { 1759 | g_DebugControl->Output(DEBUG_OUTPUT_DEBUGGEE, "*** Printing Data for Spacial PagedPool ***\n\n"); 1760 | } 1761 | GetInformationForHeap(specialHeaps[i], &heap.Allocations, Tag); 1762 | heap.Address = specialHeaps[i]; 1763 | heap.NodeNumber = -1; 1764 | heap.Special = TRUE; 1765 | heap.PoolType = heapNumberToPoolType[i]; 1766 | heap.NumberOfAllocations = heap.Allocations.size(); 1767 | g_Heaps.push_back(heap); 1768 | } 1769 | Exit: 1770 | return g_Heaps; 1771 | } 1772 | 1773 | /* 1774 | Receives an address in the pool and dumps information about it. 1775 | Such as size, pool tag, type (LFH, VS), etc. 1776 | */ 1777 | VOID 1778 | GetPoolDataForAddress ( 1779 | _In_ PVOID Address 1780 | ) 1781 | { 1782 | PVOID heapPageSegment; 1783 | PVOID heapPageSeg; 1784 | ULONG pageIndex; 1785 | ULONG result; 1786 | ULONG unitShift; 1787 | ULONG64 segmentMask; 1788 | 1789 | // 1790 | // First check what type of allocation this is to know how to handle it 1791 | // 1792 | result = BitmapBitmaskRead(Address); 1793 | 1794 | // 1795 | // Get the segment for the input address. 1796 | // There is a HEAP_PAGE_SEGMENT for each MB so we need to align our address 1797 | // to a MB to find it. 1798 | // 1799 | unitShift = 0; 1800 | segmentMask = 0; 1801 | if (result == 1) 1802 | { 1803 | // 1804 | // SegContext[0] 1805 | // 1806 | segmentMask = 0xfffffffffff00000; 1807 | unitShift = 0xC; 1808 | } 1809 | else if (result == 2) 1810 | { 1811 | // 1812 | // SegContext[1] 1813 | // 1814 | segmentMask = 0xffffffffff000000; 1815 | unitShift = 0x10; 1816 | } 1817 | else 1818 | { 1819 | // 1820 | // LargePool 1821 | // 1822 | ParseLargePoolAlloc((ULONG64)Address, true, 0, 0); 1823 | return; 1824 | } 1825 | // 1826 | // Get the heap where the address is 1827 | // 1828 | heapPageSegment = (PVOID)((ULONG_PTR)(Address)&segmentMask); 1829 | 1830 | // 1831 | // Find the index of our address' page in the descriptor's array. 1832 | // There is a descriptor for every page, so we check how far our 1833 | // page is from the segment state 1834 | // (aligned to the nearest MB or 16 MB, depends on SegContext type). 1835 | // There are 256 descriptors (0-0xFF) so we need to get the lowest byte 1836 | // as the index. 1837 | // 1838 | pageIndex = ((((ULONG64)Address - (ULONG64)heapPageSegment) >> unitShift) & 0x00000000000ff); 1839 | 1840 | // 1841 | // Allocate memory for the HEAP_PAGE_SEGMENT. 1842 | // 1843 | heapPageSeg = VirtualAlloc(NULL, g_HeapPageSegSize, MEM_COMMIT, PAGE_READWRITE); 1844 | if (heapPageSeg == nullptr) 1845 | { 1846 | g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "[%ws::%d] Failed allocating memory\n", __FUNCTIONW__, __LINE__); 1847 | goto Exit; 1848 | } 1849 | if (!SUCCEEDED(g_DataSpaces->ReadVirtual((ULONG64)heapPageSegment, heapPageSeg, g_HeapPageSegSize, nullptr))) 1850 | { 1851 | g_DebugControl->Output(DEBUG_OUTPUT_ERROR, "[%ws::%d] Failed reading memory: 0x%p\n", __FUNCTIONW__, __LINE__); 1852 | goto Exit; 1853 | } 1854 | 1855 | GetDataForDescriptor((ULONG64)heapPageSegment, heapPageSeg, pageIndex, unitShift, 0, Address, 0); 1856 | Exit: 1857 | if (heapPageSeg != nullptr) 1858 | { 1859 | VirtualFree(heapPageSeg, NULL, MEM_RELEASE); 1860 | } 1861 | } 1862 | 1863 | /* 1864 | Open a dmp file and initialize all the symbols and structures we will need to parse it. 1865 | */ 1866 | HRESULT 1867 | OpenDumpFile ( 1868 | _In_ PCSTR FilePath 1869 | ) 1870 | { 1871 | HRESULT result; 1872 | result = g_DebugClient->OpenDumpFile(FilePath); 1873 | if (!SUCCEEDED(result)) 1874 | { 1875 | return result; 1876 | } 1877 | 1878 | result = g_DebugControl->WaitForEvent(DEBUG_WAIT_DEFAULT, 0); 1879 | if (!SUCCEEDED(result)) 1880 | { 1881 | return result; 1882 | } 1883 | 1884 | if ((!SUCCEEDED(GetTypes())) || 1885 | (!SUCCEEDED(GetOffsets())) || 1886 | (!SUCCEEDED(GetSizes())) || 1887 | (!SUCCEEDED(GetHeapGlobals()))) 1888 | { 1889 | return result; 1890 | } 1891 | 1892 | return S_OK; 1893 | } 1894 | 1895 | /* 1896 | Enables or disables the chosen privilege for the current process. 1897 | Taken from here: https://github.com/lilhoser/livedump 1898 | */ 1899 | BOOL 1900 | EnablePrivilege ( 1901 | _In_ PCWSTR PrivilegeName, 1902 | _In_ BOOLEAN Acquire 1903 | ) 1904 | { 1905 | HANDLE tokenHandle; 1906 | BOOL ret; 1907 | ULONG tokenPrivilegesSize = FIELD_OFFSET(TOKEN_PRIVILEGES, Privileges[1]); 1908 | PTOKEN_PRIVILEGES tokenPrivileges = static_cast(calloc(1, tokenPrivilegesSize)); 1909 | 1910 | if (tokenPrivileges == NULL) 1911 | { 1912 | return FALSE; 1913 | } 1914 | 1915 | tokenHandle = NULL; 1916 | tokenPrivileges->PrivilegeCount = 1; 1917 | ret = LookupPrivilegeValue(NULL, 1918 | PrivilegeName, 1919 | &tokenPrivileges->Privileges[0].Luid); 1920 | if (ret == FALSE) 1921 | { 1922 | goto Exit; 1923 | } 1924 | 1925 | tokenPrivileges->Privileges[0].Attributes = Acquire ? SE_PRIVILEGE_ENABLED 1926 | : SE_PRIVILEGE_REMOVED; 1927 | 1928 | ret = OpenProcessToken(GetCurrentProcess(), 1929 | TOKEN_ADJUST_PRIVILEGES, 1930 | &tokenHandle); 1931 | if (ret == FALSE) 1932 | { 1933 | goto Exit; 1934 | } 1935 | 1936 | ret = AdjustTokenPrivileges(tokenHandle, 1937 | FALSE, 1938 | tokenPrivileges, 1939 | tokenPrivilegesSize, 1940 | NULL, 1941 | NULL); 1942 | if (ret == FALSE) 1943 | { 1944 | goto Exit; 1945 | } 1946 | 1947 | Exit: 1948 | if (tokenHandle != NULL) 1949 | { 1950 | CloseHandle(tokenHandle); 1951 | } 1952 | free(tokenPrivileges); 1953 | return ret; 1954 | } 1955 | 1956 | /* 1957 | Creates a live dump of the current machine. 1958 | Taken from here: https://github.com/lilhoser/livedump 1959 | */ 1960 | HRESULT 1961 | CreateDump ( 1962 | _In_ char* FilePath 1963 | ) 1964 | { 1965 | HRESULT result; 1966 | HANDLE handle; 1967 | HMODULE module; 1968 | SYSDBG_LIVEDUMP_CONTROL_FLAGS flags; 1969 | SYSDBG_LIVEDUMP_CONTROL_ADDPAGES pages; 1970 | SYSDBG_LIVEDUMP_CONTROL liveDumpControl; 1971 | NTSTATUS status; 1972 | ULONG returnLength; 1973 | 1974 | handle = INVALID_HANDLE_VALUE; 1975 | result = S_OK; 1976 | flags.AsUlong = 0; 1977 | pages.AsUlong = 0; 1978 | 1979 | // 1980 | // Get function addresses 1981 | // 1982 | module = LoadLibrary(L"ntdll.dll"); 1983 | if (module == NULL) 1984 | { 1985 | result = S_FALSE; 1986 | goto Exit; 1987 | } 1988 | 1989 | g_NtSystemDebugControl = (NtSystemDebugControl) 1990 | GetProcAddress(module, "NtSystemDebugControl"); 1991 | 1992 | FreeLibrary(module); 1993 | 1994 | if (g_NtSystemDebugControl == NULL) 1995 | { 1996 | result = S_FALSE; 1997 | goto Exit; 1998 | } 1999 | 2000 | // 2001 | // Get SeDebugPrivilege 2002 | // 2003 | if (!EnablePrivilege(SE_DEBUG_NAME, TRUE)) 2004 | { 2005 | result = S_FALSE; 2006 | goto Exit; 2007 | } 2008 | 2009 | // 2010 | // Create the target file (must specify synchronous I/O) 2011 | // 2012 | handle = CreateFileA(FilePath, 2013 | GENERIC_WRITE | GENERIC_READ, 2014 | 0, 2015 | NULL, 2016 | CREATE_ALWAYS, 2017 | FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, 2018 | NULL); 2019 | 2020 | if (handle == INVALID_HANDLE_VALUE) 2021 | { 2022 | result = S_FALSE; 2023 | goto Exit; 2024 | } 2025 | 2026 | // 2027 | // Try to create the requested dump 2028 | // 2029 | memset(&liveDumpControl, 0, sizeof(liveDumpControl)); 2030 | 2031 | // 2032 | // The only thing the kernel looks at in the struct we pass is the handle, 2033 | // the flags and the pages to dump. 2034 | // 2035 | liveDumpControl.DumpFileHandle = (PVOID)(handle); 2036 | liveDumpControl.AddPagesControl = pages; 2037 | liveDumpControl.Flags = flags; 2038 | 2039 | status = g_NtSystemDebugControl(CONTROL_KERNEL_DUMP, 2040 | (PVOID)(&liveDumpControl), 2041 | sizeof(liveDumpControl), 2042 | NULL, 2043 | 0, 2044 | &returnLength); 2045 | 2046 | if (NT_SUCCESS(status)) 2047 | { 2048 | result = S_OK; 2049 | } 2050 | else 2051 | { 2052 | result = S_FALSE; 2053 | goto Exit; 2054 | } 2055 | 2056 | Exit: 2057 | if (handle != INVALID_HANDLE_VALUE) 2058 | { 2059 | CloseHandle(handle); 2060 | } 2061 | return result; 2062 | } 2063 | 2064 | HRESULT 2065 | InitializeDebugGlobals( 2066 | void 2067 | ) 2068 | { 2069 | HRESULT result; 2070 | HMODULE handle; 2071 | DebugCreateFunc debugCreate; 2072 | 2073 | handle = GetModuleHandle(L"DbgEng.dll"); 2074 | if (handle == 0) 2075 | { 2076 | result = S_FALSE; 2077 | goto Exit; 2078 | } 2079 | debugCreate = (DebugCreateFunc)GetProcAddress(handle, "DebugCreate"); 2080 | 2081 | result = debugCreate(__uuidof(IDebugClient), (PVOID*)&g_DebugClient); 2082 | if (!SUCCEEDED(result)) 2083 | { 2084 | printf("DebugCreate failed with error 0x%x\n", result); 2085 | goto Exit; 2086 | } 2087 | 2088 | result = g_DebugClient->QueryInterface(__uuidof(IDebugSymbols), (PVOID*)&g_DebugSymbols); 2089 | if (!SUCCEEDED(result)) 2090 | { 2091 | printf("QueryInterface for debug symbols failed with error 0x%x\n", result); 2092 | goto Exit; 2093 | } 2094 | 2095 | result = g_DebugClient->QueryInterface(__uuidof(IDebugDataSpaces), (PVOID*)&g_DataSpaces); 2096 | if (!SUCCEEDED(result)) 2097 | { 2098 | printf("QueryInterface for debug data spaces failed with error 0x%x\n", result); 2099 | goto Exit; 2100 | } 2101 | 2102 | result = g_DebugClient->QueryInterface(__uuidof(IDebugControl), (PVOID*)&g_DebugControl); 2103 | if (!SUCCEEDED(result)) 2104 | { 2105 | printf("QueryInterface for debug control failed with error 0x%x\n", result); 2106 | goto Exit; 2107 | } 2108 | Exit: 2109 | return result; 2110 | } 2111 | 2112 | VOID 2113 | UninitializeDebugGlobals ( 2114 | void 2115 | ) 2116 | { 2117 | if (g_DebugClient != nullptr) 2118 | { 2119 | g_DebugClient->Release(); 2120 | } 2121 | if (g_DebugSymbols != nullptr) 2122 | { 2123 | g_DebugSymbols->Release(); 2124 | } 2125 | if (g_DataSpaces != nullptr) 2126 | { 2127 | g_DataSpaces->Release(); 2128 | } 2129 | if (g_DebugControl != nullptr) 2130 | { 2131 | g_DebugControl->Release(); 2132 | } 2133 | } 2134 | 2135 | /* 2136 | Open a dmp file and get information about all the heaps 2137 | in it and the pool blocks they manage. 2138 | */ 2139 | HRESULT 2140 | GetPoolInformation ( 2141 | _In_ char* FilePath, 2142 | _In_ bool CreateLiveDump, 2143 | _Outptr_ int* NumberOfHeaps 2144 | ) 2145 | { 2146 | HRESULT result; 2147 | POOL_VIEW_FLAGS flags; 2148 | 2149 | flags.AllFlags = 0; 2150 | // 2151 | // Initialize interfaces 2152 | // 2153 | 2154 | if (CreateLiveDump) 2155 | { 2156 | if (!SUCCEEDED(CreateDump(FilePath))) 2157 | { 2158 | result = S_FALSE; 2159 | goto Exit; 2160 | } 2161 | } 2162 | 2163 | result = InitializeDebugGlobals(); 2164 | 2165 | // 2166 | // Open dump file 2167 | // 2168 | 2169 | result = OpenDumpFile(FilePath); 2170 | if (!SUCCEEDED(result)) 2171 | { 2172 | printf("OpenDumpFile failed with error 0x%x\n", result); 2173 | goto Exit; 2174 | } 2175 | 2176 | GetAllHeaps(NULL, flags); 2177 | *NumberOfHeaps = g_Heaps.size(); 2178 | g_DebugClient->EndSession(DEBUG_END_ACTIVE_DETACH); 2179 | 2180 | Exit: 2181 | UninitializeDebugGlobals(); 2182 | if (CreateLiveDump) 2183 | { 2184 | DeleteFileA(FilePath); 2185 | } 2186 | return result; 2187 | } 2188 | 2189 | /* 2190 | Get all the information about one heap and remove it from g_Heaps. 2191 | Save its allocations in g_CurrentAllocs so they can be queried. 2192 | */ 2193 | bool 2194 | GetNextHeapInformation ( 2195 | _Outptr_ ULONG64* Address, 2196 | _Outptr_ int* NodeNumber, 2197 | _Outptr_ long* NumberOfAllocations, 2198 | _Outptr_ int* PoolType, 2199 | _Outptr_ bool* Special 2200 | ) 2201 | { 2202 | if (g_Heaps.empty()) 2203 | { 2204 | return 0; 2205 | } 2206 | std::list::iterator it = g_Heaps.begin(); 2207 | HEAP h = (*it); 2208 | *Address = h.Address; 2209 | *NodeNumber = h.NodeNumber; 2210 | *NumberOfAllocations = h.NumberOfAllocations; 2211 | *PoolType = h.PoolType; 2212 | *Special = h.Special; 2213 | g_CurrentAllocs = h.Allocations; 2214 | g_Heaps.erase(it); 2215 | return 1; 2216 | } 2217 | 2218 | /* 2219 | Get information about one pool block and remove it from g_CurrentAllocs. 2220 | */ 2221 | char* 2222 | GetNextAllocation( 2223 | _Outptr_ ULONG64* Address, 2224 | _Outptr_ int* Size, 2225 | _Outptr_ bool* Allocated, 2226 | _Outptr_ int* Type 2227 | ) 2228 | { 2229 | if (g_CurrentAllocs.empty()) 2230 | { 2231 | *Size = 0; 2232 | return 0; 2233 | } 2234 | std::list::iterator it = g_CurrentAllocs.begin(); 2235 | ALLOC a = (*it); 2236 | *Address = a.Address; 2237 | *Size = a.Size; 2238 | *Allocated = a.Allocated; 2239 | *Type = (int)a.Type; 2240 | g_CurrentAllocs.erase(it); 2241 | 2242 | char tag[5]; 2243 | tag[0] = a.PoolTag.c_str()[0]; 2244 | tag[1] = a.PoolTag.c_str()[1]; 2245 | tag[2] = a.PoolTag.c_str()[2]; 2246 | tag[3] = a.PoolTag.c_str()[3]; 2247 | tag[4] = '\x0'; 2248 | size_t stSize = strlen(tag) + sizeof(char); 2249 | char* pszReturn = NULL; 2250 | 2251 | pszReturn = (char*)::CoTaskMemAlloc(stSize); 2252 | strcpy_s(pszReturn, stSize, tag); 2253 | return pszReturn; 2254 | } --------------------------------------------------------------------------------