├── .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/).
--------------------------------------------------------------------------------