├── .gitignore ├── HyperVDriverThreadDetection.sln ├── HyperVDriverThreadDetection ├── App.config ├── HyperVDriverThreadDetection.csproj ├── NativeConsts.cs ├── NativeMethods.cs ├── NativeTypes.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── SystemInformationClass.cs └── README.md /.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 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.vspscc 94 | *.vssscc 95 | .builds 96 | *.pidb 97 | *.svclog 98 | *.scc 99 | 100 | # Chutzpah Test files 101 | _Chutzpah* 102 | 103 | # Visual C++ cache files 104 | ipch/ 105 | *.aps 106 | *.ncb 107 | *.opendb 108 | *.opensdf 109 | *.sdf 110 | *.cachefile 111 | *.VC.db 112 | *.VC.VC.opendb 113 | 114 | # Visual Studio profiler 115 | *.psess 116 | *.vsp 117 | *.vspx 118 | *.sap 119 | 120 | # Visual Studio Trace Files 121 | *.e2e 122 | 123 | # TFS 2012 Local Workspace 124 | $tf/ 125 | 126 | # Guidance Automation Toolkit 127 | *.gpState 128 | 129 | # ReSharper is a .NET coding add-in 130 | _ReSharper*/ 131 | *.[Rr]e[Ss]harper 132 | *.DotSettings.user 133 | 134 | # TeamCity is a build add-in 135 | _TeamCity* 136 | 137 | # DotCover is a Code Coverage Tool 138 | *.dotCover 139 | 140 | # AxoCover is a Code Coverage Tool 141 | .axoCover/* 142 | !.axoCover/settings.json 143 | 144 | # Coverlet is a free, cross platform Code Coverage Tool 145 | coverage*.json 146 | coverage*.xml 147 | coverage*.info 148 | 149 | # Visual Studio code coverage results 150 | *.coverage 151 | *.coveragexml 152 | 153 | # NCrunch 154 | _NCrunch_* 155 | .*crunch*.local.xml 156 | nCrunchTemp_* 157 | 158 | # MightyMoose 159 | *.mm.* 160 | AutoTest.Net/ 161 | 162 | # Web workbench (sass) 163 | .sass-cache/ 164 | 165 | # Installshield output folder 166 | [Ee]xpress/ 167 | 168 | # DocProject is a documentation generator add-in 169 | DocProject/buildhelp/ 170 | DocProject/Help/*.HxT 171 | DocProject/Help/*.HxC 172 | DocProject/Help/*.hhc 173 | DocProject/Help/*.hhk 174 | DocProject/Help/*.hhp 175 | DocProject/Help/Html2 176 | DocProject/Help/html 177 | 178 | # Click-Once directory 179 | publish/ 180 | 181 | # Publish Web Output 182 | *.[Pp]ublish.xml 183 | *.azurePubxml 184 | # Note: Comment the next line if you want to checkin your web deploy settings, 185 | # but database connection strings (with potential passwords) will be unencrypted 186 | *.pubxml 187 | *.publishproj 188 | 189 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 190 | # checkin your Azure Web App publish settings, but sensitive information contained 191 | # in these scripts will be unencrypted 192 | PublishScripts/ 193 | 194 | # NuGet Packages 195 | *.nupkg 196 | # NuGet Symbol Packages 197 | *.snupkg 198 | # The packages folder can be ignored because of Package Restore 199 | **/[Pp]ackages/* 200 | # except build/, which is used as an MSBuild target. 201 | !**/[Pp]ackages/build/ 202 | # Uncomment if necessary however generally it will be regenerated when needed 203 | #!**/[Pp]ackages/repositories.config 204 | # NuGet v3's project.json files produces more ignorable files 205 | *.nuget.props 206 | *.nuget.targets 207 | 208 | # Microsoft Azure Build Output 209 | csx/ 210 | *.build.csdef 211 | 212 | # Microsoft Azure Emulator 213 | ecf/ 214 | rcf/ 215 | 216 | # Windows Store app package directories and files 217 | AppPackages/ 218 | BundleArtifacts/ 219 | Package.StoreAssociation.xml 220 | _pkginfo.txt 221 | *.appx 222 | *.appxbundle 223 | *.appxupload 224 | 225 | # Visual Studio cache files 226 | # files ending in .cache can be ignored 227 | *.[Cc]ache 228 | # but keep track of directories ending in .cache 229 | !?*.[Cc]ache/ 230 | 231 | # Others 232 | ClientBin/ 233 | ~$* 234 | *~ 235 | *.dbmdl 236 | *.dbproj.schemaview 237 | *.jfm 238 | *.pfx 239 | *.publishsettings 240 | orleans.codegen.cs 241 | 242 | # Including strong name files can present a security risk 243 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 244 | #*.snk 245 | 246 | # Since there are multiple workflows, uncomment next line to ignore bower_components 247 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 248 | #bower_components/ 249 | 250 | # RIA/Silverlight projects 251 | Generated_Code/ 252 | 253 | # Backup & report files from converting an old project file 254 | # to a newer Visual Studio version. Backup files are not needed, 255 | # because we have git ;-) 256 | _UpgradeReport_Files/ 257 | Backup*/ 258 | UpgradeLog*.XML 259 | UpgradeLog*.htm 260 | ServiceFabricBackup/ 261 | *.rptproj.bak 262 | 263 | # SQL Server files 264 | *.mdf 265 | *.ldf 266 | *.ndf 267 | 268 | # Business Intelligence projects 269 | *.rdl.data 270 | *.bim.layout 271 | *.bim_*.settings 272 | *.rptproj.rsuser 273 | *- [Bb]ackup.rdl 274 | *- [Bb]ackup ([0-9]).rdl 275 | *- [Bb]ackup ([0-9][0-9]).rdl 276 | 277 | # Microsoft Fakes 278 | FakesAssemblies/ 279 | 280 | # GhostDoc plugin setting file 281 | *.GhostDoc.xml 282 | 283 | # Node.js Tools for Visual Studio 284 | .ntvs_analysis.dat 285 | node_modules/ 286 | 287 | # Visual Studio 6 build log 288 | *.plg 289 | 290 | # Visual Studio 6 workspace options file 291 | *.opt 292 | 293 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 294 | *.vbw 295 | 296 | # Visual Studio LightSwitch build output 297 | **/*.HTMLClient/GeneratedArtifacts 298 | **/*.DesktopClient/GeneratedArtifacts 299 | **/*.DesktopClient/ModelManifest.xml 300 | **/*.Server/GeneratedArtifacts 301 | **/*.Server/ModelManifest.xml 302 | _Pvt_Extensions 303 | 304 | # Paket dependency manager 305 | .paket/paket.exe 306 | paket-files/ 307 | 308 | # FAKE - F# Make 309 | .fake/ 310 | 311 | # CodeRush personal settings 312 | .cr/personal 313 | 314 | # Python Tools for Visual Studio (PTVS) 315 | __pycache__/ 316 | *.pyc 317 | 318 | # Cake - Uncomment if you are using it 319 | # tools/** 320 | # !tools/packages.config 321 | 322 | # Tabs Studio 323 | *.tss 324 | 325 | # Telerik's JustMock configuration file 326 | *.jmconfig 327 | 328 | # BizTalk build output 329 | *.btp.cs 330 | *.btm.cs 331 | *.odx.cs 332 | *.xsd.cs 333 | 334 | # OpenCover UI analysis results 335 | OpenCover/ 336 | 337 | # Azure Stream Analytics local run output 338 | ASALocalRun/ 339 | 340 | # MSBuild Binary and Structured Log 341 | *.binlog 342 | 343 | # NVidia Nsight GPU debugger configuration file 344 | *.nvuser 345 | 346 | # MFractors (Xamarin productivity tool) working folder 347 | .mfractor/ 348 | 349 | # Local History for Visual Studio 350 | .localhistory/ 351 | 352 | # BeatPulse healthcheck temp database 353 | healthchecksdb 354 | 355 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 356 | MigrationBackup/ 357 | 358 | # Ionide (cross platform F# VS Code tools) working folder 359 | .ionide/ 360 | 361 | # Fody - auto-generated XML schema 362 | FodyWeavers.xsd 363 | -------------------------------------------------------------------------------- /HyperVDriverThreadDetection.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30907.101 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HyperVDriverThreadDetection", "HyperVDriverThreadDetection\HyperVDriverThreadDetection.csproj", "{77566646-A384-4F42-ADC0-8F7EBE6BD8C9}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {77566646-A384-4F42-ADC0-8F7EBE6BD8C9}.Debug|x64.ActiveCfg = Debug|x64 15 | {77566646-A384-4F42-ADC0-8F7EBE6BD8C9}.Debug|x64.Build.0 = Debug|x64 16 | {77566646-A384-4F42-ADC0-8F7EBE6BD8C9}.Release|x64.ActiveCfg = Release|x64 17 | {77566646-A384-4F42-ADC0-8F7EBE6BD8C9}.Release|x64.Build.0 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {4270EE7D-D41E-4BF7-AD44-63C5E9C0F474} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /HyperVDriverThreadDetection/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /HyperVDriverThreadDetection/HyperVDriverThreadDetection.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {77566646-A384-4F42-ADC0-8F7EBE6BD8C9} 8 | Exe 9 | HyperVDriverThreadDetection 10 | HyperVDriverThreadDetection 11 | v4.7.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | true 37 | bin\x64\Debug\ 38 | DEBUG;TRACE 39 | full 40 | x64 41 | 7.3 42 | prompt 43 | true 44 | 45 | 46 | bin\x64\Release\ 47 | TRACE 48 | true 49 | pdbonly 50 | x64 51 | 7.3 52 | prompt 53 | true 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /HyperVDriverThreadDetection/NativeConsts.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace HyperVDriverThreadDetection 8 | { 9 | static class NativeConsts 10 | { 11 | public const uint STATUS_SUCCESS = 0; 12 | public const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004; 13 | public const uint ERROR_INSUFFICIENT_BUFFER = 0x0000007A; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /HyperVDriverThreadDetection/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace HyperVDriverThreadDetection 11 | { 12 | static class NativeMethods 13 | { 14 | [DllImport("kernel32.dll", SetLastError = true)] 15 | public static extern bool GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP RelationshipType, IntPtr Buffer, ref uint ReturnedLength); 16 | 17 | [DllImport("ntdll.dll")] 18 | public static extern uint NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, IntPtr SystemInformation, uint SystemInformationLength, out uint ReturnLength); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /HyperVDriverThreadDetection/NativeTypes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime.InteropServices; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace HyperVDriverThreadDetection 11 | { 12 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 13 | struct UNICODE_STRING 14 | { 15 | public ushort Length; 16 | public ushort MaximumLength; 17 | [MarshalAs(UnmanagedType.LPWStr)] public string Buffer; 18 | 19 | public UNICODE_STRING(string str) 20 | { 21 | Buffer = str; 22 | Length = (ushort)Encoding.Unicode.GetByteCount(str); 23 | MaximumLength = Length; 24 | } 25 | } 26 | 27 | // ref: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/thread.htm 28 | [StructLayout(LayoutKind.Sequential, Size = 0x50)] 29 | struct SYSTEM_THREAD_INFORMATION 30 | { 31 | public Int64 KernelTime; 32 | public Int64 UserTime; 33 | public Int64 CreateTime; 34 | public UInt32 WaitTime; 35 | public IntPtr StartAddress; 36 | public IntPtr ClientIdUniqueProcess; 37 | public IntPtr ClientIdUniqueThread; 38 | public Int32 Priority; 39 | public Int32 BasePriority; 40 | public UInt32 ContextSwitches; 41 | public UInt32 ThreadState; 42 | public UInt32 WaitReason; 43 | } 44 | 45 | // note: excluding most of the fields here since they aren't used. 46 | // ref: https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/ex/sysinfo/process.htm 47 | [StructLayout(LayoutKind.Explicit, Size = 0x100)] 48 | struct SYSTEM_PROCESS_INFORMATION 49 | { 50 | [FieldOffset(0x00)] 51 | public UInt32 NextEntryOffset; 52 | [FieldOffset(0x04)] 53 | public UInt32 NumberOfThreads; 54 | [FieldOffset(0x38)] 55 | public UNICODE_STRING ImageName; 56 | [FieldOffset(0x50)] 57 | public IntPtr UniqueProcessId; 58 | [FieldOffset(0x60)] 59 | public UInt32 HandleCount; 60 | [FieldOffset(0x64)] 61 | public UInt32 SessionId; 62 | [FieldOffset(0xF8)] 63 | public UInt64 OtherTransferCount; 64 | } 65 | 66 | 67 | enum LOGICAL_PROCESSOR_RELATIONSHIP 68 | { 69 | RelationProcessorCore, 70 | RelationNumaNode, 71 | RelationCache, 72 | RelationProcessorPackage, 73 | RelationGroup, 74 | RelationAll = 0xffff 75 | } 76 | 77 | [StructLayout(LayoutKind.Sequential)] 78 | struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX 79 | { 80 | public LOGICAL_PROCESSOR_RELATIONSHIP Relationship; 81 | public uint Size; 82 | public PROCESSOR_RELATIONSHIP Processor; 83 | } 84 | 85 | [StructLayout(LayoutKind.Sequential)] 86 | struct PROCESSOR_RELATIONSHIP 87 | { 88 | public byte Flags; 89 | private byte EfficiencyClass; 90 | // 20 reserved bytes 91 | private byte Reserved00; 92 | private byte Reserved01; 93 | private byte Reserved02; 94 | private byte Reserved03; 95 | private byte Reserved04; 96 | private byte Reserved05; 97 | private byte Reserved06; 98 | private byte Reserved07; 99 | private byte Reserved08; 100 | private byte Reserved09; 101 | private byte Reserved10; 102 | private byte Reserved11; 103 | private byte Reserved12; 104 | private byte Reserved13; 105 | private byte Reserved14; 106 | private byte Reserved15; 107 | private byte Reserved16; 108 | private byte Reserved17; 109 | private byte Reserved18; 110 | private byte Reserved19; 111 | public ushort GroupCount; 112 | public IntPtr GroupInfo; 113 | } 114 | 115 | #if DEBUG 116 | [StructLayout(LayoutKind.Sequential, Size = 0x50, CharSet = CharSet.Ansi)] 117 | struct RTL_PROCESS_MODULE_INFORMATION 118 | { 119 | public IntPtr Section; 120 | public IntPtr MappedBase; 121 | public IntPtr ImageBase; 122 | public UInt32 ImageSize; 123 | public UInt32 Flags; 124 | public UInt16 LoadOrderIndex; 125 | public UInt16 InitOrderIndex; 126 | public UInt16 LoadCount; 127 | public UInt16 OffsetToFileName; 128 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x100)] 129 | public string FullPathName; 130 | } 131 | #endif 132 | } 133 | -------------------------------------------------------------------------------- /HyperVDriverThreadDetection/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace HyperVDriverThreadDetection 9 | { 10 | class Program 11 | { 12 | /// 13 | /// Gets the number of logical processors on the system. 14 | /// 15 | /// The number of logical processors on the system. 16 | static int GetLogicalProcessorCount() 17 | { 18 | uint bufferLength = 0; 19 | IntPtr buffer = IntPtr.Zero; 20 | // fuse pattern to prevent looping on endless GetLogicalProcessorInformationEx calls if it always returns ERROR_INSUFFICIENT_BUFFER 21 | const int BLOWN = 100; 22 | int fuse = 0; 23 | while (fuse++ != BLOWN) 24 | { 25 | // attempt to get processor information 26 | if (!NativeMethods.GetLogicalProcessorInformationEx(LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore, buffer, ref bufferLength)) 27 | { 28 | int lastError = Marshal.GetLastWin32Error(); 29 | if (lastError == NativeConsts.ERROR_INSUFFICIENT_BUFFER) 30 | { 31 | // buffer wasn't big enough. reallocate and try again. 32 | if (buffer != IntPtr.Zero) 33 | { 34 | Marshal.FreeHGlobal(buffer); 35 | } 36 | buffer = Marshal.AllocHGlobal((int)bufferLength); 37 | continue; 38 | } 39 | 40 | // some other error occurred. 41 | Console.WriteLine("[X] GetLogicalProcessorInformationEx call failed with error 0x{0:x}", lastError); 42 | return -1; 43 | } 44 | 45 | try 46 | { 47 | // check that we got a sensible amount of data back 48 | if (bufferLength < Marshal.SizeOf()) 49 | { 50 | Console.WriteLine("[X] GetLogicalProcessorInformationEx call returned insufficient data."); 51 | return -1; 52 | } 53 | 54 | // call was successful. walk through the data. 55 | int logicalProcessorCount = 0; 56 | int offset = 0; 57 | // loop through all the logical CPU info and figure out how many CPUs we have. 58 | while (offset < bufferLength - Marshal.SizeOf()) 59 | { 60 | var procInfo = Marshal.PtrToStructure(buffer + offset); 61 | if (procInfo.Relationship == LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore) 62 | { 63 | // Flags is 0 if the processor has one logical core, or non-zero (LTP_PC_SMT) if SMT is enabled (practically this means 2 cores) 64 | logicalProcessorCount += procInfo.Processor.Flags == 0 ? 1 : 2; 65 | } 66 | offset += (int)procInfo.Size; 67 | } 68 | return logicalProcessorCount; 69 | } 70 | finally 71 | { 72 | Marshal.FreeHGlobal(buffer); 73 | } 74 | } 75 | 76 | // we only reach here if the fuse blows 77 | Console.WriteLine("[X] GetLogicalProcessorInformationEx did not return correct data after {0} attempts.", fuse); 78 | return -1; 79 | } 80 | 81 | 82 | static SYSTEM_THREAD_INFORMATION[] GetSystemThreadInformation() 83 | { 84 | uint bufferLength = 0; 85 | IntPtr buffer = IntPtr.Zero; 86 | // fuse pattern to prevent looping on endless NtQuerySystemInformation calls if it always returns ERROR_INSUFFICIENT_BUFFER 87 | const int BLOWN = 100; 88 | int fuse = 0; 89 | while (fuse++ != BLOWN) 90 | { 91 | // attempt to get process/thread information 92 | uint status = NativeMethods.NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemProcessInformation, buffer, bufferLength, out bufferLength); 93 | if (status == NativeConsts.STATUS_INFO_LENGTH_MISMATCH) 94 | { 95 | // buffer wasn't big enough. reallocate and try again. 96 | if (buffer != IntPtr.Zero) 97 | { 98 | Marshal.FreeHGlobal(buffer); 99 | } 100 | buffer = Marshal.AllocHGlobal((int)bufferLength); 101 | continue; 102 | } 103 | else if (status != NativeConsts.STATUS_SUCCESS) 104 | { 105 | // some other error occurred. 106 | Console.WriteLine("[X] NtQuerySystemInformation call failed with status 0x{0:x}", status); 107 | return null; 108 | } 109 | 110 | try 111 | { 112 | // check that we got a sensible amount of data back 113 | if (bufferLength < Marshal.SizeOf() + Marshal.SizeOf()) 114 | { 115 | Console.WriteLine("[X] NtQuerySystemInformation call returned insufficient data. This occurs when the current process is running at a low integrity level."); 116 | return null; 117 | } 118 | 119 | // call was successful. walk through the data. 120 | var threads = new List(); 121 | int offset = 0; 122 | // loop through all the logical CPU info and figure out how many CPUs we have. 123 | while (offset < bufferLength - Marshal.SizeOf()) 124 | { 125 | var processInfo = Marshal.PtrToStructure(buffer + offset); 126 | int nextProcessOffset = offset + (int)processInfo.NextEntryOffset; 127 | offset += Marshal.SizeOf(); 128 | 129 | // enumerate the threads for this process 130 | for (int t = 0; t < processInfo.NumberOfThreads; t++) 131 | { 132 | var threadInfo = Marshal.PtrToStructure(buffer + offset); 133 | offset += Marshal.SizeOf(); 134 | threads.Add(threadInfo); 135 | } 136 | 137 | if (processInfo.NextEntryOffset == 0) 138 | { 139 | // this is the end of the data 140 | break; 141 | } 142 | offset = nextProcessOffset; 143 | } 144 | 145 | return threads.ToArray(); 146 | } 147 | finally 148 | { 149 | Marshal.FreeHGlobal(buffer); 150 | } 151 | } 152 | 153 | // we only reach here if the fuse blows 154 | Console.WriteLine("[X] NtQuerySystemInformation did not return correct data after {0} attempts.", fuse); 155 | return null; 156 | } 157 | 158 | #if DEBUG 159 | static RTL_PROCESS_MODULE_INFORMATION[] GetSystemModules() 160 | { 161 | uint bufferLength = 0; 162 | IntPtr buffer = IntPtr.Zero; 163 | // fuse pattern to prevent looping on endless NtQuerySystemInformation calls if it always returns ERROR_INSUFFICIENT_BUFFER 164 | const int BLOWN = 100; 165 | int fuse = 0; 166 | while (fuse++ != BLOWN) 167 | { 168 | // attempt to get process/thread information 169 | uint status = NativeMethods.NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemModuleInformation, buffer, bufferLength, out bufferLength); 170 | if (status == NativeConsts.STATUS_INFO_LENGTH_MISMATCH) 171 | { 172 | // buffer wasn't big enough. reallocate and try again. 173 | if (buffer != IntPtr.Zero) 174 | { 175 | Marshal.FreeHGlobal(buffer); 176 | } 177 | buffer = Marshal.AllocHGlobal((int)bufferLength); 178 | continue; 179 | } 180 | else if (status != NativeConsts.STATUS_SUCCESS) 181 | { 182 | // some other error occurred. 183 | Console.WriteLine("[X] NtQuerySystemInformation call failed with status 0x{0:x}", status); 184 | return null; 185 | } 186 | 187 | try 188 | { 189 | // check that we got a sensible amount of data back 190 | if (bufferLength < IntPtr.Size + Marshal.SizeOf()) 191 | { 192 | Console.WriteLine("[X] NtQuerySystemInformation call returned insufficient data."); 193 | return null; 194 | } 195 | 196 | // call was successful. read the length field that prefixes the array. 197 | int moduleCount = Marshal.ReadInt32(buffer); 198 | if (moduleCount < 0) 199 | { 200 | Console.WriteLine("[X] NtQuerySystemInformation call returned an invalid module count."); 201 | return null; 202 | } 203 | 204 | // check that the buffer size can actually fit that many modules. 205 | long expectedLength = IntPtr.Size + (Marshal.SizeOf() * moduleCount); 206 | if (expectedLength > bufferLength) 207 | { 208 | Console.WriteLine("[X] NtQuerySystemInformation call returned insufficient data for the reported number of modules."); 209 | return null; 210 | } 211 | 212 | // call was successful. walk through the data. 213 | var modules = new List(); 214 | int offset = IntPtr.Size; 215 | int index = 0; 216 | // loop through all the module info and add it to the threa 217 | while ((offset < bufferLength - Marshal.SizeOf()) && (index++ < moduleCount)) 218 | { 219 | var moduleInfo = Marshal.PtrToStructure(buffer + offset); 220 | modules.Add(moduleInfo); 221 | offset += Marshal.SizeOf(); 222 | } 223 | 224 | return modules.ToArray(); 225 | } 226 | finally 227 | { 228 | Marshal.FreeHGlobal(buffer); 229 | } 230 | } 231 | 232 | // we only reach here if the fuse blows 233 | Console.WriteLine("[X] NtQuerySystemInformation did not return correct data after {0} attempts.", fuse); 234 | return null; 235 | } 236 | #endif 237 | 238 | static void Main(string[] args) 239 | { 240 | Console.WriteLine(); 241 | Console.WriteLine("HyperV VMBUS Driver Thread VM Detection"); 242 | Console.WriteLine(); 243 | 244 | #if DEBUG 245 | // for debug builds, query module information so we can show which drivers we've found threads for. 246 | Console.WriteLine("[*] [DEBUG] Querying module information..."); 247 | var modules = GetSystemModules(); 248 | if (modules == null) 249 | { 250 | Console.WriteLine("[*] [DEBUG] Failed to get module information. Continuing anyway."); 251 | } 252 | else 253 | { 254 | Console.WriteLine("[*] [DEBUG] {0} modules found.", modules.Length); 255 | } 256 | #endif 257 | 258 | Console.WriteLine("[*] Querying process information..."); 259 | var threads = GetSystemThreadInformation(); 260 | if ((threads == null) || (threads.Length == 0)) 261 | { 262 | Console.WriteLine("[X] Failed to get thread information. This usually occurs for low integrity processes."); 263 | return; 264 | } 265 | Console.WriteLine("[*] {0} total threads found.", threads.Length); 266 | 267 | var systemThreads = threads.Where(t => t.ClientIdUniqueProcess == new IntPtr(4)); 268 | Console.WriteLine("[*] {0} system threads found.", systemThreads.Count()); 269 | 270 | if (systemThreads.Count() == 0) 271 | { 272 | Console.WriteLine("[X] No system threads found. This usually occurs for low integrity processes."); 273 | return; 274 | } 275 | 276 | Console.WriteLine("[*] Grouping threads by start address..."); 277 | var threadsByStartAddress = new Dictionary>(); 278 | foreach (SYSTEM_THREAD_INFORMATION thread in systemThreads) 279 | { 280 | Int64 startAddr = thread.StartAddress.ToInt64(); 281 | if (!threadsByStartAddress.ContainsKey(startAddr)) 282 | { 283 | threadsByStartAddress.Add(startAddr, new List()); 284 | } 285 | threadsByStartAddress[startAddr].Add(thread); 286 | } 287 | 288 | int cpuCount = GetLogicalProcessorCount(); 289 | if (cpuCount < 0) 290 | { 291 | Console.WriteLine("[X] Failed to get the number of logical processors. Falling back to .NET method."); 292 | cpuCount = Environment.ProcessorCount; 293 | } 294 | Console.WriteLine("[*] {0} vCPUs detected.", cpuCount); 295 | 296 | if (cpuCount < 3) 297 | { 298 | Console.WriteLine("[?] Warning: this system has a small number of logical processors. This check may produce false positives."); 299 | } 300 | 301 | Console.WriteLine("[*] Looking for candidate thread groups..."); 302 | foreach (var kvp in threadsByStartAddress) 303 | { 304 | var startAddr = kvp.Key; 305 | var threadsAtThisAddr = kvp.Value; 306 | // to make the scaling slightly more flexible to future changes, this heuristic looks for any thread group that has exactly 2x, 3x, 4x, etc. as many threads as there are CPUs. 307 | if (threadsAtThisAddr.Count > cpuCount && threadsAtThisAddr.Count % cpuCount == 0) 308 | { 309 | Console.WriteLine("[?] Potential target with {0} threads at address {1:X16}", threadsAtThisAddr.Count, startAddr); 310 | #if DEBUG 311 | // in debug builds, show module information when we have it. 312 | if (modules != null) 313 | { 314 | // find a module that corresponds to the thread start address 315 | // if not found, return value will be a struct whose ImageBase pointer is zero 316 | var module = modules.FirstOrDefault(m => startAddr >= m.ImageBase.ToInt64() && 317 | startAddr < (m.ImageBase.ToInt64() + m.ImageSize)); 318 | // did we find one? 319 | if (module.ImageBase != IntPtr.Zero) 320 | { 321 | Int64 offset = startAddr - module.ImageBase.ToInt64(); 322 | Console.WriteLine("[^] Module for {0:X16} is {1}+0x{2:x}", startAddr, module.FullPathName, offset); 323 | } 324 | } 325 | #endif 326 | 327 | Console.WriteLine("[*] Checking nearby threads..."); 328 | var nearbyThreads = systemThreads.Where(t => 329 | t.StartAddress.ToInt64() < startAddr && 330 | startAddr - t.StartAddress.ToInt64() < 0x2000); 331 | Console.WriteLine("[*] {0} nearby threads found.", nearbyThreads.Count()); 332 | if (nearbyThreads.Count() == 2) 333 | { 334 | Console.WriteLine("[!] Hyper-V VMBUS driver detected!"); 335 | return; 336 | } 337 | } 338 | } 339 | 340 | Console.WriteLine("[*] No Hyper-V detected."); 341 | Console.ReadLine(); 342 | } 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /HyperVDriverThreadDetection/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("HyperVDriverThreadDetection")] 9 | [assembly: AssemblyDescription("HyperV driver thread detection PoC")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Nettitude")] 12 | [assembly: AssemblyProduct("HyperV Driver Thread Detection")] 13 | [assembly: AssemblyCopyright("Copyright © Graham Sutherland 2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("77566646-a384-4f42-adc0-8f7ebe6bd8c9")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /HyperVDriverThreadDetection/SystemInformationClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace HyperVDriverThreadDetection 8 | { 9 | enum SYSTEM_INFORMATION_CLASS 10 | { 11 | SystemBasicInformation = 0x00, 12 | SystemProcessorInformation = 0x01, 13 | SystemPerformanceInformation = 0x02, 14 | SystemTimeOfDayInformation = 0x03, 15 | SystemPathInformation = 0x04, 16 | SystemProcessInformation = 0x05, 17 | SystemCallCountInformation = 0x06, 18 | SystemDeviceInformation = 0x07, 19 | SystemProcessorPerformanceInformation = 0x08, 20 | SystemFlagsInformation = 0x09, 21 | SystemCallTimeInformation = 0x0A, 22 | SystemModuleInformation = 0x0B, 23 | SystemLocksInformation = 0x0C, 24 | SystemStackTraceInformation = 0x0D, 25 | SystemPagedPoolInformation = 0x0E, 26 | SystemNonPagedPoolInformation = 0x0F, 27 | SystemHandleInformation = 0x10, 28 | SystemObjectInformation = 0x11, 29 | SystemPageFileInformation = 0x12, 30 | SystemVdmInstemulInformation = 0x13, 31 | SystemVdmBopInformation = 0x14, 32 | SystemFileCacheInformation = 0x15, 33 | SystemPoolTagInformation = 0x16, 34 | SystemInterruptInformation = 0x17, 35 | SystemDpcBehaviorInformation = 0x18, 36 | SystemFullMemoryInformation = 0x19, 37 | SystemLoadGdiDriverInformation = 0x1A, 38 | SystemUnloadGdiDriverInformation = 0x1B, 39 | SystemTimeAdjustmentInformation = 0x1C, 40 | SystemSummaryMemoryInformation = 0x1D, 41 | SystemMirrorMemoryInformation = 0x1E, 42 | SystemPerformanceTraceInformation = 0x1F, 43 | SystemObsolete0 = 0x20, 44 | SystemExceptionInformation = 0x21, 45 | SystemCrashDumpStateInformation = 0x22, 46 | SystemKernelDebuggerInformation = 0x23, 47 | SystemContextSwitchInformation = 0x24, 48 | SystemRegistryQuotaInformation = 0x25, 49 | SystemExtendServiceTableInformation = 0x26, 50 | SystemPrioritySeperation = 0x27, 51 | SystemVerifierAddDriverInformation = 0x28, 52 | SystemVerifierRemoveDriverInformation = 0x29, 53 | SystemProcessorIdleInformation = 0x2A, 54 | SystemLegacyDriverInformation = 0x2B, 55 | SystemCurrentTimeZoneInformation = 0x2C, 56 | SystemLookasideInformation = 0x2D, 57 | SystemTimeSlipNotification = 0x2E, 58 | SystemSessionCreate = 0x2F, 59 | SystemSessionDetach = 0x30, 60 | SystemSessionInformation = 0x31, 61 | SystemRangeStartInformation = 0x32, 62 | SystemVerifierInformation = 0x33, 63 | SystemVerifierThunkExtend = 0x34, 64 | SystemSessionProcessInformation = 0x35, 65 | SystemLoadGdiDriverInSystemSpace = 0x36, 66 | SystemNumaProcessorMap = 0x37, 67 | SystemPrefetcherInformation = 0x38, 68 | SystemExtendedProcessInformation = 0x39, 69 | SystemRecommendedSharedDataAlignment = 0x3A, 70 | SystemComPlusPackage = 0x3B, 71 | SystemNumaAvailableMemory = 0x3C, 72 | SystemProcessorPowerInformation = 0x3D, 73 | SystemEmulationBasicInformation = 0x3E, 74 | SystemEmulationProcessorInformation = 0x3F, 75 | SystemExtendedHandleInformation = 0x40, 76 | SystemLostDelayedWriteInformation = 0x41, 77 | SystemBigPoolInformation = 0x42, 78 | SystemSessionPoolTagInformation = 0x43, 79 | SystemSessionMappedViewInformation = 0x44, 80 | SystemHotpatchInformation = 0x45, 81 | SystemObjectSecurityMode = 0x46, 82 | SystemWatchdogTimerHandler = 0x47, 83 | SystemWatchdogTimerInformation = 0x48, 84 | SystemLogicalProcessorInformation = 0x49, 85 | SystemWow64SharedInformationObsolete = 0x4A, 86 | SystemRegisterFirmwareTableInformationHandler = 0x4B, 87 | SystemFirmwareTableInformation = 0x4C, 88 | SystemModuleInformationEx = 0x4D, 89 | SystemVerifierTriageInformation = 0x4E, 90 | SystemSuperfetchInformation = 0x4F, 91 | SystemMemoryListInformation = 0x50, 92 | SystemFileCacheInformationEx = 0x51, 93 | SystemThreadPriorityClientIdInformation = 0x52, 94 | SystemProcessorIdleCycleTimeInformation = 0x53, 95 | SystemVerifierCancellationInformation = 0x54, 96 | SystemProcessorPowerInformationEx = 0x55, 97 | SystemRefTraceInformation = 0x56, 98 | SystemSpecialPoolInformation = 0x57, 99 | SystemProcessIdInformation = 0x58, 100 | SystemErrorPortInformation = 0x59, 101 | SystemBootEnvironmentInformation = 0x5A, 102 | SystemHypervisorInformation = 0x5B, 103 | SystemVerifierInformationEx = 0x5C, 104 | SystemTimeZoneInformation = 0x5D, 105 | SystemImageFileExecutionOptionsInformation = 0x5E, 106 | SystemCoverageInformation = 0x5F, 107 | SystemPrefetchPatchInformation = 0x60, 108 | SystemVerifierFaultsInformation = 0x61, 109 | SystemSystemPartitionInformation = 0x62, 110 | SystemSystemDiskInformation = 0x63, 111 | SystemProcessorPerformanceDistribution = 0x64, 112 | SystemNumaProximityNodeInformation = 0x65, 113 | SystemDynamicTimeZoneInformation = 0x66, 114 | SystemCodeIntegrityInformation = 0x67, 115 | SystemProcessorMicrocodeUpdateInformation = 0x68, 116 | SystemProcessorBrandString = 0x69, 117 | SystemVirtualAddressInformation = 0x6A, 118 | SystemLogicalProcessorAndGroupInformation = 0x6B, 119 | SystemProcessorCycleTimeInformation = 0x6C, 120 | SystemStoreInformation = 0x6D, 121 | SystemRegistryAppendString = 0x6E, 122 | SystemAitSamplingValue = 0x6F, 123 | SystemVhdBootInformation = 0x70, 124 | SystemCpuQuotaInformation = 0x71, 125 | SystemNativeBasicInformation = 0x72, 126 | SystemErrorPortTimeouts = 0x73, 127 | SystemLowPriorityIoInformation = 0x74, 128 | SystemBootEntropyInformation = 0x75, 129 | SystemVerifierCountersInformation = 0x76, 130 | SystemPagedPoolInformationEx = 0x77, 131 | SystemSystemPtesInformationEx = 0x78, 132 | SystemNodeDistanceInformation = 0x79, 133 | SystemAcpiAuditInformation = 0x7A, 134 | SystemBasicPerformanceInformation = 0x7B, 135 | SystemQueryPerformanceCounterInformation = 0x7C, 136 | SystemSessionBigPoolInformation = 0x7D, 137 | SystemBootGraphicsInformation = 0x7E, 138 | SystemScrubPhysicalMemoryInformation = 0x7F, 139 | SystemBadPageInformation = 0x80, 140 | SystemProcessorProfileControlArea = 0x81, 141 | SystemCombinePhysicalMemoryInformation = 0x82, 142 | SystemEntropyInterruptTimingInformation = 0x83, 143 | SystemConsoleInformation = 0x84, 144 | SystemPlatformBinaryInformation = 0x85, 145 | SystemPolicyInformation = 0x86, 146 | SystemHypervisorProcessorCountInformation = 0x87, 147 | SystemDeviceDataInformation = 0x88, 148 | SystemDeviceDataEnumerationInformation = 0x89, 149 | SystemMemoryTopologyInformation = 0x8A, 150 | SystemMemoryChannelInformation = 0x8B, 151 | SystemBootLogoInformation = 0x8C, 152 | SystemProcessorPerformanceInformationEx = 0x8D, 153 | SystemCriticalProcessErrorLogInformation = 0x8E, 154 | SystemSecureBootPolicyInformation = 0x8F, 155 | SystemPageFileInformationEx = 0x90, 156 | SystemSecureBootInformation = 0x91, 157 | SystemEntropyInterruptTimingRawInformation = 0x92, 158 | SystemPortableWorkspaceEfiLauncherInformation = 0x93, 159 | SystemFullProcessInformation = 0x94, 160 | SystemKernelDebuggerInformationEx = 0x95, 161 | SystemBootMetadataInformation = 0x96, 162 | SystemSoftRebootInformation = 0x97, 163 | SystemElamCertificateInformation = 0x98, 164 | SystemOfflineDumpConfigInformation = 0x99, 165 | SystemProcessorFeaturesInformation = 0x9A, 166 | SystemRegistryReconciliationInformation = 0x9B, 167 | SystemEdidInformation = 0x9C, 168 | SystemManufacturingInformation = 0x9D, 169 | SystemEnergyEstimationConfigInformation = 0x9E, 170 | SystemHypervisorDetailInformation = 0x9F, 171 | SystemProcessorCycleStatsInformation = 0xA0, 172 | SystemVmGenerationCountInformation = 0xA1, 173 | SystemTrustedPlatformModuleInformation = 0xA2, 174 | SystemKernelDebuggerFlags = 0xA3, 175 | SystemCodeIntegrityPolicyInformation = 0xA4, 176 | SystemIsolatedUserModeInformation = 0xA5, 177 | SystemHardwareSecurityTestInterfaceResultsInformation = 0xA6, 178 | SystemSingleModuleInformation = 0xA7, 179 | SystemAllowedCpuSetsInformation = 0xA8, 180 | SystemDmaProtectionInformation = 0xA9, 181 | SystemInterruptCpuSetsInformation = 0xAA, 182 | SystemSecureBootPolicyFullInformation = 0xAB, 183 | SystemCodeIntegrityPolicyFullInformation = 0xAC, 184 | SystemAffinitizedInterruptProcessorInformation = 0xAD, 185 | SystemRootSiloInformation = 0xAE, 186 | SystemCpuSetInformation = 0xAF, 187 | SystemCpuSetTagInformation = 0xB0, 188 | SystemWin32WerStartCallout = 0xB1, 189 | SystemSecureKernelProfileInformation = 0xB2, 190 | SystemCodeIntegrityPlatformManifestInformation = 0xB3, 191 | SystemInterruptSteeringInformation = 0xB4, 192 | SystemSuppportedProcessorArchitectures = 0xB5, 193 | SystemMemoryUsageInformation = 0xB6, 194 | SystemCodeIntegrityCertificateInformation = 0xB7, 195 | SystemPhysicalMemoryInformation = 0xB8, 196 | SystemControlFlowTransition = 0xB9, 197 | SystemKernelDebuggingAllowed = 0xBA, 198 | SystemActivityModerationExeState = 0xBB, 199 | SystemActivityModerationUserSettings = 0xBC, 200 | SystemCodeIntegrityPoliciesFullInformation = 0xBD, 201 | SystemCodeIntegrityUnlockInformation = 0xBE, 202 | SystemIntegrityQuotaInformation = 0xBF, 203 | SystemFlushInformation = 0xC0, 204 | SystemProcessorIdleMaskInformation = 0xC1, 205 | SystemSecureDumpEncryptionInformation = 0xC2, 206 | SystemWriteConstraintInformation = 0xC3, 207 | SystemKernelVaShadowInformation = 0xC4, 208 | SystemHypervisorSharedPageInformation = 0xC5, 209 | SystemFirmwareBootPerformanceInformation = 0xC6, 210 | SystemCodeIntegrityVerificationInformation = 0xC7, 211 | SystemFirmwarePartitionInformation = 0xC8, 212 | SystemSpeculationControlInformation = 0xC9, 213 | SystemDmaGuardPolicyInformation = 0xCA, 214 | SystemEnclaveLaunchControlInformation = 0xCB, 215 | SystemWorkloadAllowedCpuSetsInformation = 0xCC, 216 | SystemCodeIntegrityUnlockModeInformation = 0xCD, 217 | SystemLeapSecondInformation = 0xCE, 218 | SystemFlags2Information = 0xCF, 219 | SystemSecurityModelInformation = 0xD0, 220 | SystemCodeIntegritySyntheticCacheInformation = 0xD1, 221 | MaxSystemInfoClass = 0xD2 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HyperV Driver Thread Detection PoC 2 | 3 | This VM detection trick utilises thread information from `NtQuerySystemInformation` in order to stealthily detect HyperV's vmbus driver. 4 | 5 | This trick can be utilised to stealthily identify any driver that spawns multiple threads on the system, without querying suspicious objects such as driver objects, device objects, driver files, registry keys, or system module information. From an application behaviour perspective, the operation is functionally indistinguishable from enumerating processes. 6 | 7 | A full writeup can be found on [Nettitude Labs](https://labs.nettitude.com/blog/). --------------------------------------------------------------------------------