summaryrefslogtreecommitdiff
path: root/src/libmowgli/platform/win32/fork.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmowgli/platform/win32/fork.c')
-rw-r--r--src/libmowgli/platform/win32/fork.c166
1 files changed, 166 insertions, 0 deletions
diff --git a/src/libmowgli/platform/win32/fork.c b/src/libmowgli/platform/win32/fork.c
new file mode 100644
index 0000000..c644122
--- /dev/null
+++ b/src/libmowgli/platform/win32/fork.c
@@ -0,0 +1,166 @@
+/*
+ * libmowgli: A collection of useful routines for programming.
+ * fork.c: Fastest possible NT fork() implementation
+ *
+ * Copyright (c) 2012 TortoiseLabs, LLC.
+ *
+ * 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 is present in all copies.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * References:
+ * - undocumented.ntinternals.net
+ * - http://www.cs.miami.edu/~burt/journal/NT/processinit.html
+ */
+
+#include "mowgli.h"
+
+#ifdef NOTYET
+
+#ifdef _WIN32
+
+#ifndef _WIN32_WINNT
+
+int fork(void)
+{
+#warning fork is not possible on your platform in any sane way, sorry :(
+ return -ENOSYS;
+}
+
+#else
+
+extern NTSTATUS NTAPI CsrCallClientServer(void *message, void *userdata, uint32_t opcode, uint32_t size);
+
+/*
+ * Definition of a message sent to an NT port on the CSRSS server.
+ *
+ * Not sure what dummy1/dummy2 do, but they're junk as far as I can see.
+ */
+struct csrss_message {
+ uint32_t dummy1;
+ uint32_t opcode;
+ uint32_t status;
+ uint32_t dummy2;
+};
+
+static inline void
+inherit_handles(void)
+{
+ uint32_t n = 0x1000;
+ uint32_t *p = mowgli_alloc_array(sizeof(uint32_t), n);
+ uint32_t pid, i;
+ SYSTEM_HANDLE_INFORMATION *info;
+
+ while (ZwQuerySystemInformation(SystemHandleInformation, p, n * sizeof(*p), 0) == STATUS_INFO_LENGTH_MISMATCH)
+ {
+ n *= 2;
+
+ mowgli_free(p);
+ p = mowgli_alloc_array(sizeof(uint32_t), n);
+ }
+
+ info = (SYSTEM_HANDLE_INFORMATION *) (p + 1);
+ pid = GetCurrentProcessId();
+
+ for (i = 0; i < *p; i++)
+ {
+ if (info[i].ProcessId == pid)
+ SetHandleInformation((HANDLE) h[i].Handle, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
+ }
+
+ mowgli_free(p);
+}
+
+static inline void
+request_csrss_session(HANDLE proc_handle, HANDLE thread_handle, uint32_t pid, uint32_t tid)
+{
+ struct {
+ PORT_MESSAGE port_message;
+ struct csrss_message csrss_message;
+ PROCESS_INFORMATION process_information;
+ CLIENT_ID debugger;
+ uint32_t flags;
+ uint32_t vdminfo[2];
+ } csrmsg = {{0}, {0}, {proc_handle, thread_handle, pid, tid}, {0}, 0, {0}};
+
+ CsrCallClientServer(&csrmsg, NULL, 0x10000, sizeof csrmsg);
+}
+
+int child(void)
+{
+ typedef BOOL (*CsrpConnectToServer)(PWSTR);
+
+ CsrpConnectToServer (0x77F8F65D) (L"\\Windows");
+ __asm__("mov eax, 0; mov esp, ebp; pop ebp; ret");
+}
+
+int fork(void)
+{
+ HANDLE proc_handle, thread_handle;
+ OBJECT_ATTRIBUTES oa = { sizeof(oa) };
+ CONTEXT context = { CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT };
+ MEMORY_BASIC_INFORMATION mbi;
+ THREAD_BASIC_INFORMATION tbi;
+ PNT_TIB tib;
+ USER_STACK stack;
+ CLIENT_ID cid;
+
+ /* ensure the child has the same handles and ports */
+ inherit_handles();
+
+ /* create the actual LWP using ZwCreateProcess() */
+ ZwCreateProcess(&proc_handle, PROCESS_ALL_ACCESS, &oa, NtCurrentProcess(), TRUE, 0, 0, 0);
+
+ /* now set up a thread for that process using a context, cloning the current thread ... */
+ ZwGetContextThread(NtCurrentThread(), &context);
+ context.Eip = (unsigned long) child;
+
+ /* set up a stack for the thread now that the child sentinel is set up ... */
+ ZwQueryVirtualMemory(NtCurrentProcess(), (void *) context.Esp, MemoryBasicInformation,
+ &mbi, sizeof mbi, 0);
+
+ stack = (USER_STACK){0, 0, ((char *) mbi.BaseAddress) + mbi.RegionSize,
+ mbi.BaseAddress, mbi.AllocationBase};
+
+ /* now spawn the thread! */
+ ZwCreateThread(&thread_handle, THREAD_ALL_ACCESS, &oa, proc_handle, &cid, &context, &stack, TRUE);
+
+ /* thread is spawned, but frozen for inspection -- fix up memory protection before unfreezing */
+ ZwQueryInformationThread(NtCurrentThread(), ThreadBasicInformation, &tbi, sizeof tbi, 0);
+ tib = tbi.TebBaseAddress;
+
+ ZwQueryInformationThread(thread_handle, ThreadBasicInformation, &tbi, sizeof tbi, 0);
+ ZwWriteVirtualMemory(process_handle, tbi.TebBaseAddress, &tib->ExceptionList, sizeof(tib->ExceptionList), 0);
+
+ /* ready to go, now request a CSRSS session */
+ request_csrss_session(process_handle, thread_handle, (uint32_t) cid.UniqueProcess, (uint32_t) cid.UniqueThread);
+
+ /* CSRSS session set up or we segfaulted by now, so unfreeze the child... */
+ ZwResumeThread(thread_handle, 0);
+
+ /* release handle refcount now that process is freestanding */
+ ZwClose(thread_handle);
+ ZwClose(process_handle);
+
+ return (int) cid.UniqueProcess;
+}
+
+#endif
+
+#endif
+
+#endif