pid_t fork(void) { NTSTATUS nErrCode; CONTEXT ctxThreadContext; HANDLE hProcess; HANDLE hThread; INITIAL_TEB itInitialTeb; CLIENT_ID ciClientId; MEMORY_BASIC_INFORMATION mbiStackInfo; THREAD_BASIC_INFORMATION tbiThreadInfo; struct __tagcsrmsg{ PORT_MESSAGE PortMessage; struct CSRSS_MESSAGE CsrssMessage; PROCESS_INFORMATION ProcessInformation; CLIENT_ID Debugger; ULONG CreationFlags; ULONG VdmInfo[2]; } csrmsg; /* STEP 1: Duplicate current process */ nErrCode = NtCreateProcess ( &hProcess, PROCESS_ALL_ACCESS, NULL, NtCurrentProcess(), TRUE, 0, 0, 0 ); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("NtCreateProcess() failed with status 0x%08X\n", nErrCode); goto fail; } /* STEP 2: Duplicate current thread */ /* 2.1: duplicate registers */ ctxThreadContext.ContextFlags = CONTEXT_FULL | CONTEXT_DEBUG_REGISTERS | CONTEXT_FLOATING_POINT; /* get the current thread's registers */ nErrCode = NtGetContextThread(NtCurrentThread(), &ctxThreadContext); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("NtGetContextThread() failed with status 0x%08X\n", nErrCode); goto cleanup_and_fail; } /* redirect the child process to the child_branch label (see 4.3 below) */ ctxThreadContext.Eip = (ULONG)&&child_branch; /* 2.2: duplicate stack */ /* get stack base and size */ nErrCode = NtQueryVirtualMemory ( NtCurrentProcess(), (PVOID)ctxThreadContext.Esp, MemoryBasicInformation, &mbiStackInfo, sizeof(mbiStackInfo), 0 ); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("NtQueryVirtualMemory() failed with status 0x%08X\n", nErrCode); goto cleanup_and_fail; } itInitialTeb.StackCommit = 0; itInitialTeb.StackReserve = 0; itInitialTeb.StackBase = (PVOID)((ULONG)(mbiStackInfo.BaseAddress) + mbiStackInfo.RegionSize); itInitialTeb.StackLimit = mbiStackInfo.BaseAddress; itInitialTeb.StackAllocate = mbiStackInfo.AllocationBase; /* 2.3: create duplicate thread */ nErrCode = NtCreateThread ( &hThread, THREAD_ALL_ACCESS, NULL, hProcess, (CLIENT_ID *)&ciClientId, &ctxThreadContext, &itInitialTeb, TRUE ); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("NtCreateThread() failed with status 0x%08X\n", nErrCode); goto cleanup_and_fail; } /* 2.4: duplicate the TEB */ /* store the client id in the child thread's stack (see 4.3b) */ nErrCode = NtWriteVirtualMemory ( hProcess, &ciClientId, &ciClientId, sizeof(ciClientId), 0 ); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode); goto cleanup_and_fail; } /* get the child thread's TEB base */ nErrCode = NtQueryInformationThread ( hThread, ThreadBasicInformation, &tbiThreadInfo, sizeof(tbiThreadInfo), 0 ); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("NtQueryInformationThread() failed with status 0x%08X\n", nErrCode); goto cleanup_and_fail; } /* copy the TEB */ nErrCode = NtWriteVirtualMemory ( hProcess, tbiThreadInfo.TebBaseAddress, NtCurrentTeb(), sizeof(TEB), 0 ); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("NtWriteVirtualMemory() failed with status 0x%08X\n", nErrCode); goto cleanup_and_fail; } /* STEP 3: Call Win32 subsystem */ memset(&csrmsg, 0, sizeof(csrmsg)); csrmsg.ProcessInformation.hProcess = hProcess; csrmsg.ProcessInformation.hThread = hThread; csrmsg.ProcessInformation.dwProcessId = (DWORD)ciClientId.UniqueProcess; csrmsg.ProcessInformation.dwThreadId = (DWORD)ciClientId.UniqueThread; nErrCode = CsrClientCallServer(&csrmsg, 0, 0x10000, 0x24); /* failure */ if(!NT_SUCCESS(nErrCode)) { ERR("CsrClientCallServer() failed with status 0x%08X\n", nErrCode); goto cleanup_and_fail; } /* STEP 4: Finalization */ /* 4.1: resume thread */ nErrCode = NtResumeThread(hThread, 0); /* 4.2: close superfluous handles */ NtClose(hProcess); NtClose(hThread); /* 4.3: (parent) return the child process id */ return ((pid_t)(ciClientId.UniqueProcess)); /* 4.3b: (child) cleanup and return 0 */ child_branch: /* restore the thread and process id in the TEB */ memcpy(&NtCurrentTeb()->Cid, &ciClientId, sizeof(ciClientId)); /* return 0 */ return (0); cleanup_and_fail: NtTerminateProcess(hProcess, nErrCode); fail: errno = __status_to_errno(nErrCode); return (-1); }
ULONG fork () { LONG i; PULONG Foo; NTSTATUS status; HANDLE CurrentProcessHandle; HANDLE ProcessHandle; CONTEXT ThreadContext; CLIENT_ID Cid1; HANDLE Thread1; LARGE_INTEGER DelayTime; INITIAL_TEB Teb; CurrentProcessHandle = NtCurrentProcess(); DbgPrint("creating new process\n"); status = NtCreateProcess( &ProcessHandle, PROCESS_ALL_ACCESS, //DesiredAccess, NULL, //ObjectAttributes, CurrentProcessHandle, //ParentProcess TRUE, //InheritObjectTable, NULL, //SectionHandle NULL, //DebugPort OPTIONAL, NULL //ExceptionPort OPTIONAL ); DbgPrint("status from create process %lx\n",status); if (!NT_SUCCESS(status)) { return 0; } ThreadContext.ContextFlags = CONTEXT_FULL; status = NtGetContextThread (NtCurrentThread(), &ThreadContext); DbgPrint("status from get context %lx\n",status); if (status == 0) { #ifdef i386 ThreadContext.Eax = 0x7777; ThreadContext.Esp -= 0x18; Foo = (PULONG)ThreadContext.Esp; DbgPrint("stack value is %lx\n",*Foo); #endif #ifdef MIPS ThreadContext.IntV0 = 0x7777; #endif status = NtCreateThread( &Thread1, THREAD_ALL_ACCESS, NULL, ProcessHandle, &Cid1, &ThreadContext, &Teb, FALSE ); // DelayTime.HighPart = -1; // DelayTime.LowPart = 0; // NtDelayExecution (FALSE, &DelayTime); return 0; } else { ProcessNumber += ProcessNumber; return ProcessNumber; } }
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg) { HANDLE hThread; OBJECT_ATTRIBUTES oaThreadAttrs; CLIENT_ID ciId; CONTEXT cxThreadContext; INITIAL_TEB itInitialTeb; BOOLEAN fSuspended; ULONG nOldPageProtection; NTSTATUS nErrCode; /* initialize generic object attributes */ oaThreadAttrs.Length = sizeof(OBJECT_ATTRIBUTES); oaThreadAttrs.RootDirectory = NULL; oaThreadAttrs.ObjectName = NULL; oaThreadAttrs.Attributes = 0; oaThreadAttrs.SecurityDescriptor = NULL; oaThreadAttrs.SecurityQualityOfService = NULL; /* initialize thread attributes */ fSuspended = FALSE; /* FIXME? really needed? can we hardcode this to FALSE? */ /* stack attributes */ FIXME("stack size defaulted to 0x100000 - thread attributes ignored"); /* stack reserve size */ itInitialTeb.StackReserve = 0x100000; /* stack commit size */ itInitialTeb.StackCommit = itInitialTeb.StackReserve - PAGE_SIZE; /* guard page */ itInitialTeb.StackCommit += PAGE_SIZE; /* reserve stack */ itInitialTeb.StackAllocate = NULL; nErrCode = NtAllocateVirtualMemory ( NtCurrentProcess(), &itInitialTeb.StackAllocate, 0, &itInitialTeb.StackReserve, MEM_RESERVE, PAGE_READWRITE ); if(!NT_SUCCESS(nErrCode)) { return (__status_to_errno(nErrCode)); /* FIXME? TODO? pthread specific error codes? */ } itInitialTeb.StackBase = (PVOID)((ULONG)itInitialTeb.StackAllocate + itInitialTeb.StackReserve); itInitialTeb.StackLimit = (PVOID)((ULONG)itInitialTeb.StackBase - itInitialTeb.StackCommit); /* commit stack */ nErrCode = NtAllocateVirtualMemory ( NtCurrentProcess(), &itInitialTeb.StackLimit, 0, &itInitialTeb.StackCommit, MEM_COMMIT, PAGE_READWRITE ); if(!NT_SUCCESS(nErrCode)) { NtFreeVirtualMemory ( NtCurrentProcess(), itInitialTeb.StackAllocate, &itInitialTeb.StackReserve, MEM_RELEASE ); return (__status_to_errno(nErrCode)); } /* protect guard page */ nErrCode = NtProtectVirtualMemory ( NtCurrentProcess(), itInitialTeb.StackLimit, PAGE_SIZE, PAGE_GUARD | PAGE_READWRITE, &nOldPageProtection ); if(!NT_SUCCESS(nErrCode)) { NtFreeVirtualMemory ( NtCurrentProcess(), itInitialTeb.StackAllocate, &itInitialTeb.StackReserve, MEM_RELEASE ); return (__status_to_errno(nErrCode)); } /* initialize thread registers */ //#ifdef __i386__ memset(&cxThreadContext, 0, sizeof(CONTEXT)); cxThreadContext.Eip = (LONG)__threadentry; cxThreadContext.SegGs = USER_DS; cxThreadContext.SegFs = TEB_SELECTOR; cxThreadContext.SegEs = USER_DS; cxThreadContext.SegDs = USER_DS; cxThreadContext.SegCs = USER_CS; cxThreadContext.SegSs = USER_DS; cxThreadContext.Esp = (ULONG)itInitialTeb.StackBase - 12; cxThreadContext.EFlags = (1<<1) + (1<<9); /* initialize call stack */ *((PULONG)((ULONG)itInitialTeb.StackBase - 4)) = (ULONG)arg; /* thread argument */ *((PULONG)((ULONG)itInitialTeb.StackBase - 8)) = (ULONG)start_routine; /* thread start routine */ *((PULONG)((ULONG)itInitialTeb.StackBase - 12)) = 0xDEADBEEF; /* "shouldn't see me" */ //#else //#error Unsupported architecture //#endif INFO("about to create new thread - start routine at %#x, argument %#x", start_routine, arg); /* create thread */ nErrCode = NtCreateThread ( &hThread, THREAD_ALL_ACCESS, &oaThreadAttrs, NtCurrentProcess(), &ciId, &cxThreadContext, &itInitialTeb, fSuspended ); if(!NT_SUCCESS(nErrCode)) { NtFreeVirtualMemory ( NtCurrentProcess(), itInitialTeb.StackAllocate, &itInitialTeb.StackReserve, MEM_RELEASE ); return (__status_to_errno(nErrCode)); } /* FIXME? should we return the thread handle or the thread id? */ if(thread != 0) *thread = (pthread_t)&ciId.UniqueThread; /* for the moment, we return the id */ return (0); }