Skip to content

Instantly share code, notes, and snippets.

@Cr4sh
Created March 19, 2016 15:08
Show Gist options
  • Save Cr4sh/126d844c28a7fbfd25c6 to your computer and use it in GitHub Desktop.
Save Cr4sh/126d844c28a7fbfd25c6 to your computer and use it in GitHub Desktop.
fork() for Windows
/*
* fork.c
* Experimental fork() on Windows. Requires NT 6 subsystem or
* newer.
*
* Copyright (c) 2012 William Pitcock <nenolod@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#define _WIN32_WINNT 0x0600
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winnt.h>
#include <winternl.h>
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <process.h>
typedef struct _CLIENT_ID {
PVOID UniqueProcess;
PVOID UniqueThread;
} CLIENT_ID, *PCLIENT_ID;
typedef struct _SECTION_IMAGE_INFORMATION {
PVOID EntryPoint;
ULONG StackZeroBits;
ULONG StackReserved;
ULONG StackCommit;
ULONG ImageSubsystem;
WORD SubSystemVersionLow;
WORD SubSystemVersionHigh;
ULONG Unknown1;
ULONG ImageCharacteristics;
ULONG ImageMachineType;
ULONG Unknown2[3];
} SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION;
typedef struct _RTL_USER_PROCESS_INFORMATION {
ULONG Size;
HANDLE Process;
HANDLE Thread;
CLIENT_ID ClientId;
SECTION_IMAGE_INFORMATION ImageInformation;
} RTL_USER_PROCESS_INFORMATION, *PRTL_USER_PROCESS_INFORMATION;
#define RTL_CLONE_PROCESS_FLAGS_CREATE_SUSPENDED 0x00000001
#define RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES 0x00000002
#define RTL_CLONE_PROCESS_FLAGS_NO_SYNCHRONIZE 0x00000004
#define RTL_CLONE_PARENT 0
#define RTL_CLONE_CHILD 297
typedef DWORD pid_t;
typedef NTSTATUS (*RtlCloneUserProcess_f)(ULONG ProcessFlags,
PSECURITY_DESCRIPTOR ProcessSecurityDescriptor /* optional */,
PSECURITY_DESCRIPTOR ThreadSecurityDescriptor /* optional */,
HANDLE DebugPort /* optional */,
PRTL_USER_PROCESS_INFORMATION ProcessInformation);
pid_t fork(void)
{
HMODULE mod;
RtlCloneUserProcess_f clone_p;
RTL_USER_PROCESS_INFORMATION process_info;
NTSTATUS result;
mod = GetModuleHandle("ntdll.dll");
if (!mod)
return -ENOSYS;
clone_p = (RtlCloneUserProcess_f)GetProcAddress(mod, "RtlCloneUserProcess");
if (clone_p == NULL)
return -ENOSYS;
/* lets do this */
result = clone_p(RTL_CLONE_PROCESS_FLAGS_CREATE_SUSPENDED | RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES, NULL, NULL, NULL, &process_info);
if (result == RTL_CLONE_PARENT)
{
HANDLE me, hp, ht, hcp = 0;
DWORD pi, ti, mi;
me = GetCurrentProcess();
pi = (DWORD)process_info.ClientId.UniqueProcess;
ti = (DWORD)process_info.ClientId.UniqueThread;
assert(hp = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi));
assert(ht = OpenThread(THREAD_ALL_ACCESS, FALSE, ti));
ResumeThread(ht);
CloseHandle(ht);
CloseHandle(hp);
return (pid_t)pi;
}
else if (result == RTL_CLONE_CHILD)
{
/* fix stdio */
AllocConsole();
return 0;
}
else
return -1;
/* NOTREACHED */
return -1;
}
#ifdef __TEST__
int main(int argc, const char *argv[])
{
pid_t pid;
printf("Forking..\n");
pid = fork();
switch (pid) {
case 0:
{
FILE *f = fopen("C:\\Development\\cpp.sandbox\\win32\\win32-fork\\forktest.dat", "w");
fprintf(f, "ok\n");
fclose(f);
break;
}
default:
printf("child %d\n", pid);
while (1) { Sleep(1000); }
break;
}
}
#endif
@petrsmid
Copy link

petrsmid commented Jan 1, 2023

The fix of Console didn't work with me. I fixed it with AttachConsole(parent_pid). See the improved version here: https://gist.github.com/petrsmid/d96446beac825c8c0cf5a35240f444a8

@tostercx
Copy link

@petrsmid is this somehow working again? last I checked starting from win10 or so a lot of memory patches were necessary https://github.com/sslab-gatech/winnie/blob/master/forklib/fork.cpp

@SvarunSoda
Copy link

@Cr4sh
Howdy,
I'm having some trouble getting this to run properly on Visual Studio 2022 & Windows 10.

Trying to run this specific example and simillar ones, after calling fork(), it appears that the child process(es) do not return and continue executing. The only printed output here is from the parent process, and nothing else:

I am parent. Child process PID: 31136

Is that the intended behaviour here? Or are child processes supposed to also resume executing after fork() calls, like the fork() from unistd.h?

@tostercx
Copy link

tostercx commented May 31, 2024

@SvarunSoda I've encounter that before, not sure of the cause. The demos from https://github.com/huntandhackett/process-cloning seem to work for me on a similar setup, but I haven't tested those any further on real world applications. Also winnie's fork works but requires custom offsets for each windows version and probably has similar limitations as described in h&h repo.

edit

After re-skimming winnie's paper, the key innovation was connecting the new thread to CSRSS, which a lot of Win32 functions need to function correctly. So winnie's fork should be a lot more stable.

@SvarunSoda
Copy link

SvarunSoda commented May 31, 2024

I've encounter that before, not sure of the cause. The demos from https://github.com/huntandhackett/process-cloning seem to work for me on a similar setup, but I haven't tested those any further on real world applications. Also winnie's fork works but requires custom offsets for each windows version and probably has similar limitations as described in h&h repo.

edit

After re-skimming winnie's paper, the key innovation was connecting the new thread to CSRSS, which a lot of Win32 functions need to function correctly. So winnie's fork should be a lot more stable.

@tostercx Thanks for your reply;

Yeah I've already checked out the forklib module in the winnie project, however that project appears to create custom pointers to various things inside of csrss.dll, that appear to be different for each Windows system. That is something that is very inconvenient and impractical in my application. I was specially interested in this specific project, because that sort of thing is not used here. But it appears that this implementation has other issues that get in the way... This also uses RtlCloneUserProcess, which also has an entry in the process cloning guide on GitHub you referenced which I also already took a look at. forklib from winnie also appears to be setup entirely for a DLL export, not an executable, but that shouldn't be too hard to change.

I'm gonna try and so some more research & to reach out to the author(s) to see if I can get some more leads as to what exactly is the issue here, as this is the simplest implementation I have yet seen. I'd be really excited to get this one to work properly...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment