Driver causing a system crash every time unloaded - c++

The driver I wrote is taken from the Windows Kernel Programming book but for some reason it just doesn't work.
Here is my driver entry:
extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {
UNREFERENCED_PARAMETER(RegistryPath);
auto status = STATUS_SUCCESS;
InitializeListHead(&g_Globals.ItemsHead);
g_Globals.Mutex.Init();
PDEVICE_OBJECT DeviceObject = nullptr;
UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\??\\sysmon");
bool symLinkCreated = false;
do {
UNICODE_STRING devName = RTL_CONSTANT_STRING(L"\\Device\\sysmon");
status = IoCreateDevice(DriverObject, 0, &devName,
FILE_DEVICE_UNKNOWN, 0, TRUE, &DeviceObject);
if (!NT_SUCCESS(status)) {
KdPrint((DRIVER_PREFIX "failed to create device (0x%08X)\n",
status));
break;
}
DeviceObject->Flags |= DO_DIRECT_IO;
status = IoCreateSymbolicLink(&symLink, &devName);
if (!NT_SUCCESS(status)) {
KdPrint((DRIVER_PREFIX "failed to create sym link (0x%08X)\n",
status));
break;
}
symLinkCreated = true;
// register for process notifications
status = PsSetCreateProcessNotifyRoutineEx(OnProcessNotify, FALSE);
if (!NT_SUCCESS(status)) {
KdPrint((DRIVER_PREFIX "failed to register process callback (0x%08X)\n",
status));
break;
}
status = PsSetCreateThreadNotifyRoutine(OnThreadNotify);
if (!NT_SUCCESS(status)) {
KdPrint((DRIVER_PREFIX "failed to set thread callbacks (status=%08X)\n", status)\
);
break;
}
} while (false);
if (!NT_SUCCESS(status)) {
if (symLinkCreated)
IoDeleteSymbolicLink(&symLink);
if (DeviceObject)
IoDeleteDevice(DeviceObject);
}
DriverObject->DriverUnload = SysMonUnload;
DriverObject->MajorFunction[IRP_MJ_CREATE] =
DriverObject->MajorFunction[IRP_MJ_CLOSE] = SysMonCreateClose;
DriverObject->MajorFunction[IRP_MJ_READ] = SysMonRead;
return status;
}
now this function works well but i'm pretty sure the problem lies around here.
here is the SysMonCrearteClose function which also fails (I must say that I don't fully understand what I need to code in this function however even without calling it the driver failes, I'm sure there is a problem in here as well):
NTSTATUS SysMonCreateClose(_In_ PDEVICE_OBJECT DeviceObject, _In_ PIRP Irp) {
UNREFERENCED_PARAMETER(DeviceObject);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
now here is the SysMonUnload function:
void SysMonUnload(PDRIVER_OBJECT DriverObject) {
// unregister process notifications
PsSetCreateProcessNotifyRoutineEx(OnProcessNotify, TRUE);
UNICODE_STRING symLink = RTL_CONSTANT_STRING(L"\\??\\sysmon");
IoDeleteSymbolicLink(&symLink);
IoDeleteDevice(DriverObject->DeviceObject);
// free remaining items
while (!IsListEmpty(&g_Globals.ItemsHead)) {
auto entry = RemoveHeadList(&g_Globals.ItemsHead);
ExFreePool(CONTAINING_RECORD(entry, FullItem<ItemHeader>, Entry));
}
}
Here is the last function that matters, the SysMonRead function:
NTSTATUS SysMonRead(PDEVICE_OBJECT, PIRP Irp) {
UNREFERENCED_PARAMETER(Irp);
auto stack = IoGetCurrentIrpStackLocation(Irp);
auto len = stack->Parameters.Read.Length;
auto status = STATUS_SUCCESS;
auto count = 0;
NT_ASSERT(Irp->MdlAddress); // we're using Direct I/O
auto buffer = (UCHAR*)MmGetSystemAddressForMdlSafe(Irp->MdlAddress,
NormalPagePriority);
if (!buffer) {
status = STATUS_INSUFFICIENT_RESOURCES;
}
else {
AutoLock lock(g_Globals.Mutex);
while (true) {
if (IsListEmpty(&g_Globals.ItemsHead)) // can also check g_Globals.ItemCount
break;
auto entry = RemoveHeadList(&g_Globals.ItemsHead);
auto info = CONTAINING_RECORD(entry, FullItem<ItemHeader>, Entry);
auto size = info->Data.Size;
if (len < size) {
// user's buffer is full, insert item back
InsertHeadList(&g_Globals.ItemsHead, entry);
break;
}
g_Globals.ItemCount--;
::memcpy(buffer, &info->Data, size);
len -= size;
buffer += size;
count += size;
// free data after copy
ExFreePool(info);
}
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = count;
IoCompleteRequest(Irp, 0);
return status;
}
throughout the program I allocate memory that is freed in the unload routine and open handles to files and mutexes which I also close. Whenever I load this driver the functionality intended is working properly (when I write to a file it works, the CreateClose function does not work) however when I unload the driver the system crashes. I used windbg to try and see what the issue is to no avail, here is the error message:
*** Fatal System Error: 0x000000ce
(0xFFFFF80260A71390,0x0000000000000010,0xFFFFF80260A71390,0x0000000000000000)
Driver at fault: ProcessThreadMonitorDriver.sys.
Break instruction exception - code 80000003 (first chance)
nt_exe!DbgBreakPointWithStatus:
I noticed sometimes it said something about an invalid memory access but it was inconsistent and unclear.

Related

Writing to read only address with kernel driver c++

Im trying to write memory to a user mode process with kernel driver,
the current address im trying to write memory for is read only, I want to write 4 bytes to the current address,
the thing is if i change protection ( page ) of the process with VirtualProtectEx , it works and it writes the memory but this is only on user mode level, my intention is to change the protection of the process from kernel mode, i want to make it READWRITE, then change it back to READ from kernel space,
Now what I tried to do is giving me a BSOD ( blue screen of death ) with error : Kmode_exception_not_handld
I cant understand what in my code is triggering this BSOD my PC have very limited specs and i cant debug in VM to know..
I will write the code that works but in user mode , and what the code is not working for me in kernel space:
here the code that works:
void dispatch::handler(void* info_struct)
{
PINFO_STRUCT info = (PINFO_STRUCT)info_struct;
if (info->code == CODE_READ_MEMORY)
{
PEPROCESS target_process = NULL;
if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)info->process_id, &target_process)))
{
memory::read_memory(target_process, (void*)info->address, &info->buffer, info->size);
}
DbgPrintEx(0, 0, "[TEST]: Read Memory\n");
}
else if (info->code == CODE_WRITE_MEMORY)
{
PEPROCESS target_process = NULL;
if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)info->process_id, &target_process)))
{
memory::write_memory(target_process, &info->buffer, (void*)info->address, info->size);
}
DbgPrintEx(0, 0, "[TEST]: Write Memory\n");
}
}
NTSTATUS memory::write_memory(PEPROCESS target_process, void* source, void* target, size_t size)
{
if (!target_process) { return STATUS_INVALID_PARAMETER; }
size_t bytes = 0;
NTSTATUS status = MmCopyVirtualMemory(IoGetCurrentProcess(), source, target_process, target, size, KernelMode, &bytes);
if (!NT_SUCCESS(status) || !bytes)
{
return STATUS_INVALID_ADDRESS;
}
return status;
}
int main()
{
DWORD oldprt
ULONG writeTest1 = 3204497152;
VirtualProtectEx(ProcManager::hProcess, (PVOID)(testAddr), 4, PAGE_READWRITE, &oldprt);
driver_control::write_memory(process_id, testAddr, writeTest1);
VirtualProtectEx(ProcManager::hProcess, (PVOID)(testAddr), 4, PAGE_READONLY, &oldprt);
return 0;
}
Now what I want to do is stop using the VirtualProtectEx, and change the PAGE protection to READWRITE from kernel space, so what i did is add this in the dispatch::handler function:
else if (info->code == CODE_WRITE_MEMORY)
{
PEPROCESS target_process = NULL;
if (NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)info->process_id, &target_process)))
{
PMDL Mdl = IoAllocateMdl((void*)info->address, info->size, FALSE, FALSE, NULL);
if (!Mdl)
return false;
// Locking and mapping memory with RW-rights:
MmProbeAndLockPages(Mdl, KernelMode, IoReadAccess);
PVOID Mapping = MmMapLockedPagesSpecifyCache(Mdl, KernelMode, MmNonCached, NULL, FALSE, NormalPagePriority);
MmProtectMdlSystemAddress(Mdl, PAGE_READWRITE);
memory::write_memory(target_process, &info->buffer, (void*)info->address, info->size);
// Resources freeing:
MmUnmapLockedPages(Mapping, Mdl);
MmUnlockPages(Mdl);
IoFreeMdl(Mdl);
}
DbgPrintEx(0, 0, "[TEST]: Write Read Only Memory\n");
}
So this what I've added caused the BSOD, but why I cannot understand, what am i doing wrong here?
here is the info struct if needed to understand more, :
#define CODE_READ_MEMORY 0x1
#define CODE_WRITE_MEMORY 0x2
typedef struct _INFO_STRUCT
{
ULONG code;
ULONG process_id;
ULONG address;
ULONG buffer;
ULONG size;
}INFO_STRUCT, * PINFO_STRUCT;
any suggestions on solving this problem?

