Skip to content

Commit

Permalink
Merge branch 'sample/kbdclass'
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinDrab committed Oct 20, 2020
2 parents db29f42 + 209387a commit 0952d6d
Show file tree
Hide file tree
Showing 5 changed files with 472 additions and 0 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ The installer script (`scripts\installer.iss`) works with Inno Setup 6. That mea

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.

## Samples

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.

### Kbdsample

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.

## People

### Authors
Expand Down
25 changes: 25 additions & 0 deletions irpmon.sln
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{8504C0C7
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xml2doc", "xml2doc\xml2doc.vcxproj", "{7A2471D2-2F3F-4EA2-A822-36B2C51C7381}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{DC53B58A-18D1-4865-B709-570C322A3327}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kbdsample", "kbdsample\kbdsample.vcxproj", "{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
Expand Down Expand Up @@ -504,6 +508,26 @@ Global
{7A2471D2-2F3F-4EA2-A822-36B2C51C7381}.XP|Win32.Build.0 = XP|Win32
{7A2471D2-2F3F-4EA2-A822-36B2C51C7381}.XP|x64.ActiveCfg = XP|x64
{7A2471D2-2F3F-4EA2-A822-36B2C51C7381}.XP|x64.Build.0 = XP|x64
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Debug|ARM.ActiveCfg = Debug|Win32
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Debug|ARM64.ActiveCfg = Debug|Win32
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Debug|Win32.ActiveCfg = Debug|Win32
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Debug|Win32.Build.0 = Debug|Win32
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Debug|x64.ActiveCfg = Debug|x64
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Debug|x64.Build.0 = Debug|x64
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Release|ARM.ActiveCfg = Release|Win32
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Release|ARM64.ActiveCfg = Release|Win32
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Release|Win32.ActiveCfg = Release|Win32
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Release|Win32.Build.0 = Release|Win32
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Release|x64.ActiveCfg = Release|x64
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.Release|x64.Build.0 = Release|x64
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|ARM.ActiveCfg = Debug|Win32
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|ARM.Build.0 = Debug|Win32
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|ARM64.ActiveCfg = Debug|Win32
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|ARM64.Build.0 = Debug|Win32
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|Win32.ActiveCfg = Debug|Win32
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|Win32.Build.0 = Debug|Win32
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|x64.ActiveCfg = Debug|x64
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8}.XP|x64.Build.0 = Debug|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -530,6 +554,7 @@ Global
{22EE73A9-C35A-4185-9224-6EFD8C2AEF07} = {E22735F3-939B-4EC2-8761-33B38B315EDF}
{D7D49F20-B8EF-43D8-8D57-B48DE51C62F3} = {E22735F3-939B-4EC2-8761-33B38B315EDF}
{7A2471D2-2F3F-4EA2-A822-36B2C51C7381} = {8504C0C7-2095-4C8D-91A8-0793999E17AD}
{B6E47DB0-E74F-4DB4-88FD-403FEA3BEBA8} = {DC53B58A-18D1-4865-B709-570C322A3327}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {1780A1EC-E35B-4BB5-BC92-97AA7E63D2AE}
Expand Down
258 changes: 258 additions & 0 deletions kbdsample/kbdsample.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <windows.h>
#include "request.h"
#include "irpmondll.h"



static volatile BOOL _terminate = FALSE;


static BOOL WINAPI _CtrlHandler(DWORD fdwCtrlType)
{
BOOL ret = FALSE;

switch (fdwCtrlType) {
case CTRL_CLOSE_EVENT:
case CTRL_C_EVENT:
_terminate = TRUE;
ret = TRUE;
break;
default:
break;
}

return ret;
}


int main(int argc, char* argv[])
{
int ret = 0;
IRPMON_INIT_INFO initInfo;
HANDLE driverHandle = NULL;
HANDLE deviceHandle = NULL;
DRIVER_MONITOR_SETTINGS driverSettings;
PREQUEST_HEADER buffer = NULL;
DWORD bufferSize = 0;
IRPMNDRV_SETTINGS globalSettings;

if (!SetConsoleCtrlHandler(_CtrlHandler, TRUE)) {
ret = GetLastError();
fprintf(stderr, "[ERROR]: Unable to register Control Handler: %u\n", ret);
return ret;
}

memset(&initInfo, 0, sizeof(initInfo));
// We are connecting to driver instance running
// this computer. Since we specify no device name,
// default one will be used.
fprintf(stderr, "[INFO]: Initializing the library...\n");
initInfo.ConnectorType = ictDevice;
ret = IRPMonDllInitialize(&initInfo);
if (ret == 0) {
memset(&driverSettings, 0, sizeof(driverSettings));
// Watch for AddDevice, IRPs, IRP completions, FastIOs and driver unload
driverSettings.MonitorAddDevice = TRUE;
driverSettings.MonitorIRP = TRUE;
driverSettings.MonitorIRPCompletion = TRUE;
driverSettings.MonitorUnload = TRUE;
// Let's capture also data specific to each request.
driverSettings.MonitorData = TRUE;
// Since we plan to monitor only one device, devices
// appearing after we "go to town" do not interest us.
// StartIo does not interest us too.
// Also, FastIO is not used with keyboard drivers,
// so let's skip it too.
driverSettings.MonitorFastIo = FALSE;
driverSettings.MonitorNewDevices = FALSE;
driverSettings.MonitorStartIo = FALSE;
// We are interested in all types of IRPs
for (size_t i = 0; i < sizeof(driverSettings.IRPSettings) / sizeof(driverSettings.IRPSettings[0]); ++i)
driverSettings.IRPSettings[i] = TRUE;

fprintf(stderr, "[INFO]: Hooking the Keyboard Class driver...\n");
ret = IRPMonDllHookDriver(L"\\Driver\\kbdclass", &driverSettings, FALSE, &driverHandle, NULL);
if (ret == ERROR_ALREADY_EXISTS) {
ULONG count = 0;
PHOOKED_DRIVER_UMINFO driverHookInfo = NULL;
PHOOKED_DRIVER_UMINFO tmp = NULL;

fprintf(stderr, "[WARNING]: Driver already hooked. Let's unhook it first\n");
ret = IRPMonDllDriverHooksEnumerate(&driverHookInfo, &count);
if (ret == 0) {
ret = ERROR_FILE_NOT_FOUND;
tmp = driverHookInfo;
for (size_t i = 0; i < count; ++i) {
if (tmp->DriverName != NULL && wcsicmp(tmp->DriverName, L"\\Driver\\kbdclass") == 0) {
fprintf(stderr, "[INFO]: Found (ID 0x%p)\n", tmp->ObjectId);
ret = IRPMonDllOpenHookedDriver(tmp->ObjectId, &driverHandle);
if (ret == 0) {
ret = IRPMonDllUnhookDriver(driverHandle);
if (ret != 0)
fprintf(stderr, "[ERROR]: Unable to unhook the driver: %u\n", ret);

IRPMonDllCloseHookedDriverHandle(driverHandle);
} else fprintf(stderr, "[ERROR]: Unable to get hook driver handle: %u\n", ret);

break;
}

++tmp;
}

IRPMonDllDriverHooksFree(driverHookInfo, count);
} else fprintf(stderr, "[ERROR]: Unable to get list of hooked drivers: %u\n", ret);

if (ret == 0) {
ret = IRPMonDllHookDriver(L"\\Driver\\kbdclass", &driverSettings, FALSE, &driverHandle, NULL);
if (ret != 0)
fprintf(stderr, "[ERROR]: Unable to hook the driver %u\n", ret);
}
} else if (ret != 0)
fprintf(stderr, "[ERROR]: Error %u\n", ret);

if (ret == 0) {
fprintf(stderr, "[INFO]: Hooking the primary keyboard device...\n");
ret = IRPMonDllHookDeviceByName(L"\\Device\\KeyboardClass0", &deviceHandle, NULL);
if (ret == 0) {
// Attempt to chan ge the global driver settings in order
// not to get information about all drivers and devices and running processed
// after connecting to the request queue.
ret = IRPMonDllSettingsQuery(&globalSettings);
if (ret == 0) {
globalSettings.DriverSnapshotOnConnect = FALSE;
globalSettings.ProcessEmulateOnConnect = FALSE;
ret = IRPMonDllSettingsSet(&globalSettings, FALSE);
if (ret != 0)
fprintf(stderr, "[WARNING]: Unable to alter global driver settings: %u\n", ret);
} else fprintf(stderr, "[WARNING]: Unable to query global driver settings: %u\n", ret);

// Now, everything is set. Let's tell the IRPMon driver
// to start actually monitoring the requests.
fprintf(stderr, "[INFO]: Starting to watch for requests...\n");
ret = IRPMonDllDriverStartMonitoring(driverHandle);
if (ret == 0) {
// Connect to the request queue.
fprintf(stderr, "[INFO]: Connecting to the request queue...\n");
ret = IRPMonDllConnect();
if (ret == 0) {
fprintf(stderr, "[INFO]: We are now set! Press CTRL+C to stop monitoring and exit the application peacefully\n");
while (!_terminate) {
// Attempt to get a request from the queue
ret = IRPMonDllGetRequest(buffer, bufferSize);
switch (ret) {
case ERROR_SUCCESS: {
PREQUEST_HEADER request = NULL;

// Since multiple requests can be returned
// in one call to IRPMonDllGetRequest, let's
// carefully examine all of them.
request = buffer;
do {
PREQUEST_IRP irp = NULL;
PREQUEST_IRP_COMPLETION irpc = NULL;
PREQUEST_ADDDEVICE ad = NULL;
PREQUEST_UNLOAD du = NULL;

switch (request->Type) {
case ertIRP:
irp = CONTAINING_RECORD(request, REQUEST_IRP, Header);
fprintf(stderr, "[IRP]: irp=0x%p, major=%u, minor=%u, datasize=%zu\n", irp->IRPAddress, irp->MajorFunction, irp->MinorFunction, irp->DataSize);
break;
case ertIRPCompletion:
irpc = CONTAINING_RECORD(request, REQUEST_IRP_COMPLETION, Header);
fprintf(stderr, "[IRPCOMPLETE]: irp=0x%p, status=0x%lx, info=%zu, datasize=%zu\n", irpc->IRPAddress, irpc->CompletionStatus, irpc->CompletionInformation, irpc->DataSize);
break;
case ertAddDevice:
ad = CONTAINING_RECORD(request, REQUEST_ADDDEVICE, Header);
fprintf(stderr, "[ADDDEVICE]: driver=0x%p, device=0x%p\n", ad->Header.Driver, ad->Header.Device);
break;
case ertDriverUnload:
du = CONTAINING_RECORD(request, REQUEST_UNLOAD, Header);
fprintf(stderr, "[UNLOAD]: driver=0x%p", du->Header.Driver);
break;
default:
fprintf(stderr, "[REQUEST]: Type %u\n", request->Type);
break;
}

// This value is nonzero when the buffer returned
// by the driver contains at least one request behind
// this one. If the REQUEST_FLAG_NEXT_AVAILABLE is set
// in the request->Flags, more requests are still present
// in the request queue in the kernel.
if (request->Entry.Flink == NULL)
break;

request = (PREQUEST_HEADER)((unsigned char *)request + RequestGetSize(request));
} while (TRUE);
} break;
case ERROR_INSUFFICIENT_BUFFER: {
PREQUEST_HEADER tmp = NULL;

// Our buffer is not large enough, let's resize it!
fprintf(stderr, "[WARNING]: Buffer of size %u is not enough, enlarging to %u\n", bufferSize, bufferSize*2 + 128);
bufferSize = bufferSize*2 + 128;
tmp = realloc(buffer, bufferSize);
if (tmp == NULL) {
fprintf(stderr, "[ERROR]: Buffer allocation failed with errno of %u\n", errno);
_terminate = TRUE;
continue;
}

buffer = tmp;
} break;
case ERROR_NO_MORE_ITEMS:
// The queue is empty. Well, this is very common
// for keyboard devices. Let's just wait.
fputc('.', stderr);
Sleep(1000);
break;
default:
// An unexpected error occurred.
// Well, no software is perfect, right?
fprintf(stderr, "[ERROR]: Unable to get request: %u\n", ret);
_terminate = TRUE;
break;
}
}

// Let's free the buffer. This will also work
// when the buffer is NUULL (i.e. have never been allocated).
free(buffer);
fprintf(stderr, "[INFO]: Disconnecting from the request queue\n");
ret = IRPMonDllDisconnect();
if (ret != 0)
fprintf(stderr, "[WARNING]: Error %u\n", ret);
} else fprintf(stderr, "[ERROR]: Error %u\n", ret);

fprintf(stderr, "[INFO]: Stopping the monitoring\n");
ret = IRPMonDllDriverStopMonitoring(driverHandle);
if (ret != 0)
fprintf(stderr, "[WARNING]: Error %u\n", ret);
} else fprintf(stderr, "[ERROR]: Error %u\n", ret);

fprintf(stderr, "[INFO]: Unhooking the primary keyboard device\n");
ret = IRPMonDllUnhookDevice(deviceHandle);
if (ret != 0)
fprintf(stderr, "[WARNING]: Error %u\n", ret);
} else fprintf(stderr, "[ERROR]: Error %u\n", ret);

fprintf(stderr, "[INFO]: Unhooking the Keyboard Class driver\n");
ret = IRPMonDllUnhookDriver(driverHandle);
if (ret != 0)
fprintf(stderr, "[WARNING]: Error %u\n", ret);
}

fprintf(stderr, "[INFO]: Cleanup the library\n");
IRPMonDllFinalize();
} else fprintf(stderr, "[ERROR]: Error %u\n", ret);

SetConsoleCtrlHandler(_CtrlHandler, FALSE);

return ret;
}
Loading

0 comments on commit 0952d6d

Please sign in to comment.