我按照here的示例启动进程并读取输出,并阅读了here的信息和示例,将匿名管道更改为命名管道,以便异步读取输入。到目前为止,一切都很好。
但是,在第二页,有一个关于远程访问的注释:
命名管道可用于在同一计算机上的进程之间或通过网络的不同计算机上的进程之间提供通信。如果服务器服务正在运行,则可以远程访问所有命名管道。如果您打算仅在本地使用命名管道,请拒绝对 NT AUTHORITY\NETWORK 的访问或切换到本地 RPC。
由于该管道仅用于读取已启动程序的输出,因此我仅在本地使用命名管道(即使该程序在其他情况下确实与网络进行通信)。但是,我不知道如何拒绝对 NT AUTHORITY\NETWORK 的访问。我该怎么做?页面上没有链接,谷歌也没有为我提供任何进一步的帮助。或者,如果我在
PIPE_REJECT_REMOTE_CLIENTS
的 dwPipeMode
参数中设置 CreateNamedPipe
标志就足够了(尽管我假设没有)?
如有必要,这里是相关代码的粗略轮廓,为简单起见,省略了错误处理:
SECURITY_ATTRIBUTES attr;
attr.nLength = sizeof(SECURITY_ATTRIBUTES);
attr.bInheritHandle = TRUE;
attr.lpSecurityDescriptor = NULL;
HANDLE event = CreateEvent(&attr, TRUE, TRUE, NULL);
OVERLAPPED overlap;
overlap.hEvent = event;
overlap.Offset = 0;
overlap.OffsetHigh = 0;
HANDLE pipeHandle{ CreateNamedPipeW(
pipename.c_str(),
PIPE_ACCESS_OUTBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS,
1,
BUFSIZE * sizeof(TCHAR),
BUFSIZE * sizeof(TCHAR),
5000,
&attr
)};
BOOL connected = ConnectNamedPipe(pipeHandle, &overlap);
STARTUPINFO si;
PROCESS_INFORMATION info;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&info, sizeof(info));
si.hStdError = pipeHandle;
si.hStdOutput = pipeHandle;
si.dwFlags |= STARTF_USESTDHANDLES;
CreateProcessW(
exe.c_str(),
command.data(),
NULL,
NULL,
TRUE,
CREATE_SUSPENDED | CREATE_NO_WINDOW,
NULL,
NULL,
&si,
&info);
HANDLE job = CreateJobObjectW(NULL, NULL);
AssignProcessToJobObject(job, info.hProcess);
ResumeThread(info.hThread);
CloseHandle(pipeHandle);
HANDLE file = CreateFileW(
pipename.c_str(),
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL);
DWORD dwRead, dwWritten;
CHAR buf[BUFSIZE];
BOOL success = FALSE;
HANDLE waitHandles[] = {info.hProcess, cancelSimulation, overlap.hEvent};
DWORD procResult{};
while (true) {
//monitor with WaitForMultipleObjects
//and handle the thread being cancelled, the process exiting, and stdout getting data
}
一般情况下,可以在对象上设置适当的安全描述符。例如,我们可以获取自己的登录 sid,并只允许访问该 sid 的对象。因此只有 LogonSession 中的进程才能打开对象。 (当然,提升的管理员/系统无论如何都可以找到打开此类对象的方法)。
但在具体情况下 - 你不需要在管道上有名称(从 win 7 的某些版本开始)。可能创建无名管道对(在此之前,真正的无名管道对根本不存在 - 系统 api 创建随机命名对,但不是真正的无名管道对)。并注意仅存在单一类型的管道 - 管道。管道的名称(即使它是空的)不会改变对象的类型和功能。 下一个代码可以用于创建未命名对:
NTSTATUS CreatePipePair(_Out_ PHANDLE phServerPipe,
_Out_ PHANDLE phClientPipe,
_In_ ULONG ClientOptions = FILE_SYNCHRONOUS_IO_NONALERT,
_In_ ULONG ServerOptions = 0)
{
HANDLE hFile;
IO_STATUS_BLOCK iosb;
UNICODE_STRING NamedPipe = RTL_CONSTANT_STRING(L"\\Device\\NamedPipe\\");
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &NamedPipe, OBJ_CASE_INSENSITIVE };
NTSTATUS status;
if (0 <= (status = NtOpenFile(&hFile, SYNCHRONIZE, &oa, &iosb,
FILE_SHARE_VALID_FLAGS, 0)))
{
oa.RootDirectory = hFile;
LARGE_INTEGER timeout = { 0, MINLONG };
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|SYNCHRONIZE,
&oa, &iosb, FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_CREATE,
ServerOptions,
FILE_PIPE_BYTE_STREAM_TYPE, FILE_PIPE_BYTE_STREAM_MODE,
FILE_PIPE_QUEUE_OPERATION, 1, 0, 0, &timeout)))
{
oa.RootDirectory = *phServerPipe;
oa.Attributes = 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, ClientOptions)))
{
NtClose(oa.RootDirectory);
*phServerPipe = 0;
}
}
NtClose(hFile);
}
return status;
}
一些异步 I/O 的演示代码:
class uIRP;
class __declspec(novtable) IoObj
{
friend class uIRP;
HANDLE _M_hFile = 0;
PTP_IO _M_Io = 0;
LONG _M_nRef = 1;
virtual void IOCompletionRoutine(ULONG Code, NTSTATUS status, ULONG_PTR dwNumberOfBytesTransfered, uIRP* Irp) = 0;
protected:
void StartIo()
{
AddRef();
if (_M_Io) TpStartAsyncIoOperation(_M_Io);
}
HANDLE get_File() { return _M_hFile; }
virtual ~IoObj()
{
if (_M_Io) TpReleaseIoCompletion(_M_Io);
if (_M_hFile) NtClose(_M_hFile);
}
public:
void AddRef()
{
InterlockedIncrementNoFence(&_M_nRef);
}
void Release()
{
if (!InterlockedDecrement(&_M_nRef)) delete this;
}
void Assign(HANDLE hFile)
{
_M_hFile = hFile;
}
};
class uIRP : public IO_STATUS_BLOCK
{
PVOID _M_pv;
ULONG _M_Code;
LONG _M_nRef = 1;
void OnIoComplete(IoObj* pObj, PIO_STATUS_BLOCK IoSB)
{
pObj->IOCompletionRoutine(_M_Code, IoSB->Status, IoSB->Information, this);
pObj->Release();
Release();
}
static VOID NTAPI S_OnIoComplete(
_Inout_ PTP_CALLBACK_INSTANCE /*Instance*/,
_Inout_opt_ PVOID Context,
_In_ PVOID ApcContext,
_In_ PIO_STATUS_BLOCK IoSB,
_In_ PTP_IO /*Io*/)
{
reinterpret_cast<uIRP*>(ApcContext)->OnIoComplete(reinterpret_cast<IoObj*>(Context), IoSB);
}
public:
static NTSTATUS BindIoCompletion(IoObj* pObj, HANDLE hFile)
{
return TpAllocIoCompletion(&pObj->_M_Io, hFile, S_OnIoComplete, pObj, 0);
}
uIRP(IoObj* pObj, ULONG Code, PVOID pv = 0) : _M_Code(Code), _M_pv(pv)
{
Status = STATUS_PENDING;
Information = 0;
pObj->StartIo();
}
void Reuse(IoObj* pObj, ULONG Code, PVOID pv = 0)
{
AddRef();
_M_Code = Code, _M_pv = pv;
Status = STATUS_PENDING;
Information = 0;
pObj->StartIo();
}
void AddRef()
{
InterlockedIncrementNoFence(&_M_nRef);
}
void Release()
{
if (!InterlockedDecrement(&_M_nRef)) delete this;
}
void CheckNtStatus(IoObj* pObj, NTSTATUS status)
{
if (NT_ERROR(status))
{
TpCancelAsyncIoOperation(pObj->_M_Io);
Status = status;
OnIoComplete(pObj, this);
}
}
};
class Pipe : public IoObj
{
enum { op_write = 'wwww', op_read = 'rrrr' };
ULONG _M_dwThreadId = GetCurrentThreadId();
char _M_buf[0x100];
virtual ~Pipe()
{
PostThreadMessageW(_M_dwThreadId, WM_QUIT, 0, 0);
}
NTSTATUS Write(const void* pv, ULONG cb)
{
if (uIRP* Irp = new uIRP(this, op_write))
{
return Irp->CheckNtStatus(this,
NtWriteFile(get_File(), 0, 0, Irp, Irp, const_cast<void*>(pv), cb, 0, 0)), 0;
}
return STATUS_NO_MEMORY;
}
NTSTATUS Read(uIRP* Irp)
{
return Irp->CheckNtStatus(this,
NtReadFile(get_File(), 0, 0, Irp, Irp, _M_buf, sizeof(_M_buf), 0, 0)), 0;
}
virtual void IOCompletionRoutine(ULONG Code, NTSTATUS status, ULONG_PTR dwNumberOfBytesTransfered, uIRP* Irp)
{
if (0 > status)
{
DbgPrint("\n%hs<%p>(%.4hs, %x, %x, %p)\n", __FUNCTION__, this, &Code, status, dwNumberOfBytesTransfered, Irp);
return ;
}
switch (Code)
{
case op_read:
if (dwNumberOfBytesTransfered)
{
PCHAR psz = _M_buf;
do
{
Code = (ULONG)min(0x80, dwNumberOfBytesTransfered);
DbgPrint("%.*hs", Code, psz);
psz += Code;
} while (dwNumberOfBytesTransfered -= Code);
}
Irp->Reuse(this, op_read);
Read(Irp);
break;
case op_write:
break;
default:
__debugbreak();
}
}
public:
NTSTATUS Start(PHANDLE phClient)
{
HANDLE hPipe;
NTSTATUS status = CreatePipePair(&hPipe, phClient);
if (0 <= status)
{
if (0 <= (status = uIRP::BindIoCompletion(this, hPipe)))
{
Assign(hPipe);
if (uIRP* Irp = new uIRP(this, op_read))
{
return Read(Irp);
}
status = STATUS_NO_MEMORY;
}
NtClose(*phClient);
*phClient = 0;
}
return status;
}
};
NTSTATUS StartProcess(PCWSTR lpApplicationName, PWSTR lpCommandLine = 0)
{
PROCESS_INFORMATION pi;
STARTUPINFOEXW si = { { sizeof(si) } };
si.StartupInfo.dwFlags = STARTF_USESTDHANDLES;
HRESULT hr;
SIZE_T s = 0;
while (ERROR_INSUFFICIENT_BUFFER == (hr = BOOL_TO_ERROR(InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &s))))
{
si.lpAttributeList = (PPROC_THREAD_ATTRIBUTE_LIST)alloca(s);
}
if (NOERROR == hr)
{
if (NOERROR == (hr = BOOL_TO_ERROR(UpdateProcThreadAttribute(si.lpAttributeList,
0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &si.StartupInfo.hStdError, sizeof(HANDLE), 0, 0))))
{
hr = STATUS_NO_MEMORY;
if (Pipe* pipe = new Pipe)
{
hr = pipe->Start(&si.StartupInfo.hStdError);
pipe->Release();
if (0 <= hr)
{
si.StartupInfo.hStdOutput = si.StartupInfo.hStdError;
si.StartupInfo.hStdInput = si.StartupInfo.hStdError;
if (CreateProcessW(lpApplicationName, lpCommandLine, 0, 0, TRUE,
EXTENDED_STARTUPINFO_PRESENT|DETACHED_PROCESS, 0, 0, &si.StartupInfo, &pi))
{
NtClose(pi.hThread);
NtClose(pi.hProcess);
}
else
{
hr = GetLastError();
}
NtClose(si.StartupInfo.hStdError);
}
}
}
DeleteProcThreadAttributeList(si.lpAttributeList);
}
return hr;
}
WCHAR app[0x100];
if (ExpandEnvironmentStringsW(L"%SystemRoot%\\system32\\ipconfig.exe", app, _countof(app)))
{
if (S_OK == StartProcess(app, const_cast<PWSTR>(L"* /?")))
{
MSG msg;
while (0 < GetMessageW(&msg, 0, 0, 0))
{
}
}
}