Skip to content

Instantly share code, notes, and snippets.

@rbmm
Created April 27, 2025 01:35
Show Gist options
  • Save rbmm/aef5c8344a8b2686cd7398ade0db0fdd to your computer and use it in GitHub Desktop.
Save rbmm/aef5c8344a8b2686cd7398ade0db0fdd to your computer and use it in GitHub Desktop.
#include "stdafx.h"
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, 0, 0)))
{
oa.RootDirectory = hFile;
LARGE_INTEGER timeout = { 0, (LONG)MINLONG };
UNICODE_STRING empty = {};
oa.ObjectName = &empty;
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, 0,
ClientOptions)))
{
NtClose(oa.RootDirectory);
*phServerPipe = 0;
}
}
NtClose(hFile);
}
return status;
}
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;
PWSTR pwz = 0;
ULONG cch = 0;
while (cch = MultiByteToWideChar(CP_UTF8, 0, psz, (ULONG)dwNumberOfBytesTransfered, pwz, cch))
{
if (pwz)
{
psz = 1 + (PCHAR)pwz;
if (cch = WideCharToMultiByte(CP_ACP, 0, pwz, cch, psz, cch*sizeof(WCHAR)-1, 0, 0))
{
do
{
Code = (ULONG)min(0x80, cch);
DbgPrint("%.*hs", Code, psz);
psz += Code;
} while (cch -= Code);
}
break;
}
pwz = (PWSTR)alloca(cch*sizeof(WCHAR));
}
}
Irp->Reuse(this, op_read);
Read(Irp);
break;
case op_write:
break;
default:
__debugbreak();
}
}
public:
NTSTATUS Write(PCSTR pcsz)
{
return Write(pcsz, (ULONG)strlen(pcsz));
}
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;
}
};
#define RTL_USER_PROC_UTF8_PROCESS 0x08000000
NTSTATUS SetPtocessUtf8(HANDLE hProcess)
{
PROCESS_BASIC_INFORMATION pbi;
_RTL_USER_PROCESS_PARAMETERS* ProcessParameters;
ULONG Flags;
NTSTATUS status;
0 <= (status = NtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), 0)) &&
0 <= (status = ZwReadVirtualMemory(hProcess,
&reinterpret_cast<_PEB*>(pbi.PebBaseAddress)->ProcessParameters,
&ProcessParameters, sizeof(ProcessParameters), 0)) &&
0 <= (status = ZwReadVirtualMemory(hProcess, &ProcessParameters->Flags, &Flags, sizeof(Flags), 0)) &&
0 <= (status = ZwWriteVirtualMemory(hProcess, &ProcessParameters->Flags, &(Flags |= RTL_USER_PROC_UTF8_PROCESS), sizeof(Flags), 0));
return status;
}
inline ULONG BOOL_TO_ERROR(BOOL f)
{
return f ? NOERROR : GetLastError();
}
NTSTATUS StartProcess(PCWSTR lpApplicationName, PWSTR lpCommandLine = 0, BOOL bDetached = TRUE)
{
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 (CreateProcess(lpApplicationName, lpCommandLine, 0, 0, TRUE,
bDetached
? DETACHED_PROCESS|EXTENDED_STARTUPINFO_PRESENT|CREATE_SUSPENDED
: CREATE_NO_WINDOW|EXTENDED_STARTUPINFO_PRESENT|CREATE_SUSPENDED,
0, 0, &si.StartupInfo, &pi))
{
SetPtocessUtf8(pi.hProcess);
ResumeThread(pi.hThread);
NtClose(pi.hThread);
NtClose(pi.hProcess);
}
else
{
hr = GetLastError();
}
NtClose(si.StartupInfo.hStdError);
}
}
}
DeleteProcThreadAttributeList(si.lpAttributeList);
}
return hr;
}
void test_out(PCWSTR lpApplicationName, PWSTR lpCommandLine = 0, BOOL bDetached = TRUE)
{
WCHAR app[MAX_PATH];
if (SearchPathW(0, lpApplicationName, 0, _countof(app), app, 0))
{
SetEnvironmentVariableW(L"OutputEncoding", L"UTF-8");//Unicode // Ansi
if (S_OK == StartProcess(app, lpCommandLine, bDetached))
{
MSG msg;
while (0 < GetMessageW(&msg, 0, 0, 0)) ;
}
}
}
void test_out()
{
test_out(L"ipconfig.exe", const_cast<PWSTR>(L"* /?"));
test_out(L"powershell.exe", const_cast<PWSTR>(L"* /?"), FALSE);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment