Can NtWriteFile() produce a short write? - winapi

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?

Related

How to open a process by name with NtOpenProcess

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

Capture output from console program with overlapping and events

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.

ReadFile and WriteFile with Overlapped IO result if not ERROR_IO_PENDING?

The docs for FILE_FLAG_OVERLAPPED on WriteFile() say you must provide OVERLAP and recommend NULL for lpNumberOfBytesWritten because value is misleading. However docs for GetOverlappedResult() say to only call if WriteFile() returned FALSE with ERROR_IO_PENDING. So that leaves the case where ReadFile() / WriteFile() complete in the API call itself. How are you supposed to get the number of bytes read/written? Do you presume it's the number requested? But WriteFile() says "When writing to a non-blocking, byte-mode pipe handle with insufficient buffer space, WriteFile returns TRUE with * lpNumberOfBytesWritten < nNumberOfBytesToWrite".
TIA!!
If hFile was opened with FILE_FLAG_OVERLAPPED The
lpNumberOfBytesWritten parameter should be set to NULL.
this is not true (mistake or lie). the lpNumberOfBytesWritten can be set to NULL, but not should. if I/O request complete synchronous with success in *lpNumberOfBytesWritten will be valid number of bytes.
note also that lpNumberOfBytes must not point to location which will be valid until operation is complete (like lpOverlapped) - it can point to local variable in function for example and you can exit from function before I/O is complete - and this is will be ok too. system simply copy InternalHigh from OVERLAPPED to *lpNumberOfBytes. so in pseudo code :
if (lpNumberOfBytes) *lpNumberOfBytes = (ULONG)lpOverlapped->InternalHigh;
obvivous correct value in *lpNumberOfBytes will be only if I/O already completed with success. so and can use it only in this case. system doesn't remember value of lpNumberOfBytes - because this it must be valid only during [Write|Read]File call but not during I/O active (which can be longer in case asynchronous I/O)
GetOverlappedResult we can call if I/O request complete synchronous with success (in case ReadFile or WriteFile, if thay return TRUE) of if pending returned. this api can not be called only in case I/O request just fail (ReadFile or WriteFile return FALSE and GetLastError() != ERROR_IO_PENDING)
so the best always pass not 0 lpNumberOfBytes to api and use it, if api complete just with success. otherwise use GetOverlappedResult or if say you use BindIoCompletionCallback - you direct got dwNumberOfBytesTransfered in callback.
so in conceptually can use next code:
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
HANDLE hFile = CreateFile(*, FILE_GENERIC_READ, FILE_SHARE_READ, 0,
OPEN_EXISTING, FILE_FLAG_OVERLAPPED, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
UCHAR buf[0x200];
OVERLAPPED ov = {};
ULONG NumberOfBytesRead;
ULONG dwError = BOOL_TO_ERROR(ReadFile(hFile, buf, sizeof(buf), &NumberOfBytesRead, &ov));
switch (dwError)
{
case ERROR_IO_PENDING:
dwError = BOOL_TO_ERROR(GetOverlappedResult(hFile, &ov, &NumberOfBytesRead, TRUE));
if (dwError != NOERROR) goto __default;
[[fallthrough]];
case NOERROR:
DbgPrint("NumberOfBytesRead=%x\n", NumberOfBytesRead);
// use GetOverlappedResult(hFile, &ov, &NumberOfBytesRead, TRUE) here also possible
break;
__default:
default:
DbgPrint("dwError = %u\n", dwError);
}
CloseHandle(hFile);
}

kernel mode driver write to file

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.

How to fix garbled text with using WriteFile on a pipe?

