/*++ Copyright (c) 1992 Microsoft Corporation Module Name: process.c Abstract: This module maintains state about each process/thread created by the application setup/install program. Author: Steve Wood (stevewo) 09-Aug-1994 Revision History: --*/ #include "instaler.h" BOOLEAN AddProcess( LPDEBUG_EVENT DebugEvent, PPROCESS_INFO *ReturnedProcess ) { NTSTATUS Status; PPROCESS_INFO Process; RTL_USER_PROCESS_PARAMETERS ProcessParameters; PEB Peb; PWSTR FreeBuffer, s; Process = AllocMem( sizeof( *Process ) ); if (Process == NULL) { return FALSE; } Process->Id = DebugEvent->dwProcessId; Process->Handle = DebugEvent->u.CreateProcessInfo.hProcess; InitializeListHead( &Process->ThreadListHead ); InitializeListHead( &Process->BreakpointListHead ); InitializeListHead( &Process->OpenHandleListHead ); InsertTailList( &ProcessListHead, &Process->Entry ); *ReturnedProcess = Process; Status = NtQueryInformationProcess( Process->Handle, ProcessBasicInformation, &Process->ProcessInformation, sizeof( Process->ProcessInformation ), NULL ); FreeBuffer = NULL; if (ReadMemory( Process, Process->ProcessInformation.PebBaseAddress, &Peb, sizeof( Peb ), "PEB" ) && Peb.ProcessParameters != NULL && ReadMemory( Process, Peb.ProcessParameters, &ProcessParameters, sizeof( ProcessParameters ), "ProcessParameters" ) && ProcessParameters.ImagePathName.Length != 0 && (FreeBuffer = AllocMem( ProcessParameters.ImagePathName.Length + sizeof( UNICODE_NULL ) )) != NULL && ReadMemory( Process, ProcessParameters.Flags & RTL_USER_PROC_PARAMS_NORMALIZED ? ProcessParameters.ImagePathName.Buffer : (PWSTR)((ULONG)(ProcessParameters.ImagePathName.Buffer) + (PCHAR)(Peb.ProcessParameters)), FreeBuffer, ProcessParameters.ImagePathName.Length, "Image File Name" ) ) { s = (PWSTR)((PCHAR)FreeBuffer + ProcessParameters.ImagePathName.Length); while (s > FreeBuffer && s[ -1 ] != OBJ_NAME_PATH_SEPARATOR) { s--; } wcsncpy( Process->ImageFileName, s, (sizeof( Process->ImageFileName ) - sizeof( UNICODE_NULL )) / sizeof( WCHAR ) ); } FreeMem( &FreeBuffer ); return TRUE; } BOOLEAN DeleteProcess( PPROCESS_INFO Process ) { PLIST_ENTRY Next, Head; PTHREAD_INFO Thread; PBREAKPOINT_INFO Breakpoint; POPENHANDLE_INFO p; RemoveEntryList( &Process->Entry ); Head = &Process->ThreadListHead; Next = Head->Flink; while (Next != Head) { Thread = CONTAINING_RECORD( Next, THREAD_INFO, Entry ); Next = Next->Flink; DeleteThread( Process, Thread ); } Head = &Process->BreakpointListHead; Next = Head->Flink; while (Next != Head) { Breakpoint = CONTAINING_RECORD( Next, BREAKPOINT_INFO, Entry ); Next = Next->Flink; DestroyBreakpoint( Breakpoint->Address, Process, NULL ); } Head = &Process->OpenHandleListHead; Next = Head->Flink; while (Next != Head) { p = CONTAINING_RECORD( Next, OPENHANDLE_INFO, Entry ); Next = Next->Flink; DeleteOpenHandle( Process, p->Handle, p->Type ); } FreeMem( &Process ); return TRUE; } BOOLEAN AddThread( LPDEBUG_EVENT DebugEvent, PPROCESS_INFO Process, PTHREAD_INFO *ReturnedThread ) { PTHREAD_INFO Thread; Thread = AllocMem( sizeof( *Thread ) ); if (Thread == NULL) { return FALSE; } Thread->Id = DebugEvent->dwThreadId; if (DebugEvent->dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) { Thread->Handle = DebugEvent->u.CreateProcessInfo.hThread; Thread->StartAddress = DebugEvent->u.CreateProcessInfo.lpStartAddress; } else { Thread->Handle = DebugEvent->u.CreateThread.hThread; Thread->StartAddress = DebugEvent->u.CreateThread.lpStartAddress; } Thread->SingleStepExpected = FALSE; InitializeListHead( &Thread->BreakpointListHead ); InsertTailList( &Process->ThreadListHead, &Thread->Entry ); *ReturnedThread = Thread; return TRUE; } BOOLEAN DeleteThread( PPROCESS_INFO Process, PTHREAD_INFO Thread ) { PLIST_ENTRY Next, Head; PBREAKPOINT_INFO Breakpoint; RemoveEntryList( &Thread->Entry ); Head = &Thread->BreakpointListHead; Next = Head->Flink; while (Next != Head) { Breakpoint = CONTAINING_RECORD( Next, BREAKPOINT_INFO, Entry ); Next = Next->Flink; DestroyBreakpoint( Breakpoint->Address, Process, Thread ); } FreeMem( &Thread ); return TRUE; } PPROCESS_INFO FindProcessById( ULONG Id ) { PLIST_ENTRY Next, Head; PPROCESS_INFO Process; Head = &ProcessListHead; Next = Head->Flink; while (Next != Head) { Process = CONTAINING_RECORD( Next, PROCESS_INFO, Entry ); if (Process->Id == Id) { return Process; } Next = Next->Flink; } return NULL; } BOOLEAN FindProcessAndThreadForEvent( LPDEBUG_EVENT DebugEvent, PPROCESS_INFO *ReturnedProcess, PTHREAD_INFO *ReturnedThread ) { PLIST_ENTRY Next, Head; PPROCESS_INFO Process; PTHREAD_INFO Thread; Head = &ProcessListHead; Next = Head->Flink; Process = NULL; Thread = NULL; while (Next != Head) { Process = CONTAINING_RECORD( Next, PROCESS_INFO, Entry ); if (Process->Id == DebugEvent->dwProcessId) { Head = &Process->ThreadListHead; Next = Head->Flink; while (Next != Head) { Thread = CONTAINING_RECORD( Next, THREAD_INFO, Entry ); if (Thread->Id == DebugEvent->dwThreadId) { break; } Thread = NULL; Next = Next->Flink; } break; } Process = NULL; Next = Next->Flink; } *ReturnedProcess = Process; *ReturnedThread = Thread; if (DebugEvent->dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) { if (Process != NULL) { DeclareError( INSTALER_DUPLICATE_PROCESS_ID, 0, DebugEvent->dwProcessId ); return FALSE; } } else if (DebugEvent->dwDebugEventCode == CREATE_THREAD_DEBUG_EVENT) { if (Thread != NULL) { DeclareError( INSTALER_DUPLICATE_THREAD_ID, 0, DebugEvent->dwThreadId, DebugEvent->dwProcessId ); return FALSE; } if (Process == NULL) { DeclareError( INSTALER_MISSING_PROCESS_ID, 0, DebugEvent->dwProcessId ); return FALSE; } } else if (Process == NULL) { DeclareError( INSTALER_MISSING_PROCESS_ID, 0, DebugEvent->dwProcessId ); return FALSE; } else if (Thread == NULL) { DeclareError( INSTALER_MISSING_THREAD_ID, 0, DebugEvent->dwThreadId, DebugEvent->dwProcessId ); return FALSE; } return TRUE; } VOID SuspendAllButThisThread( PPROCESS_INFO Process, PTHREAD_INFO Thread ) { PTHREAD_INFO Thread1; PLIST_ENTRY Next, Head; if (Thread != NULL) { Head = &Process->ThreadListHead; Next = Head->Flink; while (Next != Head) { Thread1 = CONTAINING_RECORD( Next, THREAD_INFO, Entry ); if (Thread1 != Thread) { NtSuspendThread( Thread1->Handle, NULL ); } Next = Next->Flink; } } return; } VOID ResumeAllButThisThread( PPROCESS_INFO Process, PTHREAD_INFO Thread ) { PTHREAD_INFO Thread1; PLIST_ENTRY Next, Head; if (Thread != NULL) { Head = &Process->ThreadListHead; Next = Head->Flink; while (Next != Head) { Thread1 = CONTAINING_RECORD( Next, THREAD_INFO, Entry ); if (Thread1 != Thread) { NtResumeThread( Thread1->Handle, NULL ); } Next = Next->Flink; } } return; } PBREAKPOINT_INFO FindBreakpoint( LPVOID Address, PPROCESS_INFO Process, PTHREAD_INFO Thread ) { PBREAKPOINT_INFO Breakpoint; PLIST_ENTRY Next, Head; if (Thread != NULL) { Head = &Thread->BreakpointListHead; Next = Head->Flink; while (Next != Head) { Breakpoint = CONTAINING_RECORD( Next, BREAKPOINT_INFO, Entry ); if (Breakpoint->Address == Address) { return Breakpoint; } Next = Next->Flink; } } Head = &Process->BreakpointListHead; Next = Head->Flink; while (Next != Head) { Breakpoint = CONTAINING_RECORD( Next, BREAKPOINT_INFO, Entry ); if (Breakpoint->Address == Address) { return Breakpoint; } Next = Next->Flink; } return NULL; } BOOLEAN CreateBreakpoint( LPVOID Address, PPROCESS_INFO Process, PTHREAD_INFO Thread, UCHAR ApiIndex, PAPI_SAVED_PARAMETERS SavedParameters, PBREAKPOINT_INFO *ReturnedBreakpoint ) { PBREAKPOINT_INFO Breakpoint; Breakpoint = FindBreakpoint( Address, Process, Thread ); if (ARGUMENT_PRESENT( ReturnedBreakpoint )) { *ReturnedBreakpoint = Breakpoint; } if (Breakpoint != NULL) { return (Breakpoint->ApiIndex == ApiIndex); } Breakpoint = AllocMem( sizeof( *Breakpoint ) ); if (Breakpoint == NULL) { return FALSE; } Breakpoint->Address = Address; Breakpoint->ApiIndex = ApiIndex; if (ARGUMENT_PRESENT( SavedParameters )) { Breakpoint->SavedParameters = *SavedParameters; Breakpoint->SavedParametersValid = TRUE; } else { Breakpoint->SavedParametersValid = FALSE; } if (Thread != NULL) { InsertTailList( &Thread->BreakpointListHead, &Breakpoint->Entry ); } else { InsertTailList( &Process->BreakpointListHead, &Breakpoint->Entry ); } InstallBreakpoint( Process, Breakpoint ); if (ARGUMENT_PRESENT( ReturnedBreakpoint )) { *ReturnedBreakpoint = Breakpoint; } return TRUE; } BOOLEAN DestroyBreakpoint( LPVOID Address, PPROCESS_INFO Process, PTHREAD_INFO Thread ) { PBREAKPOINT_INFO Breakpoint; Breakpoint = FindBreakpoint( Address, Process, Thread ); if (Breakpoint == NULL) { return FALSE; } RemoveBreakpoint( Process, Breakpoint ); RemoveEntryList( &Breakpoint->Entry ); FreeMem( &Breakpoint ); return TRUE; } BOOLEAN HandleThreadsForSingleStep( PPROCESS_INFO Process, PTHREAD_INFO ThreadToSingleStep, BOOLEAN SuspendThreads ) { PLIST_ENTRY Next, Head; PTHREAD_INFO Thread; Head = &Process->ThreadListHead; Next = Head->Flink; while (Next != Head) { Thread = CONTAINING_RECORD( Next, THREAD_INFO, Entry ); if (Thread != ThreadToSingleStep) { if (SuspendThreads) { if (Thread->BreakpointToStepOver == NULL) { SuspendThread( Thread->Handle ); } } else { ResumeThread( Thread->Handle ); } break; } Next = Next->Flink; } return TRUE; } BOOLEAN ReadMemory( PPROCESS_INFO Process, PVOID Address, PVOID DataRead, ULONG BytesToRead, PCHAR Reason ) { ULONG BytesRead; if (!ReadProcessMemory( Process->Handle, Address, DataRead, BytesToRead, &BytesRead ) || BytesRead != BytesToRead ) { DbgEvent( MEMORYERROR, ( "Read memory from %x for %x bytes failed (%u) - '%s'\n", Address, BytesToRead, GetLastError(), Reason ) ); return FALSE; } else { return TRUE; } } BOOLEAN WriteMemory( PPROCESS_INFO Process, PVOID Address, PVOID DataToWrite, ULONG BytesToWrite, PCHAR Reason ) { ULONG BytesWritten; ULONG OldProtection; BOOLEAN Result; if (WriteProcessMemory( Process->Handle, Address, DataToWrite, BytesToWrite, &BytesWritten ) && BytesWritten == BytesToWrite ) { return TRUE; } Result = FALSE; if (GetLastError() == ERROR_NOACCESS && VirtualProtectEx( Process->Handle, Address, BytesToWrite, PAGE_READWRITE, &OldProtection ) ) { if (WriteProcessMemory( Process->Handle, Address, DataToWrite, BytesToWrite, &BytesWritten ) && BytesWritten == BytesToWrite ) { Result = TRUE; } VirtualProtectEx( Process->Handle, Address, BytesToWrite, OldProtection, &OldProtection ); if (Result) { return TRUE; } } DbgEvent( MEMORYERROR, ( "Write memory to %x for %x bytes failed (%u) - '%s'\n", Address, BytesToWrite, GetLastError(), Reason ) ); return FALSE; } PVOID AllocMem( ULONG Size ) { PVOID p; p = HeapAlloc( AppHeap, HEAP_ZERO_MEMORY, Size ); if (p == NULL) { DbgEvent( INTERNALERROR, ( "HeapAlloc( %0x8 ) failed\n", Size ) ); } return p; } VOID FreeMem( PVOID *p ) { if (*p != NULL) { HeapFree( AppHeap, 0, *p ); *p = NULL; } return; }