Skip to content

Commit 0952d6d

Browse files
committed
Merge branch 'sample/kbdclass'
2 parents db29f42 + 209387a commit 0952d6d

File tree

5 files changed

+472
-0
lines changed

5 files changed

+472
-0
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ The installer script (`scripts\installer.iss`) works with Inno Setup 6. That mea
2626

2727
You need to alter the signing scripts to respect your signing certificate. Similarly, the SDK version may need to be modified to match version installed on your machine.
2828

29+
## Samples
30+
31+
I hope to add some sample programs demonstrating how to use IRPMon DLLs to your advantage. So, you will not be dependent on the GUI application.
32+
33+
### Kbdsample
34+
35+
This sample hooks primary keyboard device (`\Device\KeyboardClass0`) and display basic information about detected requests. It also shows how to enumerate hooked drivers and unhook them inf necessary (e.g. if you wish to hook a driver that is already hooked, you may need to unhook it first). See the [kbdsample](kbdsample) directory for more information.
36+
2937
## People
3038

3139
### Authors

irpmon.sln

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{8504C0C7
9494
EndProject
9595
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xml2doc", "xml2doc\xml2doc.vcxproj", "{7A2471D2-2F3F-4EA2-A822-36B2C51C7381}"
9696
EndProject
97+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{DC53B58A-18D1-4865-B709-570C322A3327}"
98+
EndProject
99+
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kbdsample", "kbdsample\kbdsample.vcxproj", "{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}"
100+
EndProject
97101
Global
98102
GlobalSection(SolutionConfigurationPlatforms) = preSolution
99103
Debug|ARM = Debug|ARM
@@ -504,6 +508,26 @@ Global
504508
{7A2471D2-2F3F-4EA2-A822-36B2C51C7381}.XP|Win32.Build.0 = XP|Win32
505509
{7A2471D2-2F3F-4EA2-A822-36B2C51C7381}.XP|x64.ActiveCfg = XP|x64
506510
{7A2471D2-2F3F-4EA2-A822-36B2C51C7381}.XP|x64.Build.0 = XP|x64
511+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Debug|ARM.ActiveCfg = Debug|Win32
512+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Debug|ARM64.ActiveCfg = Debug|Win32
513+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Debug|Win32.ActiveCfg = Debug|Win32
514+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Debug|Win32.Build.0 = Debug|Win32
515+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Debug|x64.ActiveCfg = Debug|x64
516+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Debug|x64.Build.0 = Debug|x64
517+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Release|ARM.ActiveCfg = Release|Win32
518+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Release|ARM64.ActiveCfg = Release|Win32
519+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Release|Win32.ActiveCfg = Release|Win32
520+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Release|Win32.Build.0 = Release|Win32
521+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Release|x64.ActiveCfg = Release|x64
522+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Release|x64.Build.0 = Release|x64
523+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|ARM.ActiveCfg = Debug|Win32
524+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|ARM.Build.0 = Debug|Win32
525+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|ARM64.ActiveCfg = Debug|Win32
526+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|ARM64.Build.0 = Debug|Win32
527+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|Win32.ActiveCfg = Debug|Win32
528+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|Win32.Build.0 = Debug|Win32
529+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|x64.ActiveCfg = Debug|x64
530+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|x64.Build.0 = Debug|x64
507531
EndGlobalSection
508532
GlobalSection(SolutionProperties) = preSolution
509533
HideSolutionNode = FALSE
@@ -530,6 +554,7 @@ Global
530554
{22EE73A9-C35A-4185-9224-6EFD8C2AEF07} = {E22735F3-939B-4EC2-8761-33B38B315EDF}
531555
{D7D49F20-B8EF-43D8-8D57-B48DE51C62F3} = {E22735F3-939B-4EC2-8761-33B38B315EDF}
532556
{7A2471D2-2F3F-4EA2-A822-36B2C51C7381} = {8504C0C7-2095-4C8D-91A8-0793999E17AD}
557+
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8} = {DC53B58A-18D1-4865-B709-570C322A3327}
533558
EndGlobalSection
534559
GlobalSection(ExtensibilityGlobals) = postSolution
535560
SolutionGuid = {1780A1EC-E35B-4BB5-BC92-97AA7E63D2AE}

