summaryrefslogtreecommitdiff
path: root/packages/w32/fork.go
diff options
context:
space:
mode:
Diffstat (limited to 'packages/w32/fork.go')
-rw-r--r--packages/w32/fork.go174
1 files changed, 174 insertions, 0 deletions
diff --git a/packages/w32/fork.go b/packages/w32/fork.go
new file mode 100644
index 0000000..b5543b9
--- /dev/null
+++ b/packages/w32/fork.go
@@ -0,0 +1,174 @@
+// Copyright 2010-2012 The W32 Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package w32
+
+// #include <stdlib.h>
+//import (
+// "C"
+//)
+
+// Based on C code found here https://gist.github.com/juntalis/4366916
+// Original code license:
+/*
+ * 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.
+ */
+
+import (
+ "fmt"
+ "syscall"
+ "unsafe"
+)
+
+var (
+ ntdll = syscall.NewLazyDLL("ntdll.dll")
+
+ procRtlCloneUserProcess = ntdll.NewProc("RtlCloneUserProcess")
+ procAllocConsole = modkernel32.NewProc("AllocConsole")
+ procOpenProcess = modkernel32.NewProc("OpenProcess")
+ procOpenThread = modkernel32.NewProc("OpenThread")
+ procResumeThread = modkernel32.NewProc("ResumeThread")
+)
+
+func OpenProcess(desiredAccess int, inheritHandle bool, processId uintptr) (h HANDLE, e error) {
+ inherit := uintptr(0)
+ if inheritHandle {
+ inherit = 1
+ }
+
+ ret, _, lastErr := procOpenProcess.Call(
+ uintptr(desiredAccess),
+ inherit,
+ uintptr(processId),
+ )
+
+ if ret == 0 {
+ e = lastErr
+ }
+
+ h = HANDLE(ret)
+ return
+}
+
+func OpenThread(desiredAccess int, inheritHandle bool, threadId uintptr) (h HANDLE, e error) {
+ inherit := uintptr(0)
+ if inheritHandle {
+ inherit = 1
+ }
+
+ ret, _, lastErr := procOpenThread.Call(
+ uintptr(desiredAccess),
+ inherit,
+ uintptr(threadId),
+ )
+
+ if ret == 0 {
+ e = lastErr
+ }
+
+ h = HANDLE(ret)
+ return
+}
+
+// DWORD WINAPI ResumeThread(
+// _In_ HANDLE hThread
+// );
+func ResumeThread(ht HANDLE) (e error) {
+
+ ret, _, lastErr := procResumeThread.Call(
+ uintptr(ht),
+ )
+ if ret == ^uintptr(0) { // -1
+ e = lastErr
+ }
+ return
+}
+
+// BOOL WINAPI AllocConsole(void);
+func AllocConsole() (e error) {
+ ret, _, lastErr := procAllocConsole.Call()
+ if ret != ERROR_SUCCESS {
+ e = lastErr
+ }
+ return
+}
+
+// NTSYSAPI
+// NTSTATUS
+// NTAPI RtlCloneUserProcess (
+// _In_ ULONG ProcessFlags,
+// _In_opt_ PSECURITY_DESCRIPTOR ProcessSecurityDescriptor,
+// _In_opt_ PSECURITY_DESCRIPTOR ThreadSecurityDescriptor,
+// _In_opt_ HANDLE DebugPort,
+// _Out_ PRTL_USER_PROCESS_INFORMATION ProcessInformation
+// )
+
+func RtlCloneUserProcess(
+ ProcessFlags uint32,
+ ProcessSecurityDescriptor, ThreadSecurityDescriptor *SECURITY_DESCRIPTOR, // in advapi32_typedef.go
+ DebugPort HANDLE,
+ ProcessInformation *RTL_USER_PROCESS_INFORMATION,
+) (status uintptr) {
+
+ status, _, _ = procRtlCloneUserProcess.Call(
+ uintptr(ProcessFlags),
+ uintptr(unsafe.Pointer(ProcessSecurityDescriptor)),
+ uintptr(unsafe.Pointer(ThreadSecurityDescriptor)),
+ uintptr(DebugPort),
+ uintptr(unsafe.Pointer(ProcessInformation)),
+ )
+
+ return
+}
+
+// Fork creates a clone of the current process using the undocumented
+// RtlCloneUserProcess call in ntdll, similar to unix fork(). The
+// return value in the parent is the child PID. In the child it is 0.
+func Fork() (pid uintptr, e error) {
+
+ pi := &RTL_USER_PROCESS_INFORMATION{}
+
+ ret := RtlCloneUserProcess(
+ RTL_CLONE_PROCESS_FLAGS_CREATE_SUSPENDED|RTL_CLONE_PROCESS_FLAGS_INHERIT_HANDLES,
+ nil,
+ nil,
+ HANDLE(0),
+ pi,
+ )
+
+ switch ret {
+ case RTL_CLONE_PARENT:
+ pid = pi.ClientId.UniqueProcess
+ ht, err := OpenThread(THREAD_ALL_ACCESS, false, pi.ClientId.UniqueThread)
+ if err != nil {
+ e = fmt.Errorf("OpenThread: %s", err)
+ }
+ err = ResumeThread(ht)
+ if err != nil {
+ e = fmt.Errorf("ResumeThread: %s", err)
+ }
+ CloseHandle(ht)
+ case RTL_CLONE_CHILD:
+ pid = 0
+ err := AllocConsole()
+ if err != nil {
+ e = fmt.Errorf("AllocConsole: %s", err)
+ }
+ default:
+ e = fmt.Errorf("0x%x", ret)
+ }
+ return
+}