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); }
UdbgTest1() { NTSTATUS st; HANDLE ExitThread, SpinThread, DebugProcess; CLIENT_ID ExitClientId, SpinClientId; DBGKM_APIMSG m; PDBGKM_CREATE_THREAD CreateThreadArgs; PDBGKM_CREATE_PROCESS CreateProcessArgs; PDBGKM_EXIT_THREAD ExitThreadArgs; PDBGKM_EXIT_PROCESS ExitProcessArgs; ULONG Psp; DbgPrint("UdbgTest1: (1)...\n"); // // Verify that a process can be created with a debug // port. // st = NtCreateProcess( &DebugProcess, PROCESS_ALL_ACCESS, NULL, NtCurrentProcess(), FALSE, NULL, DebugPort, NULL ); ASSERT(NT_SUCCESS(st)); st = RtlCreateUserThread( DebugProcess, NULL, TRUE, 0L, 0L, 0L, ThreadThatExits, (PVOID) STATUS_ABANDONED, &ExitThread, &ExitClientId ); ASSERT(NT_SUCCESS(st)); st = RtlCreateUserThread( DebugProcess, NULL, TRUE, 0L, 0L, 0L, ThreadThatSpins, NULL, &SpinThread, &SpinClientId ); ASSERT(NT_SUCCESS(st)); DbgPrint("UdbgTest1: (2)...\n"); // // Verify that CreateProcess Messages Arrive, and that // they are correct // st = NtResumeThread(SpinThread,NULL); ASSERT(NT_SUCCESS(st)); st = NtReplyWaitReceivePort( DebugPort, NULL, NULL, (PPORT_MESSAGE)&m ); ASSERT(NT_SUCCESS(st)); ASSERT(m.ApiNumber == DbgKmCreateProcessApi); CreateThreadArgs = &m.u.CreateProcess.InitialThread; CreateProcessArgs = &m.u.CreateProcess; ASSERT( CreateThreadArgs->SubSystemKey == 0 && CreateThreadArgs->StartAddress == (PVOID)ThreadThatSpins ); ASSERT( CreateProcessArgs->SubSystemKey == 0); DbgPrint("UdbgTest1: (3)...\n"); // // Verify that other threads in the process are properly suspended // st = NtSuspendThread(ExitThread,&Psp); ASSERT(NT_SUCCESS(st) && Psp == 2); st = NtResumeThread(ExitThread,&Psp); ASSERT(NT_SUCCESS(st) && Psp == 3); st = NtReplyPort(DebugPort,(PPORT_MESSAGE)&m); ASSERT(NT_SUCCESS(st)); DbgPrint("UdbgTest1: (4)...\n"); // // Verify that CreateThread Messages Arrive, and that // they are correct // st = NtResumeThread(ExitThread,&Psp); ASSERT(NT_SUCCESS(st)); st = NtReplyWaitReceivePort( DebugPort, NULL, NULL, (PPORT_MESSAGE)&m ); ASSERT(NT_SUCCESS(st)); ASSERT(m.ApiNumber == DbgKmCreateThreadApi); CreateThreadArgs = &m.u.CreateThread; ASSERT( CreateThreadArgs->SubSystemKey == 0 && CreateThreadArgs->StartAddress == (PVOID)ThreadThatExits ); st = NtReplyPort(DebugPort,(PPORT_MESSAGE)&m); ASSERT(NT_SUCCESS(st)); DbgPrint("UdbgTest1: (5)...\n"); // // Verify that ExitThread Messages Arrive, and that // they are correct // st = NtReplyWaitReceivePort( DebugPort, NULL, NULL, (PPORT_MESSAGE)&m ); ASSERT(NT_SUCCESS(st)); ASSERT(m.ApiNumber == DbgKmExitThreadApi); ExitThreadArgs = &m.u.ExitThread; ASSERT( ExitThreadArgs->ExitStatus == STATUS_ABANDONED ); st = NtReplyPort(DebugPort,(PPORT_MESSAGE)&m); ASSERT(NT_SUCCESS(st)); st = NtWaitForSingleObject(ExitThread,FALSE,NULL); ASSERT(NT_SUCCESS(st)); DbgPrint("UdbgTest1: (6)...\n"); // // Verify that ExitThread Messages Arrive, and that // they are correct // st = NtTerminateProcess(DebugProcess,STATUS_REPARSE); ASSERT(NT_SUCCESS(st)); st = NtReplyWaitReceivePort( DebugPort, NULL, NULL, (PPORT_MESSAGE)&m ); ASSERT(NT_SUCCESS(st)); ASSERT(m.ApiNumber == DbgKmExitThreadApi); ExitThreadArgs = &m.u.ExitThread; ASSERT( ExitThreadArgs->ExitStatus == STATUS_REPARSE ); st = NtReplyPort(DebugPort,(PPORT_MESSAGE)&m); ASSERT(NT_SUCCESS(st)); DbgPrint("UdbgTest1: (7)...\n"); // // Verify that ExitProcess Messages Arrive, and that // they are correct // st = NtReplyWaitReceivePort( DebugPort, NULL, NULL, (PPORT_MESSAGE)&m ); ASSERT(NT_SUCCESS(st)); ASSERT(m.ApiNumber == DbgKmExitProcessApi); ExitProcessArgs = &m.u.ExitProcess; ASSERT( ExitProcessArgs->ExitStatus == STATUS_REPARSE ); st = NtReplyPort(DebugPort,(PPORT_MESSAGE)&m); ASSERT(NT_SUCCESS(st)); st = NtWaitForSingleObject(ExitThread,FALSE,NULL); ASSERT(NT_SUCCESS(st)); st = NtWaitForSingleObject(DebugProcess,FALSE,NULL); ASSERT(NT_SUCCESS(st)); NtClose(ExitThread); NtClose(SpinThread); NtClose(DebugProcess); DbgPrint("UdbgTest1: END OF TEST ***\n"); }
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; } }
UdbgTest2() { NTSTATUS st; HANDLE ExceptionThread, DebugProcess; DBGKM_APIMSG m; PDBGKM_CREATE_THREAD CreateThreadArgs; PDBGKM_CREATE_PROCESS CreateProcessArgs; PDBGKM_EXIT_THREAD ExitThreadArgs; PDBGKM_EXIT_PROCESS ExitProcessArgs; PDBGKM_EXCEPTION ExceptionArgs; ULONG Psp; DbgPrint("UdbgTest2: (1)...\n"); // // Verify that a process can be created with a debug // port. // st = NtCreateProcess( &DebugProcess, PROCESS_ALL_ACCESS, NULL, NtCurrentProcess(), FALSE, NULL, DebugPort, NULL ); ASSERT(NT_SUCCESS(st)); st = RtlCreateUserThread( DebugProcess, NULL, TRUE, 0L, 0L, 0L, ThreadThatExcepts, (PVOID) STATUS_ABANDONED, &ExceptionThread, NULL ); ASSERT(NT_SUCCESS(st)); DbgPrint("UdbgTest2: (2)...\n"); // // Verify that CreateThread Messages Arrive, and that // they are correct // st = NtResumeThread(ExceptionThread,NULL); ASSERT(NT_SUCCESS(st)); st = NtReplyWaitReceivePort( DebugPort, NULL, NULL, (PPORT_MESSAGE)&m ); ASSERT(NT_SUCCESS(st)); ASSERT(m.ApiNumber == DbgKmCreateProcessApi); CreateThreadArgs = &m.u.CreateProcess.InitialThread; CreateProcessArgs = &m.u.CreateProcess; ASSERT( CreateThreadArgs->SubSystemKey == 0 && CreateThreadArgs->StartAddress == (PVOID)ThreadThatExcepts ); ASSERT( CreateProcessArgs->SubSystemKey == 0); st = NtReplyPort(DebugPort,(PPORT_MESSAGE)&m); ASSERT(NT_SUCCESS(st)); DbgPrint("UdbgTest2: (3)...\n"); // // Verify that First Chance Exception Messages Arrive, and that // they are correct // st = NtReplyWaitReceivePort( DebugPort, NULL, NULL, (PPORT_MESSAGE)&m ); ASSERT(NT_SUCCESS(st)); ASSERT(m.ApiNumber == DbgKmExceptionApi); ExceptionArgs = &m.u.Exception; ASSERT( ExceptionArgs->FirstChance == TRUE ); m.ReturnedStatus = DBG_EXCEPTION_NOT_HANDLED; st = NtReplyPort(DebugPort,(PPORT_MESSAGE)&m); ASSERT(NT_SUCCESS(st)); DbgPrint("UdbgTest2: (4)...\n"); // // Verify that First Chance Exception Messages Arrive, and that // they are correct // st = NtReplyWaitReceivePort( DebugPort, NULL, NULL, (PPORT_MESSAGE)&m ); ASSERT(NT_SUCCESS(st)); ASSERT(m.ApiNumber == DbgKmExceptionApi); ExceptionArgs = &m.u.Exception; ASSERT( ExceptionArgs->FirstChance == FALSE ); m.ReturnedStatus = DBG_EXCEPTION_HANDLED; skip4: st = NtTerminateProcess(DebugProcess,STATUS_REPARSE); ASSERT(NT_SUCCESS(st)); st = NtReplyPort(DebugPort,(PPORT_MESSAGE)&m); ASSERT(NT_SUCCESS(st)); st = NtReplyWaitReceivePort( DebugPort, NULL, NULL, (PPORT_MESSAGE)&m ); ASSERT(NT_SUCCESS(st)); ASSERT(m.ApiNumber == DbgKmExitThreadApi); ExitThreadArgs = &m.u.ExitThread; ASSERT( ExitThreadArgs->ExitStatus == STATUS_REPARSE ); st = NtReplyPort(DebugPort,(PPORT_MESSAGE)&m); ASSERT(NT_SUCCESS(st)); DbgPrint("UdbgTest2: (5)...\n"); // // Verify that ExitProcess Messages Arrive, and that // they are correct // st = NtReplyWaitReceivePort( DebugPort, NULL, NULL, (PPORT_MESSAGE)&m ); ASSERT(NT_SUCCESS(st)); ASSERT(m.ApiNumber == DbgKmExitProcessApi); ExitProcessArgs = &m.u.ExitProcess; ASSERT( ExitProcessArgs->ExitStatus == STATUS_REPARSE ); st = NtReplyPort(DebugPort,(PPORT_MESSAGE)&m); ASSERT(NT_SUCCESS(st)); st = NtWaitForSingleObject(ExceptionThread,FALSE,NULL); ASSERT(NT_SUCCESS(st)); st = NtWaitForSingleObject(DebugProcess,FALSE,NULL); ASSERT(NT_SUCCESS(st)); NtClose(ExceptionThread); NtClose(DebugProcess); DbgPrint("UdbgTest2: END OF TEST ***\n"); }