I am working on a windows keyboard filter driver and I need to write to a file. I tried using zwcreate,zwwrite, and zwclose but the driver is not running at PASSIVE_LEVEL and I got the BSOD. I have never written a windows driver before. Thanks for the help!
EDIT: Thanks J. Passing!
Schedule workitems (IoAllocateWorkItem/IoQueueWorkItem) and handle all file I/O from within the workitem callback routines.
I'm not sure if it is a good idea to let the kernel driver write to a file in the first place. The best way to do that IMHO is to provide a user space program that communicates with the driver, gets the data and then writes it to disk.
This is true for Unix, but not for Windows.
PCUCHAR buffer[] = {0x00, 0x01, 0x02, 0x03}; // for example
ULONG bufferSize = sizeof(buffer);
UNICODE_STRING filePath; // Must be with DOS prefix: \??\C:\MyFolder\logs.txt
HANDLE hFile;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
InitializeObjectAttributes(&ObjectAttributes, &filePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL, NULL);
NTSTATUS Status = ZwCreateFile(&hFile, FILE_GENERIC_READ | FILE_GENERIC_WRITE, &ObjectAttributes,
&IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_CREATE,
FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
if (!NT_SUCCESS(Status))
{
DbgPrint("[DRV_NAME]: Creating file error");
return Status;
}
Status = ZwWriteFile(hFile, NULL, NULL, NULL, &IoStatusBlock, (PVOID)buffer, bufferSize, NULL, NULL);
if (!NT_SUCCESS(Status))
{
InjDbgPrint("[DRV_NAME]: Writing file error");
return Status;
}
ZwClose(hFile);
A wonderful example for using Zw-Tools to write files from a device driver is Clandestiny's Klog found at rootkit.com. It is currently helping me a lot.
And well, I kinda agree with Johannes that it is not advisable to do classical userland-work (file/net/...-access) directly from a driver. Not only is it errorprone, it is also might break unforseeable in the future. User interfaces normally are much more steady and resilient.
Related
MSDN states that on older versions of Windows, NtOpenProcess supports opening a process by name but fails to document that actual syntax of the name string.
In Windows Server 2003, Windows XP, and Windows 2000, the caller has the option of supplying either a client ID or an object name (but not both). If the ObjectName field of the structure pointed to by ObjectAttributes contains a non-NULL pointer to an object name, ClientId must be NULL.
I've tried various versions of %d, %#x and %x, what is the correct syntax for the object name?
HANDLE handle = 0;
WCHAR b[99];
wsprintfW(b, L"Process\\%x", GetCurrentProcessId()); // What is the syntax supposed to be? "Process" is the name of the process object type but I'm not sure if it's required here.
UNICODE_STRING name;
RtlInitUnicodeString(&name, b);
OBJECT_ATTRIBUTES oa;
InitializeObjectAttributes(&oa, &name, 0, NULL, NULL);
NTSTATUS status = NtOpenProcess(&handle, SYNCHRONIZE, &oa, NULL);
_tprintf(_T("%X %p\n"), status, handle);
(I realize this question is outdated by about 20 years but I'm just curious)
With help from RbMm in the comments I was able to get it to work but since you are limited to processes named on purpose and the functions to do that are undocumented the whole feature is rather useless.
if (LOBYTE(GetVersion()) != 5) return -1;
UNICODE_STRING name;
OBJECT_ATTRIBUTES oa;
RtlInitUnicodeString(&name, L"\\BaseNamedObjects\\HelloWorld");
NTSTATUS status;
HANDLE handle = 0;
InitializeObjectAttributes(&oa, &name, 0, NULL, NULL);
status = NtCreateProcessEx(&handle, STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|0x0FFF, &oa, GetCurrentProcess(), 0, NULL, NULL, NULL, 0);
_tprintf(_T("create %X %p pid=%d\n"), status, handle, status ? 0 : GetProcessId(handle));
if (status) return status;
status = NtOpenProcess(&handle, SYNCHRONIZE|PROCESS_QUERY_INFORMATION|PROCESS_TERMINATE, &oa, NULL);
_tprintf(_T("open %X %p pid=%d\n"), status, handle, status ? 0 : GetProcessId(handle));
if (status) return status;
Sleep(1000*60);
TerminateProcess(handle, 0); // Kill zombie child
Consider this snippet (handle was opened with FILE_SYNCHRONOUS_IO_NONALERT flag):
IO_STATUS_BLOCK io;
NTSTATUS r = NtWriteFile(h, NULL, NULL, NULL, &io, data, data_len, &pos, NULL);
if NT_SUCCESS(r)
assert(io.Information == data_len); // can we succeed with short write?
else
// is IO_STATUS_BLOCK filled?
assert(io.Information == 0); // can we fail and yet write some data?
Is it possible for NtWriteFile() to succeed and yet write less than requested?
Is it possible for NtWriteFile() to fail and yet write some of data? If yes -- how to determine amount of data written?
I know lots of similar questions on this topic have been asked before but so far I have been unable to find a solution that actually works. I want to start a console program from my program and capture its output. My implementation should be in a way that is compatible with WaitForMultipleObjects(), i.e. I want to get notified whenever there is new data to read in the pipe.
My implementation is based on this example from MSDN. However, I had to modify it a little because I need overlapped I/O in order to be able to wait for ReadFile() to finish. So I'm using named pipes created using Dave Hart's MyCreatePipeEx() function from here.
This is my actual code. I have removed error checks for readability reasons.
HANDLE hReadEvent;
HANDLE hStdIn_Rd, hStdIn_Wr;
HANDLE hStdOut_Rd, hStdOut_Wr;
SECURITY_ATTRIBUTES saAttr;
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
OVERLAPPED ovl;
HANDLE hEvt[2];
DWORD mask, gotbytes;
BYTE buf[4097];
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
MyCreatePipeEx(&hStdOut_Rd, &hStdOut_Wr, &saAttr, 0, FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
MyCreatePipeEx(&hStdIn_Rd, &hStdIn_Wr, &saAttr, 0, FILE_FLAG_OVERLAPPED, FILE_FLAG_OVERLAPPED);
SetHandleInformation(hStdOut_Rd, HANDLE_FLAG_INHERIT, 0);
SetHandleInformation(hStdIn_Wr, HANDLE_FLAG_INHERIT, 0);
memset(&piProcInfo, 0, sizeof(PROCESS_INFORMATION));
memset(&siStartInfo, 0, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = hStdOut_Wr;
siStartInfo.hStdOutput = hStdOut_Wr;
siStartInfo.hStdInput = hStdIn_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
CreateProcess(NULL, "test.exe", NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);
hReadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
for(;;) {
int i = 0;
hEvt[i++] = piProcInfo.hProcess;
memset(&ovl, 0, sizeof(OVERLAPPED));
ovl.hEvent = hReadEvent;
if(!ReadFile(hStdOut_Rd, buf, 4096, &gotbytes, &ovl)) {
if(GetLastError() == ERROR_IO_PENDING) hEvt[i++] = hReadEvent;
} else {
buf[gotbytes] = 0;
printf("%s", buf);
}
mask = WaitForMultipleObjects(i, hEvt, FALSE, INFINITE);
if(mask == WAIT_OBJECT_0 + 1) {
if(GetOverlappedResult(hStdOut_Rd, &ovl, &gotbytes, FALSE)) {
buf[gotbytes] = 0;
printf("%s", buf);
}
} else if(mask == WAIT_OBJECT_0) {
break;
}
}
The problem with this code is the following: As you can see, I'm reading in chunks of 4kb using ReadFile() because I obviously don't know how much data the external program test.exe will output. Doing it this way was suggested here:
To read a variable amount of data from the client process just issue
read requests of whatever size you find convenient and be prepared to
handle read events that are shorter than you requested. Don't
interpret a short but non-zero length as EOF. Keep issuing read
requests until you get a zero length read or an error.
However, this doesn't work. The event object passed to ReadFile() as part of the OVERLAPPED structure will only trigger once there are 4kb in the buffer. If the external program just prints "Hello", the event won't trigger at all. There need to be 4kb in the buffer for hReadEvent to actually trigger.
So I thought I should read byte by byte instead and modified my program to use ReadFile() like this:
if(!ReadFile(hStdOut_Rd, buf, 1, &gotbytes, &ovl)) {
However, this doesn't work either. If I do it like this, the read event is not triggered at all which is really confusing me. When using 4096 bytes, the event does indeed trigger as soon as there are 4096 bytes in the pipe, but when using 1 byte it doesn't work at all.
So how am I supposed to solve this? I'm pretty much out of ideas here. Is there no way to have the ReadFile() event trigger whenever there is some new data in the pipe? Can't be that difficult, can it?
Just for the record, while there are some problems with my code (see discussion in comments below the OP), the general problem is that it's not really possible to capture the output of arbitrary external programs because they will typically use block buffering when their output is redirected to a pipe, which means that output will only arrive at the capturing program once that buffer is flushed so real time capturing is not really possible.
Some workarounds have been suggested though:
1) (Windows) Here is a workaround that uses GetConsoleScreenBuffer() to capture the output from arbitrary console programs but it currently only supports one console page of output.
2) (Linux) On Linux, it's apparently possible to use pseudo-terminals to force the external program to use unbuffered output. Here is an example.
I am calling NtCreateProcessEx with the section handle argument set to NULL in order to create a user mode process that is initialized with a copy of the parents address space.
I want the child process to run under a different image name other than the one of the parent process.
Is this even possible?
Here's my call to NtCreateProcessEx:
HANDLE fileHandle;
OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
UNICODE_STRING InputString;
RtlInitUnicodeString( &InputString, L"C:\\Users\\user\\Documents\\codeblocks_projects\\test\\bin\\Release\\test.exe" );
ObjectAttributes.Length = sizeof( OBJECT_ATTRIBUTES );
ObjectAttributes.ObjectName = &InputString;
NTSTATUS status = NtCreateProcessEx( &fileHandle, PROCESS_QUERY_INFORMATION, &ObjectAttributes, GetCurrentProcess(), PS_INHERIT_HANDLES, NULL, NULL, NULL, FALSE );
printf_s( "%x\n", status );
Status is 0xC0000033 - STATUS_OBJECT_NAME_INVALID, if I don't pass any object attributes, the call works fine.
What am I missing here?
My guess is that this not only poorly documented; it is also impossible. At least, it seems effectively impossible nowadays, perhaps due to security concerns.
The documentation for NtOpenProcess indicates that even identifying a process by name hasn't been possible since Vista:
https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-ntopenprocess
I just tried an alternative approach using NtSetInformationProcess:
#define PHNT_NO_INLINE_INIT_STRING
#include <phnt_windows.h>
#include <phnt.h>
#include <stdio.h>
int main() {
NTSTATUS status;
HANDLE handle;
status
= NtCreateProcess(&handle,
PROCESS_ALL_ACCESS,
NULL,
NtCurrentProcess(),
/*InheritObjectTable=*/TRUE,
NULL,
NULL,
NULL
);
UNICODE_STRING newName;
RtlInitUnicodeString(&newName, L"dummy.exe");
status
= NtSetInformationProcess(
handle,
ProcessImageFileName,
&newName,
sizeof newName
);
// fails with 0xC0000003, STATUS_INVALID_INFO_CLASS
printf("status: %x\n", status);
// pause to observe the new "zombie child" in Process Explorer
printf("sleeping...\n");
Sleep(5000);
return 0;
}
As noted in the code, NtSetInformationProcess fails with STATUS_INVALID_INFO_CLASS, even though this is allowed by the corresponding NtQueryInformationProcess:
https://learn.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryinformationprocess
Looking at the ReactOS source, this seems to be a deliberate omission. I also found this:
https://reactos.org/pipermail/ros-diffs/2004-November/002066.html
As of this writing, the above reads "don't allow the ProcessImageFileName information class for NtSetInformationProcess() anymore".
All this suggests it may have been possible in the past. After all, how was exec() in the old POSIX subsystem implemented? After fork() created an initial thread in the new process and set the thread's context to be a copy of the parent's, the child might then call exec(), whereupon the POSIX implementation probably destroyed and re-created the child process from within. At some point, the subsystem would have to set the ProcessImageFileName somehow.
I have a card reader ( no memory stick is inserted ).
When i insert into my compter it shows an empty drive in My Computer...
Is it possible to know whether a drive is having a media ( sorry i am not sure how to call it ) or not...
I find the suggestion of MSalters to use IOCTL_STORAGE_CHECK_VERIFY very good. There are a small trick in the usage of IOCTL_STORAGE_CHECK_VERIFY. Before the usage of IOCTL code in the function DeviceIoControl one need to open the corresponding device with respect of CreateFile function:
HANDLE hDevice = CreateFile (szDeviceName, // like "\\.\E:"
0, // no access to the drive
FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
NULL, OPEN_EXISTING, 0, NULL);
For the usage of DeviceIoControl one can use 0 as a second parameter of CreateFile, because we will not use ReadFile, WriteFile etc functions to access the device. The implementation of IOCTL_STORAGE_CHECK_VERIFY do follow to some read of data requests. So to be able to use IOCTL_STORAGE_CHECK_VERIFY without having ERROR_ACCESS_DENIED (5) error we have to open the device as following
HANDLE hDevice = CreateFile (szDeviceName, // like "\\.\E:"
FILE_READ_DATA, // read access to the data
FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
NULL, OPEN_EXISTING, 0, NULL);
There exist another version of IOCTL_STORAGE_CHECK_VERIFY - IOCTL_STORAGE_CHECK_VERIFY2 which works absolutely the same as IOCTL_STORAGE_CHECK_VERIFY but much more quickly (see http://msdn.microsoft.com/en-us/library/ff560538.aspx). To use IOCTL_STORAGE_CHECK_VERIFY2 one can open device with only FILE_READ_ATTRIBUTES access:
HANDLE hDevice = CreateFile (szDeviceName, // like "\\.\E:"
FILE_READ_ATTRIBUTES, // read access to the attributes
FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
NULL, OPEN_EXISTING, 0, NULL);
The code which test the existence of the media in the drive can look like following
DWORD cbBytesReturned;
bSuccess = DeviceIoControl (hDevice, // device to be queried
IOCTL_STORAGE_CHECK_VERIFY2,
NULL, 0, // no input buffer
NULL, 0, // no output buffer
&cbBytesReturned, // # bytes returned
(LPOVERLAPPED) NULL); // synchronous I/O
if (bSuccess)
_tprintf (TEXT("the device media are accessible\n"));
else if (GetLastError() == ERROR_NOT_READY)
_tprintf (TEXT("the device media are not accessible\n"));
The reason for this behavior is historical, and dates back to floppy drives and MS-DOS. The A: drive would still be the A: drive even if there was no floppy in it.
It is sometimes possible to check whether a drive with removable media is empty. Card readers and CD drives usually support this, floppy drives don't. You would send the drive a IOCTL_STORAGE_CHECK_VERIFY .