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