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
Related
I am fairly new to kernel programming and I have a little problem getting all disk drives information like name,serialnumber from kernel mode. I use below code to get all disks symbolic links which works perfectly fine.
static VOID DeviceInterfaceTest_Func() {
NTSTATUS Status;
PWSTR SymbolicLinkList;
PWSTR SymbolicLinkListPtr;
GUID Guid = {
0x53F5630D,
0xB6BF,
0x11D0,
{
0x94,
0xF2,
0x00,
0xA0,
0xC9,
0x1E,
0xFB,
0x8B
}
}; //Defined in mountmgr.h
Status = IoGetDeviceInterfaces( &
Guid,
NULL,
0, &
SymbolicLinkList);
if (!NT_SUCCESS(Status)) {
return;
}
KdPrint(("IoGetDeviceInterfaces results:\n"));
for (SymbolicLinkListPtr = SymbolicLinkList; SymbolicLinkListPtr[0] != 0 && SymbolicLinkListPtr[1] != 0; SymbolicLinkListPtr += wcslen(SymbolicLinkListPtr) + 1) {
KdPrint(("Symbolic Link: %S\n", SymbolicLinkListPtr));
PUNICODE_STRING PTarget {};
UNICODE_STRING Input;
NTSTATUS s = 0;
Input.Length = sizeof((PWSTR) & SymbolicLinkListPtr);
Input.MaximumLength = 200 * sizeof(WCHAR);
Input.Buffer = (PWSTR) ExAllocatePool2(PagedPool, Input.MaximumLength, 0);
s = SymbolicLinkTarget( & Input, PTarget);
if (s == STATUS_SUCCESS) {
//KdPrint(("%S\n", PTarget->Buffer));
KdPrint(("Finished!\n"));
}
}
ExFreePool(SymbolicLinkList);
}
However when i try to use InitializeObjectAttributes function to extract data of symbolic link inside for loop I checking their names with KdPrint and all them are null as a result i can't use ZwOpenSymbolicLinkObject, because when i use it i get BSOD. What am I doing wrong? Is my method valid to get disk information or I should use another method? Below is the code of SymbolicLinkTarget
NTSTATUS SymbolicLinkTarget(_In_ PUNICODE_STRING SymbolicLinkStr, _Out_ PUNICODE_STRING PTarget) {
OBJECT_ATTRIBUTES ObjectAtiribute {};
NTSTATUS Status = 0;
HANDLE Handle = nullptr;
InitializeObjectAttributes( & ObjectAtiribute, SymbolicLinkStr, OBJ_CASE_INSENSITIVE, 0, 0);
KdPrint(("Object length:%u \n", ObjectAtiribute.Length));
KdPrint(("Object name:%s \n", ObjectAtiribute.ObjectName - > Buffer));
Status = ZwOpenSymbolicLinkObject(&Handle, GENERIC_READ, &ObjectAtiribute);
if (Status != STATUS_SUCCESS)
{
KdPrint(("ZwOpenSymbolicLinkObject failed (0x%08X)\n", Status));
return Status;
}
UNREFERENCED_PARAMETER(PTarget);
ULONG Tag1 = 'Tag1';
PTarget->MaximumLength = 200 * sizeof(WCHAR);
PTarget->Length = 0;
PTarget->Buffer = (PWCH)ExAllocatePool2(PagedPool, PTarget->MaximumLength, Tag1);
if (!PTarget->Buffer)
{
ZwClose(Handle);
return STATUS_INSUFFICIENT_RESOURCES;
}
Status = ZwQuerySymbolicLinkObject(Handle, PTarget, NULL);
ZwClose(Handle);
if (Status != STATUS_SUCCESS)
{
KdPrint(("ZwQuerySymbolicLinkObject failed (0x%08X)\n", Status));
ExFreePool(PTarget->Buffer);
return Status;
}
return STATUS_SUCCESS;
}
Thank you very much for helping.
There are multiple problems in your functions. Let start with he main one:
In SymbolicLinkTarget():
OBJECT_ATTRIBUTES ObjectAtiribute {};
InitializeObjectAttributes( & ObjectAtiribute, SymbolicLinkStr, OBJ_CASE_INSENSITIVE, 0, 0);
You are going to initialize ObjectAtiribute from SymbolicLinkStr (and the other parameters) but in DeviceInterfaceTest_Func() you actually never set Input to contain a string!
UNICODE_STRING Input;
NTSTATUS s = 0;
Input.Length = sizeof((PWSTR) & SymbolicLinkListPtr);
Input.MaximumLength = 200 * sizeof(WCHAR);
Input.Buffer = (PWSTR) ExAllocatePool2(PagedPool, Input.MaximumLength, 0);
s = SymbolicLinkTarget( & Input, PTarget);
Input.Length
This is wrong:
Input.Length = sizeof((PWSTR) & SymbolicLinkListPtr);
Input.Length will be set to the size of a pointer. According to the UNICODE_STRING (ntdef.h; subauth.h) the length is:
Specifies the length, in bytes, of the string pointed to by the Buffer member, not including the terminating NULL character, if any.
So:
size_t str_len_no_null = wcslen(SymbolicLinkListPtr); // number of chars, not bytes!
Input.Length = str_len_no_null * sizeof(WCHAR);
Notice the wcslen() is already in the init-statement of the for loop, I would train to extract it to have it in the loop body.
Input.MaximumLength
Input.MaximumLength = 200 * sizeof(WCHAR);
What if the string is more lager than 200 characters?
MaximumLength is defined as such:
Specifies the total size, in bytes, of memory allocated for Buffer. Up to MaximumLength bytes may be written into the buffer without trampling memory.
Thus it's safe to just do:
size_t max_length_bytes = Input.Length + (1 * sizeof(WCHAR)); // add room for possible null.
Input.MaximumLength = max_length_bytes;
The allocation for the Buffer member can be kept in place. Now you need to copy the string into the buffer.
UNICODE_STRING init
size_t str_len_no_null = wcslen(SymbolicLinkListPtr); // number of chars, not bytes!
Input.Length = str_len_no_null * sizeof(WCHAR);
size_t max_length_bytes = Input.Length + (1 * sizeof(WCHAR)); // add room for possible null.
Input.MaximumLength = max_length_bytes;
Input.Buffer = (PWSTR) ExAllocatePool2(PagedPool, Input.MaximumLength, 0); // note: you should define a Tag for your Driver.
if(Input.buffer == NULL) {
// not enough memory.
return;
}
status = RtlStringCbCopyW(Input.Buffer, max_length_bytes, SymbolicLinkListPtr);
// TODO: check status
Now that you know how to do it manually, throw your code and use RtlUnicodeStringInit
Other things & hints
Always checks the return status / value of the functions you use. In kernel mode, this is super important.
NTSTATUS check is always done using one of the status macros (usually NT_SUCCESS)
Use string safe functions.
nitpicking: A success return value of IoGetDeviceInterfaces may also indicate an empty buffer. Although you check that in the for loop init-statement, I would have checked that right after the function so the intent is clearer.
KdPrint(("Object name:%s \n", ObjectAtiribute.ObjectName - > Buffer));
It's %S (wide char) not %s (char); see format specification. you can pass a UNICODE_STRING and use the %Z formatter. Also be wary of - > which is strange (you probably meant ->).
InitializeObjectAttributes( & ObjectAtiribute, SymbolicLinkStr, OBJ_CASE_INSENSITIVE, 0, 0);
Use OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE if the resulting handle is not meant to cross the kernel <-> user-mode boundary (in your case, it doesn't have to cross that boundary). Otherwise you leak a kernel handle to user-mode, which has security implications.
This is also required when you call ZwOpenSymbolicLinkObject and you are not running in a system thread:
If the caller is not running in a system thread context, it must set the OBJ_KERNEL_HANDLE attribute when it calls InitializeObjectAttributes.
You can define GUIDs with DEFINE_GUID; see Defining and Exporting New GUIDs and Including GUIDs in Driver Code. In your case you don't need to export it.
This is probably nitpicking, but use nullptr (c++) or NULL (c) instead of 0 to convey the idea that you are checking for a pointer and not just the integral value of 0.
I am writing a WinDBG extension to debug a device driver, and need to call an external binary to debug the device's firmware. I would like to show the output of this binary in the WinDBG console.
My initial idea was to simply pipe the output of the binary to a buffer and print that buffer with ControlledOutput. However, I get a 'broken pipe' error when I try to read from the pipe in my extension.
Here is how I create the external process in my extension:
SECURITY_ATTRIBUTES sAttr;
HANDLE childOutRead = NULL;
HANDLE childOutWrite = NULL;
PROCESS_INFORMATION childProcInfo;
STARTUPINFO childStartInfo;
char buf[4096];
sAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
sAttr.bInheritHandle = TRUE;
sAttr.lpSecurityDescriptor = NULL;
CreatePipe(&childOutRead, &childOutWrite, &sAttr, 0);
// don't inherit read end
SetHandleInformation(childOutRead, HANDLE_FLAG_INHERIT, 0);
ZeroMemory(&childProcInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory(&childStartInfo, sizeof(STARTUPINFO));
childStartInfo.cb = sizeof(STARTUPINFO);
childStartInfo.hStdError = childOutWrite;
childStartInfo.hStdOut = childOutWrite;
childStartInfo.hStdIn = GetStdHandle(STD_INPUT_HANDLE);
childStartInfo.dwFlags |= STARTF_USESTDHANDLES;
CreateProcessA(NULL, "myBinary.exe someArgs",
NULL, NULL, TRUE, 0, NULL, NULL,
&childStartInfo, &childProcInfo);
// close the handle not used in parent
CloseHandle(childOutWrite);
// read output
while (1) {
DWORD read;
BOOL r;
DWORD error;
r = ReadFile(childOutRead, buf, sizeof(buf), &read, NULL);
if (!r) {
error = GetLastError();
windbgPrintf("got error 0x%x\n", error);
break;
}
if (read == 0) break;
windbgPrint(buf, read);
}
ReadFile fails with error 0x6D, BROKEN_PIPE. This makes me suspect that the pipe is somehow not being inherited.
I have nearly identical code working in a test outside of WinDBG, so it must be doing something differently. How do I get pipes working in this way inside WinDBG?
I recently tried to write a code to check if a thread in Windows is alive or not. I searched this forum and found discussions like: How to check if a process or thread is alive or not given their IDs in C++?.
I understand I can use OpenThread API. However it doesn't seem to work in my code as follows.
DWORD WINAPI myThread( LPVOID lpParam )
{
cout<<"child thread"<<endl;
return 0;
}
int main(void)
{
DWORD lTldID = 0;
HANDLE lTldHD = CreateThread(NULL, 0, myThread, 0, 0, &lTldID);
WaitForSingleObject(lTldHD, INFINITE);
HANDLE lHD = OpenThread(0x0040, FALSE, lTldID);
return 1;
}
I expect that the HANDLE lHD should be NULL since the thread 'myThread' should have finished at the time of calling OpenThread(). However, I always got NOT NULL values like 0x00000068. I don't know why this happened. Any idea?
Thanks,
Xiaomo
BOOL WINAPI GetExitCodeThread(
_In_ HANDLE hThread,
_Out_ LPDWORD lpExitCode
);
This function returns immediately. If the specified thread has not terminated and the function succeeds, the status returned is STILL_ACTIVE.
I would like to crash a running program of my choice (e.g., notepad++, becrypt, word) for software testing purposes.
I know how to BSOD, I know how to cause a program I write to crash, I know how to end process - but how to crash an existing process I do not!
any help?
Well, use CreateRemoteThread on a remote process and invoke something [1] that crashes the process reliably. I'm not sure whether CreateRemoteThread guards against null pointers, but you could pass an address in the null page to it and have the remote process execute that.
[1] null pointer or null page access, division by zero, invoking a privileged instruction, int3 ...
Example:
#include <stdio.h>
#include <tchar.h>
#include <Windows.h>
BOOL setCurrentPrivilege(BOOL bEnable, LPCTSTR lpszPrivilege)
{
HANDLE hToken = 0;
if(::OpenThreadToken(::GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &hToken)
|| ::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
TOKEN_PRIVILEGES tp;
LUID luid;
if(!::LookupPrivilegeValue(
NULL, // lookup privilege on local system
lpszPrivilege, // privilege to lookup
&luid ) ) // receives LUID of privilege
{
::CloseHandle(hToken);
return FALSE;
}
tp.PrivilegeCount = 1;
tp.Privileges[0].Luid = luid;
tp.Privileges[0].Attributes = (bEnable) ? SE_PRIVILEGE_ENABLED : 0;
// Enable the privilege or disable all privileges.
if(!::AdjustTokenPrivileges(
hToken,
FALSE,
&tp,
sizeof(TOKEN_PRIVILEGES),
(PTOKEN_PRIVILEGES) NULL,
(PDWORD) NULL)
)
{
CloseHandle(hToken);
return FALSE;
}
::CloseHandle(hToken);
}
return TRUE;
}
int killProcess(DWORD processID)
{
HANDLE hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, processID);
if(hProcess)
{
if(!setCurrentPrivilege(TRUE, SE_DEBUG_NAME))
{
_tprintf(TEXT("Could not enable debug privilege\n"));
}
HANDLE hThread = ::CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)1, NULL, 0, NULL);
if(hThread)
{
::CloseHandle(hThread);
}
else
{
_tprintf(TEXT("Error: %d\n"), GetLastError());
::CloseHandle(hProcess);
return 1;
}
::CloseHandle(hProcess);
}
return 0;
}
int __cdecl _tmain(int argc, _TCHAR *argv[])
{
killProcess(3016);
}
Of course you'll want to adjust the PID in the call to killProcess. Compiled with WNET DDK and tested on 2003 Server R2.
The gist here is that we tell the remote process to execute code at address 0x1 ((LPTHREAD_START_ROUTINE)1), which is inside the null page but not a null pointer (in case there are checks against that). The crud around the function, in particular setCurrentPrivilege is used to gain full debug privileges so we can do our evil deed.
You can use DLL injection technique in order to inject your code into another process. Then in your injected code do something simple like abort() or division by zero.
A two steps mechanism is needed:
inject the process to crash (using an injection library, using Detours, using a Hook installation, etc..). What you choose depends on the time and knowledge you have and other preconditions (like credentials, anti-injection protection, size of the foot-print you want to leave..)
perform an invalid operation in the injected process (like int 2Eh, divide by null, etc..)
Here's how to do that with the winapiexec tool:
winapiexec64.exe CreateRemoteThread ( OpenProcess 0x1F0FFF 0 1234 ) 0 0 0xDEAD 0 0 0
Replace 1234 with the process id and run the command, the process will crash.
Is it possible to use overlapped I/O with an anonymous pipe? CreatePipe() does not have any way of specifying FILE_FLAG_OVERLAPPED, so I assume ReadFile() will block, even if I supply an OVERLAPPED-structure.
Here is an implementation for an anonymous pipe function with the possibility to specify FILE_FLAG_OVERLAPPED:
/******************************************************************************\
* This is a part of the Microsoft Source Code Samples.
* Copyright 1995 - 1997 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\******************************************************************************/
/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
pipeex.c
Abstract:
CreatePipe-like function that lets one or both handles be overlapped
Author:
Dave Hart Summer 1997
Revision History:
--*/
#include <windows.h>
#include <stdio.h>
static volatile long PipeSerialNumber;
BOOL
APIENTRY
MyCreatePipeEx(
OUT LPHANDLE lpReadPipe,
OUT LPHANDLE lpWritePipe,
IN LPSECURITY_ATTRIBUTES lpPipeAttributes,
IN DWORD nSize,
DWORD dwReadMode,
DWORD dwWriteMode
)
/*++
Routine Description:
The CreatePipeEx API is used to create an anonymous pipe I/O device.
Unlike CreatePipe FILE_FLAG_OVERLAPPED may be specified for one or
both handles.
Two handles to the device are created. One handle is opened for
reading and the other is opened for writing. These handles may be
used in subsequent calls to ReadFile and WriteFile to transmit data
through the pipe.
Arguments:
lpReadPipe - Returns a handle to the read side of the pipe. Data
may be read from the pipe by specifying this handle value in a
subsequent call to ReadFile.
lpWritePipe - Returns a handle to the write side of the pipe. Data
may be written to the pipe by specifying this handle value in a
subsequent call to WriteFile.
lpPipeAttributes - An optional parameter that may be used to specify
the attributes of the new pipe. If the parameter is not
specified, then the pipe is created without a security
descriptor, and the resulting handles are not inherited on
process creation. Otherwise, the optional security attributes
are used on the pipe, and the inherit handles flag effects both
pipe handles.
nSize - Supplies the requested buffer size for the pipe. This is
only a suggestion and is used by the operating system to
calculate an appropriate buffering mechanism. A value of zero
indicates that the system is to choose the default buffering
scheme.
Return Value:
TRUE - The operation was successful.
FALSE/NULL - The operation failed. Extended error status is available
using GetLastError.
--*/
{
HANDLE ReadPipeHandle, WritePipeHandle;
DWORD dwError;
UCHAR PipeNameBuffer[ MAX_PATH ];
//
// Only one valid OpenMode flag - FILE_FLAG_OVERLAPPED
//
if ((dwReadMode | dwWriteMode) & (~FILE_FLAG_OVERLAPPED)) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
//
// Set the default timeout to 120 seconds
//
if (nSize == 0) {
nSize = 4096;
}
sprintf( PipeNameBuffer,
"\\\\.\\Pipe\\RemoteExeAnon.%08x.%08x",
GetCurrentProcessId(),
InterlockedIncrement(&PipeSerialNumber)
);
ReadPipeHandle = CreateNamedPipeA(
PipeNameBuffer,
PIPE_ACCESS_INBOUND | dwReadMode,
PIPE_TYPE_BYTE | PIPE_WAIT,
1, // Number of pipes
nSize, // Out buffer size
nSize, // In buffer size
120 * 1000, // Timeout in ms
lpPipeAttributes
);
if (! ReadPipeHandle) {
return FALSE;
}
WritePipeHandle = CreateFileA(
PipeNameBuffer,
GENERIC_WRITE,
0, // No sharing
lpPipeAttributes,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | dwWriteMode,
NULL // Template file
);
if (INVALID_HANDLE_VALUE == WritePipeHandle) {
dwError = GetLastError();
CloseHandle( ReadPipeHandle );
SetLastError(dwError);
return FALSE;
}
*lpReadPipe = ReadPipeHandle;
*lpWritePipe = WritePipeHandle;
return( TRUE );
}
No. As explained here, anonymous pipes do not support asynchronous I/O. You need to use a named pipe. There's example code to do this on MSDN here and here.
first of all need understand - what is Anonymous Pipes and what, are exist difference between anonymous and Named Pipes at all.
really exist only single pipe type (implemented by npfs.sys). no any difference, except name, between named and anonymous pipes at all. both is only pipes.
so called anonymous pipes - this is special/random named pipes before win7 and true unnamed pipes begin from win7.
when msdn write that "anonymous pipe is one-way pipe" - this is lie. as any pipe it can be one-way or duplex. when msdn write that "Asynchronous (overlapped) read and write operations are not supported by anonymous pipes." - this is lie. of course pipes support asynchronous io. the name of pipe not affect this.
before win7 really unnamed pipes even not exist at all. CreatePipe function use Win32Pipes.%08x.%08x format for create name of "Anonymous Pipe".
static LONG PipeSerialNumber;
WCHAR name[64];
swprintf(name, L"\\Device\\NamedPipe\\Win32Pipes.%08x.%08x",
GetCurrentProcessId(), InterlockedIncrement(&PipeSerialNumber));
begin from win7 CreatePipe use another technique (relative file open) for create pipe pair - now it really anonymous.
for example code witch create pipe pair where one pipe is asynchronous and not inheritable. and another pipe is synchronous and inheritable. both pipes is duplex (support both read and write)
ULONG CreatePipeAnonymousPair7(PHANDLE phServerPipe, PHANDLE phClientPipe)
{
HANDLE hNamedPipe;
IO_STATUS_BLOCK iosb;
static UNICODE_STRING NamedPipe = RTL_CONSTANT_STRING(L"\\Device\\NamedPipe\\");
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, const_cast<PUNICODE_STRING>(&NamedPipe), OBJ_CASE_INSENSITIVE };
NTSTATUS status;
if (0 <= (status = NtOpenFile(&hNamedPipe, SYNCHRONIZE, &oa, &iosb, FILE_SHARE_VALID_FLAGS, 0)))
{
oa.RootDirectory = hNamedPipe;
static LARGE_INTEGER timeout = { 0, MINLONG };
static UNICODE_STRING empty = {};
oa.ObjectName = ∅
if (0 <= (status = ZwCreateNamedPipeFile(phServerPipe,
FILE_READ_ATTRIBUTES|FILE_READ_DATA|
FILE_WRITE_ATTRIBUTES|FILE_WRITE_DATA|
FILE_CREATE_PIPE_INSTANCE,
&oa, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_CREATE, 0, FILE_PIPE_BYTE_STREAM_TYPE, FILE_PIPE_BYTE_STREAM_MODE,
FILE_PIPE_QUEUE_OPERATION, 1, 0, 0, &timeout)))
{
oa.RootDirectory = *phServerPipe;
oa.Attributes = OBJ_CASE_INSENSITIVE|OBJ_INHERIT;
if (0 > (status = NtOpenFile(phClientPipe, SYNCHRONIZE|FILE_READ_ATTRIBUTES|FILE_READ_DATA|
FILE_WRITE_ATTRIBUTES|FILE_WRITE_DATA, &oa, &iosb,
FILE_SHARE_VALID_FLAGS, FILE_SYNCHRONOUS_IO_NONALERT)))
{
NtClose(oa.RootDirectory);
}
}
NtClose(hNamedPipe);
}
return RtlNtStatusToDosError(status);
}
ULONG CreatePipeAnonymousPair(PHANDLE phServerPipe, PHANDLE phClientPipe)
{
static char flag_supported = -1;
if (flag_supported < 0)
{
ULONG dwMajorVersion, dwMinorVersion;
RtlGetNtVersionNumbers(&dwMajorVersion, &dwMinorVersion, 0);
flag_supported = _WIN32_WINNT_WIN7 <= ((dwMajorVersion << 8)| dwMinorVersion);
}
if (flag_supported)
{
return CreatePipeAnonymousPair7(phServerPipe, phClientPipe);
}
static LONG PipeSerialNumber;
WCHAR name[64];
swprintf(name, L"\\\\?\\pipe\\Win32Pipes.%08x.%08x", GetCurrentProcessId(), InterlockedIncrement(&PipeSerialNumber));
HANDLE hClient, hServer = CreateNamedPipeW(name,
PIPE_ACCESS_DUPLEX|FILE_READ_DATA|FILE_WRITE_DATA|FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE, 1, 0, 0, 0, 0);
if (hServer != INVALID_HANDLE_VALUE)
{
static SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
hClient = CreateFileW(name, FILE_GENERIC_READ|FILE_GENERIC_WRITE,
FILE_SHARE_READ|FILE_SHARE_WRITE, &sa, OPEN_EXISTING, 0, 0);
if (hClient != INVALID_HANDLE_VALUE)
{
*phServerPipe = hServer, *phClientPipe = hClient;
return NOERROR;
}
CloseHandle(hServer);
}
return GetLastError();
}