├── .gitignore
├── NCC Group's Mimikatz Detector - Busylight.sln
├── NCCDetectDLL
├── NCCDetectDLL.vcxproj
├── NCCDetectDLL.vcxproj.filters
├── dllmain.cpp
├── framework.h
├── pch.cpp
└── pch.h
├── README.md
├── driver
├── detector-mini.c
├── detector-mini.h
└── umdf2
│ ├── DetectUm.inx
│ ├── DetectUm.rc
│ ├── NCCDetectUm.vcxproj
│ ├── NCCDetectUm.vcxproj.Filters
│ └── util.c
├── inc
└── common.h
├── install.bat
└── uninstall.bat
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | [Xx]64/
19 | [Xx]86/
20 | [Bb]uild/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 | signed/
25 |
26 | # Visual Studio 2015 cache/options directory
27 | .vs/
28 | # Uncomment if you have tasks that create the project's static files in wwwroot
29 | #wwwroot/
30 |
31 | # MSTest test Results
32 | [Tt]est[Rr]esult*/
33 | [Bb]uild[Ll]og.*
34 |
35 | # NUNIT
36 | *.VisualState.xml
37 | TestResult.xml
38 |
39 | # Build Results of an ATL Project
40 | [Dd]ebugPS/
41 | [Rr]eleasePS/
42 | dlldata.c
43 |
44 | # DNX
45 | project.lock.json
46 | artifacts/
47 |
48 | *_i.c
49 | *_p.c
50 | *_i.h
51 | *.ilk
52 | *.meta
53 | *.obj
54 | *.pch
55 | *.pdb
56 | *.pgc
57 | *.pgd
58 | *.rsp
59 | *.sbr
60 | *.tlb
61 | *.tli
62 | *.tlh
63 | *.tmp
64 | *.tmp_proj
65 | *.log
66 | *.vspscc
67 | *.vssscc
68 | .builds
69 | *.pidb
70 | *.svclog
71 | *.scc
72 |
73 | # Chutzpah Test files
74 | _Chutzpah*
75 |
76 | # Visual C++ cache files
77 | ipch/
78 | *.aps
79 | *.ncb
80 | *.opendb
81 | *.opensdf
82 | *.sdf
83 | *.cachefile
84 | *.VC.db
85 |
86 | # Visual Studio profiler
87 | *.psess
88 | *.vsp
89 | *.vspx
90 | *.sap
91 |
92 | # TFS 2012 Local Workspace
93 | $tf/
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 | *.DotSettings.user
102 |
103 | # JustCode is a .NET coding add-in
104 | .JustCode
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | _NCrunch_*
114 | .*crunch*.local.xml
115 | nCrunchTemp_*
116 |
117 | # MightyMoose
118 | *.mm.*
119 | AutoTest.Net/
120 |
121 | # Web workbench (sass)
122 | .sass-cache/
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.[Pp]ublish.xml
142 | *.azurePubxml
143 |
144 | # TODO: Un-comment the next line if you do not want to checkin
145 | # your web deploy settings because they may include unencrypted
146 | # passwords
147 | #*.pubxml
148 | *.publishproj
149 |
150 | # NuGet Packages
151 | *.nupkg
152 | # The packages folder can be ignored because of Package Restore
153 | **/packages/*
154 | # except build/, which is used as an MSBuild target.
155 | !**/packages/build/
156 | # Uncomment if necessary however generally it will be regenerated when needed
157 | #!**/packages/repositories.config
158 | # NuGet v3's project.json files produces more ignoreable files
159 | *.nuget.props
160 | *.nuget.targets
161 |
162 | # Microsoft Azure Build Output
163 | csx/
164 | *.build.csdef
165 |
166 | # Microsoft Azure Emulator
167 | ecf/
168 | rcf/
169 |
170 | # Windows Store app package directory
171 | AppPackages/
172 | BundleArtifacts/
173 |
174 | # Visual Studio cache files
175 | # files ending in .cache can be ignored
176 | *.[Cc]ache
177 | # but keep track of directories ending in .cache
178 | !*.[Cc]ache/
179 |
180 | # Others
181 | ClientBin/
182 | [Ss]tyle[Cc]op.*
183 | ~$*
184 | *~
185 | *.dbmdl
186 | *.dbproj.schemaview
187 | *.pfx
188 | *.publishsettings
189 | node_modules/
190 | orleans.codegen.cs
191 |
192 | # RIA/Silverlight projects
193 | Generated_Code/
194 |
195 | # Backup & report files from converting an old project file
196 | # to a newer Visual Studio version. Backup files are not needed,
197 | # because we have git ;-)
198 | _UpgradeReport_Files/
199 | Backup*/
200 | UpgradeLog*.XML
201 | UpgradeLog*.htm
202 |
203 | # SQL Server files
204 | *.mdf
205 | *.ldf
206 |
207 | # Business Intelligence projects
208 | *.rdl.data
209 | *.bim.layout
210 | *.bim_*.settings
211 |
212 | # Microsoft Fakes
213 | FakesAssemblies/
214 |
215 | # GhostDoc plugin setting file
216 | *.GhostDoc.xml
217 |
218 | # Node.js Tools for Visual Studio
219 | .ntvs_analysis.dat
220 |
221 | # Visual Studio 6 build log
222 | *.plg
223 |
224 | # Visual Studio 6 workspace options file
225 | *.opt
226 |
227 | # Visual Studio LightSwitch build output
228 | **/*.HTMLClient/GeneratedArtifacts
229 | **/*.DesktopClient/GeneratedArtifacts
230 | **/*.DesktopClient/ModelManifest.xml
231 | **/*.Server/GeneratedArtifacts
232 | **/*.Server/ModelManifest.xml
233 | _Pvt_Extensions
234 |
235 | # LightSwitch generated files
236 | GeneratedArtifacts/
237 | ModelManifest.xml
238 |
239 | # Paket dependency manager
240 | .paket/paket.exe
241 |
242 | # FAKE - F# Make
243 | .fake/
244 |
245 |
--------------------------------------------------------------------------------
/NCC Group's Mimikatz Detector - Busylight.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.32002.261
5 | MinimumVisualStudioVersion = 12.0
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Umdf2", "Umdf2", "{8F9E6911-FDD4-4D28-9821-8A3AC44EBC82}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Driver", "Driver", "{837BF49F-1143-4D82-A340-99AAFAE83F72}"
9 | EndProject
10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DetectUm", "driver\umdf2\NCCDetectUm.vcxproj", "{EEECA421-064D-4E2E-8832-B0F43A9B394C}"
11 | EndProject
12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NCCDetectDLL", "NCCDetectDLL\NCCDetectDLL.vcxproj", "{3FB302B6-0B57-479A-A2DD-1FA87D01C45D}"
13 | EndProject
14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DLL", "DLL", "{60BEDA33-0403-4F53-80DA-C298A45F2CBF}"
15 | EndProject
16 | Global
17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
18 | Debug|Win32 = Debug|Win32
19 | Debug|x64 = Debug|x64
20 | Release|Win32 = Release|Win32
21 | Release|x64 = Release|x64
22 | EndGlobalSection
23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
24 | {EEECA421-064D-4E2E-8832-B0F43A9B394C}.Debug|Win32.ActiveCfg = Debug|Win32
25 | {EEECA421-064D-4E2E-8832-B0F43A9B394C}.Debug|Win32.Build.0 = Debug|Win32
26 | {EEECA421-064D-4E2E-8832-B0F43A9B394C}.Debug|x64.ActiveCfg = Debug|x64
27 | {EEECA421-064D-4E2E-8832-B0F43A9B394C}.Debug|x64.Build.0 = Debug|x64
28 | {EEECA421-064D-4E2E-8832-B0F43A9B394C}.Release|Win32.ActiveCfg = Release|Win32
29 | {EEECA421-064D-4E2E-8832-B0F43A9B394C}.Release|Win32.Build.0 = Release|Win32
30 | {EEECA421-064D-4E2E-8832-B0F43A9B394C}.Release|x64.ActiveCfg = Release|x64
31 | {EEECA421-064D-4E2E-8832-B0F43A9B394C}.Release|x64.Build.0 = Release|x64
32 | {3FB302B6-0B57-479A-A2DD-1FA87D01C45D}.Debug|Win32.ActiveCfg = Debug|Win32
33 | {3FB302B6-0B57-479A-A2DD-1FA87D01C45D}.Debug|Win32.Build.0 = Debug|Win32
34 | {3FB302B6-0B57-479A-A2DD-1FA87D01C45D}.Debug|x64.ActiveCfg = Debug|x64
35 | {3FB302B6-0B57-479A-A2DD-1FA87D01C45D}.Debug|x64.Build.0 = Debug|x64
36 | {3FB302B6-0B57-479A-A2DD-1FA87D01C45D}.Release|Win32.ActiveCfg = Release|Win32
37 | {3FB302B6-0B57-479A-A2DD-1FA87D01C45D}.Release|Win32.Build.0 = Release|Win32
38 | {3FB302B6-0B57-479A-A2DD-1FA87D01C45D}.Release|x64.ActiveCfg = Release|x64
39 | {3FB302B6-0B57-479A-A2DD-1FA87D01C45D}.Release|x64.Build.0 = Release|x64
40 | EndGlobalSection
41 | GlobalSection(SolutionProperties) = preSolution
42 | HideSolutionNode = FALSE
43 | EndGlobalSection
44 | GlobalSection(NestedProjects) = preSolution
45 | {8F9E6911-FDD4-4D28-9821-8A3AC44EBC82} = {837BF49F-1143-4D82-A340-99AAFAE83F72}
46 | {EEECA421-064D-4E2E-8832-B0F43A9B394C} = {8F9E6911-FDD4-4D28-9821-8A3AC44EBC82}
47 | {3FB302B6-0B57-479A-A2DD-1FA87D01C45D} = {60BEDA33-0403-4F53-80DA-C298A45F2CBF}
48 | EndGlobalSection
49 | GlobalSection(ExtensibilityGlobals) = postSolution
50 | SolutionGuid = {329127AC-8C5E-44FC-8483-9DCEFC45BCC3}
51 | EndGlobalSection
52 | EndGlobal
53 |
--------------------------------------------------------------------------------
/NCCDetectDLL/NCCDetectDLL.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 | 16.0
23 | Win32Proj
24 | {3fb302b6-0b57-479a-a2dd-1fa87d01c45d}
25 | NCCDetectDLL
26 | 10.0
27 | NCCDetectDLL
28 |
29 |
30 |
31 | DynamicLibrary
32 | true
33 | v142
34 | Unicode
35 |
36 |
37 | DynamicLibrary
38 | false
39 | v142
40 | true
41 | Unicode
42 |
43 |
44 | DynamicLibrary
45 | true
46 | v142
47 | Unicode
48 |
49 |
50 | DynamicLibrary
51 | false
52 | v142
53 | true
54 | Unicode
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | true
76 |
77 |
78 | false
79 |
80 |
81 | true
82 |
83 |
84 | false
85 | DetectDLL
86 |
87 |
88 |
89 | Level3
90 | true
91 | WIN32;_DEBUG;NCCDETECTDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
92 | true
93 | Use
94 | pch.h
95 |
96 |
97 | Windows
98 | true
99 | false
100 | ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
101 |
102 |
103 |
104 |
105 | Level3
106 | true
107 | true
108 | true
109 | WIN32;NDEBUG;NCCDETECTDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
110 | true
111 | Use
112 | pch.h
113 |
114 |
115 | Windows
116 | true
117 | true
118 | true
119 | false
120 | ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
121 |
122 |
123 |
124 |
125 | Level3
126 | true
127 | _DEBUG;NCCDETECTDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
128 | true
129 | NotUsing
130 | pch.h
131 |
132 |
133 | Windows
134 | true
135 | false
136 | ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
137 |
138 |
139 |
140 |
141 | Level3
142 | true
143 | true
144 | true
145 | NDEBUG;NCCDETECTDLL_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
146 | true
147 | NotUsing
148 | pch.h
149 | %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm;..\..\inc;..
150 |
151 |
152 | Windows
153 | true
154 | true
155 | true
156 | false
157 | ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 | Create
168 | Create
169 | Create
170 | Create
171 |
172 |
173 |
174 |
175 |
176 |
--------------------------------------------------------------------------------
/NCCDetectDLL/NCCDetectDLL.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 | Header Files
20 |
21 |
22 | Header Files
23 |
24 |
25 |
26 |
27 | Source Files
28 |
29 |
30 | Source Files
31 |
32 |
33 |
--------------------------------------------------------------------------------
/NCCDetectDLL/dllmain.cpp:
--------------------------------------------------------------------------------
1 | #ifndef WIN32_LEAN_AND_MEAN
2 | #define WIN32_LEAN_AND_MEAN
3 | #endif
4 |
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 |
11 | #pragma comment(lib,"Ws2_32.lib")
12 |
13 | #define DEBUGLOG TRUE
14 | #define EVENTLOG TRUE
15 | #define SYSLOG FALSE
16 | #define HOSTNAME "192.168.0.31"
17 | #define PORT 514
18 | #define SYSLOG_DGRAM_SIZE 1024
19 |
20 | #define MAX_NAME 256
21 |
22 | #if EVENTLOG == TRUE
23 | HANDLE hEventLog;
24 | PSID sidCurrentAccount = NULL;
25 | #endif
26 |
27 | #if SYSLOG == TRUE
28 | // copy&pasta&edit from https://github.com/asankah/syslog-win32/blob/master/syslogc.c
29 | static SOCKET socketSyslog = INVALID_SOCKET;
30 | BOOL bSyslogConnected = FALSE;
31 | CHAR szComputerName[256];
32 |
33 | void vsyslog(int pri, char* fmt, va_list ap)
34 | {
35 | static char month[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
36 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
37 | char datagramm[SYSLOG_DGRAM_SIZE];
38 | SYSTEMTIME stm;
39 | int len;
40 | char* p;
41 |
42 | if (!bSyslogConnected)
43 | return;
44 |
45 | GetLocalTime(&stm);
46 | len = sprintf_s(datagramm, sizeof(datagramm),
47 | "<%d>%s %2d %02d:%02d:%02d %s %s%s: ",
48 | pri,
49 | month[stm.wMonth - 1], stm.wDay, stm.wHour, stm.wMinute, stm.wSecond,
50 | szComputerName, "umdf2", "[0]");
51 | len += vsprintf_s(datagramm + len, SYSLOG_DGRAM_SIZE - len, fmt, ap);
52 | p = strchr(datagramm, '\n');
53 | if (p)
54 | *p = 0;
55 | p = strchr(datagramm, '\r');
56 | if (p)
57 | *p = 0;
58 |
59 | sendto(socketSyslog, datagramm, len, 0, NULL, 0);
60 |
61 | return;
62 | }
63 |
64 | void syslog(int pri, char* fmt, ...)
65 | {
66 | va_list ap;
67 |
68 | va_start(ap, fmt);
69 | vsyslog(pri, fmt, ap);
70 | va_end(ap);
71 | }
72 |
73 | #endif
74 |
75 |
76 | INT GetSid(PSID* ppSid)
77 | {
78 | // Validate the input parameter.
79 | if (ppSid == NULL)
80 | {
81 | return -1;
82 | }
83 |
84 | // Create buffers that may be large enough.
85 | // If a buffer is too small, the count parameter will be set to the size needed.
86 | DWORD cbSid = 0;
87 | DWORD dwSidBufferSize = 32;
88 | SID_NAME_USE eSidType;
89 | DWORD dwErrorCode = 0;
90 | DWORD dwUserNameLength = 256, dwDomainSize = 256;
91 | TCHAR szUserName[256], szDomainName[256];
92 |
93 |
94 | // Create buffers for the SID and the domain name.
95 | *ppSid = (PSID) new BYTE[dwSidBufferSize];
96 | if (*ppSid == NULL)
97 | {
98 | return -1;
99 | }
100 | memset(*ppSid, 0, dwSidBufferSize);
101 |
102 | if (!GetUserNameW(szUserName, &dwUserNameLength)) {
103 | return -1;
104 | }
105 |
106 | // Obtain the SID for the account name passed.
107 | for (; ; )
108 | {
109 |
110 | // Set the count variables to the buffer sizes and retrieve the SID.
111 | cbSid = dwSidBufferSize;
112 | if (LookupAccountNameW(
113 | NULL, // Computer name. NULL for the local computer
114 | szUserName,
115 | *ppSid, // Pointer to the SID buffer. Use NULL to get the size needed,
116 | &cbSid, // Size of the SID buffer needed.
117 | szDomainName, // wszDomainName,
118 | &dwDomainSize,
119 | &eSidType
120 | ))
121 | {
122 | if (IsValidSid(*ppSid) == FALSE)
123 | {
124 | //wprintf(L"The SID for %s is invalid.\n", wszAccName);
125 | dwErrorCode = -2;
126 | }
127 | break;
128 | }
129 | dwErrorCode = GetLastError();
130 |
131 | // Check if one of the buffers was too small.
132 | if (dwErrorCode == ERROR_INSUFFICIENT_BUFFER)
133 | {
134 | if (cbSid > dwSidBufferSize)
135 | {
136 |
137 | // Reallocate memory for the SID buffer.
138 | //wprintf(L"The SID buffer was too small. It will be reallocated.\n");
139 | FreeSid(*ppSid);
140 | *ppSid = (PSID) new BYTE[cbSid];
141 | if (*ppSid == NULL)
142 | {
143 | return -1;
144 | }
145 | memset(*ppSid, 0, cbSid);
146 | dwSidBufferSize = cbSid;
147 | }
148 | }
149 | else
150 | {
151 | return -1;
152 | break;
153 | }
154 | }
155 |
156 | return 0;
157 | }
158 |
159 |
160 |
161 | void DebugOut(wchar_t* fmt, ...)
162 | {
163 | va_list argp;
164 | va_start(argp, fmt);
165 | wchar_t dbg_out[4096];
166 | vswprintf_s(dbg_out, fmt, argp);
167 | va_end(argp);
168 | OutputDebugString(dbg_out);
169 | }
170 |
171 | VOID init()
172 | {
173 | #if EVENTLOG == TRUE
174 | GetSid(&sidCurrentAccount);
175 |
176 | hEventLog = OpenEventLogA(NULL, "Application");
177 | if (hEventLog == NULL)
178 | {
179 | OutputDebugStringA(("Eventlog source failed to open\n"));
180 | }
181 | #endif
182 | #if SYSLOG == TRUE
183 | WSADATA wsd;
184 | struct hostent* phe = NULL;
185 | struct addrinfo hints, *res;
186 | SOCKADDR_IN syslog_hostaddr;
187 | PADDRINFOA addr = NULL;
188 |
189 | if (WSAStartup(MAKEWORD(2, 2), &wsd)) {
190 | OutputDebugStringA(("WSAStartup failed\n"));
191 | return;
192 | }
193 |
194 | memset(&syslog_hostaddr, 0, sizeof(SOCKADDR_IN));
195 | memset(&hints, 0, sizeof(hints));
196 | syslog_hostaddr.sin_family = AF_INET;
197 | syslog_hostaddr.sin_port = htons(PORT);
198 |
199 | DWORD a = -1;
200 | if (a = getaddrinfo(HOSTNAME, NULL, &hints, &res))
201 | {
202 | DebugOut((wchar_t*)L"getaddrinfo failed %d %d\n", a, GetLastError());
203 | return;
204 | }
205 |
206 | syslog_hostaddr.sin_addr.s_addr = (unsigned int)((struct sockaddr_in*)(res->ai_addr))->sin_addr.s_addr;
207 | freeaddrinfo(res);
208 |
209 | socketSyslog = socket(AF_INET, SOCK_DGRAM, 0);
210 | if (INVALID_SOCKET == socketSyslog)
211 | {
212 | OutputDebugStringA(("socket failed\n"));
213 | return;
214 | }
215 |
216 | if (connect(socketSyslog, (struct sockaddr*)&syslog_hostaddr, sizeof(syslog_hostaddr)) < 0)
217 | {
218 | OutputDebugStringA(("connect failed\n"));
219 | return;
220 | }
221 |
222 | DWORD length = 256;
223 | if (!GetComputerNameA(szComputerName, &length))
224 | {
225 | OutputDebugStringA(("GetComputerNameA failed\n"));
226 | return;
227 | }
228 |
229 | bSyslogConnected = TRUE;
230 |
231 | #endif
232 | }
233 |
234 | VOID deinit()
235 | {
236 | #if SYSLOG == TRUE
237 | if (socketSyslog != INVALID_SOCKET)
238 | {
239 | closesocket(socketSyslog);
240 | socketSyslog = INVALID_SOCKET;
241 | bSyslogConnected = FALSE;
242 | }
243 | WSACleanup();
244 | #endif
245 | }
246 |
247 |
248 | BOOL APIENTRY DllMain( HMODULE hModule,
249 | DWORD ul_reason_for_call,
250 | LPVOID lpReserved
251 | )
252 | {
253 | switch (ul_reason_for_call)
254 | {
255 | case DLL_PROCESS_ATTACH:
256 | init();
257 | break;
258 | case DLL_THREAD_ATTACH:
259 | break;
260 | case DLL_THREAD_DETACH:
261 | break;
262 | case DLL_PROCESS_DETACH:
263 | deinit();
264 | break;
265 | }
266 | return TRUE;
267 | }
268 |
269 | BOOL GetLogonFromToken(HANDLE hToken, CHAR *szUser, CHAR *szDomain)
270 | {
271 | DWORD dwSize = MAX_NAME;
272 | BOOL bSuccess = FALSE;
273 | DWORD dwLength = 0;
274 | PTOKEN_USER ptu = NULL;
275 | SID_NAME_USE SidType;
276 |
277 | if (NULL == hToken)
278 | return FALSE;
279 |
280 | if (!GetTokenInformation(hToken, TokenUser, (LPVOID)ptu, 0, &dwLength))
281 | {
282 | if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
283 | return FALSE;
284 |
285 | ptu = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwLength);
286 |
287 | if (ptu == NULL)
288 | return FALSE;
289 |
290 | }
291 |
292 | if (!GetTokenInformation(hToken, TokenUser, (LPVOID)ptu, dwLength, &dwLength))
293 | {
294 | HeapFree(GetProcessHeap(), NULL, (LPVOID)ptu);
295 | return FALSE;
296 | }
297 |
298 | if (!LookupAccountSidA(NULL, ptu->User.Sid, (LPSTR)szUser, &dwSize, (LPSTR)szDomain, &dwSize, &SidType))
299 | {
300 | DWORD dwResult = GetLastError();
301 | if (dwResult == ERROR_NONE_MAPPED)
302 | strncpy_s(szUser, MAX_NAME, "NONE_MAPPED", strlen("NONE_MAPPED"));
303 | else
304 | {
305 | return FALSE;
306 | }
307 | }
308 |
309 | return TRUE;
310 | }
311 |
312 | BOOL GetUserFromProcess(const DWORD procId, CHAR *szUser, CHAR *szDomain)
313 | {
314 | HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, procId);
315 | if (hProcess == NULL)
316 | return FALSE;
317 |
318 | HANDLE hToken = NULL;
319 |
320 | if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken))
321 | {
322 | CloseHandle(hProcess);
323 | return FALSE;
324 | }
325 | BOOL bres = GetLogonFromToken(hToken, szUser, szDomain);
326 |
327 | CloseHandle(hToken);
328 | CloseHandle(hProcess);
329 |
330 | return bres;
331 | }
332 |
333 | extern "C" __declspec(dllexport) VOID start(ULONG ulPid)
334 | {
335 | char* szDebugMessage = NULL;
336 | char szUser[MAX_NAME];
337 | char szDomain[MAX_NAME];
338 |
339 | memset(szUser, 0, MAX_NAME);
340 | memset(szDomain, 0, MAX_NAME);
341 |
342 | if ((szDebugMessage = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1024)) == NULL)
343 | {
344 | return;
345 | }
346 |
347 | if (GetUserFromProcess(ulPid, szUser, szDomain))
348 | sprintf_s(szDebugMessage, 1024, "Tool started. PID: %lu User: %s\\%s", ulPid, szDomain, szUser);
349 | else
350 | sprintf_s(szDebugMessage, 1024, "Tool started. PID: %lu", ulPid);
351 |
352 |
353 | #if DEBUGLOG == TRUE
354 | OutputDebugStringA((szDebugMessage));
355 | #endif
356 | #if EVENTLOG == TRUE
357 | if (hEventLog != NULL)
358 | {
359 | ReportEventA(hEventLog, EVENTLOG_WARNING_TYPE, 0, 0, sidCurrentAccount, 1, 0, (LPCSTR *)&szDebugMessage, NULL);
360 | }
361 | #endif
362 | #if SYSLOG == TRUE
363 | vsyslog(0, szDebugMessage, 0);
364 | #endif
365 | HeapFree(GetProcessHeap(), NULL, szDebugMessage);
366 | }
367 |
368 | extern "C" __declspec(dllexport) VOID keepalive(ULONG ulPid)
369 | {
370 | char* szDebugMessage = NULL;
371 | char szUser[MAX_NAME];
372 | char szDomain[MAX_NAME];
373 |
374 | memset(szUser, 0, MAX_NAME);
375 | memset(szDomain, 0, MAX_NAME);
376 |
377 | if ((szDebugMessage = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1024)) == NULL)
378 | {
379 | return;
380 | }
381 |
382 | if (GetUserFromProcess(ulPid, szUser, szDomain))
383 | sprintf_s(szDebugMessage, 1024, "Tool sent keepalive. PID: %lu User: %s\\%s", ulPid, szDomain, szUser);
384 | else
385 | sprintf_s(szDebugMessage, 1024, "Tool sent keepalive. PID: %lu", ulPid);
386 | #if DEBUGLOG == TRUE
387 | OutputDebugStringA((szDebugMessage));
388 | #endif
389 | #if EVENTLOG == TRUE
390 | if (hEventLog != NULL)
391 | {
392 | ReportEventA(hEventLog, EVENTLOG_WARNING_TYPE, 0, 0, sidCurrentAccount, 1, 0, (LPCSTR*)&szDebugMessage, NULL);
393 | }
394 | #endif
395 | #if SYSLOG == TRUE
396 | vsyslog(0, szDebugMessage, 0);
397 | #endif
398 | HeapFree(GetProcessHeap(), NULL, szDebugMessage);
399 | }
400 |
401 | extern "C" __declspec(dllexport) VOID stop(ULONG ulPid)
402 | {
403 | char* szDebugMessage = NULL;
404 | char szUser[MAX_NAME];
405 | char szDomain[MAX_NAME];
406 |
407 | memset(szUser, 0, MAX_NAME);
408 | memset(szDomain, 0, MAX_NAME);
409 |
410 | if ((szDebugMessage = (char*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 1024)) == NULL)
411 | {
412 | return;
413 | }
414 |
415 | if (GetUserFromProcess(ulPid, szUser, szDomain))
416 | sprintf_s(szDebugMessage, 1024, "Tool stopped. PID: %lu User: %s\\%s", ulPid, szDomain, szUser);
417 | else
418 | sprintf_s(szDebugMessage, 1024, "Tool stopped. PID: %lu", ulPid);
419 | #if DEBUGLOG == TRUE
420 | OutputDebugStringA((szDebugMessage));
421 | #endif
422 | #if EVENTLOG == TRUE
423 | if (hEventLog != NULL)
424 | {
425 | ReportEventA(hEventLog, EVENTLOG_WARNING_TYPE, 0, 0, sidCurrentAccount, 1, 0, (LPCSTR*)&szDebugMessage, NULL);
426 | }
427 | #endif
428 | #if SYSLOG == TRUE
429 | vsyslog(0, szDebugMessage, 0);
430 | #endif
431 | HeapFree(GetProcessHeap(), NULL, szDebugMessage);
432 | }
433 |
--------------------------------------------------------------------------------
/NCCDetectDLL/framework.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
4 | // Windows Header Files
5 | #include
6 |
--------------------------------------------------------------------------------
/NCCDetectDLL/pch.cpp:
--------------------------------------------------------------------------------
1 | // pch.cpp: source file corresponding to the pre-compiled header
2 |
3 | #include "pch.h"
4 |
5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
6 |
--------------------------------------------------------------------------------
/NCCDetectDLL/pch.h:
--------------------------------------------------------------------------------
1 | // pch.h: This is a precompiled header file.
2 | // Files listed below are compiled only once, improving build performance for future builds.
3 | // This also affects IntelliSense performance, including code completion and many code browsing features.
4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds.
5 | // Do not add files here that you will be updating frequently as this negates the performance advantage.
6 |
7 | #ifndef PCH_H
8 | #define PCH_H
9 |
10 | // add headers that you want to pre-compile here
11 | #include "framework.h"
12 |
13 | #endif //PCH_H
14 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mimikatz detector driver - Busylight
2 |
3 | USB HID driver emulation with PID/VID (0x3bca/0x27bb) of Plenom A/S Busylight Alpha, that is supported by Mimikatz.
4 | When mimikatz is executed, a thread is spwaned by default that tries to locate one of the busylights that is supported. All HID devices are enumerated, if PID/VID is matching then packets are sent to flash the busylight in different colours.
5 |
6 | There are three types of packets that are sent to this device by mimikatz:
7 | * keepalive - sent in every second or so
8 | * start - sent only when device if found
9 | * stop - when mimikatz is terminated or `busylight::off` is called
10 |
11 | Since this device emulates a HID device that fully mimics the real USB device, mimikatz cannot distinguish between the two.
12 |
13 | ## What does the driver do (DetectUm)?
14 |
15 | The driver is a umdf2 driver, which means:
16 | * Works from Windows 8.1 and up
17 | * Runs in user-space instead of kernel space
18 | * Runs as NT Authority\Local Serive (low priv)
19 | * Can be signed with a signing certificate
20 |
21 | It emulates the behaviour of the busylight transparently. The driver checks if any of the three (start, keepalive, stop) packets have been received, if so, it loads a DLL and calls the three exported functions respectively.
22 | The DLL doesn't need to be signed, although it might help trusted distribution. The functionality of the DLL can be changed easily.
23 |
24 | ## What does the DLL do (DetectDLL)?
25 |
26 | It has three exported functions:
27 | * void start()
28 | * void keepalive()
29 | * void stop()
30 |
31 | These are called by the driver respectively. Currently three different reporting modes are implemented:
32 | * Eventlog logging
33 | * Debug message logging (can be seen by a debugger attached to the WUDFhost.exe)
34 | * Remote syslog logging (might require more testing)
35 |
36 | ## What does the DLL log (DetectDLL)?
37 |
38 | The following format is shown in the evenlog (or on the two other logging interface) under Windows Logs\Application
39 |
40 | ```
41 | Tool started. PID: 13456
42 | ```
43 |
44 | Pretty basic, but the two most important information is there. It is either started/stopped or keepalive, which means that the tool was running at the moment. PID is the process ID.
45 | Since the UMDF drivers are running the userspace with a low privileged user, information about other processes cannot be retrived in depth, this should be done by another service with higher privileges.
46 |
47 |
48 | ## Installation
49 | Following needs to be performed as elevated user:
50 | * Execute the following command: `install.bat`
51 |
52 | ## Uninstallation
53 | Following needs to be performed as elevated user:
54 | * Execute the following command: `uninstall.bat`
55 |
56 | ## Tests
57 | It was tested on three different Windows 10 installations with rsyslog server.
58 |
59 | The following Mimikatz variants were tested:
60 | * Original version of Mimikatz since *8th of October 2015* (Detected)
61 | * Original compiled into DLL (Detected)
62 | * Original compiled into PowerShell (Invoke-Mimikatz) (Detected)
63 | * PowerSploit - Invoke-Mimikatz (Detected)
64 | * CrackMapExec - Invoke-Mimikatz (Detected)
65 | * Shellenium - Invoke-Mimikatz (Detected)
66 |
67 |
68 | * Cobalt Strike (NOT Detected)
69 | * Metasploit kiwi module (NOT Detected)
70 | * Pypykatz (NOT Detected)
--------------------------------------------------------------------------------
/driver/detector-mini.c:
--------------------------------------------------------------------------------
1 | /*++
2 |
3 | Copyright (C) Microsoft Corporation, All Rights Reserved.
4 |
5 | Module Name:
6 |
7 | vhidmini.cpp
8 |
9 | Abstract:
10 |
11 | This module contains the implementation of the driver
12 |
13 | Environment:
14 |
15 | Windows Driver Framework (WDF)
16 |
17 | --*/
18 |
19 | #include "detector-mini.h"
20 |
21 | //
22 | // This is the default report descriptor for the virtual Hid device returned
23 | // by the mini driver in response to IOCTL_HID_GET_REPORT_DESCRIPTOR.
24 | //
25 | HID_REPORT_DESCRIPTOR G_DefaultReportDescriptor[] = {
26 | 0x06,0x00, 0xFF, // USAGE_PAGE (Vender Defined Usage Page)
27 | 0x09,0x01, // USAGE (Vendor Usage 0x01)
28 | 0xA1,0x01, // COLLECTION (Application)
29 | //0x85,CONTROL_FEATURE_REPORT_ID, // REPORT_ID (1)
30 | 0x09,0x01, // USAGE (Vendor Usage 0x01)
31 | 0x15,0x00, // LOGICAL_MINIMUM(0)
32 | 0x26,0xff, 0x00, // LOGICAL_MAXIMUM(255)
33 | 0x75,0x08, // REPORT_SIZE (0x08)
34 | 0x96,(FEATURE_REPORT_SIZE_CB & 0xff), (FEATURE_REPORT_SIZE_CB >> 8), // REPORT_COUNT
35 | 0xB1,0x00, // FEATURE (Data,Ary,Abs)
36 | 0x09,0x01, // USAGE (Vendor Usage 0x01)
37 | 0x75,0x08, // REPORT_SIZE (0x08)
38 | 0x96,(INPUT_REPORT_SIZE_CB & 0xff), (INPUT_REPORT_SIZE_CB >> 8), // REPORT_COUNT
39 | 0x81,0x00, // INPUT (Data,Ary,Abs)
40 | 0x09,0x01, // USAGE (Vendor Usage 0x01)
41 | 0x75,0x08, // REPORT_SIZE (0x08)
42 | 0x96,(OUTPUT_REPORT_SIZE_CB & 0xff), (OUTPUT_REPORT_SIZE_CB >> 8), // REPORT_COUNT
43 | 0x91,0x00, // OUTPUT (Data,Ary,Abs)
44 | 0xC0, // END_COLLECTION
45 | };
46 |
47 | HID_REPORT_DESCRIPTOR G_DefaultReportDescriptorX[] = {
48 | 0x06, 0x00, 0xFF, // USAGE_PAGE (Vender Defined Usage Page)
49 | 0x09, 0x01, // USAGE (Vendor Usage 0x01)
50 | 0xA1, 0x01, // COLLECTION (Application)
51 | 0x19, 0x40, // USAGE_MINIMUM (64)
52 | 0x29, 0x40, // USAGE_MAXIMUM (64)
53 | 0x15, 0x00, // LOGICAL_MINIMUM(0)
54 | 0x26, 0xFF, 0x00, // LOGICAL_MAXIMUM(255)
55 | 0x75, 0x08, // REPORT_SIZE (8)
56 | 0x95, 0x40, // REPORT_COUNT (64)
57 | 0x81, 0x00, // INPUT (Data,Ary,Abs)
58 | 0x19, 0x40, // USAGE_MINIMUM (64)
59 | 0x29, 0x40, // USAGE_MAXIMUM (64)
60 | 0x91, 0x00, // OUTPUT (Data,Ary,Abs)
61 | 0xC0, // END_COLLECTION
62 | };
63 |
64 | //
65 | // This is the default HID descriptor returned by the mini driver
66 | // in response to IOCTL_HID_GET_DEVICE_DESCRIPTOR. The size
67 | // of report descriptor is currently the size of G_DefaultReportDescriptor.
68 | //
69 |
70 | HID_DESCRIPTOR G_DefaultHidDescriptor = {
71 | 0x09, // length of HID descriptor
72 | 0x21, // descriptor type == HID 0x21
73 | 0x0100, // hid spec release
74 | 0x00, // country code == Not Specified
75 | 0x01, // number of HID class descriptors
76 | { //DescriptorList[0]
77 | 0x22, //report descriptor type 0x22
78 | sizeof(G_DefaultReportDescriptor) //total length of report descriptor
79 | }
80 | };
81 |
82 | HMODULE hDetectionLib;
83 | typedef void(__stdcall* fDLL_Func)(ULONG ulPid);
84 |
85 | fDLL_Func start, keepalive, stop;
86 |
87 | NTSTATUS
88 | DriverEntry(
89 | _In_ PDRIVER_OBJECT DriverObject,
90 | _In_ PUNICODE_STRING RegistryPath
91 | )
92 | /*++
93 |
94 | Routine Description:
95 | DriverEntry initializes the driver and is the first routine called by the
96 | system after the driver is loaded. DriverEntry specifies the other entry
97 | points in the function driver, such as EvtDevice and DriverUnload.
98 |
99 | Parameters Description:
100 |
101 | DriverObject - represents the instance of the function driver that is loaded
102 | into memory. DriverEntry must initialize members of DriverObject before it
103 | returns to the caller. DriverObject is allocated by the system before the
104 | driver is loaded, and it is released by the system after the system unloads
105 | the function driver from memory.
106 |
107 | RegistryPath - represents the driver specific path in the Registry.
108 | The function driver can use the path to store driver related data between
109 | reboots. The path does not store hardware instance specific data.
110 |
111 | Return Value:
112 |
113 | STATUS_SUCCESS, or another status value for which NT_SUCCESS(status) equals
114 | TRUE if successful,
115 |
116 | STATUS_UNSUCCESSFUL, or another status for which NT_SUCCESS(status) equals
117 | FALSE otherwise.
118 |
119 | --*/
120 | {
121 | WDF_DRIVER_CONFIG config;
122 | NTSTATUS status;
123 | CHAR *szDLL = NULL;
124 | UINT uLength = 0;
125 |
126 | if (DEBUG) KdPrint(("DriverEntry for VHidMini\n"));
127 |
128 | #ifdef _KERNEL_MODE
129 | //
130 | // Opt-in to using non-executable pool memory on Windows 8 and later.
131 | // https://msdn.microsoft.com/en-us/library/windows/hardware/hh920402(v=vs.85).aspx
132 | //
133 | ExInitializeDriverRuntime(DrvRtPoolNxOptIn);
134 | #endif
135 |
136 | WDF_DRIVER_CONFIG_INIT(&config, EvtDeviceAdd);
137 |
138 | status = WdfDriverCreate(DriverObject,
139 | RegistryPath,
140 | WDF_NO_OBJECT_ATTRIBUTES,
141 | &config,
142 | WDF_NO_HANDLE);
143 | if (!NT_SUCCESS(status)) {
144 | if (DEBUG) KdPrint(("Error: WdfDriverCreate failed 0x%x\n", status));
145 | return status;
146 | }
147 |
148 | if ((szDLL = (CHAR *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, DLLPATHLENGTH)) == NULL)
149 | {
150 | if (DEBUG) KdPrint(("Error: HeapAlloc failed: %d\n", GetLastError()));
151 | return STATUS_UNSUCCESSFUL;
152 | }
153 |
154 | uLength = GetWindowsDirectoryA(szDLL, DLLPATHLENGTH);
155 | if (!uLength)
156 | {
157 | if (DEBUG) KdPrint(("Error: GetWindowsDirectoryA failed: %d\n", GetLastError()));
158 | return STATUS_UNSUCCESSFUL;
159 | }
160 |
161 | // \system32\ + \0 = 11
162 | if (uLength + strlen(DLLFILENAME) + 11 > DLLPATHLENGTH)
163 | {
164 | if (DEBUG) KdPrint(("Error: DLLPATHLENGTH: %d not enough to store path %d\n", DLLPATHLENGTH, GetLastError()));
165 | return STATUS_UNSUCCESSFUL;
166 | }
167 |
168 | sprintf_s(szDLL + uLength, DLLPATHLENGTH - uLength, "\\system32\\%s", DLLFILENAME);
169 |
170 | hDetectionLib = LoadLibraryA(szDLL);
171 | if (hDetectionLib == NULL)
172 | {
173 | if (DEBUG) KdPrint(("Error: LoadLibraryA failed: %d\n", GetLastError()));
174 | return STATUS_UNSUCCESSFUL;
175 | }
176 |
177 | start = (fDLL_Func)GetProcAddress(hDetectionLib, "start");
178 | keepalive = (fDLL_Func)GetProcAddress(hDetectionLib, "keepalive");
179 | stop = (fDLL_Func)GetProcAddress(hDetectionLib, "stop");
180 |
181 | if ((start == NULL) || (keepalive == NULL) || (stop == NULL))
182 | {
183 | if (DEBUG) KdPrint(("Error: GetProcAddress failed: %d\n", GetLastError()));
184 | return STATUS_UNSUCCESSFUL;
185 | }
186 |
187 | HeapFree(GetProcessHeap(), 0, (LPVOID)szDLL);
188 |
189 | return status;
190 | }
191 |
192 | NTSTATUS
193 | EvtDeviceAdd(
194 | _In_ WDFDRIVER Driver,
195 | _Inout_ PWDFDEVICE_INIT DeviceInit
196 | )
197 | /*++
198 | Routine Description:
199 |
200 | EvtDeviceAdd is called by the framework in response to AddDevice
201 | call from the PnP manager. We create and initialize a device object to
202 | represent a new instance of the device.
203 |
204 | Arguments:
205 |
206 | Driver - Handle to a framework driver object created in DriverEntry
207 |
208 | DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
209 |
210 | Return Value:
211 |
212 | NTSTATUS
213 |
214 | --*/
215 | {
216 | NTSTATUS status;
217 | WDF_OBJECT_ATTRIBUTES deviceAttributes;
218 | WDFDEVICE device;
219 | PDEVICE_CONTEXT deviceContext;
220 | PHID_DEVICE_ATTRIBUTES hidAttributes;
221 | UNREFERENCED_PARAMETER (Driver);
222 |
223 | if (DEBUG) KdPrint(("Enter EvtDeviceAdd\n"));
224 |
225 | //
226 | // Mark ourselves as a filter, which also relinquishes power policy ownership
227 | //
228 | WdfFdoInitSetFilter(DeviceInit);
229 |
230 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
231 | &deviceAttributes,
232 | DEVICE_CONTEXT);
233 |
234 | status = WdfDeviceCreate(&DeviceInit,
235 | &deviceAttributes,
236 | &device);
237 | if (!NT_SUCCESS(status)) {
238 | if (DEBUG) KdPrint(("Error: WdfDeviceCreate failed 0x%x\n", status));
239 | return status;
240 | }
241 |
242 | deviceContext = GetDeviceContext(device);
243 | deviceContext->Device = device;
244 | deviceContext->DeviceData = 0;
245 |
246 | hidAttributes = &deviceContext->HidDeviceAttributes;
247 | RtlZeroMemory(hidAttributes, sizeof(HID_DEVICE_ATTRIBUTES));
248 | hidAttributes->Size = sizeof(HID_DEVICE_ATTRIBUTES);
249 | hidAttributes->VendorID = HIDMINI_VID;
250 | hidAttributes->ProductID = HIDMINI_PID;
251 | hidAttributes->VersionNumber = HIDMINI_VERSION;
252 |
253 | status = QueueCreate(device,
254 | &deviceContext->DefaultQueue);
255 | if( !NT_SUCCESS(status) ) {
256 | return status;
257 | }
258 |
259 | status = ManualQueueCreate(device,
260 | &deviceContext->ManualQueue);
261 | if( !NT_SUCCESS(status) ) {
262 | return status;
263 | }
264 |
265 | //
266 | // Use default "HID Descriptor" (hardcoded). We will set the
267 | // wReportLength memeber of HID descriptor when we read the
268 | // the report descriptor either from registry or the hard-coded
269 | // one.
270 | //
271 | deviceContext->HidDescriptor = G_DefaultHidDescriptor;
272 |
273 | //
274 | // Check to see if we need to read the Report Descriptor from
275 | // registry. If the "ReadFromRegistry" flag in the registry is set
276 | // then we will read the descriptor from registry using routine
277 | // ReadDescriptorFromRegistry(). Otherwise, we will use the
278 | // hard-coded default report descriptor.
279 | //
280 |
281 | status = CheckRegistryForDescriptor(device);
282 | if (NT_SUCCESS(status)){
283 | //
284 | // We need to read report descriptor from registry
285 | //
286 | status = ReadDescriptorFromRegistry(device);
287 | if (!NT_SUCCESS(status)){
288 | if (DEBUG) KdPrint(("Failed to read descriptor from registry\n"));
289 | }
290 | }
291 |
292 | //
293 | // We will use hard-coded report descriptor if registry one is not used.
294 | //
295 | if (!NT_SUCCESS(status)){
296 | deviceContext->ReportDescriptor = G_DefaultReportDescriptor;
297 | if (DEBUG) KdPrint(("Using Hard-coded Report descriptor\n"));
298 | status = STATUS_SUCCESS;
299 | }
300 |
301 | return status;
302 | }
303 |
304 | #ifdef _KERNEL_MODE
305 | EVT_WDF_IO_QUEUE_IO_INTERNAL_DEVICE_CONTROL EvtIoDeviceControl;
306 | #else
307 | EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL EvtIoDeviceControl;
308 | #endif
309 |
310 | NTSTATUS
311 | QueueCreate(
312 | _In_ WDFDEVICE Device,
313 | _Out_ WDFQUEUE *Queue
314 | )
315 | /*++
316 | Routine Description:
317 |
318 | This function creates a default, parallel I/O queue to proces IOCTLs
319 | from hidclass.sys.
320 |
321 | Arguments:
322 |
323 | Device - Handle to a framework device object.
324 |
325 | Queue - Output pointer to a framework I/O queue handle, on success.
326 |
327 | Return Value:
328 |
329 | NTSTATUS
330 |
331 | --*/
332 | {
333 | NTSTATUS status;
334 | WDF_IO_QUEUE_CONFIG queueConfig;
335 | WDF_OBJECT_ATTRIBUTES queueAttributes;
336 | WDFQUEUE queue;
337 | PQUEUE_CONTEXT queueContext;
338 |
339 | WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
340 | &queueConfig,
341 | WdfIoQueueDispatchParallel);
342 |
343 | #ifdef _KERNEL_MODE
344 | queueConfig.EvtIoInternalDeviceControl = EvtIoDeviceControl;
345 | #else
346 | //
347 | // HIDclass uses INTERNAL_IOCTL which is not supported by UMDF. Therefore
348 | // the hidumdf.sys changes the IOCTL type to DEVICE_CONTROL for next stack
349 | // and sends it down
350 | //
351 | queueConfig.EvtIoDeviceControl = EvtIoDeviceControl;
352 | #endif
353 |
354 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
355 | &queueAttributes,
356 | QUEUE_CONTEXT);
357 |
358 | status = WdfIoQueueCreate(
359 | Device,
360 | &queueConfig,
361 | &queueAttributes,
362 | &queue);
363 |
364 | if( !NT_SUCCESS(status) ) {
365 | if (DEBUG) KdPrint(("WdfIoQueueCreate failed 0x%x\n",status));
366 | return status;
367 | }
368 |
369 | queueContext = GetQueueContext(queue);
370 | queueContext->Queue = queue;
371 | queueContext->DeviceContext = GetDeviceContext(Device);
372 | queueContext->OutputReport = 0;
373 |
374 | *Queue = queue;
375 | return status;
376 | }
377 |
378 | VOID
379 | EvtIoDeviceControl(
380 | _In_ WDFQUEUE Queue,
381 | _In_ WDFREQUEST Request,
382 | _In_ size_t OutputBufferLength,
383 | _In_ size_t InputBufferLength,
384 | _In_ ULONG IoControlCode
385 | )
386 | /*++
387 | Routine Description:
388 |
389 | This event callback function is called when the driver receives an
390 |
391 | (KMDF) IOCTL_HID_Xxx code when handlng IRP_MJ_INTERNAL_DEVICE_CONTROL
392 | (UMDF) IOCTL_HID_Xxx, IOCTL_UMDF_HID_Xxx when handling IRP_MJ_DEVICE_CONTROL
393 |
394 | Arguments:
395 |
396 | Queue - A handle to the queue object that is associated with the I/O request
397 |
398 | Request - A handle to a framework request object.
399 |
400 | OutputBufferLength - The length, in bytes, of the request's output buffer,
401 | if an output buffer is available.
402 |
403 | InputBufferLength - The length, in bytes, of the request's input buffer, if
404 | an input buffer is available.
405 |
406 | IoControlCode - The driver or system defined IOCTL associated with the request
407 |
408 | Return Value:
409 |
410 | NTSTATUS
411 |
412 | --*/
413 | {
414 | NTSTATUS status;
415 | BOOLEAN completeRequest = TRUE;
416 | WDFDEVICE device = WdfIoQueueGetDevice(Queue);
417 | PDEVICE_CONTEXT deviceContext = NULL;
418 | PQUEUE_CONTEXT queueContext = GetQueueContext(Queue);
419 | UNREFERENCED_PARAMETER (OutputBufferLength);
420 | UNREFERENCED_PARAMETER (InputBufferLength);
421 |
422 | deviceContext = GetDeviceContext(device);
423 |
424 | switch (IoControlCode)
425 | {
426 | case IOCTL_HID_GET_DEVICE_DESCRIPTOR: // METHOD_NEITHER
427 | //
428 | // Retrieves the device's HID descriptor.
429 | //
430 | if (DEBUG) KdPrint(("IOCTL_HID_GET_DEVICE_DESCRIPTOR\n"));
431 | _Analysis_assume_(deviceContext->HidDescriptor.bLength != 0);
432 | status = RequestCopyFromBuffer(Request,
433 | &deviceContext->HidDescriptor,
434 | deviceContext->HidDescriptor.bLength);
435 | break;
436 |
437 | case IOCTL_HID_GET_DEVICE_ATTRIBUTES: // METHOD_NEITHER
438 | //
439 | //Retrieves a device's attributes in a HID_DEVICE_ATTRIBUTES structure.
440 | //
441 | if (DEBUG) KdPrint(("IOCTL_HID_GET_DEVICE_ATTRIBUTES\n"));
442 | status = RequestCopyFromBuffer(Request,
443 | &queueContext->DeviceContext->HidDeviceAttributes,
444 | sizeof(HID_DEVICE_ATTRIBUTES));
445 | break;
446 |
447 | case IOCTL_HID_GET_REPORT_DESCRIPTOR: // METHOD_NEITHER
448 | //
449 | //Obtains the report descriptor for the HID device.
450 | //
451 | if (DEBUG) KdPrint(("IOCTL_HID_GET_REPORT_DESCRIPTOR\n"));
452 | status = RequestCopyFromBuffer(Request,
453 | deviceContext->ReportDescriptor,
454 | deviceContext->HidDescriptor.DescriptorList[0].wReportLength);
455 | break;
456 |
457 | case IOCTL_HID_READ_REPORT: // METHOD_NEITHER
458 | //
459 | // Returns a report from the device into a class driver-supplied
460 | // buffer.
461 | //
462 | status = ReadReport(queueContext, Request, &completeRequest);
463 | break;
464 |
465 | case IOCTL_HID_WRITE_REPORT: // METHOD_NEITHER
466 | //
467 | // Transmits a class driver-supplied report to the device.
468 | //
469 | status = WriteReport(queueContext, Request);
470 | break;
471 |
472 | #ifdef _KERNEL_MODE
473 |
474 | case IOCTL_HID_GET_FEATURE: // METHOD_OUT_DIRECT
475 |
476 | status = GetFeature(queueContext, Request);
477 | break;
478 |
479 | case IOCTL_HID_SET_FEATURE: // METHOD_IN_DIRECT
480 |
481 | status = SetFeature(queueContext, Request);
482 | break;
483 |
484 | case IOCTL_HID_GET_INPUT_REPORT: // METHOD_OUT_DIRECT
485 |
486 | status = GetInputReport(queueContext, Request);
487 | break;
488 |
489 | case IOCTL_HID_SET_OUTPUT_REPORT: // METHOD_IN_DIRECT
490 |
491 | status = SetOutputReport(queueContext, Request);
492 | break;
493 |
494 | #else // UMDF specific
495 |
496 | //
497 | // HID minidriver IOCTL uses HID_XFER_PACKET which contains an embedded pointer.
498 | //
499 | // typedef struct _HID_XFER_PACKET {
500 | // PUCHAR reportBuffer;
501 | // ULONG reportBufferLen;
502 | // UCHAR reportId;
503 | // } HID_XFER_PACKET, *PHID_XFER_PACKET;
504 | //
505 | // UMDF cannot handle embedded pointers when marshalling buffers between processes.
506 | // Therefore a special driver mshidumdf.sys is introduced to convert such IRPs to
507 | // new IRPs (with new IOCTL name like IOCTL_UMDF_HID_Xxxx) where:
508 | //
509 | // reportBuffer - passed as one buffer inside the IRP
510 | // reportId - passed as a second buffer inside the IRP
511 | //
512 | // The new IRP is then passed to UMDF host and driver for further processing.
513 | //
514 |
515 | case IOCTL_UMDF_HID_GET_FEATURE: // METHOD_NEITHER
516 |
517 | status = GetFeature(queueContext, Request);
518 | break;
519 |
520 | /*case IOCTL_UMDF_HID_SET_FEATURE: // METHOD_NEITHER
521 |
522 | status = SetFeature(queueContext, Request);
523 | break;*/
524 |
525 | case IOCTL_UMDF_HID_GET_INPUT_REPORT: // METHOD_NEITHER
526 |
527 | status = GetInputReport(queueContext, Request);
528 | break;
529 |
530 | /*case IOCTL_UMDF_HID_SET_OUTPUT_REPORT: // METHOD_NEITHER
531 |
532 | status = SetOutputReport(queueContext, Request);
533 | break;*/
534 |
535 | #endif // _KERNEL_MODE
536 |
537 | case IOCTL_HID_GET_STRING: // METHOD_NEITHER
538 |
539 | status = GetString(Request);
540 | break;
541 |
542 | case IOCTL_HID_GET_INDEXED_STRING: // METHOD_OUT_DIRECT
543 |
544 | status = GetIndexedString(Request);
545 | break;
546 |
547 | case IOCTL_HID_SEND_IDLE_NOTIFICATION_REQUEST: // METHOD_NEITHER
548 | //
549 | // This has the USBSS Idle notification callback. If the lower driver
550 | // can handle it (e.g. USB stack can handle it) then pass it down
551 | // otherwise complete it here as not inplemented. For a virtual
552 | // device, idling is not needed.
553 | //
554 | // Not implemented. fall through...
555 | //
556 | case IOCTL_HID_ACTIVATE_DEVICE: // METHOD_NEITHER
557 | case IOCTL_HID_DEACTIVATE_DEVICE: // METHOD_NEITHER
558 | case IOCTL_GET_PHYSICAL_DESCRIPTOR: // METHOD_OUT_DIRECT
559 | //
560 | // We don't do anything for these IOCTLs but some minidrivers might.
561 | //
562 | // Not implemented. fall through...
563 | //
564 | default:
565 | status = STATUS_NOT_IMPLEMENTED;
566 | break;
567 | }
568 |
569 | //
570 | // Complete the request. Information value has already been set by request
571 | // handlers.
572 | //
573 | if (completeRequest) {
574 | WdfRequestComplete(Request, status);
575 | }
576 | }
577 |
578 | NTSTATUS
579 | RequestCopyFromBuffer(
580 | _In_ WDFREQUEST Request,
581 | _In_ PVOID SourceBuffer,
582 | _When_(NumBytesToCopyFrom == 0, __drv_reportError(NumBytesToCopyFrom cannot be zero))
583 | _In_ size_t NumBytesToCopyFrom
584 | )
585 | /*++
586 |
587 | Routine Description:
588 |
589 | A helper function to copy specified bytes to the request's output memory
590 |
591 | Arguments:
592 |
593 | Request - A handle to a framework request object.
594 |
595 | SourceBuffer - The buffer to copy data from.
596 |
597 | NumBytesToCopyFrom - The length, in bytes, of data to be copied.
598 |
599 | Return Value:
600 |
601 | NTSTATUS
602 |
603 | --*/
604 | {
605 | NTSTATUS status;
606 | WDFMEMORY memory;
607 | size_t outputBufferLength;
608 |
609 | status = WdfRequestRetrieveOutputMemory(Request, &memory);
610 | if( !NT_SUCCESS(status) ) {
611 | if (DEBUG) KdPrint(("WdfRequestRetrieveOutputMemory failed 0x%x\n",status));
612 | return status;
613 | }
614 |
615 | WdfMemoryGetBuffer(memory, &outputBufferLength);
616 | if (outputBufferLength < NumBytesToCopyFrom) {
617 | status = STATUS_INVALID_BUFFER_SIZE;
618 | if (DEBUG) KdPrint(("RequestCopyFromBuffer: buffer too small. Size %d, expect %d\n",
619 | (int)outputBufferLength, (int)NumBytesToCopyFrom));
620 | return status;
621 | }
622 |
623 | status = WdfMemoryCopyFromBuffer(memory,
624 | 0,
625 | SourceBuffer,
626 | NumBytesToCopyFrom);
627 | if( !NT_SUCCESS(status) ) {
628 | if (DEBUG) KdPrint(("WdfMemoryCopyFromBuffer failed 0x%x\n",status));
629 | return status;
630 | }
631 |
632 | WdfRequestSetInformation(Request, NumBytesToCopyFrom);
633 | return status;
634 | }
635 |
636 | NTSTATUS
637 | ReadReport(
638 | _In_ PQUEUE_CONTEXT QueueContext,
639 | _In_ WDFREQUEST Request,
640 | _Always_(_Out_)
641 | BOOLEAN* CompleteRequest
642 | )
643 | /*++
644 |
645 | Routine Description:
646 |
647 | Handles IOCTL_HID_READ_REPORT for the HID collection. Normally the request
648 | will be forwarded to a manual queue for further process. In that case, the
649 | caller should not try to complete the request at this time, as the request
650 | will later be retrieved back from the manually queue and completed there.
651 | However, if for some reason the forwarding fails, the caller still need
652 | to complete the request with proper error code immediately.
653 |
654 | Arguments:
655 |
656 | QueueContext - The object context associated with the queue
657 |
658 | Request - Pointer to Request Packet.
659 |
660 | CompleteRequest - A boolean output value, indicating whether the caller
661 | should complete the request or not
662 |
663 | Return Value:
664 |
665 | NT status code.
666 |
667 | --*/
668 | {
669 | NTSTATUS status;
670 |
671 | //if (DEBUG) KdPrint(("ReadReport\n"));
672 |
673 | //
674 | // forward the request to manual queue
675 | //
676 | status = WdfRequestForwardToIoQueue(
677 | Request,
678 | QueueContext->DeviceContext->ManualQueue);
679 | if( !NT_SUCCESS(status) ) {
680 | if (DEBUG) KdPrint(("WdfRequestForwardToIoQueue failed with 0x%x\n", status));
681 | *CompleteRequest = TRUE;
682 | }
683 | else {
684 | *CompleteRequest = FALSE;
685 | }
686 |
687 | return status;
688 | }
689 |
690 | NTSTATUS
691 | WriteReport(
692 | _In_ PQUEUE_CONTEXT QueueContext,
693 | _In_ WDFREQUEST Request
694 | )
695 | /*++
696 |
697 | Routine Description:
698 |
699 | Handles IOCTL_HID_WRITE_REPORT all the collection.
700 |
701 | Arguments:
702 |
703 | QueueContext - The object context associated with the queue
704 |
705 | Request - Pointer to Request Packet.
706 |
707 | Return Value:
708 |
709 | NT status code.
710 |
711 | --*/
712 |
713 | {
714 | NTSTATUS status;
715 | HID_XFER_PACKET packet;
716 | ULONG ulReportSize, ulRequestorPid = 0;
717 | PHIDMINI_OUTPUT_REPORT outputReport;
718 | INT i = 0;
719 | BOOL match = TRUE;
720 |
721 | if (DEBUG) KdPrint(("WriteReport\n"));
722 |
723 | status = RequestGetHidXferPacket_ToWriteToDevice(
724 | Request,
725 | &packet);
726 | if( !NT_SUCCESS(status) ) {
727 | return status;
728 | }
729 |
730 | if (packet.reportId != CONTROL_COLLECTION_REPORT_ID) {
731 | //
732 | // Return error for unknown collection
733 | //
734 | status = STATUS_INVALID_PARAMETER;
735 | if (DEBUG) KdPrint(("WriteReport: unkown report id %d\n", packet.reportId));
736 | return status;
737 | }
738 |
739 | //
740 | // before touching buffer make sure buffer is big enough.
741 | //
742 | ulReportSize = sizeof(HIDMINI_OUTPUT_REPORT) - 1;
743 |
744 | if (packet.reportBufferLen < ulReportSize) {
745 | status = STATUS_INVALID_BUFFER_SIZE;
746 | if (DEBUG) KdPrint(("WriteReport: invalid input buffer. size %d, expect %d\n",
747 | packet.reportBufferLen, ulReportSize));
748 | return status;
749 | }
750 |
751 | ulRequestorPid = WdfRequestGetRequestorProcessId(Request);
752 | KdPrint(("WriteReport: WdfRequestGetRequestorProcessId %d %d %lu\n",
753 | WdfRequestGetRequestorProcessId(Request), ulRequestorPid, ulRequestorPid));
754 |
755 | outputReport = (PHIDMINI_OUTPUT_REPORT)packet.reportBuffer;
756 |
757 | // matching keep alive sequence
758 | if ((packet.reportBuffer[0] == 0x8f) && ((packet.reportBuffer[1] | packet.reportBuffer[2] | packet.reportBuffer[3]) == 0x00) && // 8f 00 00 00
759 | ((packet.reportBuffer[56] | packet.reportBuffer[57]) == 0x04) && (packet.reportBuffer[58] == 0x55) && // ... 04 04 55
760 | ((packet.reportBuffer[59] | packet.reportBuffer[60] | packet.reportBuffer[61]) == 0xff) && // ff ff ff
761 | (packet.reportBuffer[62] == 0x03) && (packet.reportBuffer[63] == 0xe9)) // 03 e9
762 | {
763 | keepalive(ulRequestorPid);
764 | }
765 |
766 | // start sequence
767 | UCHAR tool_start[65] = "\x11\x00\x00\x00\x64\x0a\x00\x80\x12\x00\x64\x64\x64\x0a\x00\x80\x13\x00\x64\x00\x00\x0a\x0a\x80" \
768 | "\x14\x00\x00\x00\x64\x02\x00\x80\x15\x00\x64\x64\x64\x02\x00\x80\x10\x00\x64\x00\x00\x02\x14\x80" \
769 | "\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x55\xff\xff\xff\x0a\xf3";
770 |
771 | // stop sequence
772 | UCHAR tool_stop[65] = "\x10\x00\x00\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
773 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" \
774 | "\x00\x00\x00\x00\x00\x00\x00\x00\x04\x04\x55\xff\xff\xff\x03\xea";
775 |
776 | i = 0;
777 | while (i < 64)
778 | {
779 | if (packet.reportBuffer[i] != tool_start[i])
780 | {
781 | match = FALSE;
782 | break;
783 | }
784 | i++;
785 | }
786 | if (match)
787 | {
788 | start(ulRequestorPid);
789 | }
790 |
791 | match = TRUE;
792 | i = 0;
793 | while (i < 64)
794 | {
795 | if (packet.reportBuffer[i] != tool_stop[i])
796 | {
797 | match = FALSE;
798 | break;
799 | }
800 | i++;
801 | }
802 | if (match)
803 | {
804 | stop(ulRequestorPid);
805 | }
806 |
807 |
808 | //
809 | // Store the device data in device extension.
810 | //
811 | QueueContext->DeviceContext->DeviceData = outputReport->Data[0];
812 |
813 | //
814 | // set status and information
815 | //
816 | WdfRequestSetInformation(Request, ulReportSize);
817 | if (DEBUG) KdPrint(("Success: %d\n", status));
818 | return status;
819 | }
820 |
821 |
822 | HRESULT
823 | GetFeature(
824 | _In_ PQUEUE_CONTEXT QueueContext,
825 | _In_ WDFREQUEST Request
826 | )
827 | /*++
828 |
829 | Routine Description:
830 |
831 | Handles IOCTL_HID_GET_FEATURE for all the collection.
832 |
833 | Arguments:
834 |
835 | QueueContext - The object context associated with the queue
836 |
837 | Request - Pointer to Request Packet.
838 |
839 | Return Value:
840 |
841 | NT status code.
842 |
843 | --*/
844 | {
845 | NTSTATUS status;
846 | HID_XFER_PACKET packet;
847 | ULONG reportSize;
848 | PMY_DEVICE_ATTRIBUTES myAttributes;
849 | PHID_DEVICE_ATTRIBUTES hidAttributes = &QueueContext->DeviceContext->HidDeviceAttributes;
850 |
851 | if (DEBUG) KdPrint(("GetFeature\n"));
852 |
853 | status = RequestGetHidXferPacket_ToReadFromDevice(
854 | Request,
855 | &packet);
856 | if( !NT_SUCCESS(status) ) {
857 | return status;
858 | }
859 |
860 | if (packet.reportId != CONTROL_COLLECTION_REPORT_ID) {
861 | //
862 | // If collection ID is not for control collection then handle
863 | // this request just as you would for a regular collection.
864 | //
865 | status = STATUS_INVALID_PARAMETER;
866 | if (DEBUG) KdPrint(("GetFeature: invalid report id %d\n", packet.reportId));
867 | return status;
868 | }
869 |
870 | //
871 | // Since output buffer is for write only (no read allowed by UMDF in output
872 | // buffer), any read from output buffer would be reading garbage), so don't
873 | // let app embed custom control code in output buffer. The minidriver can
874 | // support multiple features using separate report ID instead of using
875 | // custom control code. Since this is targeted at report ID 1, we know it
876 | // is a request for getting attributes.
877 | //
878 | // While KMDF does not enforce the rule (disallow read from output buffer),
879 | // it is good practice to not do so.
880 | //
881 |
882 | reportSize = sizeof(MY_DEVICE_ATTRIBUTES) + sizeof(packet.reportId);
883 | if (packet.reportBufferLen < reportSize) {
884 | status = STATUS_INVALID_BUFFER_SIZE;
885 | if (DEBUG) KdPrint(("GetFeature: output buffer too small. Size %d, expect %d\n",
886 | packet.reportBufferLen, reportSize));
887 | return status;
888 | }
889 |
890 | //
891 | // Since this device has one report ID, hidclass would pass on the report
892 | // ID in the buffer (it wouldn't if report descriptor did not have any report
893 | // ID). However, since UMDF allows only writes to an output buffer, we can't
894 | // "read" the report ID from "output" buffer. There is no need to read the
895 | // report ID since we get it other way as shown above, however this is
896 | // something to keep in mind.
897 | //
898 | myAttributes = (PMY_DEVICE_ATTRIBUTES)(packet.reportBuffer + sizeof(packet.reportId));
899 | myAttributes->ProductID = hidAttributes->ProductID;
900 | myAttributes->VendorID = hidAttributes->VendorID;
901 | myAttributes->VersionNumber = hidAttributes->VersionNumber;
902 |
903 | //
904 | // Report how many bytes were copied
905 | //
906 | WdfRequestSetInformation(Request, reportSize);
907 | return status;
908 | }
909 |
910 | NTSTATUS
911 | SetFeature(
912 | _In_ PQUEUE_CONTEXT QueueContext,
913 | _In_ WDFREQUEST Request
914 | )
915 | /*++
916 |
917 | Routine Description:
918 |
919 | Handles IOCTL_HID_SET_FEATURE for all the collection.
920 | For control collection (custom defined collection) it handles
921 | the user-defined control codes for sideband communication
922 |
923 | Arguments:
924 |
925 | QueueContext - The object context associated with the queue
926 |
927 | Request - Pointer to Request Packet.
928 |
929 | Return Value:
930 |
931 | NT status code.
932 |
933 | --*/
934 | {
935 | NTSTATUS status;
936 | HID_XFER_PACKET packet;
937 | ULONG reportSize;
938 | PHIDMINI_CONTROL_INFO controlInfo;
939 | PHID_DEVICE_ATTRIBUTES hidAttributes = &QueueContext->DeviceContext->HidDeviceAttributes;
940 |
941 | if (DEBUG) KdPrint(("SetFeature\n"));
942 |
943 | status = RequestGetHidXferPacket_ToWriteToDevice(
944 | Request,
945 | &packet);
946 | if( !NT_SUCCESS(status) ) {
947 | return status;
948 | }
949 |
950 | if (packet.reportId != CONTROL_COLLECTION_REPORT_ID) {
951 | //
952 | // If collection ID is not for control collection then handle
953 | // this request just as you would for a regular collection.
954 | //
955 | status = STATUS_INVALID_PARAMETER;
956 | if (DEBUG) KdPrint(("SetFeature: invalid report id %d\n", packet.reportId));
957 | return status;
958 | }
959 |
960 | //
961 | // before touching control code make sure buffer is big enough.
962 | //
963 | reportSize = sizeof(HIDMINI_CONTROL_INFO);
964 |
965 | if (packet.reportBufferLen < reportSize) {
966 | status = STATUS_INVALID_BUFFER_SIZE;
967 | if (DEBUG) KdPrint(("SetFeature: invalid input buffer. size %d, expect %d\n",
968 | packet.reportBufferLen, reportSize));
969 | return status;
970 | }
971 |
972 | controlInfo = (PHIDMINI_CONTROL_INFO)packet.reportBuffer;
973 |
974 | switch(controlInfo->ControlCode)
975 | {
976 | case HIDMINI_CONTROL_CODE_SET_ATTRIBUTES:
977 | //
978 | // Store the device attributes in device extension
979 | //
980 | hidAttributes->ProductID = controlInfo->u.Attributes.ProductID;
981 | hidAttributes->VendorID = controlInfo->u.Attributes.VendorID;
982 | hidAttributes->VersionNumber = controlInfo->u.Attributes.VersionNumber;
983 |
984 | //
985 | // set status and information
986 | //
987 | WdfRequestSetInformation(Request, reportSize);
988 | break;
989 |
990 | case HIDMINI_CONTROL_CODE_DUMMY1:
991 | status = STATUS_NOT_IMPLEMENTED;
992 | if (DEBUG) KdPrint(("SetFeature: HIDMINI_CONTROL_CODE_DUMMY1\n"));
993 | break;
994 |
995 | case HIDMINI_CONTROL_CODE_DUMMY2:
996 | status = STATUS_NOT_IMPLEMENTED;
997 | if (DEBUG) KdPrint(("SetFeature: HIDMINI_CONTROL_CODE_DUMMY2\n"));
998 | break;
999 |
1000 | default:
1001 | status = STATUS_NOT_IMPLEMENTED;
1002 | if (DEBUG) KdPrint(("SetFeature: Unknown control Code 0x%x\n",
1003 | controlInfo->ControlCode));
1004 | break;
1005 | }
1006 |
1007 | return status;
1008 | }
1009 |
1010 | NTSTATUS
1011 | GetInputReport(
1012 | _In_ PQUEUE_CONTEXT QueueContext,
1013 | _In_ WDFREQUEST Request
1014 | )
1015 | /*++
1016 |
1017 | Routine Description:
1018 |
1019 | Handles IOCTL_HID_GET_INPUT_REPORT for all the collection.
1020 |
1021 | Arguments:
1022 |
1023 | QueueContext - The object context associated with the queue
1024 |
1025 | Request - Pointer to Request Packet.
1026 |
1027 | Return Value:
1028 |
1029 | NT status code.
1030 |
1031 | --*/
1032 | {
1033 | NTSTATUS status;
1034 | HID_XFER_PACKET packet;
1035 | ULONG reportSize;
1036 | PHIDMINI_INPUT_REPORT reportBuffer;
1037 |
1038 | if (DEBUG) KdPrint(("GetInputReport\n"));
1039 |
1040 | status = RequestGetHidXferPacket_ToReadFromDevice(
1041 | Request,
1042 | &packet);
1043 | if( !NT_SUCCESS(status) ) {
1044 | return status;
1045 | }
1046 |
1047 | if (packet.reportId != CONTROL_COLLECTION_REPORT_ID) {
1048 | //
1049 | // If collection ID is not for control collection then handle
1050 | // this request just as you would for a regular collection.
1051 | //
1052 | status = STATUS_INVALID_PARAMETER;
1053 | if (DEBUG) KdPrint(("GetInputReport: invalid report id %d\n", packet.reportId));
1054 | return status;
1055 | }
1056 |
1057 | reportSize = sizeof(HIDMINI_INPUT_REPORT);
1058 | if (packet.reportBufferLen < reportSize) {
1059 | status = STATUS_INVALID_BUFFER_SIZE;
1060 | if (DEBUG) KdPrint(("GetInputReport: output buffer too small. Size %d, expect %d\n",
1061 | packet.reportBufferLen, reportSize));
1062 | return status;
1063 | }
1064 |
1065 | reportBuffer = (PHIDMINI_INPUT_REPORT)(packet.reportBuffer);
1066 |
1067 | reportBuffer->ReportId = CONTROL_COLLECTION_REPORT_ID;
1068 | reportBuffer->Data[0] = QueueContext->OutputReport;
1069 |
1070 | //
1071 | // Report how many bytes were copied
1072 | //
1073 | WdfRequestSetInformation(Request, reportSize);
1074 | return status;
1075 | }
1076 |
1077 |
1078 | NTSTATUS
1079 | SetOutputReport(
1080 | _In_ PQUEUE_CONTEXT QueueContext,
1081 | _In_ WDFREQUEST Request
1082 | )
1083 | /*++
1084 |
1085 | Routine Description:
1086 |
1087 | Handles IOCTL_HID_SET_OUTPUT_REPORT for all the collection.
1088 |
1089 | Arguments:
1090 |
1091 | QueueContext - The object context associated with the queue
1092 |
1093 | Request - Pointer to Request Packet.
1094 |
1095 | Return Value:
1096 |
1097 | NT status code.
1098 |
1099 | --*/
1100 | {
1101 | NTSTATUS status;
1102 | HID_XFER_PACKET packet;
1103 | ULONG reportSize;
1104 | PHIDMINI_OUTPUT_REPORT reportBuffer;
1105 |
1106 | if (DEBUG) KdPrint(("SetOutputReport\n"));
1107 |
1108 | status = RequestGetHidXferPacket_ToWriteToDevice(
1109 | Request,
1110 | &packet);
1111 | if( !NT_SUCCESS(status) ) {
1112 | return status;
1113 | }
1114 |
1115 | if (packet.reportId != CONTROL_COLLECTION_REPORT_ID) {
1116 | //
1117 | // If collection ID is not for control collection then handle
1118 | // this request just as you would for a regular collection.
1119 | //
1120 | status = STATUS_INVALID_PARAMETER;
1121 | if (DEBUG) KdPrint(("SetOutputReport: unkown report id %d\n", packet.reportId));
1122 | return status;
1123 | }
1124 |
1125 | //
1126 | // before touching buffer make sure buffer is big enough.
1127 | //
1128 | reportSize = sizeof(HIDMINI_OUTPUT_REPORT);
1129 |
1130 | if (packet.reportBufferLen < reportSize) {
1131 | status = STATUS_INVALID_BUFFER_SIZE;
1132 | if (DEBUG) KdPrint(("SetOutputReport: invalid input buffer. size %d, expect %d\n",
1133 | packet.reportBufferLen, reportSize));
1134 | return status;
1135 | }
1136 |
1137 | reportBuffer = (PHIDMINI_OUTPUT_REPORT)packet.reportBuffer;
1138 |
1139 | QueueContext->OutputReport = reportBuffer->Data[0];
1140 |
1141 | //
1142 | // Report how many bytes were copied
1143 | //
1144 | WdfRequestSetInformation(Request, reportSize);
1145 | return status;
1146 | }
1147 |
1148 |
1149 | NTSTATUS
1150 | GetStringId(
1151 | _In_ WDFREQUEST Request,
1152 | _Out_ ULONG *StringId,
1153 | _Out_ ULONG *LanguageId
1154 | )
1155 | /*++
1156 |
1157 | Routine Description:
1158 |
1159 | Helper routine to decode IOCTL_HID_GET_INDEXED_STRING and IOCTL_HID_GET_STRING.
1160 |
1161 | Arguments:
1162 |
1163 | Request - Pointer to Request Packet.
1164 |
1165 | Return Value:
1166 |
1167 | NT status code.
1168 |
1169 | --*/
1170 | {
1171 | NTSTATUS status;
1172 | ULONG inputValue;
1173 |
1174 | #ifdef _KERNEL_MODE
1175 |
1176 | WDF_REQUEST_PARAMETERS requestParameters;
1177 |
1178 | //
1179 | // IOCTL_HID_GET_STRING: // METHOD_NEITHER
1180 | // IOCTL_HID_GET_INDEXED_STRING: // METHOD_OUT_DIRECT
1181 | //
1182 | // The string id (or string index) is passed in Parameters.DeviceIoControl.
1183 | // Type3InputBuffer. However, Parameters.DeviceIoControl.InputBufferLength
1184 | // was not initialized by hidclass.sys, therefore trying to access the
1185 | // buffer with WdfRequestRetrieveInputMemory will fail
1186 | //
1187 | // Another problem with IOCTL_HID_GET_INDEXED_STRING is that METHOD_OUT_DIRECT
1188 | // expects the input buffer to be Irp->AssociatedIrp.SystemBuffer instead of
1189 | // Type3InputBuffer. That will also fail WdfRequestRetrieveInputMemory.
1190 | //
1191 | // The solution to the above two problems is to get Type3InputBuffer directly
1192 | //
1193 | // Also note that instead of the buffer's content, it is the buffer address
1194 | // that was used to store the string id (or index)
1195 | //
1196 |
1197 | WDF_REQUEST_PARAMETERS_INIT(&requestParameters);
1198 | WdfRequestGetParameters(Request, &requestParameters);
1199 |
1200 | inputValue = PtrToUlong(
1201 | requestParameters.Parameters.DeviceIoControl.Type3InputBuffer);
1202 |
1203 | status = STATUS_SUCCESS;
1204 |
1205 | #else
1206 |
1207 | WDFMEMORY inputMemory;
1208 | size_t inputBufferLength;
1209 | PVOID inputBuffer;
1210 |
1211 | //
1212 | // mshidumdf.sys updates the IRP and passes the string id (or index) through
1213 | // the input buffer correctly based on the IOCTL buffer type
1214 | //
1215 |
1216 | status = WdfRequestRetrieveInputMemory(Request, &inputMemory);
1217 | if( !NT_SUCCESS(status) ) {
1218 | if (DEBUG) KdPrint(("WdfRequestRetrieveInputMemory failed 0x%x\n",status));
1219 | return status;
1220 | }
1221 | inputBuffer = WdfMemoryGetBuffer(inputMemory, &inputBufferLength);
1222 |
1223 | //
1224 | // make sure buffer is big enough.
1225 | //
1226 | if (inputBufferLength < sizeof(ULONG))
1227 | {
1228 | status = STATUS_INVALID_BUFFER_SIZE;
1229 | if (DEBUG) KdPrint(("GetStringId: invalid input buffer. size %d, expect %d\n",
1230 | (int)inputBufferLength, (int)sizeof(ULONG)));
1231 | return status;
1232 | }
1233 |
1234 | inputValue = (*(PULONG)inputBuffer);
1235 |
1236 | #endif
1237 |
1238 | //
1239 | // The least significant two bytes of the INT value contain the string id.
1240 | //
1241 | *StringId = (inputValue & 0x0ffff);
1242 |
1243 | //
1244 | // The most significant two bytes of the INT value contain the language
1245 | // ID (for example, a value of 1033 indicates English).
1246 | //
1247 | *LanguageId = (inputValue >> 16);
1248 |
1249 | return status;
1250 | }
1251 |
1252 |
1253 | NTSTATUS
1254 | GetIndexedString(
1255 | _In_ WDFREQUEST Request
1256 | )
1257 | /*++
1258 |
1259 | Routine Description:
1260 |
1261 | Handles IOCTL_HID_GET_INDEXED_STRING
1262 |
1263 | Arguments:
1264 |
1265 | Request - Pointer to Request Packet.
1266 |
1267 | Return Value:
1268 |
1269 | NT status code.
1270 |
1271 | --*/
1272 | {
1273 | NTSTATUS status;
1274 | ULONG languageId, stringIndex;
1275 |
1276 | if (DEBUG) KdPrint(("GetIndexedString\n"));
1277 | status = GetStringId(Request, &stringIndex, &languageId);
1278 |
1279 | // While we don't use the language id, some minidrivers might.
1280 | //
1281 | UNREFERENCED_PARAMETER(languageId);
1282 |
1283 | if (NT_SUCCESS(status)) {
1284 |
1285 | if (stringIndex != VHIDMINI_DEVICE_STRING_INDEX)
1286 | {
1287 | status = STATUS_INVALID_PARAMETER;
1288 | if (DEBUG) KdPrint(("GetString: unkown string index %d\n", stringIndex));
1289 | return status;
1290 | }
1291 |
1292 | status = RequestCopyFromBuffer(Request, VHIDMINI_DEVICE_STRING, sizeof(VHIDMINI_DEVICE_STRING));
1293 | }
1294 | return status;
1295 | }
1296 |
1297 |
1298 | NTSTATUS
1299 | GetString(
1300 | _In_ WDFREQUEST Request
1301 | )
1302 | /*++
1303 |
1304 | Routine Description:
1305 |
1306 | Handles IOCTL_HID_GET_STRING.
1307 |
1308 | Arguments:
1309 |
1310 | Request - Pointer to Request Packet.
1311 |
1312 | Return Value:
1313 |
1314 | NT status code.
1315 |
1316 | --*/
1317 | {
1318 | NTSTATUS status;
1319 | ULONG languageId, stringId;
1320 | size_t stringSizeCb;
1321 | PWSTR string;
1322 |
1323 | status = GetStringId(Request, &stringId, &languageId);
1324 |
1325 | // While we don't use the language id, some minidrivers might.
1326 | //
1327 | UNREFERENCED_PARAMETER(languageId);
1328 | if (DEBUG) KdPrint(("GetString\n"));
1329 | if (!NT_SUCCESS(status)) {
1330 | return status;
1331 | }
1332 |
1333 | switch (stringId){
1334 | case HID_STRING_ID_IMANUFACTURER:
1335 | stringSizeCb = sizeof(VHIDMINI_MANUFACTURER_STRING);
1336 | string = VHIDMINI_MANUFACTURER_STRING;
1337 | break;
1338 | case HID_STRING_ID_IPRODUCT:
1339 | stringSizeCb = sizeof(VHIDMINI_PRODUCT_STRING);
1340 | string = VHIDMINI_PRODUCT_STRING;
1341 | break;
1342 | case HID_STRING_ID_ISERIALNUMBER:
1343 | stringSizeCb = sizeof(VHIDMINI_SERIAL_NUMBER_STRING);
1344 | string = VHIDMINI_SERIAL_NUMBER_STRING;
1345 | break;
1346 | default:
1347 | status = STATUS_INVALID_PARAMETER;
1348 | if (DEBUG) KdPrint(("GetString: unkown string id %d\n", stringId));
1349 | return status;
1350 | }
1351 |
1352 | status = RequestCopyFromBuffer(Request, string, stringSizeCb);
1353 | return status;
1354 | }
1355 |
1356 |
1357 | NTSTATUS
1358 | ManualQueueCreate(
1359 | _In_ WDFDEVICE Device,
1360 | _Out_ WDFQUEUE *Queue
1361 | )
1362 | /*++
1363 | Routine Description:
1364 |
1365 | This function creates a manual I/O queue to receive IOCTL_HID_READ_REPORT
1366 | forwarded from the device's default queue handler.
1367 |
1368 | It also creates a periodic timer to check the queue and complete any pending
1369 | request with data from the device. Here timer expiring is used to simulate
1370 | a hardware event that new data is ready.
1371 |
1372 | The workflow is like this:
1373 |
1374 | - Hidclass.sys sends an ioctl to the miniport to read input report.
1375 |
1376 | - The request reaches the driver's default queue. As data may not be avaiable
1377 | yet, the request is forwarded to a second manual queue temporarily.
1378 |
1379 | - Later when data is ready (as simulated by timer expiring), the driver
1380 | checks for any pending request in the manual queue, and then completes it.
1381 |
1382 | - Hidclass gets notified for the read request completion and return data to
1383 | the caller.
1384 |
1385 | On the other hand, for IOCTL_HID_WRITE_REPORT request, the driver simply
1386 | sends the request to the hardware (as simulated by storing the data at
1387 | DeviceContext->DeviceData) and completes the request immediately. There is
1388 | no need to use another queue for write operation.
1389 |
1390 | Arguments:
1391 |
1392 | Device - Handle to a framework device object.
1393 |
1394 | Queue - Output pointer to a framework I/O queue handle, on success.
1395 |
1396 | Return Value:
1397 |
1398 | NTSTATUS
1399 |
1400 | --*/
1401 | {
1402 | NTSTATUS status;
1403 | WDF_IO_QUEUE_CONFIG queueConfig;
1404 | WDF_OBJECT_ATTRIBUTES queueAttributes;
1405 | WDFQUEUE queue;
1406 | PMANUAL_QUEUE_CONTEXT queueContext;
1407 | WDF_TIMER_CONFIG timerConfig;
1408 | WDF_OBJECT_ATTRIBUTES timerAttributes;
1409 | ULONG timerPeriodInSeconds = 5;
1410 |
1411 | WDF_IO_QUEUE_CONFIG_INIT(
1412 | &queueConfig,
1413 | WdfIoQueueDispatchManual);
1414 |
1415 | WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(
1416 | &queueAttributes,
1417 | MANUAL_QUEUE_CONTEXT);
1418 |
1419 | status = WdfIoQueueCreate(
1420 | Device,
1421 | &queueConfig,
1422 | &queueAttributes,
1423 | &queue);
1424 |
1425 | if( !NT_SUCCESS(status) ) {
1426 | if (DEBUG) KdPrint(("WdfIoQueueCreate failed 0x%x\n",status));
1427 | return status;
1428 | }
1429 |
1430 | queueContext = GetManualQueueContext(queue);
1431 | queueContext->Queue = queue;
1432 | queueContext->DeviceContext = GetDeviceContext(Device);
1433 |
1434 | WDF_TIMER_CONFIG_INIT_PERIODIC(
1435 | &timerConfig,
1436 | EvtTimerFunc,
1437 | timerPeriodInSeconds * 1000);
1438 |
1439 | WDF_OBJECT_ATTRIBUTES_INIT(&timerAttributes);
1440 | timerAttributes.ParentObject = queue;
1441 | status = WdfTimerCreate(&timerConfig,
1442 | &timerAttributes,
1443 | &queueContext->Timer);
1444 |
1445 | if( !NT_SUCCESS(status) ) {
1446 | if (DEBUG) KdPrint(("WdfTimerCreate failed 0x%x\n",status));
1447 | return status;
1448 | }
1449 |
1450 | WdfTimerStart(queueContext->Timer, WDF_REL_TIMEOUT_IN_SEC(1));
1451 |
1452 | *Queue = queue;
1453 |
1454 | return status;
1455 | }
1456 |
1457 | void
1458 | EvtTimerFunc(
1459 | _In_ WDFTIMER Timer
1460 | )
1461 | /*++
1462 | Routine Description:
1463 |
1464 | This periodic timer callback routine checks the device's manual queue and
1465 | completes any pending request with data from the device.
1466 |
1467 | Arguments:
1468 |
1469 | Timer - Handle to a timer object that was obtained from WdfTimerCreate.
1470 |
1471 | Return Value:
1472 |
1473 | VOID
1474 |
1475 | --*/
1476 | {
1477 | NTSTATUS status;
1478 | WDFQUEUE queue;
1479 | PMANUAL_QUEUE_CONTEXT queueContext;
1480 | WDFREQUEST request;
1481 | HIDMINI_INPUT_REPORT readReport;
1482 |
1483 | //if (DEBUG) KdPrint(("EvtTimerFunc\n"));
1484 |
1485 | queue = (WDFQUEUE)WdfTimerGetParentObject(Timer);
1486 | queueContext = GetManualQueueContext(queue);
1487 |
1488 | //
1489 | // see if we have a request in manual queue
1490 | //
1491 | status = WdfIoQueueRetrieveNextRequest(
1492 | queueContext->Queue,
1493 | &request);
1494 |
1495 | if (NT_SUCCESS(status)) {
1496 |
1497 | readReport.ReportId = CONTROL_FEATURE_REPORT_ID;
1498 | readReport.Data[0] = queueContext->DeviceContext->DeviceData;
1499 |
1500 | status = RequestCopyFromBuffer(request,
1501 | &readReport,
1502 | sizeof(readReport) - 1);
1503 |
1504 | WdfRequestComplete(request, status);
1505 | }
1506 | }
1507 |
1508 | NTSTATUS
1509 | CheckRegistryForDescriptor(
1510 | WDFDEVICE Device
1511 | )
1512 | /*++
1513 |
1514 | Routine Description:
1515 |
1516 | Read "ReadFromRegistry" key value from device parameters in the registry.
1517 |
1518 | Arguments:
1519 |
1520 | device - pointer to a device object.
1521 |
1522 | Return Value:
1523 |
1524 | NT status code.
1525 |
1526 | --*/
1527 |
1528 | {
1529 | WDFKEY hKey = NULL;
1530 | NTSTATUS status;
1531 | UNICODE_STRING valueName;
1532 | ULONG value;
1533 |
1534 | status = WdfDeviceOpenRegistryKey(Device,
1535 | PLUGPLAY_REGKEY_DEVICE,
1536 | KEY_READ,
1537 | WDF_NO_OBJECT_ATTRIBUTES,
1538 | &hKey);
1539 | if (NT_SUCCESS(status)) {
1540 |
1541 | RtlInitUnicodeString(&valueName, L"ReadFromRegistry");
1542 |
1543 | status = WdfRegistryQueryULong (hKey,
1544 | &valueName,
1545 | &value);
1546 |
1547 | if (NT_SUCCESS (status)) {
1548 | if (value == 0) {
1549 | status = STATUS_UNSUCCESSFUL;
1550 | }
1551 | }
1552 |
1553 | WdfRegistryClose(hKey);
1554 | }
1555 |
1556 | return status;
1557 | }
1558 |
1559 | NTSTATUS
1560 | ReadDescriptorFromRegistry(
1561 | WDFDEVICE Device
1562 | )
1563 | /*++
1564 |
1565 | Routine Description:
1566 |
1567 | Read HID report descriptor from registry
1568 |
1569 | Arguments:
1570 |
1571 | device - pointer to a device object.
1572 |
1573 | Return Value:
1574 |
1575 | NT status code.
1576 |
1577 | --*/
1578 | {
1579 | WDFKEY hKey = NULL;
1580 | NTSTATUS status;
1581 | UNICODE_STRING valueName;
1582 | WDFMEMORY memory;
1583 | size_t bufferSize;
1584 | PVOID reportDescriptor;
1585 | PDEVICE_CONTEXT deviceContext;
1586 | WDF_OBJECT_ATTRIBUTES attributes;
1587 |
1588 |
1589 | if (DEBUG) KdPrint(("ReadDescriptorFromRegistry\n"));
1590 |
1591 | deviceContext = GetDeviceContext(Device);
1592 |
1593 | status = WdfDeviceOpenRegistryKey(Device,
1594 | PLUGPLAY_REGKEY_DEVICE,
1595 | KEY_READ,
1596 | WDF_NO_OBJECT_ATTRIBUTES,
1597 | &hKey);
1598 |
1599 | if (NT_SUCCESS(status)) {
1600 |
1601 | RtlInitUnicodeString(&valueName, L"MyReportDescriptor");
1602 |
1603 | WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
1604 | attributes.ParentObject = Device;
1605 |
1606 | status = WdfRegistryQueryMemory (hKey,
1607 | &valueName,
1608 | NonPagedPool,
1609 | &attributes,
1610 | &memory,
1611 | NULL);
1612 |
1613 | if (NT_SUCCESS (status)) {
1614 |
1615 | reportDescriptor = WdfMemoryGetBuffer(memory, &bufferSize);
1616 |
1617 | if (DEBUG) KdPrint(("No. of report descriptor bytes copied: %d\n", (INT) bufferSize));
1618 |
1619 | //
1620 | // Store the registry report descriptor in the device extension
1621 | //
1622 | deviceContext->ReadReportDescFromRegistry = TRUE;
1623 | deviceContext->ReportDescriptor = reportDescriptor;
1624 | deviceContext->HidDescriptor.DescriptorList[0].wReportLength = (USHORT)bufferSize;
1625 | }
1626 |
1627 | WdfRegistryClose(hKey);
1628 | }
1629 |
1630 | return status;
1631 | }
1632 |
1633 |
--------------------------------------------------------------------------------
/driver/detector-mini.h:
--------------------------------------------------------------------------------
1 | /*++
2 |
3 | Copyright (C) Microsoft Corporation, All Rights Reserved
4 |
5 | Module Name:
6 |
7 | vhidmini.h
8 |
9 | Abstract:
10 |
11 | This module contains the type definitions for the driver
12 |
13 | Environment:
14 |
15 | Windows Driver Framework (WDF)
16 |
17 | --*/
18 |
19 | #ifdef _KERNEL_MODE
20 | #include
21 | #else
22 | #include
23 | #endif
24 |
25 | #include
26 |
27 | #include // located in $(DDK_INC_PATH)/wdm
28 | #include
29 | #include "common.h"
30 |
31 | typedef UCHAR HID_REPORT_DESCRIPTOR, *PHID_REPORT_DESCRIPTOR;
32 |
33 | DRIVER_INITIALIZE DriverEntry;
34 | EVT_WDF_DRIVER_DEVICE_ADD EvtDeviceAdd;
35 | EVT_WDF_TIMER EvtTimerFunc;
36 |
37 | typedef struct _DEVICE_CONTEXT
38 | {
39 | WDFDEVICE Device;
40 | WDFQUEUE DefaultQueue;
41 | WDFQUEUE ManualQueue;
42 | HID_DEVICE_ATTRIBUTES HidDeviceAttributes;
43 | BYTE DeviceData;
44 | HID_DESCRIPTOR HidDescriptor;
45 | PHID_REPORT_DESCRIPTOR ReportDescriptor;
46 | BOOLEAN ReadReportDescFromRegistry;
47 |
48 | } DEVICE_CONTEXT, *PDEVICE_CONTEXT;
49 |
50 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, GetDeviceContext);
51 |
52 | typedef struct _QUEUE_CONTEXT
53 | {
54 | WDFQUEUE Queue;
55 | PDEVICE_CONTEXT DeviceContext;
56 | UCHAR OutputReport;
57 |
58 | } QUEUE_CONTEXT, *PQUEUE_CONTEXT;
59 |
60 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_CONTEXT, GetQueueContext);
61 |
62 | NTSTATUS
63 | QueueCreate(
64 | _In_ WDFDEVICE Device,
65 | _Out_ WDFQUEUE *Queue
66 | );
67 |
68 | typedef struct _MANUAL_QUEUE_CONTEXT
69 | {
70 | WDFQUEUE Queue;
71 | PDEVICE_CONTEXT DeviceContext;
72 | WDFTIMER Timer;
73 |
74 | } MANUAL_QUEUE_CONTEXT, *PMANUAL_QUEUE_CONTEXT;
75 |
76 | WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(MANUAL_QUEUE_CONTEXT, GetManualQueueContext);
77 |
78 | NTSTATUS
79 | ManualQueueCreate(
80 | _In_ WDFDEVICE Device,
81 | _Out_ WDFQUEUE *Queue
82 | );
83 |
84 | NTSTATUS
85 | ReadReport(
86 | _In_ PQUEUE_CONTEXT QueueContext,
87 | _In_ WDFREQUEST Request,
88 | _Always_(_Out_)
89 | BOOLEAN* CompleteRequest
90 | );
91 |
92 | NTSTATUS
93 | WriteReport(
94 | _In_ PQUEUE_CONTEXT QueueContext,
95 | _In_ WDFREQUEST Request
96 | );
97 |
98 | NTSTATUS
99 | GetFeature(
100 | _In_ PQUEUE_CONTEXT QueueContext,
101 | _In_ WDFREQUEST Request
102 | );
103 |
104 | NTSTATUS
105 | SetFeature(
106 | _In_ PQUEUE_CONTEXT QueueContext,
107 | _In_ WDFREQUEST Request
108 | );
109 |
110 | NTSTATUS
111 | GetInputReport(
112 | _In_ PQUEUE_CONTEXT QueueContext,
113 | _In_ WDFREQUEST Request
114 | );
115 |
116 | NTSTATUS
117 | SetOutputReport(
118 | _In_ PQUEUE_CONTEXT QueueContext,
119 | _In_ WDFREQUEST Request
120 | );
121 |
122 | NTSTATUS
123 | GetString(
124 | _In_ WDFREQUEST Request
125 | );
126 |
127 | NTSTATUS
128 | GetIndexedString(
129 | _In_ WDFREQUEST Request
130 | );
131 |
132 | NTSTATUS
133 | GetStringId(
134 | _In_ WDFREQUEST Request,
135 | _Out_ ULONG *StringId,
136 | _Out_ ULONG *LanguageId
137 | );
138 |
139 | NTSTATUS
140 | RequestCopyFromBuffer(
141 | _In_ WDFREQUEST Request,
142 | _In_ PVOID SourceBuffer,
143 | _When_(NumBytesToCopyFrom == 0, __drv_reportError(NumBytesToCopyFrom cannot be zero))
144 | _In_ size_t NumBytesToCopyFrom
145 | );
146 |
147 | NTSTATUS
148 | RequestGetHidXferPacket_ToReadFromDevice(
149 | _In_ WDFREQUEST Request,
150 | _Out_ HID_XFER_PACKET *Packet
151 | );
152 |
153 | NTSTATUS
154 | RequestGetHidXferPacket_ToWriteToDevice(
155 | _In_ WDFREQUEST Request,
156 | _Out_ HID_XFER_PACKET *Packet
157 | );
158 |
159 | NTSTATUS
160 | CheckRegistryForDescriptor(
161 | _In_ WDFDEVICE Device
162 | );
163 |
164 | NTSTATUS
165 | ReadDescriptorFromRegistry(
166 | _In_ WDFDEVICE Device
167 | );
168 |
169 | //
170 | // Misc definitions
171 | //
172 | #define CONTROL_FEATURE_REPORT_ID 0x01
173 |
174 | //
175 | // These are the device attributes returned by the mini driver in response
176 | // to IOCTL_HID_GET_DEVICE_ATTRIBUTES.
177 | //
178 | #define HIDMINI_PID 0x3bca // Busylight Alpha
179 | #define HIDMINI_VID 0x27bb // Plenom A/S
180 | #define HIDMINI_VERSION 0x0101
181 |
182 | #define DLLPATHLENGTH 1024
183 | #define DLLFILENAME "DetectDLL.dll"
184 | #define DEBUG TRUE
--------------------------------------------------------------------------------
/driver/umdf2/DetectUm.inx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nccgroup/mimikatz-detector-busylight/43772df31d93ba2e7337706e22275e228cdae661/driver/umdf2/DetectUm.inx
--------------------------------------------------------------------------------
/driver/umdf2/DetectUm.rc:
--------------------------------------------------------------------------------
1 | //---------------------------------------------------------------------------
2 | // vhidmini.rc
3 | //
4 | // Copyright (c) Microsoft Corporation, All Rights Reserved
5 | //---------------------------------------------------------------------------
6 |
7 |
8 | #include
9 | #include
10 |
11 | #define VER_FILETYPE VFT_DLL
12 | #define VER_FILESUBTYPE VFT_UNKNOWN
13 | #define VER_FILEDESCRIPTION_STR "HID minidriver (UMDF 2 version)"
14 | #define VER_INTERNALNAME_STR "DetectUm.dll"
15 |
16 | #include "common.ver"
17 |
--------------------------------------------------------------------------------
/driver/umdf2/NCCDetectUm.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 | {EEECA421-064D-4E2E-8832-B0F43A9B394C}
23 | $(MSBuildProjectName)
24 | 2
25 | Debug
26 | Win32
27 | {1F3C5D0E-2636-4FC0-946E-F3B967B2715D}
28 | DetectUm
29 | $(LatestTargetPlatformVersion)
30 |
31 |
32 |
33 | Windows10
34 | False
35 | Universal
36 | UMDF
37 | WindowsUserModeDriver10.0
38 | DynamicLibrary
39 |
40 |
41 | Windows10
42 | True
43 | Universal
44 | UMDF
45 | WindowsUserModeDriver10.0
46 | DynamicLibrary
47 |
48 |
49 | Windows10
50 | False
51 | Universal
52 | UMDF
53 | WindowsUserModeDriver10.0
54 | DynamicLibrary
55 |
56 |
57 | Windows10
58 | True
59 | Universal
60 | UMDF
61 | WindowsUserModeDriver10.0
62 | DynamicLibrary
63 |
64 |
65 |
66 | $(SolutionDir)$(Platform)\$(Configuration)\
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | DetectUm
83 | $(Platform)\$(Configuration)\
84 |
85 |
86 | NCCDetectUm
87 |
88 |
89 | NCCDetectUm
90 |
91 |
92 | NCCDetectUm
93 |
94 |
95 |
96 | %(PreprocessorDefinitions);_UNICODE;UNICODE
97 | %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm;..\..\inc;..
98 |
99 |
100 | %(PreprocessorDefinitions);_UNICODE;UNICODE
101 | %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm;..\..\inc;..
102 |
103 |
104 | %(PreprocessorDefinitions);_UNICODE;UNICODE
105 | %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm;..\..\inc;..
106 |
107 |
108 | %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib
109 |
110 |
111 | sha256
112 |
113 |
114 |
115 |
116 | %(PreprocessorDefinitions);_UNICODE;UNICODE
117 | %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm;..\..\inc;..
118 |
119 |
120 | %(PreprocessorDefinitions);_UNICODE;UNICODE
121 | %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm;..\..\inc;..
122 |
123 |
124 | %(PreprocessorDefinitions);_UNICODE;UNICODE
125 | %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm;..\..\inc;..
126 |
127 |
128 | %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib
129 |
130 |
131 | sha256
132 |
133 |
134 |
135 |
136 | %(PreprocessorDefinitions);_UNICODE;UNICODE
137 | %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm;..\..\inc;..
138 |
139 |
140 | %(PreprocessorDefinitions);_UNICODE;UNICODE
141 | %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm;..\..\inc;..
142 |
143 |
144 | %(PreprocessorDefinitions);_UNICODE;UNICODE
145 | %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm;..\..\inc;..
146 |
147 |
148 | %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib
149 |
150 |
151 | sha256
152 |
153 |
154 |
155 |
156 | %(PreprocessorDefinitions);_UNICODE;UNICODE
157 | %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm;..\..\inc;..
158 |
159 |
160 | %(PreprocessorDefinitions);_UNICODE;UNICODE
161 | %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm;..\..\inc;..
162 |
163 |
164 | %(PreprocessorDefinitions);_UNICODE;UNICODE
165 | %(AdditionalIncludeDirectories);$(DDK_INC_PATH);$(DDK_INC_PATH)\wdm;..\..\inc;..
166 |
167 |
168 | %(AdditionalDependencies);$(SDK_LIB_PATH)\mincore.lib
169 |
170 |
171 | sha256
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
--------------------------------------------------------------------------------
/driver/umdf2/NCCDetectUm.vcxproj.Filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*
6 | {5E8FFB43-EAED-4E7B-B3FB-F07D5E007FB9}
7 |
8 |
9 | h;hpp;hxx;hm;inl;inc;xsd
10 | {D8864470-23E9-4C3B-8ED0-394EABC43745}
11 |
12 |
13 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml
14 | {923B77E9-A80C-48EC-88B0-CF700BB1D99C}
15 |
16 |
17 | inf;inv;inx;mof;mc;
18 | {0FC06B74-1E9A-4467-8098-08280033C42A}
19 |
20 |
21 |
22 |
23 | Source Files
24 |
25 |
26 | Source Files
27 |
28 |
29 |
30 |
31 | Resource Files
32 |
33 |
34 |
35 |
36 | Driver Files
37 |
38 |
39 |
40 |
41 | Header Files
42 |
43 |
44 |
--------------------------------------------------------------------------------
/driver/umdf2/util.c:
--------------------------------------------------------------------------------
1 | /*++
2 |
3 | Copyright (C) Microsoft Corporation, All Rights Reserved.
4 |
5 | Module Name:
6 |
7 | util.cpp
8 |
9 | Abstract:
10 |
11 | This module contains the implementation of the driver
12 |
13 | Environment:
14 |
15 | Windows Driver Framework (WDF)
16 |
17 | --*/
18 |
19 | #include "detector-mini.h"
20 |
21 | //
22 | // HID minidriver IOCTL uses HID_XFER_PACKET which contains an embedded pointer.
23 | //
24 | // typedef struct _HID_XFER_PACKET {
25 | // PUCHAR reportBuffer;
26 | // ULONG reportBufferLen;
27 | // UCHAR reportId;
28 | // } HID_XFER_PACKET, *PHID_XFER_PACKET;
29 | //
30 | // UMDF cannot handle embedded pointers when marshalling buffers between processes.
31 | // Therefore a special driver mshidumdf.sys is introduced to convert such IRPs to
32 | // new IRPs (with new IOCTL name like IOCTL_UMDF_HID_Xxxx) where:
33 | //
34 | // reportBuffer - passed as one buffer inside the IRP
35 | // reportId - passed as a second buffer inside the IRP
36 | //
37 | // The new IRP is then passed to UMDF host and driver for further processing.
38 | //
39 |
40 | NTSTATUS
41 | RequestGetHidXferPacket_ToReadFromDevice(
42 | _In_ WDFREQUEST Request,
43 | _Out_ HID_XFER_PACKET *Packet
44 | )
45 | {
46 | //
47 | // Driver need to write to the output buffer (so that App can read from it)
48 | //
49 | // Report Buffer: Output Buffer
50 | // Report Id : Input Buffer
51 | //
52 |
53 | NTSTATUS status;
54 | WDFMEMORY inputMemory;
55 | WDFMEMORY outputMemory;
56 | size_t inputBufferLength;
57 | size_t outputBufferLength;
58 | PVOID inputBuffer;
59 | PVOID outputBuffer;
60 |
61 | //
62 | // Get report Id from input buffer
63 | //
64 | KdPrint(("RequestGetHidXferPacket_ToReadFromDevice\n"));
65 | status = WdfRequestRetrieveInputMemory(Request, &inputMemory);
66 | if( !NT_SUCCESS(status) ) {
67 | KdPrint(("WdfRequestRetrieveInputMemory failed 0x%x\n",status));
68 | return status;
69 | }
70 | inputBuffer = WdfMemoryGetBuffer(inputMemory, &inputBufferLength);
71 |
72 | if (inputBufferLength < sizeof(UCHAR)) {
73 | status = STATUS_INVALID_BUFFER_SIZE;
74 | KdPrint(("WdfRequestRetrieveInputMemory: invalid input buffer. size %d, expect %d\n",
75 | (int)inputBufferLength, (int)sizeof(UCHAR)));
76 | return status;
77 | }
78 |
79 | Packet->reportId = *(PUCHAR)inputBuffer;
80 |
81 | //
82 | // Get report buffer from output buffer
83 | //
84 | status = WdfRequestRetrieveOutputMemory(Request, &outputMemory);
85 | if( !NT_SUCCESS(status) ) {
86 | KdPrint(("WdfRequestRetrieveOutputMemory failed 0x%x\n",status));
87 | return status;
88 | }
89 |
90 | outputBuffer = WdfMemoryGetBuffer(outputMemory, &outputBufferLength);
91 |
92 | Packet->reportBuffer = (PUCHAR) outputBuffer;
93 | Packet->reportBufferLen = (ULONG) outputBufferLength;
94 |
95 | return status;
96 | }
97 |
98 | NTSTATUS
99 | RequestGetHidXferPacket_ToWriteToDevice(
100 | _In_ WDFREQUEST Request,
101 | _Out_ HID_XFER_PACKET *Packet
102 | )
103 | {
104 | //
105 | // Driver need to read from the input buffer (which was written by App)
106 | //
107 | // Report Buffer: Input Buffer
108 | // Report Id : Output Buffer Length
109 | //
110 | // Note that the report id is not stored inside the output buffer, as the
111 | // driver has no read-access right to the output buffer, and trying to read
112 | // from the buffer will cause an access violation error.
113 | //
114 | // The workaround is to store the report id in the OutputBufferLength field,
115 | // to which the driver does have read-access right.
116 | //
117 |
118 | NTSTATUS status;
119 | WDFMEMORY inputMemory;
120 | WDFMEMORY outputMemory;
121 | size_t inputBufferLength;
122 | size_t outputBufferLength;
123 | PVOID inputBuffer;
124 |
125 | //
126 | // Get report Id from output buffer length
127 | //
128 |
129 | //KdPrint(("RequestGetHidXferPacket_ToWriteToDevice\n"));
130 | status = WdfRequestRetrieveOutputMemory(Request, &outputMemory);
131 | if( !NT_SUCCESS(status) && (status != STATUS_BUFFER_TOO_SMALL)) {
132 | KdPrint(("WdfRequestRetrieveOutputMemory failed 0x%x\n",status));
133 | return status;
134 | }
135 | if (status == STATUS_BUFFER_TOO_SMALL)
136 | {
137 | //KdPrint(("STATUS_BUFFER_TOO_SMALL INFO 0x%x\n", status));
138 | Packet->reportId = 0x01;
139 | }
140 | else
141 | {
142 | WdfMemoryGetBuffer(outputMemory, &outputBufferLength);
143 | Packet->reportId = (UCHAR)outputBufferLength;
144 | }
145 |
146 | //
147 | // Get report buffer from input buffer
148 | //
149 | status = WdfRequestRetrieveInputMemory(Request, &inputMemory);
150 | if( !NT_SUCCESS(status) ) {
151 | KdPrint(("WdfRequestRetrieveInputMemory failed 0x%x\n",status));
152 | return status;
153 | }
154 | inputBuffer = WdfMemoryGetBuffer(inputMemory, &inputBufferLength);
155 |
156 | Packet->reportBuffer = (PUCHAR) inputBuffer;
157 | Packet->reportBufferLen = (ULONG) inputBufferLength;
158 |
159 | return status;
160 | }
161 |
--------------------------------------------------------------------------------
/inc/common.h:
--------------------------------------------------------------------------------
1 | /*++
2 |
3 | Copyright (c) Microsoft Corporation. All rights reserved.
4 |
5 | THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
6 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
7 | IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
8 | PURPOSE.
9 |
10 | Module Name:
11 |
12 | common.h
13 |
14 | Environment:
15 |
16 | User mode
17 |
18 | --*/
19 |
20 | #ifndef __VHIDMINI_COMMON_H__
21 | #define __VHIDMINI_COMMON_H__
22 |
23 | //
24 | // Custom control codes are defined here. They are to be used for sideband
25 | // communication with the hid minidriver. These control codes are sent to
26 | // the hid minidriver using Hid_SetFeature() API to a custom collection
27 | // defined especially to handle such requests.
28 | //
29 | #define HIDMINI_CONTROL_CODE_SET_ATTRIBUTES 0x00
30 | #define HIDMINI_CONTROL_CODE_DUMMY1 0x01
31 | #define HIDMINI_CONTROL_CODE_DUMMY2 0x02
32 |
33 | //
34 | // This is the report id of the collection to which the control codes are sent
35 | //
36 | #define CONTROL_COLLECTION_REPORT_ID 0x01
37 | #define TEST_COLLECTION_REPORT_ID 0x02
38 |
39 | #define MAXIMUM_STRING_LENGTH (126 * sizeof(WCHAR))
40 | #define VHIDMINI_MANUFACTURER_STRING L"NCC Group Plc"
41 | #define VHIDMINI_PRODUCT_STRING L"Emulated HID device"
42 | #define VHIDMINI_SERIAL_NUMBER_STRING L"GPSK02872K"
43 | #define VHIDMINI_DEVICE_STRING L"Emulated HID device"
44 | #define VHIDMINI_DEVICE_STRING_INDEX 5
45 | #include
46 |
47 | typedef struct _MY_DEVICE_ATTRIBUTES {
48 |
49 | USHORT VendorID;
50 | USHORT ProductID;
51 | USHORT VersionNumber;
52 |
53 | } MY_DEVICE_ATTRIBUTES, *PMY_DEVICE_ATTRIBUTES;
54 |
55 | typedef struct _HIDMINI_CONTROL_INFO {
56 |
57 | //
58 | //report ID of the collection to which the control request is sent
59 | //
60 | UCHAR ReportId;
61 |
62 | //
63 | // One byte control code (user-defined) for communication with hid
64 | // mini driver
65 | //
66 | UCHAR ControlCode;
67 |
68 | //
69 | // This union contains input data for the control request.
70 | //
71 | union {
72 | MY_DEVICE_ATTRIBUTES Attributes;
73 | struct {
74 | ULONG Dummy1;
75 | ULONG Dummy2;
76 | } Dummy;
77 | } u;
78 |
79 | } HIDMINI_CONTROL_INFO, * PHIDMINI_CONTROL_INFO;
80 |
81 | //
82 | // input from device to system
83 | //
84 | typedef struct _HIDMINI_INPUT_REPORT {
85 |
86 | UCHAR ReportId;
87 | UCHAR Data[64];
88 |
89 | } HIDMINI_INPUT_REPORT, *PHIDMINI_INPUT_REPORT;
90 |
91 | //
92 | // output to device from system
93 | //
94 | typedef struct _HIDMINI_OUTPUT_REPORT {
95 |
96 | UCHAR ReportId;
97 | UCHAR Data[64]; // TODO
98 |
99 | } HIDMINI_OUTPUT_REPORT, *PHIDMINI_OUTPUT_REPORT;
100 |
101 | #include
102 |
103 | //
104 | // SetFeature request requires that the feature report buffer size be exactly
105 | // same as the size of report described in the hid report descriptor (
106 | // excluding the report ID). Since HIDMINI_CONTROL_INFO includes report ID,
107 | // we subtract one from the size.
108 | //
109 | #define FEATURE_REPORT_SIZE_CB ((USHORT)(sizeof(HIDMINI_CONTROL_INFO) - 1))
110 | #define INPUT_REPORT_SIZE_CB ((USHORT)(sizeof(HIDMINI_INPUT_REPORT) - 1))
111 | #define OUTPUT_REPORT_SIZE_CB ((USHORT)(sizeof(HIDMINI_OUTPUT_REPORT) - 1))
112 |
113 | #endif //__VHIDMINI_COMMON_H__
114 |
--------------------------------------------------------------------------------
/install.bat:
--------------------------------------------------------------------------------
1 | copy /Y x64\Release\DetectDLL.dll C:\Windows\system32\
2 | rem certutil -addstore "TrustedPublisher" certificate\nccgroup.cer
3 | tools\x64\devcon.exe install x64\Release\DetectUm\DetectUm.inf root\DetectUm
4 |
--------------------------------------------------------------------------------
/uninstall.bat:
--------------------------------------------------------------------------------
1 | tools\x64\devcon.exe remove x64\Release\DetectUm\DetectUm.inf root\DetectUm
2 |
--------------------------------------------------------------------------------