├── 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 |
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 | }
--------------------------------------------------------------------------------