How to track the accurate windows startup time using C++(win32/MFC)

I am creating an application which track the System startup time.
I tried GetTickcount() method and WMI query.In both cases I got the same solution.
But the time that I obtained is different from actual startup time.
By researching I found that because of fast startup option enabled in power option the system is not going for boot when we shutdown it.
Thing that I needed is time actual startup time.How can we get the actual startup time using C++?
I shut downed the system and turned ON it on 24-Jun-20, 8:22:05 AM but boot time that I got is 22-Jun-20, 5:11:05 PM
When the fast startup option enabled, click start menu -> shutdown will put the machine into sleep mode/hibernation instead of shutdown. But restart menu isn't affected. (And shutting down from command line isn't affected too as my test). So, boot time will not be reset.
You could try the following methods:
Turn off the "fast startup" option.
Add a task to the task schedule to log the time when the system start.
Read the Windows Event Log:
Event ID 27 records the kernel boot events. As you can see in the picture, boot type 0x1 means it was a fast startup. You could just read the newest one and get the create time.
Sample(Refer to this sample document: Querying for Events):
#include <windows.h>
#include <sddl.h>
#include <stdio.h>
#include <winevt.h>
#pragma comment(lib, "wevtapi.lib")
#define ARRAY_SIZE 1
#define TIMEOUT 1000 // 1 second; Set and use in place of INFINITE in EvtNext call
DWORD PrintResults(EVT_HANDLE hResults);
DWORD PrintEvent(EVT_HANDLE hEvent); // Shown in the Rendering Events topic
void main(void)
{
DWORD status = ERROR_SUCCESS;
EVT_HANDLE hResults = NULL;
LPCWSTR pwsPath = L"System";
LPCWSTR pwsQuery = L"Event/System[EventID=27]";
hResults = EvtQuery(NULL, pwsPath, pwsQuery, EvtQueryChannelPath | EvtQueryReverseDirection);
if (NULL == hResults)
{
status = GetLastError();
if (ERROR_EVT_CHANNEL_NOT_FOUND == status)
wprintf(L"The channel was not found.\n");
else if (ERROR_EVT_INVALID_QUERY == status)
// You can call the EvtGetExtendedStatus function to try to get
// additional information as to what is wrong with the query.
wprintf(L"The query is not valid.\n");
else
wprintf(L"EvtQuery failed with %lu.\n", status);
goto cleanup;
}
PrintResults(hResults);
cleanup:
if (hResults)
EvtClose(hResults);
}
// Enumerate all the events in the result set.
DWORD PrintResults(EVT_HANDLE hResults)
{
DWORD status = ERROR_SUCCESS;
EVT_HANDLE hEvents[ARRAY_SIZE];
DWORD dwReturned = 0;
// Get a block of events from the result set.
if (!EvtNext(hResults, ARRAY_SIZE, hEvents, INFINITE, 0, &dwReturned))
{
if (ERROR_NO_MORE_ITEMS != (status = GetLastError()))
{
wprintf(L"EvtNext failed with %lu\n", status);
}
goto cleanup;
}
// For each event, call the PrintEvent function which renders the
// event for display. PrintEvent is shown in RenderingEvents.
/*for (DWORD i = 0; i < dwReturned; i++)
{
if (ERROR_SUCCESS == (status = PrintEvent(hEvents[i])))
{
EvtClose(hEvents[i]);
hEvents[i] = NULL;
}
else
{
goto cleanup;
}
}*/
if (ERROR_SUCCESS == (status = PrintEvent(hEvents[0])))
{
EvtClose(hEvents[0]);
hEvents[0] = NULL;
}
else
{
goto cleanup;
}
cleanup:
for (DWORD i = 0; i < dwReturned; i++)
{
if (NULL != hEvents[i])
EvtClose(hEvents[i]);
}
return status;
}
DWORD PrintEvent(EVT_HANDLE hEvent)
{
DWORD status = ERROR_SUCCESS;
DWORD dwBufferSize = 0;
DWORD dwBufferUsed = 0;
DWORD dwPropertyCount = 0;
LPWSTR pRenderedContent = NULL;
if (!EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount))
{
if (ERROR_INSUFFICIENT_BUFFER == (status = GetLastError()))
{
dwBufferSize = dwBufferUsed;
pRenderedContent = (LPWSTR)malloc(dwBufferSize);
if (pRenderedContent)
{
EvtRender(NULL, hEvent, EvtRenderEventXml, dwBufferSize, pRenderedContent, &dwBufferUsed, &dwPropertyCount);
}
else
{
wprintf(L"malloc failed\n");
status = ERROR_OUTOFMEMORY;
goto cleanup;
}
}
if (ERROR_SUCCESS != (status = GetLastError()))
{
wprintf(L"EvtRender failed with %d\n", status);
goto cleanup;
}
}
wprintf(L"%s\n\n", pRenderedContent);
cleanup:
if (pRenderedContent)
free(pRenderedContent);
return status;
}
Result:

