Created
April 27, 2025 01:35
-
-
Save rbmm/aef5c8344a8b2686cd7398ade0db0fdd to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 = ∅ | |
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