text: // todo: investigate why MessageBoxA doesnt work
  
  #include "stdafx.h"
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <wchar.h>
  
  #include "fork.h"
  
  
  // Uncodumented headers csrss stuff
  #include "csrss.h"
  
  // ReWolf's library to fuck with 64-bit memory from 32-bit WoW64 programs
  #ifndef _WIN64
  #include "wow64ext.h"
  #endif
  
  #define _CRT_SECURE_NO_WARNINGS
  
  #ifndef _DEBUG
  #define printf(...)
  #define DebugBreak()
  #endif
  
  // When a new child process is spawned, the parent must call
  // CsrClientCallServer with API number BasepCreateProcess to notify
  // the csrss subsystem of the new process. However, this seems to
  // be optional as the child process will work without doing this
  // call.
  BOOL NotifyCsrssParent(HANDLE hProcess, HANDLE hThread)
  {
  	PROCESS_BASIC_INFORMATION info;
  	if (!NT_SUCCESS(NtQueryInformationProcess(hProcess,
  		ProcessBasicInformation, &info,
  		sizeof(info), 0))) {
  		printf("FORKLIB: NtQueryInformationProcess failed!\n");
  		return FALSE;
  	}
  
  	BOOL bIsWow64;
  	if (!IsWow64Process(GetCurrentProcess(), &bIsWow64))
  	{
  		printf("FORKLIB: IsWow64Process failed!\n");
  		return FALSE;
  	}
  
  	NTSTATUS result;
  	if (bIsWow64)
  	{
  		CSR_API_MSG64 csrmsg;
  		RtlZeroMemory(&csrmsg, sizeof(csrmsg));
  		csrmsg.CreateProcessRequest.PebAddressNative = (ULONGLONG)info.PebBaseAddress;
  		csrmsg.CreateProcessRequest.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
  		csrmsg.CreateProcessRequest.ProcessHandle = (ULONGLONG)hProcess;
  		csrmsg.CreateProcessRequest.ThreadHandle = (ULONGLONG)hThread;
  		csrmsg.CreateProcessRequest.ClientId.UniqueProcess = GetProcessId(hProcess);
  		csrmsg.CreateProcessRequest.ClientId.UniqueThread = GetThreadId(hThread);
  		//result = CsrClientCallServer64(&csrmsg, NULL, CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateProcess), sizeof(csrmsg.CreateProcessRequest));
  	}
  	else
  	{
  		CSR_API_MSG csrmsg;
  		RtlZeroMemory(&csrmsg, sizeof(csrmsg));
  		csrmsg.CreateProcessRequest.PebAddressNative = info.PebBaseAddress;
  #ifdef _WIN64
  		csrmsg.CreateProcessRequest.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64;
  #else
  		csrmsg.CreateProcessRequest.ProcessorArchitecture = PROCESSOR_ARCHITECTURE_INTEL;
  #endif
  		csrmsg.CreateProcessRequest.ProcessHandle = hProcess;
  		csrmsg.CreateProcessRequest.ThreadHandle = hThread;
  		csrmsg.CreateProcessRequest.ClientId.UniqueProcess = (HANDLE)GetProcessId(hProcess);
  		csrmsg.CreateProcessRequest.ClientId.UniqueThread = (HANDLE)GetThreadId(hThread);
  		//result = CsrClientCallServer(&csrmsg, NULL, CSR_MAKE_API_NUMBER(BASESRV_SERVERDLL_INDEX, BasepCreateProcess), sizeof(csrmsg.CreateProcessRequest));
  	}
  
  	/*
  	if (!NT_SUCCESS(result))
  	{
  		printf("CsrClientCallServer(BasepCreateThread) failed!\n");
  		return FALSE;
  	}
  	*/
  
  	printf("FORKLIB: Successfully notified Csr of child!\n");
  	return TRUE;
  }
  
  // When the a new process is spawned, it must call CsrClientConnectToServer
  // and RtlRegisterThreadWithCsrss to connect to the various csrss subsystems
  // (such as Windows subsystem, Console subsystem, etc). If this is not done,
  // then nearly every function in the Win32 API will lead to segfault. It seems
  // that internally the APIs depend on csrss in some way.
  //
  // j00ru documented csrss on his blog:
  // https://j00ru.vexillium.org/2010/07/windows-csrss-write-up-inter-process-communication-part-1/
  // https://j00ru.vexillium.org/2010/07/windows-csrss-write-up-inter-process-communication-part-2/
  //
  // However, our situation is even trickier than usual, since we are a *forked*
  // process, meaning that all memory values are cloned from the parent process.
  // This is important because CsrClientConnectToServer and RtlRegisterThreadWithCsrss
  // seem to initialize some of the global variables in ntdll, and the two functions
  // will not work if these variable are already initialized.
  // Therefore, it's our responsibility to also *manually de-initialize* these
  // global variables by zeroing them before reconnecting with csrss.
  //
  // Yet *another* complication is WoW64: WoW64 enables the execution of 32-bit
  // executeables on 64-bit Windows. The program sees a 32-bit address space and
  // the 32-bit version of all system dlls. However, the 32-bit ntdll is really just
  // a shim to call the 64-bit version of all of the functions it exposes. In other
  // words on WoW64, there are actually *two* copies of ntdll loaded: the 32-bit
  // version exposed by WoW64, and the 64-bit version that is loaded into every
  // process. Therefore, we need to de-initialize the global variables in *both the
  // 64- and 32-bit version of ntdll*. This is accomplished using some tRicKErY
  // by jumping to 64-bit code from 32-bit.
  //
  // This method only supports Windows 10. Somewhere between Windows 7 and Windows 10,
  // Microsoft refactored Windows to rely less and less on csrss. Hence the API
  // and structures are much simpler on Windows 10 than Windows 7, and as a result
  // our job is much easier.
  BOOL ConnectCsrChild()
  {
  	BOOL bIsWow64;
  	if (!IsWow64Process(GetCurrentProcess(), &bIsWow64))
  	{
  		printf("FORKLIB: IsWow64Process failed!\n");
  		return FALSE;
  	}
  
  	// Zero Csr fields???
  	// Required or else Csr calls will crash
  	printf("FORKLIB: De-initialize ntdll csr data\n");
  	HMODULE ntdll = GetModuleHandleA("ntdll.dll");
  #ifdef _WIN64
  	void* pCsrData = (void*)((uintptr_t)ntdll + csrDataRva_x64); // HARDCODED OFFSET, see csrss_offsets.h
  	printf("FORKLIB: Csr data = %p\n", pCsrData);
  	memset(pCsrData, 0, csrDataSize_x64);
  #else
  	void* pCsrData = (void*)((uintptr_t)ntdll + csrDataRva_x86); // HARDCODED OFFSET,  see csrss_offsets.h
  	printf("FORKLIB: Csr data = %p\n", pCsrData);
  	memset(pCsrData, 0, csrDataSize_x86);
  
  	if (bIsWow64)
  	{
  		DWORD64 ntdll64 = GetModuleHandle64(L"ntdll.dll");
  		printf("FORKLIB: ntdll 64 = %llx\n", ntdll64);
  		char mem[csrDataSize_wow64];
  		memset(mem, 0, sizeof(mem));
  		DWORD64 pCsrData64 = ntdll64 + csrDataRva_wow64; // HARDCODED OFFSET, see csrss_offsets.h
  		printf("FORKLIB: Csr data 64 = %llx\n", ntdll64);
  		setMem64(pCsrData64, mem, sizeof(mem));
  	}
  #endif
  
  	DWORD session_id;
  	wchar_t ObjectDirectory[100];
  	ProcessIdToSessionId(GetProcessId(GetCurrentProcess()), &session_id);		
  	swprintf(ObjectDirectory, 100, L"\\Sessions\\%d\\Windows", session_id);		
  	printf("FORKLIB: Session_id: %d\n", session_id);
  
  	// Not required?
  	printf("FORKLIB: Link Console subsystem...\n");
  	void* pCtrlRoutine = (void*)GetProcAddress(GetModuleHandleA("kernelbase"), "CtrlRoutine");
  	BOOLEAN trash;
  	//if (!NT_SUCCESS(CsrClientConnectToServer(L"\\Sessions\\" CSRSS_SESSIONID L"\\Windows", 1, &pCtrlRoutine, 8, &trash)))
  	if (!NT_SUCCESS(CsrClientConnectToServer(ObjectDirectory, 1, &pCtrlRoutine, 8, &trash)))
  	{
  		printf("FORKLIB: CsrClientConnectToServer failed!\n");
  		return FALSE;
  	}
  
  	printf("FORKLIB: Link Windows subsystem...\n");
  	// passing &gfServerProcess is not necessary, actually? passing &trash is okay?
  	char buf[0x240]; // this seem to just be all zero everytime?
  	memset(buf, 0, sizeof(buf));
  	//if (!NT_SUCCESS(CsrClientConnectToServer(L"\\Sessions\\" CSRSS_SESSIONID L"\\Windows", 3, buf, 0x240, &trash)))
  	if (!NT_SUCCESS(CsrClientConnectToServer(ObjectDirectory, 3, buf, 0x240, &trash)))
  	{
  		printf("FORKLIB: CsrClientConnectToServer failed!\n");
  		return FALSE;
  	}
  
  	printf("FORKLIB: Connect to Csr...\n");
  	if (!NT_SUCCESS(RtlRegisterThreadWithCsrss()))
  	{
  		printf("FORKLIB: RtlRegisterThreadWithCsrss failed!\n");
  		return FALSE;
  	}
  
  	printf("FORKLIB: Connected to Csr!\n");
  	return TRUE;
  }
  
  // Fix stdio handles of the child. If this isn't done, the child
  // will inherit the stdio of the parent, and operations to those
  // file descriptors will just not work.
  void ReopenStdioHandles()
  {
  	freopen_s((FILE**)stdout, "CONOUT$", "w+", stdout);
  	freopen_s((FILE**)stdin, "CONIN$", "r", stdin);
  	freopen_s((FILE**)stderr, "CONOUT$", "w+", stdout);
  
  	SetStdHandle(STD_INPUT_HANDLE, stdin);
  	SetStdHandle(STD_OUTPUT_HANDLE, stdout);
  	SetStdHandle(STD_ERROR_HANDLE, stderr);
  }
  
  #ifndef _WIN64
  LONG WINAPI DiscardException(EXCEPTION_POINTERS *ExceptionInfo)
  {
  	return EXCEPTION_CONTINUE_EXECUTION;
  }
  #endif
  
  extern "C" DWORD fork(LPPROCESS_INFORMATION lpProcessInformation) {
  	printf("FORKLIB: Before the fork, my pid is %d\n", GetProcessId(GetCurrentProcess()));
  
  	PS_CREATE_INFO procInfo;
  	RtlZeroMemory(&procInfo, sizeof(procInfo));
  	HANDLE hProcess = NULL;
  	HANDLE hThread = NULL;
  	procInfo.Size = sizeof(PS_CREATE_INFO);
  
  #ifndef _WIN64
  	// WTF???? Discard *BIZARRE* segfault in ntdll from read fs:[0x18] that you can ignore???
  	// LPTOP_LEVEL_EXCEPTION_FILTER oldFilter = SetUnhandledExceptionFilter(DiscardException);
  	auto handle = AddVectoredExceptionHandler(TRUE, DiscardException);
  #endif
  
  	// This is the part that actually does the forking. Everything else is just
  	// to clean up after the mess that's created afterwards
  	NTSTATUS result = NtCreateUserProcess(
  		&hProcess, &hThread,
  		MAXIMUM_ALLOWED, MAXIMUM_ALLOWED,
  		NULL,
  		NULL,
  		PROCESS_CREATE_FLAGS_INHERIT_FROM_PARENT | PROCESS_CREATE_FLAGS_INHERIT_HANDLES, THREAD_CREATE_FLAGS_CREATE_SUSPENDED,
  		NULL,
  		&procInfo,
  		NULL);
  
  #ifndef _WIN64
  	// Clear the exception handler installed earlier.
  	// SetUnhandledExceptionFilter(oldFilter);
  	RemoveVectoredExceptionHandler(handle);
  #endif
  	if (!result)
  	{
  		// Parent process
  		printf("FORKLIB: I'm the parent\n");
  		printf("FORKLIB: hThread = %p, hProcess = %p\n", hThread, hProcess);
  		printf("FORKLIB: Thread ID = %x\n", GetThreadId(hThread));
  		printf("FORKLIB: Result = %d\n", result);
  		// Not needed??
  		if (!NotifyCsrssParent(hProcess, hThread))
  		{
  			printf("FORKLIB: NotifyCsrssParent failed\n");
  			TerminateProcess(hProcess, 1);
  			return -1;
  		}
  
  		if (lpProcessInformation)
  		{
  			lpProcessInformation->hProcess = hProcess;
  			lpProcessInformation->hThread = hThread;
  			lpProcessInformation->dwProcessId = GetProcessId(hProcess);
  			lpProcessInformation->dwThreadId = GetThreadId(hThread);
  		}
  
  		ResumeThread(hThread); // allow the child to connect to Csr.
  		return GetProcessId(hProcess);
  	}
  	else
  	{
  #ifdef _DEBUG
  		// while (true) { ; }
  #endif // _DEBUG
  
  		// Child process
  		// this may cause problem
  		// FreeConsole();
  
  
  
  
  
  		// Remove these calls to improve performance, at the cost of losing stdio.
  #ifdef _DEBUG
  		//AllocConsole();
  		//SetStdHandle(STD_INPUT_HANDLE, stdin);
  		//SetStdHandle(STD_OUTPUT_HANDLE, stdout);
  		//SetStdHandle(STD_ERROR_HANDLE, stderr);
  #endif
  		printf("I'm the child\n");
  
  		if (!ConnectCsrChild())
  		{
  			DebugBreak();
  			ExitProcess(1);
  		}
  
  #ifdef _DEBUG
  		// Not safe to do fopen until after ConnectCsrChild
  		ReopenStdioHandles();
  #endif
  		
  		return 0;
  	}
  }
  