kbdsample/kbdsample.c

Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
2+
#include <stdio.h>
3+
#include <string.h>
4+
#include <stdlib.h>
5+
#include <windows.h>
6+
#include "request.h"
7+
#include "irpmondll.h"
8+
9+
10+
11+
static volatile BOOL _terminate = FALSE;
12+
13+
14+
static BOOL WINAPI _CtrlHandler(DWORD fdwCtrlType)
15+
{
16+
BOOL ret = FALSE;
17+
18+
switch (fdwCtrlType) {
19+
case CTRL_CLOSE_EVENT:
20+
case CTRL_C_EVENT:
21+
_terminate = TRUE;
22+
ret = TRUE;
23+
break;
24+
default:
25+
break;
26+
}
27+
28+
return ret;
29+
}
30+
31+
32+
int main(int argc, char* argv[])
33+
{
34+
int ret = 0;
35+
IRPMON_INIT_INFO initInfo;
36+
HANDLE driverHandle = NULL;
37+
HANDLE deviceHandle = NULL;
38+
DRIVER_MONITOR_SETTINGS driverSettings;
39+
PREQUEST_HEADER buffer = NULL;
40+
DWORD bufferSize = 0;
41+
IRPMNDRV_SETTINGS globalSettings;
42+
43+
if (!SetConsoleCtrlHandler(_CtrlHandler, TRUE)) {
44+
ret = GetLastError();
45+
fprintf(stderr, "[ERROR]: Unable to register Control Handler: %u\n", ret);
46+
return ret;
47+
}
48+
49+
memset(&initInfo, 0, sizeof(initInfo));
50+
// We are connecting to driver instance running
51+
// this computer. Since we specify no device name,
52+
// default one will be used.
53+
fprintf(stderr, "[INFO]: Initializing the library...\n");
54+
initInfo.ConnectorType = ictDevice;
55+
ret = IRPMonDllInitialize(&initInfo);
56+
if (ret == 0) {
57+
memset(&driverSettings, 0, sizeof(driverSettings));
58+
// Watch for AddDevice, IRPs, IRP completions, FastIOs and driver unload
59+
driverSettings.MonitorAddDevice = TRUE;
60+
driverSettings.MonitorIRP = TRUE;
61+
driverSettings.MonitorIRPCompletion = TRUE;
62+
driverSettings.MonitorUnload = TRUE;
63+
// Let's capture also data specific to each request.
64+
driverSettings.MonitorData = TRUE;
65+
// Since we plan to monitor only one device, devices
66+
// appearing after we "go to town" do not interest us.
67+
// StartIo does not interest us too.
68+
// Also, FastIO is not used with keyboard drivers,
69+
// so let's skip it too.
70+
driverSettings.MonitorFastIo = FALSE;
71+
driverSettings.MonitorNewDevices = FALSE;
72+
driverSettings.MonitorStartIo = FALSE;
73+
// We are interested in all types of IRPs
74+
for (size_t i = 0; i < sizeof(driverSettings.IRPSettings) / sizeof(driverSettings.IRPSettings[0]); ++i)
75+
driverSettings.IRPSettings[i] = TRUE;
76+
77+
fprintf(stderr, "[INFO]: Hooking the Keyboard Class driver...\n");
78+
ret = IRPMonDllHookDriver(L"\\Driver\\kbdclass", &driverSettings, FALSE, &driverHandle, NULL);
79+
if (ret == ERROR_ALREADY_EXISTS) {
80+
ULONG count = 0;
81+
PHOOKED_DRIVER_UMINFO driverHookInfo = NULL;
82+
PHOOKED_DRIVER_UMINFO tmp = NULL;
83+
84+
fprintf(stderr, "[WARNING]: Driver already hooked. Let's unhook it first\n");
85+
ret = IRPMonDllDriverHooksEnumerate(&driverHookInfo, &count);
86+
if (ret == 0) {
87+
ret = ERROR_FILE_NOT_FOUND;
88+
tmp = driverHookInfo;
89+
for (size_t i = 0; i < count; ++i) {
90+
if (tmp->DriverName != NULL && wcsicmp(tmp->DriverName, L"\\Driver\\kbdclass") == 0) {
91+
fprintf(stderr, "[INFO]: Found (ID 0x%p)\n", tmp->ObjectId);
92+
ret = IRPMonDllOpenHookedDriver(tmp->ObjectId, &driverHandle);
93+
if (ret == 0) {
94+
ret = IRPMonDllUnhookDriver(driverHandle);
95+
if (ret != 0)
96+
fprintf(stderr, "[ERROR]: Unable to unhook the driver: %u\n", ret);
97+
98+
IRPMonDllCloseHookedDriverHandle(driverHandle);
99+
} else fprintf(stderr, "[ERROR]: Unable to get hook driver handle: %u\n", ret);
100+
101+
break;
102+
}
103+
104+
++tmp;
105+
}
106+
107+
IRPMonDllDriverHooksFree(driverHookInfo, count);
108+
} else fprintf(stderr, "[ERROR]: Unable to get list of hooked drivers: %u\n", ret);
109+
110+
if (ret == 0) {
111+
ret = IRPMonDllHookDriver(L"\\Driver\\kbdclass", &driverSettings, FALSE, &driverHandle, NULL);
112+
if (ret != 0)
113+
fprintf(stderr, "[ERROR]: Unable to hook the driver %u\n", ret);
114+
}
115+
} else if (ret != 0)
116+
fprintf(stderr, "[ERROR]: Error %u\n", ret);
117+
118+
if (ret == 0) {
119+
fprintf(stderr, "[INFO]: Hooking the primary keyboard device...\n");
120+
ret = IRPMonDllHookDeviceByName(L"\\Device\\KeyboardClass0", &deviceHandle, NULL);
121+
if (ret == 0) {
122+
// Attempt to chan ge the global driver settings in order
123+
// not to get information about all drivers and devices and running processed
124+
// after connecting to the request queue.
125+
ret = IRPMonDllSettingsQuery(&globalSettings);
126+
if (ret == 0) {
127+
globalSettings.DriverSnapshotOnConnect = FALSE;
128+
globalSettings.ProcessEmulateOnConnect = FALSE;
129+
ret = IRPMonDllSettingsSet(&globalSettings, FALSE);
130+
if (ret != 0)
131+
fprintf(stderr, "[WARNING]: Unable to alter global driver settings: %u\n", ret);
132+
} else fprintf(stderr, "[WARNING]: Unable to query global driver settings: %u\n", ret);
133+
134+
// Now, everything is set. Let's tell the IRPMon driver
135+
// to start actually monitoring the requests.
136+
fprintf(stderr, "[INFO]: Starting to watch for requests...\n");
137+
ret = IRPMonDllDriverStartMonitoring(driverHandle);
138+
if (ret == 0) {
139+
// Connect to the request queue.
140+
fprintf(stderr, "[INFO]: Connecting to the request queue...\n");
141+
ret = IRPMonDllConnect();
142+
if (ret == 0) {
143+
fprintf(stderr, "[INFO]: We are now set! Press CTRL+C to stop monitoring and exit the application peacefully\n");
144+
while (!_terminate) {
145+
// Attempt to get a request from the queue
146+
ret = IRPMonDllGetRequest(buffer, bufferSize);
147+
switch (ret) {
148+
case ERROR_SUCCESS: {
149+
PREQUEST_HEADER request = NULL;
150+
151+
// Since multiple requests can be returned
152+
// in one call to IRPMonDllGetRequest, let's
153+
// carefully examine all of them.
154+
request = buffer;
155+
do {
156+
PREQUEST_IRP irp = NULL;
157+
PREQUEST_IRP_COMPLETION irpc = NULL;
158+
PREQUEST_ADDDEVICE ad = NULL;
159+
PREQUEST_UNLOAD du = NULL;
160+
161+
switch (request->Type) {
162+
case ertIRP:
163+
irp = CONTAINING_RECORD(request, REQUEST_IRP, Header);
164+
fprintf(stderr, "[IRP]: irp=0x%p, major=%u, minor=%u, datasize=%zu\n", irp->IRPAddress, irp->MajorFunction, irp->MinorFunction, irp->DataSize);
165+
break;
166+
case ertIRPCompletion:
167+
irpc = CONTAINING_RECORD(request, REQUEST_IRP_COMPLETION, Header);
168+
fprintf(stderr, "[IRPCOMPLETE]: irp=0x%p, status=0x%lx, info=%zu, datasize=%zu\n", irpc->IRPAddress, irpc->CompletionStatus, irpc->CompletionInformation, irpc->DataSize);
169+
break;
170+
case ertAddDevice:
171+
ad = CONTAINING_RECORD(request, REQUEST_ADDDEVICE, Header);
172+
fprintf(stderr, "[ADDDEVICE]: driver=0x%p, device=0x%p\n", ad->Header.Driver, ad->Header.Device);
173+
break;
174+
case ertDriverUnload:
175+
du = CONTAINING_RECORD(request, REQUEST_UNLOAD, Header);
176+
fprintf(stderr, "[UNLOAD]: driver=0x%p", du->Header.Driver);
177+
break;
178+
default:
179+
fprintf(stderr, "[REQUEST]: Type %u\n", request->Type);
180+
break;
181+
}
182+
183+
// This value is nonzero when the buffer returned
184+
// by the driver contains at least one request behind
185+
// this one. If the REQUEST_FLAG_NEXT_AVAILABLE is set
186+
// in the request->Flags, more requests are still present
187+
// in the request queue in the kernel.
188+
if (request->Entry.Flink == NULL)
189+
break;
190+
191+
request = (PREQUEST_HEADER)((unsigned char *)request + RequestGetSize(request));
192+
} while (TRUE);
193+
} break;
194+
case ERROR_INSUFFICIENT_BUFFER: {
195+
PREQUEST_HEADER tmp = NULL;
196+
197+
// Our buffer is not large enough, let's resize it!
198+
fprintf(stderr, "[WARNING]: Buffer of size %u is not enough, enlarging to %u\n", bufferSize, bufferSize*2 + 128);
199+
bufferSize = bufferSize*2 + 128;
200+
tmp = realloc(buffer, bufferSize);
201+
if (tmp == NULL) {
202+
fprintf(stderr, "[ERROR]: Buffer allocation failed with errno of %u\n", errno);
203+
_terminate = TRUE;
204+
continue;
205+
}
206+
207+
buffer = tmp;
208+
} break;
209+
case ERROR_NO_MORE_ITEMS:
210+
// The queue is empty. Well, this is very common
211+
// for keyboard devices. Let's just wait.
212+
fputc('.', stderr);
213+
Sleep(1000);
214+
break;
215+
default:
216+
// An unexpected error occurred.
217+
// Well, no software is perfect, right?
218+
fprintf(stderr, "[ERROR]: Unable to get request: %u\n", ret);
219+
_terminate = TRUE;
220+
break;
221+
}
222+
}
223+
224+
// Let's free the buffer. This will also work
225+
// when the buffer is NUULL (i.e. have never been allocated).
226+
free(buffer);
227+
fprintf(stderr, "[INFO]: Disconnecting from the request queue\n");
228+
ret = IRPMonDllDisconnect();
229+
if (ret != 0)
230+
fprintf(stderr, "[WARNING]: Error %u\n", ret);
231+
} else fprintf(stderr, "[ERROR]: Error %u\n", ret);
232+
233+
fprintf(stderr, "[INFO]: Stopping the monitoring\n");
234+
ret = IRPMonDllDriverStopMonitoring(driverHandle);
235+
if (ret != 0)
236+
fprintf(stderr, "[WARNING]: Error %u\n", ret);
237+
} else fprintf(stderr, "[ERROR]: Error %u\n", ret);
238+
239+
fprintf(stderr, "[INFO]: Unhooking the primary keyboard device\n");
240+
ret = IRPMonDllUnhookDevice(deviceHandle);
241+
if (ret != 0)
242+
fprintf(stderr, "[WARNING]: Error %u\n", ret);
243+
} else fprintf(stderr, "[ERROR]: Error %u\n", ret);
244+
245+
fprintf(stderr, "[INFO]: Unhooking the Keyboard Class driver\n");
246+
ret = IRPMonDllUnhookDriver(driverHandle);
247+
if (ret != 0)
248+
fprintf(stderr, "[WARNING]: Error %u\n", ret);
249+
}
250+
251+
fprintf(stderr, "[INFO]: Cleanup the library\n");
252+
IRPMonDllFinalize();
253+
} else fprintf(stderr, "[ERROR]: Error %u\n", ret);
254+
255+
SetConsoleCtrlHandler(_CtrlHandler, FALSE);
256+
257+
return ret;
258+
}

0 commit comments

Comments
 (0)