I have a Win32 application that I'm making, and it sends a string from one process to another via a named pipe. However, the process that calls ReadFile on the pipe gets the string with some garbled data in it. It returns the number of bytes written correctly, but the last 8 characters or so of the string are garbled.
Here is the code for creating the pipe, and writing to it:
myPipe = CreateNamedPipe(L"\\\\.\\pipe\\testpipe", PIPE_ACCESS_OUTBOUND, PIPE_NOWAIT, 10, 512, 512, 10, NULL);
TCHAR title[128];
GetWindowText(foundHwnd, title, 128);
wstring windowTitle(title);
vector<wstring> splitVec;
boost::split(splitVec, windowTitle, boost::algorithm::is_any_of(wstring(L"|")));
WriteFile(myPipe, splitVec[0].c_str(), splitVec[0].size(), &wrote, NULL);
And here is the code that reads it:
if (WaitNamedPipe(L"\\\\.\\pipe\\testpipe", 5000) == 0) {
MessageBox(NULL, L"Unable to wait for pipe", L"Error", MB_OK);
return false;
}
myPipe = CreateFile(L"\\\\.\\pipe\\testpipe", GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (myPipe == INVALID_HANDLE_VALUE) {
MessageBox(NULL, L"Unable to open pipe", L"Error", MB_OK);
return false;
}
// Other code here...
TCHAR buf[512];
DWORD read;
success = ReadFile(myPipe, buf, 512, &read, NULL);
if (read > 0)
MessageBox(NULL, buf, L"Got Data", MB_OK);
When MessageBox is shown, the end of the string is garbled and I have no idea why. Any ideas?
Thanks!
I think the key here is to make sure that your strings are null terminated and that you send the termination character as well. You shouldn't have to send the entire buffer if the communications is synchronous or if you set it up in PIPE_READMODE_MESSAGE. ReadFile will return when either the specified number of bytes has been read or a write operation completes on the other end of the pipe. I believe that the "garbled" text is really garbage in the read buffer on the client side of the pipe and because you are not transmitting the string termination character, it is including this in the text sent to the message box. Either clear your read buffer before sending or send the string termination character with the message and I think it will work without the overhead of sending a full buffer.
Here is sample client from MSDN. Note how the client sends exactly the number of characters in the message + 1 (including the termination character) and receives into a fixed buffer of size 512. If you look at a server example, you'll see the same pattern.
Some observations on the code you posted:
You need to either 1) explicitly send the null terminated byte, or 2) append one to the data you read.
Since you are reading 512 bytes, you should also be sending exactly 512 bytes.
You can send variable length strings instead by first sending the size of the string, and then sending that many bytes. That way when you read the data you will know how many bytes to read for the actual string.
The problem with what you did will be seen as soon as you send 2 things over the pipe, and you read past what you really want in the first read.
If you are only sending 1 thing over the pipe, you can keep your code, but send size() + 1 when you write to the pipe.
ReadFile / WriteFile were meant to send binary data, not necessarily strings. So you can make a function called ReadString and WriteString that implements my suggestion about reading/writing first the size then the actual string.
Try something like this:
Here is the code for creating the pipe, and writing to it:
myPipe = CreateNamedPipe(L"\\\\.\\pipe\\testpipe", PIPE_ACCESS_OUTBOUND, PIPE_NOWAIT, 10, 512, 512, 10, NULL);
TCHAR title[128];
GetWindowText(foundHwnd, title, 128);
WriteFile(myPipe, title, 128*sizeof(TCHAR), &wrote, NULL);//<---In this case we are sending a null terminated string buffer.
And here is the code that reads it:
if (WaitNamedPipe(L"\\\\.\\pipe\\testpipe", 5000) == 0) {
MessageBox(NULL, L"Unable to wait for pipe", L"Error", MB_OK);
return false;
}
myPipe = CreateFile(L"\\\\.\\pipe\\testpipe", GENERIC_READ, FILE_SHARE_READ, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (myPipe == INVALID_HANDLE_VALUE) {
MessageBox(NULL, L"Unable to open pipe", L"Error", MB_OK);
return false;
}
// Other code here...
TCHAR buf[128];
DWORD read;
success = ReadFile(myPipe, buf, 128*sizeof(TCHAR), &read, NULL);
if (read > 0)
MessageBox(NULL, buf, L"Got Data", MB_OK);
I ran into this problem with "garbage in the pipe" when writing a generic function to read stdout from any process executed at the command prompt. Therefore, I couldn't alter what was being written to the pipe (as is commonly suggested), I could only alter the read side. So, I "cheated".
If the pipe data didn't end in a null terminator, I replaced the last char with one! It seemed to work for me. I saw this work perfectly where there were nulls and where there were not at the end of my data chunks.
I worried that I might lose a critical last char (and it's possible that you might!), but for my immediate purposes, that didn't happen. You might consider adding a null rather than replacing the end under some circumstances...
Here's code snippit:
const unsigned int MAX_PIPE_PEEKS = 100;
DWORD bytesInPipe = 0;
unsigned int pipePeeks=0;
while( (bytesInPipe==0) && (pipePeeks < MAX_PIPE_PEEKS) )
{
bSuccess = PeekNamedPipe( g_hChildStd_OUT_Rd, NULL, 0, NULL,
&bytesInPipe, NULL );
if( !bSuccess ) return bSuccess; // Bail on critical failure
++pipePeeks;
}
if( bytesInPipe > 0 )
{
// Read the data written to the pipe (and implicitly clear it)
DWORD dwRead;
CHAR *pipeContents = new CHAR[ bytesInPipe ];
bSuccess = ReadFile( g_hChildStd_OUT_Rd, pipeContents,
bytesInPipe, &dwRead, NULL );
if( !bSuccess || dwRead == 0 ) return FALSE; // Bail on critical failure
// "Cheat" - eliminate garbage at the end of the pipe
if( pipeContents[ bytesInPipe ] != '\0' )
pipeContents[ bytesInPipe ] = '\0';
}
UPDATE:
After further testing, I found that this is not quite reliable (shocking, huh?). I think I'm on the right track though for a relatively simple solution. Any ideas for getting this quick patch to work?

Resources