Windows biometric service runs SensorAdapterStartCapture in a loop when calling WinBioCaptureSample

I am implementing a Windows Biometric Driver using the umdf sample from github.
When I call WinBioCaptureSample the next plugin's methods run in a loop.
SensorAdapterClearContext
EngineAdapterClearContext
SensorAdapterStartCapture
SensorAdapterFinishCapture
I used TraceView to debug my driver and it shows the next trace messages when is stuck in a loop.
00000001 driver 352840 439560 1 1 04\05\2018-16:46:13:12 CBiometricDevice::OnGetSensorStatus Called.
00000002 driver 352840 439560 1 2 04\05\2018-16:46:13:12 CBiometricDevice::OnGetAttributes Called.
00000003 driver 352840 439560 1 3 04\05\2018-16:46:13:12 CBiometricDevice::OnCaptureDataBuffer too small - must be at least 0x18.
00000004 driver 352840 439560 1 4 04\05\2018-16:46:13:12 CBiometricDevice::OnCaptureData Called.
00000005 driver 352840 439560 4 5 04\05\2018-16:46:13:28 CBiometricDevice::OnGetSensorStatus Called.
00000006 driver 352840 439560 1 6 04\05\2018-16:46:13:29 CBiometricDevice::OnCaptureDataBuffer too small - must be at least 0x18.
00000007 driver 352840 439560 1 7 04\05\2018-16:46:13:29 CBiometricDevice::OnCaptureData Called.
00000008 driver 352840 439560 1 8 04\05\2018-16:46:13:30 CBiometricDevice::OnGetSensorStatus Called.
00000009 driver 352840 439560 4 9 04\05\2018-16:46:13:30 CBiometricDevice::OnCaptureDataBuffer too small - must be at least 0x18.
00000010 driver 352840 439560 1 10 04\05\2018-16:46:13:31 CBiometricDevice::OnCaptureData Called.
...
The method CBiometricDevice::OnGetSensorStatus always returns WINBIO_SENSOR_READY
diagnostics->WinBioHresult = S_OK;
diagnostics->SensorStatus = WINBIO_SENSOR_READY;
MyRequest.SetInformation(diagnostics->PayloadSize);
MyRequest.SetCompletionHr(S_OK);
Next is the method CBiometricDevice::OnCaptureData
DWORD WINAPI
CaptureSleepThread(
LPVOID lpParam
)
{
CBiometricDevice *device = (CBiometricDevice *) lpParam;
PCAPTURE_SLEEP_PARAMS sleepParams = device->GetCaptureSleepParams();
if (sleepParams->SleepValue > 60)
{
sleepParams->SleepValue = 60;
}
Sleep(sleepParams->SleepValue * 1000);
UCHAR szBuffer[] = { 0x08, 0x01, 0x00, 0x02 };
ULONG cbRead = 4;
sleepParams->captureData->WinBioHresult = S_OK;
sleepParams->captureData->SensorStatus = WINBIO_SENSOR_ACCEPT;
sleepParams->captureData->RejectDetail = 0;
sleepParams->captureData->CaptureData.Size = cbRead;
RtlCopyMemory(sleepParams->captureData->CaptureData.Data, szBuffer, cbRead);
device->CompletePendingRequest(sleepParams->Hr, sleepParams->Information);
return 0;
}
CBiometricDevice::OnCaptureData(
_Inout_ IWDFIoRequest *FxRequest
)
{
ULONG controlCode = 0;
PWINBIO_CAPTURE_PARAMETERS captureParams = NULL;
SIZE_T inputBufferSize = 0;
PWINBIO_CAPTURE_DATA captureData = NULL;
SIZE_T outputBufferSize = 0;
bool requestPending = false;
EnterCriticalSection(&m_RequestLock);
if (m_PendingRequest == NULL)
{
if (m_SleepThread != INVALID_HANDLE_VALUE)
{
LeaveCriticalSection(&m_RequestLock);
// TODO: Add code to signal thread to exit.
WaitForSingleObject(m_SleepThread, INFINITE);
CloseHandle(m_SleepThread);
m_SleepThread = INVALID_HANDLE_VALUE;
EnterCriticalSection(&m_RequestLock);
}
if (m_PendingRequest == NULL)
{
m_PendingRequest = FxRequest;
m_PendingRequest->MarkCancelable(this);
}
else
{
requestPending = true;
}
}
else
{
requestPending = true;
}
LeaveCriticalSection(&m_RequestLock);
if (requestPending)
{
FxRequest->Complete(WINBIO_E_DATA_COLLECTION_IN_PROGRESS);
return;
}
GetIoRequestParams(FxRequest,
&controlCode,
(PUCHAR *)&captureParams,
&inputBufferSize,
(PUCHAR *)&captureData,
&outputBufferSize);
if (inputBufferSize < sizeof (WINBIO_CAPTURE_PARAMETERS))
{
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC!Invalid argument(s).");
CompletePendingRequest(E_INVALIDARG, 0);
return;
}
if (outputBufferSize < sizeof(DWORD))
{
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC!Output buffer NULL or too small to return size information.");
CompletePendingRequest(E_INVALIDARG, 0);
return;
}
if (outputBufferSize < sizeof (WINBIO_CAPTURE_DATA))
{
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC!Buffer too small - must be at least 0x%x.", sizeof (WINBIO_CAPTURE_DATA));
DWORD cbSize = 262144;//obtained from MAXIMUM_TRANSFER_SIZE policy of WinUsb
captureData->PayloadSize = (DWORD) sizeof(WINBIO_CAPTURE_DATA) + cbSize;
CompletePendingRequest(S_OK, sizeof(DWORD));
return;
}
RtlZeroMemory(captureData, outputBufferSize);
captureData->PayloadSize = (DWORD) outputBufferSize;// (DWORD) sizeof(WINBIO_CAPTURE_DATA);
captureData->WinBioHresult = WINBIO_E_NO_CAPTURE_DATA;
captureData->SensorStatus = WINBIO_SENSOR_FAILURE;
captureData->RejectDetail= 0;
captureData->CaptureData.Size = 0;
if (captureParams->Purpose == WINBIO_NO_PURPOSE_AVAILABLE)
{
captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_PURPOSE;
}
else if ((captureParams->Format.Type != WINBIO_ANSI_381_FORMAT_TYPE) ||
(captureParams->Format.Owner != WINBIO_ANSI_381_FORMAT_OWNER))
{
captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_DATA_FORMAT;
}
else if (captureParams->Flags != WINBIO_DATA_FLAG_RAW)
{
captureData->WinBioHresult = WINBIO_E_UNSUPPORTED_DATA_TYPE;
}
struct _WINUSB_PIPE_INFORMATION InputPipeInfo, OutputPipeInfo;
m_pIUsbInputPipe->GetInformation(&InputPipeInfo);
m_pIUsbOutputPipe->GetInformation(&OutputPipeInfo);
m_SleepParams.PipeInId = InputPipeInfo.PipeId;
m_SleepParams.PipeOutId = OutputPipeInfo.PipeId;
m_SleepParams.hDeviceHandle = m_pIUsbInterface->GetWinUsbHandle();
m_SleepParams.cbSize = (DWORD) outputBufferSize;
m_SleepParams.SleepValue = 5;
m_SleepParams.Hr = S_OK;
m_SleepParams.Information = captureData->PayloadSize;
m_SleepParams.captureData = captureData;
m_SleepThread = CreateThread(NULL, // default security attributes
0, // use default stack size
CaptureSleepThread, // thread function name
this, // argument to thread function
0, // use default creation flags
NULL); // returns the thread identifier
TraceEvents(TRACE_LEVEL_ERROR,
BIOMETRIC_TRACE_DEVICE,
"%!FUNC! Called.");
}
The methods SensorAdapterStartCapture and SensorAdapterFinishCapture returns S_OK
static HRESULT
WINAPI
SensorAdapterStartCapture(
_Inout_ PWINBIO_PIPELINE Pipeline,
_In_ WINBIO_BIR_PURPOSE Purpose,
_Out_ LPOVERLAPPED *Overlapped
)
{
HRESULT hr = S_OK;
WINBIO_SENSOR_STATUS sensorStatus = WINBIO_SENSOR_FAILURE;
WINBIO_CAPTURE_PARAMETERS captureParameters = { 0 };
BOOL result = TRUE;
DWORD bytesReturned = 0;
// Verify that pointer arguments are not NULL.
if (!ARGUMENT_PRESENT(Pipeline) ||
!ARGUMENT_PRESENT(Purpose) ||
!ARGUMENT_PRESENT(Overlapped))
{
return E_POINTER;
}
// Retrieve the context from the pipeline.
PWINIBIO_SENSOR_CONTEXT sensorContext =
(PWINIBIO_SENSOR_CONTEXT)Pipeline->SensorContext;
// Verify the state of the pipeline.
if (sensorContext == NULL ||
Pipeline->SensorHandle == INVALID_HANDLE_VALUE)
{
return WINBIO_E_INVALID_DEVICE_STATE;
}
*Overlapped = NULL;
// Synchronously retrieve the status.
hr = SensorAdapterQueryStatus(Pipeline, &sensorStatus);
if (FAILED(hr))
{
return hr;
}
// Determine whether the sensor requires calibration.
//if (sensorStatus == WINBIO_SENSOR_NOT_CALIBRATED)
//{
// Call a custom function that sends IOCTLs to
// the sensor to calibrate it. This operation is
// synchronous.
//hr = _SensorAdapterCalibrate(Pipeline);
// Retrieve the status again to determine whether the
// sensor is ready.
//if (SUCCEEDED(hr))
//{
// hr = SensorAdapterQueryStatus(Pipeline, &sensorStatus);
//}
//if (FAILED(hr))
//{
// return hr;
//}
//}
if (sensorStatus == WINBIO_SENSOR_BUSY)
{
return WINBIO_E_DEVICE_BUSY;
}
if (sensorStatus != WINBIO_SENSOR_READY)
{
return WINBIO_E_INVALID_DEVICE_STATE;
}
// Determine whether the data format has been previously determined.
// If it has not, find a format supported by both the engine and
// the sensor.
if ((sensorContext->Format.Owner == 0) &&
(sensorContext->Format.Type == 0))
{
// Retrieve the format preferred by the engine.
hr = Pipeline->EngineInterface->QueryPreferredFormat(
Pipeline,
&sensorContext->Format,
&sensorContext->VendorFormat
);
if (SUCCEEDED(hr))
{
// Call a private function that queries the sensor driver
// and attaches an attribute array to the sensor context.
// This operation is synchronous.
hr = _SensorAdapterGetAttributes(Pipeline);
}
if (SUCCEEDED(hr))
{
// Search the sensor attributes array for the format
// preferred by the engine adapter.
DWORD i = 0;
for (i = 0; i < sensorContext->AttributesBuffer->SupportedFormatEntries; i++)
{
if ((sensorContext->AttributesBuffer->SupportedFormat[i].Owner == sensorContext->Format.Owner) &&
(sensorContext->AttributesBuffer->SupportedFormat[i].Type == sensorContext->Format.Type))
{
break;
}
}
if (i == sensorContext->AttributesBuffer->SupportedFormatEntries)
{
// No match was found. Use the default.
sensorContext->Format.Owner = WINBIO_ANSI_381_FORMAT_OWNER;
sensorContext->Format.Type = WINBIO_ANSI_381_FORMAT_TYPE;
}
}
else
{
return hr;
}
}
// Set up the parameter-input block needed for the IOCTL.
captureParameters.PayloadSize = sizeof(WINBIO_CAPTURE_PARAMETERS);
captureParameters.Purpose = Purpose;
captureParameters.Format.Owner = sensorContext->Format.Owner;
captureParameters.Format.Type = sensorContext->Format.Type;
CopyMemory(&captureParameters.VendorFormat, &sensorContext->VendorFormat, sizeof(WINBIO_UUID));
captureParameters.Flags = WINBIO_DATA_FLAG_RAW;
// Determine whether a buffer has already been allocated for this sensor.
if (sensorContext->CaptureBuffer == NULL)
{
DWORD allocationSize = 0;
sensorContext->CaptureBufferSize = 0;
// This sample assumes that the sensor driver returns
// a fixed-size DWORD buffer containing the required
// size of the capture buffer if it receives a buffer
// that is smaller than sizeof(WINBIO_CAPTURE_DATA).
//
// Call the driver with a small buffer to get the
// allocation size required for this sensor.
//
// Because this operation is asynchronous, you must block
// and wait for it to complete.
result = DeviceIoControl(
Pipeline->SensorHandle,
IOCTL_BIOMETRIC_CAPTURE_DATA,
&captureParameters,
sizeof(WINBIO_CAPTURE_PARAMETERS),
&allocationSize,
sizeof(DWORD),
&bytesReturned,
&sensorContext->Overlapped
);
if (!result && GetLastError() == ERROR_IO_PENDING)
{
SetLastError(ERROR_SUCCESS);
result = GetOverlappedResult(
Pipeline->SensorHandle,
&sensorContext->Overlapped,
&bytesReturned,
TRUE
);
}
if (!result || bytesReturned != sizeof(DWORD))
{
// An error occurred.
hr = _AdapterGetHresultFromWin32(GetLastError());
return hr;
}
// Make sure that you allocate at least the minimum buffer
// size needed to get the payload structure.
if (allocationSize < sizeof(WINBIO_CAPTURE_DATA))
{
allocationSize = sizeof(WINBIO_CAPTURE_DATA);
}
// Allocate the buffer.
sensorContext->CaptureBuffer = (PWINBIO_CAPTURE_DATA)_AdapterAlloc(allocationSize);
if (!sensorContext->CaptureBuffer)
{
sensorContext->CaptureBufferSize = 0;
return E_OUTOFMEMORY;
}
sensorContext->CaptureBufferSize = allocationSize;
}
else
{
// The buffer has already been allocated. Clear the buffer contents.
SensorAdapterClearContext(Pipeline);
}
// Send the capture request. Because this is an asynchronous operation,
// the IOCTL call will return immediately regardless of
// whether the I/O has completed.
result = DeviceIoControl(
Pipeline->SensorHandle,
IOCTL_BIOMETRIC_CAPTURE_DATA,
&captureParameters,
sizeof(WINBIO_CAPTURE_PARAMETERS),
sensorContext->CaptureBuffer,
(DWORD)sensorContext->CaptureBufferSize,
&bytesReturned,
&sensorContext->Overlapped
);
if (result ||
(!result && GetLastError() == ERROR_IO_PENDING))
{
*Overlapped = &sensorContext->Overlapped;
return S_OK;
}
else
{
hr = _AdapterGetHresultFromWin32(GetLastError());
return hr;
}
}
static HRESULT
WINAPI
SensorAdapterFinishCapture(
_Inout_ PWINBIO_PIPELINE Pipeline,
_Out_ PWINBIO_REJECT_DETAIL RejectDetail
)
{
HRESULT hr = S_OK;
//WINBIO_SENSOR_STATUS sensorStatus = WINBIO_SENSOR_FAILURE;
WINBIO_CAPTURE_PARAMETERS captureParameters = { 0 };
BOOL result = TRUE;
DWORD bytesReturned = 0;
// Verify that pointer arguments are not NULL.
if (!ARGUMENT_PRESENT(Pipeline) ||
!ARGUMENT_PRESENT(RejectDetail))
{
return E_POINTER;
}
// Retrieve the context from the pipeline.
PWINIBIO_SENSOR_CONTEXT sensorContext =
(PWINIBIO_SENSOR_CONTEXT)Pipeline->SensorContext;
// Verify the state of the pipeline.
if (sensorContext == NULL ||
Pipeline->SensorHandle == INVALID_HANDLE_VALUE)
{
return WINBIO_E_INVALID_DEVICE_STATE;
}
// Initialize the RejectDetail argument.
*RejectDetail = 0;
// Wait for I/O completion. This sample assumes that the I/O operation was
// started using the code example shown in the SensorAdapterStartCapture
// documentation.
SetLastError(ERROR_SUCCESS);
result = GetOverlappedResult(
Pipeline->SensorHandle,
&sensorContext->Overlapped,
&bytesReturned,
TRUE
);
if (!result)
{
// There was an I/O error.
return _AdapterGetHresultFromWin32(GetLastError());
}
if (bytesReturned == sizeof(DWORD))
{
// The buffer is not large enough. This can happen if a device needs a
// bigger buffer depending on the purpose. Allocate a larger buffer and
// force the caller to reissue their I/O request.
DWORD allocationSize = sensorContext->CaptureBuffer->PayloadSize;
// Allocate at least the minimum buffer size needed to retrieve the
// payload structure.
if (allocationSize < sizeof(WINBIO_CAPTURE_DATA))
{
allocationSize = sizeof(WINBIO_CAPTURE_DATA);
}
// Free the old buffer and allocate a new one.
_AdapterRelease(sensorContext->CaptureBuffer);
sensorContext->CaptureBuffer = NULL;
sensorContext->CaptureBuffer =
(PWINBIO_CAPTURE_DATA)_AdapterAlloc(allocationSize);
if (sensorContext->CaptureBuffer == NULL)
{
sensorContext->CaptureBufferSize = 0;
return E_OUTOFMEMORY;
}
sensorContext->CaptureBufferSize = allocationSize;
return WINBIO_E_BAD_CAPTURE;
}
// Normalize the status value before sending it back to the biometric service.
if (sensorContext->CaptureBuffer != NULL &&
sensorContext->CaptureBufferSize >= sizeof(WINBIO_CAPTURE_DATA))
{
switch (sensorContext->CaptureBuffer->SensorStatus)
{
case WINBIO_SENSOR_ACCEPT:
{
// The capture was acceptable.
DWORD cbRead = sensorContext->CaptureBuffer->CaptureData.Size;
UCHAR * data = sensorContext->CaptureBuffer->CaptureData.Data;
//wprintf(L"%d ", cbRead);
break;
}
case WINBIO_SENSOR_REJECT:
// The capture was not acceptable. Overwrite the WinBioHresult value
// in case it has not been properly set.
sensorContext->CaptureBuffer->WinBioHresult = WINBIO_E_BAD_CAPTURE;
break;
case WINBIO_SENSOR_BUSY:
// The device is busy. Reset the WinBioHresult value in case it
// has not been properly set.
sensorContext->CaptureBuffer->WinBioHresult = WINBIO_E_DEVICE_BUSY;
break;
case WINBIO_SENSOR_READY:
case WINBIO_SENSOR_NOT_CALIBRATED:
case WINBIO_SENSOR_FAILURE:
default:
// There has been a device failure. Reset the WinBioHresult value
// in case it has not been properly set.
sensorContext->CaptureBuffer->WinBioHresult = WINBIO_E_INVALID_DEVICE_STATE;
break;
}
*RejectDetail = sensorContext->CaptureBuffer->RejectDetail;
hr = sensorContext->CaptureBuffer->WinBioHresult;
}
else
{
// The buffer is not large enough or the buffer pointer is NULL.
hr = WINBIO_E_INVALID_DEVICE_STATE;
}
return hr;
}
I used the next code from this github project
HRESULT CaptureSample()
{
HRESULT hr = S_OK;
WINBIO_SESSION_HANDLE sessionHandle = NULL;
WINBIO_UNIT_ID unitId = 0;
WINBIO_REJECT_DETAIL rejectDetail = 0;
PWINBIO_BIR sample = NULL;
SIZE_T sampleSize = 0;
// Connect to the system pool.
hr = WinBioOpenSession(
WINBIO_TYPE_FINGERPRINT, // Service provider
WINBIO_POOL_SYSTEM, // Pool type
WINBIO_FLAG_RAW, // Access: Capture raw data //To call WinBioCaptureSample function successfully, you must open the session handle by specifying WINBIO_FLAG_RAW
//WINBIO_FLAG_RAW: The client application captures raw biometric data using WinBioCaptureSample.
NULL, // Array of biometric unit IDs //NULL if the PoolType parameter is WINBIO_POOL_SYSTEM
0, // Count of biometric unit IDs//zero if the PoolType parameter is WINBIO_POOL_SYSTEM.
WINBIO_DB_DEFAULT, // Default database
&sessionHandle // [out] Session handle
);
if(FAILED(hr))
{
std::cout << "WinBioOpenSession failed. hr = 0x" << std::hex << hr << std::dec << "\n";
if(sample != NULL)
{
WinBioFree(sample);
sample = NULL;
}
if(sessionHandle != NULL)
{
WinBioCloseSession(sessionHandle);
sessionHandle = NULL;
}
return hr;
}
// Capture a biometric sample.
std::cout << "Calling WinBioCaptureSample - Swipe sensor...\n";
hr = WinBioCaptureSample(
sessionHandle,
WINBIO_NO_PURPOSE_AVAILABLE,
WINBIO_DATA_FLAG_RAW,//WINBIO_DATA_FLAG_RAW
&unitId,
&sample,
&sampleSize,
&rejectDetail
);
if(FAILED(hr))
{
if(hr == WINBIO_E_BAD_CAPTURE)
std:: cout << "Bad capture; reason: " << rejectDetail << "\n";
else
std::cout << "WinBioCaptureSample failed.hr = 0x" << std::hex << hr << std::dec << "\n";
if(sample != NULL)
{
WinBioFree(sample);
sample = NULL;
}
if(sessionHandle != NULL)
{
WinBioCloseSession(sessionHandle);
sessionHandle = NULL;
}
return hr;
}
std::cout << "Swipe processed - Unit ID: " << unitId << "\n";
std::cout << "Captured " << sampleSize << " bytes.\n";
if(sample != NULL)
{
WinBioFree(sample);
sample = NULL;
}
if(sessionHandle != NULL)
{
WinBioCloseSession(sessionHandle);
sessionHandle = NULL;
}
return hr;
}
int main()
{
EnumerateSensors();
CreateDirectoryA("data", NULL);
CaptureSample();
}
Sometimes my code is stuck in a loop and other times is not :(
Any hint is welcomed Thanks.
It seems I might have found a temporary solution to my problem.
First what I did is to change the sensor mode from basic to advanced.
[DriverPlugInAddReg]
HKR,WinBio\Configurations,DefaultConfiguration,,"0"
HKR,WinBio\Configurations\0,SensorMode,0x10001,2 ; Basic - 1, Advanced - 2
And the loop won't start
But I also noticed If I comment the call to the Sleep method inside CaptureSleepThread the loop starts again.
My guess is that Windows Biometric Service is expecting the method CaptureSleepThread to take some time to be considered successful but if the method ends very fast it is considered to be failed despite of the successful responses S_OK and WINBIO_SENSOR_ACCEPT and Windows Biometric Service will retry again calling to SensorAdapterStartCapture causing a loop.
DWORD WINAPI
CaptureSleepThread(
LPVOID lpParam
)
{
CBiometricDevice *device = (CBiometricDevice *) lpParam;
PCAPTURE_SLEEP_PARAMS sleepParams = device->GetCaptureSleepParams();
//
// 1 minute or half a minute delay is the trick.
//
if (sleepParams->SleepValue > 60)
{
sleepParams->SleepValue = 60;
}
Sleep(sleepParams->SleepValue * 1000);
UCHAR szBuffer[] = { 0x08, 0x01, 0x00, 0x02 };
ULONG cbRead = 4;
sleepParams->captureData->WinBioHresult = S_OK;
sleepParams->captureData->SensorStatus = WINBIO_SENSOR_ACCEPT;
sleepParams->captureData->RejectDetail = 0;
sleepParams->captureData->CaptureData.Size = cbRead;
RtlCopyMemory(sleepParams->captureData->CaptureData.Data, szBuffer, cbRead);
device->CompletePendingRequest(sleepParams->Hr, sleepParams->Information);
return 0;
}
Other things I observed can cause a loop is when the call fails and not proper response is set.
//No delay is going to cause a loop
//Sleep(sleepParams->SleepValue * 1000);
//zeroes buffer is considered failed
UCHAR szBuffer[] = { 0x00, 0x00, 0x00, 0x00 };
//zero size is a failed read
ULONG cbRead = 0;
//returning other than S_FALSE or S_OK will cause a loop
sleepParams->captureData->WinBioHresult = S_FALSE;
sleepParams->captureData->SensorStatus = WINBIO_SENSOR_ACCEPT;
sleepParams->captureData->RejectDetail = 0;
sleepParams->captureData->CaptureData.Size = cbRead;
//It is going to fail if CaptureData.Data is empty
//RtlCopyMemory(sleepParams->captureData->CaptureData.Data, szBuffer, cbRead);
Also attaching a debugger like WinDbg or Visual Studio will cause a loop so is better to debug using only Trace messages and TraceView tool and attach a debugger when necessary and a loop will be expected in this case, just ignore it.

How can I get symbol link name in windows NT driver?

I'm writing a windows nt driver. I define a DEVICE_EXTENSION
typedef struct _DEVICE_EXTENSION {
PDEVICE_OBJECT pDevice;
UNICODE_STRING ustrDeviceName;
UNICODE_STRING ustrSymLinkName;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
And I create a device,
status = IoCreateDevice(pDriverObject,
sizeof(DEVICE_EXTENSION),
&devName,
FILE_DEVICE_UNKNOWN,
0, TRUE,
&pDevObj);
if (!NT_SUCCESS(status))
{
DbgPrint("CreateDevice Error...\n");
return status;
}
pDevObj->Flags |= DO_BUFFERED_IO;
pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
pDevExt->pDevice = pDevObj;
pDevExt->ustrDeviceName = devName;
UNICODE_STRING symLinkName;
RtlInitUnicodeString(&symLinkName, DOS_DEVICE_NAME);
pDevExt->ustrSymLinkName = symLinkName;
status = IoCreateSymbolicLink(&symLinkName, &devName);
you can see, I store symLinkName in DEVICE_EXTENSION pDevExt. When it unloads from device, I read this symLinkName
NTSTATUS status;
PDEVICE_OBJECT pNextObj;
DbgPrint(("Enter DriverUnload\n"));
pNextObj = pDriverObject->DeviceObject;
UNICODE_STRING pLinkName;
while (pNextObj != NULL)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;
RtlCopyUnicodeString(&pLinkName, &(pDevExt->ustrSymLinkName));
DbgPrint("Start delete symlinkname %wZ ...\n", &pLinkName);n // meet a error
status = IoDeleteSymbolicLink(&pLinkName);
if (!NT_SUCCESS(status))
{
DbgPrint("Delete SymbolLink Error\n");
goto finish;
}
pNextObj = pNextObj->NextDevice;
IoDeleteDevice(pDevExt->pDevice);
}
Before executing IoDeleteSymbolicLink, I want to print this pLinkName, but I meet a error.
To solve this problem ,I try many methods.
while (pNextObj != NULL)
{
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pNextObj->DeviceExtension;
// RtlCopyUnicodeString(&pLinkName, &(pDevExt->ustrSymLinkName));
RtlInitUnicodeString(&pLinkName, DOS_DEVICE_NAME);
DbgPrint("Start delete symlinkname %wZ ...\n", &pLinkName);
status = IoDeleteSymbolicLink(&pLinkName);
if (!NT_SUCCESS(status))
{
DbgPrint("Delete SymbolLink Error\n");
goto finish;
}
pNextObj = pNextObj->NextDevice;
IoDeleteDevice(pDevExt->pDevice);
}
this will execute successfully, but I don't know why this happen.
I guess you are using this #pragma alloc_text(INIT, DriverEntry) in your code. If so, this is the explanation:
/*
* These compiler directives tell the Operating System how to load the
* driver into memory. The "INIT" section is discardable as you only
* need the driver entry upon initialization, then it can be discarded.
*
*/
After loading, INIT sesison will be discarded and your pDevExt->ustrSymLinkName data has been released if it is in that session. You can remove all #pragma alloc_text to avoid this problem.

How get current process image file full name in filter driver?

In filter driver I can call IoGetCurrentProcess to get an PEPROCESS structure, and than call PsGetProcessImageFileName to get file name.
My questions is how I can get full name of the process image file?
You can use ZwQueryInformationProcess with the information class of 27. THe following code uses this routine to obtain the full image file name from process' handle.
NTSTATUS GetProcessNameByHandle(_In_ HANDLE ProcessHandle, _Out_ PUNICODE_STRING *Name)
{
ULONG retLength = 0;
ULONG pniSize = 512;
PUNICODE_STRING pni = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
do {
pni = (PUNICODE_STRING)ExAllocatePoolWithTag(PagedPool, pniSize, POOL_TAG);
if (pni != NULL) {
status = ZwQueryInformationProcess(ProcessHandle, 27, pni, pniSize, &retLength);
if (!NT_SUCCESS(status)) {
ExFreePoolWithTag(pni, POOL_TAG);
pniSize *= 2;
}
} else status = STATUS_INSUFFICIENT_RESOURCES;
} while (status == STATUS_INFO_LENGTH_MISMATCH);
if (NT_SUCCESS(status))
*Name = pni;
return status;
}
You can obtain the process handle by the following ways:
ObOpenObjectByPointer, you need process' EPROCESS address (PsLookupProcessByProcessId may help).
ZwOpenProcess – youn need to know PID of the target process.
However, using this code in every invocation of your minifilter's pre/post callback can be quite time-consuming. I solve this problem by caching process names in a hash table that uses PID as a key. Notify routines (PsSetXXXNotifyRoutine(Ex)) may prove very useful when building and managing such a table.
here I found full code like #Martin Drab code
EDIT: new fixed code
NTSTATUS
GetProcessImageName(
PEPROCESS eProcess,
PUNICODE_STRING* ProcessImageName
)
{
NTSTATUS status = STATUS_UNSUCCESSFUL;
ULONG returnedLength;
HANDLE hProcess = NULL;
PAGED_CODE(); // this eliminates the possibility of the IDLE Thread/Process
if (eProcess == NULL)
{
return STATUS_INVALID_PARAMETER_1;
}
status = ObOpenObjectByPointer(eProcess,
0, NULL, 0, 0, KernelMode, &hProcess);
if (!NT_SUCCESS(status))
{
DbgPrint("ObOpenObjectByPointer Failed: %08x\n", status);
return status;
}
if (ZwQueryInformationProcess == NULL)
{
UNICODE_STRING routineName = RTL_CONSTANT_STRING(L"ZwQueryInformationProcess");
ZwQueryInformationProcess =
(QUERY_INFO_PROCESS)MmGetSystemRoutineAddress(&routineName);
if (ZwQueryInformationProcess == NULL)
{
DbgPrint("Cannot resolve ZwQueryInformationProcess\n");
status = STATUS_UNSUCCESSFUL;
goto cleanUp;
}
}
/* Query the actual size of the process path */
status = ZwQueryInformationProcess(hProcess,
ProcessImageFileName,
NULL, // buffer
0, // buffer size
&returnedLength);
if (STATUS_INFO_LENGTH_MISMATCH != status) {
DbgPrint("ZwQueryInformationProcess status = %x\n", status);
goto cleanUp;
}
*ProcessImageName = kmalloc(returnedLength);
if (ProcessImageName == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
goto cleanUp;
}
/* Retrieve the process path from the handle to the process */
status = ZwQueryInformationProcess(hProcess,
ProcessImageFileName,
*ProcessImageName,
returnedLength,
&returnedLength);
if (!NT_SUCCESS(status)) kfree(*ProcessImageName);
cleanUp:
ZwClose(hProcess);
return status;
}
FLT_POSTOP_CALLBACK_STATUS
PostCreate(
_Inout_ PFLT_CALLBACK_DATA Data,
_In_ PCFLT_RELATED_OBJECTS FltObjects,
_In_opt_ PVOID CompletionContext,
_In_ FLT_POST_OPERATION_FLAGS Flags
)
{
PUNICODE_STRING pni = NULL;
NTSTATUS status = STATUS_UNSUCCESSFUL;
status = GetProcessImageName(IoThreadToProcess(Data->Thread), &pni);
if (NT_SUCCESS(status))
{
DbgPrint("ProcessName = %ws\n", pni->Buffer);
kfree(pni);
}
else
{
DbgPrint("GetProcessImageName status = %x\n", status);
}
// ...
}