bool DecoderWindows::decodeCreateThread( DEBUG_EVENT &e, Event::ptr &newEvt, int_process* &proc, std::vector<Event::ptr> &events ) { pthrd_printf("Decoded CreateThread event, PID: %d, TID: %d\n", e.dwProcessId, e.dwThreadId); proc = ProcPool()->findProcByPid(e.dwProcessId); assert(proc); // FIXME once things actually work windows_process* wproc = dynamic_cast<windows_process*>(proc); assert(wproc); if(e.dwDebugEventCode == CREATE_PROCESS_DEBUG_EVENT) { newEvt = WinEventThreadInfo::ptr(new WinEventThreadInfo((Dyninst::LWP)(e.dwThreadId), e.u.CreateProcessInfo.hThread, e.u.CreateProcessInfo.lpStartAddress, e.u.CreateProcessInfo.lpThreadLocalBase)); newEvt->setThread(proc->threadPool()->initialThread()->thread()); wproc->plat_setHandles(e.u.CreateProcessInfo.hProcess, e.u.CreateProcessInfo.hFile, (Dyninst::Address)e.u.CreateProcessInfo.lpBaseOfImage); } else { newEvt = WinEventNewThread::ptr(new WinEventNewThread((Dyninst::LWP)(e.dwThreadId), e.u.CreateThread.hThread, e.u.CreateThread.lpStartAddress, e.u.CreateThread.lpThreadLocalBase)); } newEvt->setProcess(proc->proc()); newEvt->setSyncType(Event::sync_process); events.push_back(newEvt); return true; }
Event::ptr DecoderWindows::decodeBreakpointEvent(DEBUG_EVENT e, int_process* proc, int_thread* thread) { Event::ptr evt; // This handles user-level breakpoints; if we can't associate this with a thread we know about, it's not // something the user installed (or we're in an inconsistent state). Forward to the mutatee. if(!thread) return evt; Dyninst::Address adjusted_addr = (Dyninst::Address)(e.u.Exception.ExceptionRecord.ExceptionAddress); if (rpcMgr()->isRPCTrap(thread, adjusted_addr)) { pthrd_printf("Decoded event to rpc completion on %d/%d at %lx\n", proc->getPid(), thread->getLWP(), adjusted_addr); evt = Event::ptr(new EventRPC(thread->runningRPC()->getWrapperForDecode())); return evt; } sw_breakpoint *ibp = proc->getBreakpoint(adjusted_addr); if (ibp) { pthrd_printf("Decoded breakpoint on %d/%d at %lx\n", proc->getPid(), thread->getLWP(), adjusted_addr); EventBreakpoint::ptr event_bp = EventBreakpoint::ptr(new EventBreakpoint(new int_eventBreakpoint(adjusted_addr, ibp, thread))); evt = event_bp; evt->setThread(thread->thread()); } return evt; }
PCEventHandler::RTSignalResult PCEventHandler::handleRTSignal(EventSignal::const_ptr ev, PCProcess *evProc) const { // Check whether the signal was sent from the RT library by checking variables // in the library -- if we cannot be determine whether this signal came from // the RT library, assume it did not. if( evProc->runtime_lib.size() == 0 ) return NotRTSignal; Address sync_event_breakpoint_addr = evProc->getRTEventBreakpointAddr(); Address sync_event_id_addr = evProc->getRTEventIdAddr(); Address sync_event_arg1_addr = evProc->getRTEventArg1Addr(); int breakpoint = 0; int status = 0; Address arg1 = 0; int zero = 0; // Check that all addresses could be determined if( sync_event_breakpoint_addr == 0 || sync_event_id_addr == 0 || sync_event_arg1_addr == 0 ) { return NotRTSignal; } // First, check breakpoint... if( !evProc->readDataWord((const void *)sync_event_breakpoint_addr, sizeof(int), &breakpoint, false) ) return NotRTSignal; switch(breakpoint) { case NoRTBreakpoint: proccontrol_printf("%s[%d]: signal is not RT library signal\n", FILE__, __LINE__); return NotRTSignal; case NormalRTBreakpoint: case SoftRTBreakpoint: // More work to do break; default: proccontrol_printf("%s[%d]: invalid value for RT library breakpoint variable\n", FILE__, __LINE__); return NotRTSignal; } // Make sure we don't get this event twice.... if( !evProc->writeDataWord((void *)sync_event_breakpoint_addr, sizeof(int), &zero) ) { proccontrol_printf("%s[%d]: failed to reset RT library breakpoint variable\n", FILE__, __LINE__); return NotRTSignal; } // Get the type of the event if( !evProc->readDataWord((const void *)sync_event_id_addr, sizeof(int), &status, false) ) return NotRTSignal; if( status == DSE_undefined ) { proccontrol_printf("%s[%d]: signal is not RT library signal\n", FILE__, __LINE__); return NotRTSignal; } // get runtime library arg1 address if( !evProc->readDataWord((const void *)sync_event_arg1_addr, evProc->getAddressWidth(), &arg1, false) ) { proccontrol_printf("%s[%d]: failed to read RT library arg1 variable\n", FILE__, __LINE__); return NotRTSignal; } if( !isValidRTSignal(ev->getSignal(), (RTBreakpointVal) breakpoint, arg1, status) ) return NotRTSignal; BPatch_process *bproc = BPatch::bpatch->getProcessByPid(evProc->getPid()); if( bproc == NULL ) { proccontrol_printf("%s[%d]: no corresponding BPatch_process for process %d\n", FILE__, __LINE__, evProc->getPid()); return ErrorInDecoding; } // See pcEventHandler.h (SYSCALL HANDLING) for a description of what // is going on here Event::ptr newEvt; switch(status) { case DSE_forkEntry: proccontrol_printf("%s[%d]: decoded forkEntry, arg = %lx\n", FILE__, __LINE__, arg1); if (PCEventMuxer::useBreakpoint(EventType(EventType::Pre, EventType::Fork))) { proccontrol_printf("%s[%d]: reporting fork entry event to BPatch layer\n", FILE__, __LINE__); BPatch::bpatch->registerForkingProcess(evProc->getPid(), NULL); } break; case DSE_forkExit: proccontrol_printf("%s[%d]: decoded forkExit, arg = %lx\n", FILE__, __LINE__, arg1); if (PCEventMuxer::useBreakpoint(EventType(EventType::Post, EventType::Fork))) { proccontrol_printf("%s[%d]: reporting fork exit event to ProcControlAPI\n", FILE__, __LINE__); newEvt = Event::ptr(new EventFork(EventType::Pre, (Dyninst::PID)arg1)); } break; case DSE_execEntry: proccontrol_printf("%s[%d]: decoded execEntry, arg = %lx\n", FILE__, __LINE__, arg1); // For now, just note that the process is going to exec evProc->setExecing(true); break; case DSE_execExit: proccontrol_printf("%s[%d]: decoded execExit, arg = %lx\n", FILE__, __LINE__, arg1); // This is not currently used by Dyninst internals for anything // We rely on ProcControlAPI for this and it should be impossible // to get this via a breakpoint return ErrorInDecoding; case DSE_exitEntry: proccontrol_printf("%s[%d]: decoded exitEntry, arg = %lx\n", FILE__, __LINE__, arg1); /* Entry of exit, used for the callback. We need to trap before the process has actually exited as the callback may want to read from the process */ if (PCEventMuxer::useBreakpoint(EventType(EventType::Pre, EventType::Exit))) { if (PCEventMuxer::useCallback(EventType(EventType::Pre, EventType::Exit))) { proccontrol_printf("%s[%d]: reporting exit entry event to ProcControlAPI\n", FILE__, __LINE__); newEvt = Event::ptr(new EventExit(EventType::Pre, (int)arg1)); } else { proccontrol_printf("%s[%d]: reporting exit entry event to BPatch layer\n", FILE__, __LINE__); evProc->triggerNormalExit((int)arg1); } } break; case DSE_loadLibrary: proccontrol_printf("%s[%d]: decoded loadLibrary (error), arg = %lx\n", FILE__, __LINE__, arg1); // This is no longer used return ErrorInDecoding; case DSE_lwpExit: proccontrol_printf("%s[%d]: decoded lwpExit (error), arg = %lx\n", FILE__, __LINE__, arg1); // This is not currently used on any platform return ErrorInDecoding; case DSE_snippetBreakpoint: proccontrol_printf("%s[%d]: decoded snippetBreak, arg = %lx\n", FILE__, __LINE__, arg1); bproc->setLastSignal(ev->getSignal()); evProc->setDesiredProcessState(PCProcess::ps_stopped); break; case DSE_stopThread: proccontrol_printf("%s[%d]: decoded stopThread, arg = %lx\n", FILE__, __LINE__, arg1); bproc->setLastSignal(ev->getSignal()); if( !handleStopThread(evProc, arg1) ) { proccontrol_printf("%s[%d]: failed to handle stopped thread event\n", FILE__, __LINE__); return ErrorInDecoding; } break; case DSE_dynFuncCall: proccontrol_printf("%s[%d]: decoded dynamic callsite event, arg = %lx\n", FILE__, __LINE__, arg1); if( !handleDynFuncCall(evProc, bproc, arg1) ) { proccontrol_printf("%s[%d]: failed to handle dynamic callsite event\n", FILE__, __LINE__); return ErrorInDecoding; } break; case DSE_userMessage: proccontrol_printf("%s[%d]: decoded user message event, arg = %lx\n", FILE__, __LINE__, arg1); if( !handleUserMessage(evProc, bproc, arg1) ) { proccontrol_printf("%s[%d]: failed to handle user message event\n", FILE__, __LINE__); return ErrorInDecoding; } break; default: return NotRTSignal; } // Behavior common to all syscalls if( newEvt != NULL ) { // Report the event to ProcControlAPI, make sure process remains stopped evProc->setReportingEvent(true); newEvt->setProcess(ev->getProcess()); newEvt->setThread(ev->getThread()); // In the callback thread, the process and thread are stopped newEvt->setSyncType(Event::sync_process); newEvt->setUserEvent(true); ProcControlAPI::mbox()->enqueue(newEvt); } return IsRTSignal; }
bool DecoderWindows::decode(ArchEvent *ae, std::vector<Event::ptr> &events) { static Address ntdll_ignore_breakpoint_address = 0; assert(ae); ArchEventWindows* winEvt = static_cast<ArchEventWindows*>(ae); assert(winEvt); Event::ptr newEvt = Event::ptr(); int_thread *thread = ProcPool()->findThread((Dyninst::LWP)(winEvt->evt.dwThreadId)); int_process* proc = ProcPool()->findProcByPid(winEvt->evt.dwProcessId); if(!proc) { return false; } windows_process* windows_proc = dynamic_cast<windows_process*>(proc); if(!windows_proc) { perr_printf("DecoderWindows::decode() called on nonexistent/deleted process %d/%d\n", winEvt->evt.dwProcessId, winEvt->evt.dwThreadId); return false; } //windows_proc->clearPendingDebugBreak(); DEBUG_EVENT e = winEvt->evt; switch(e.dwDebugEventCode) { case CREATE_PROCESS_DEBUG_EVENT: case CREATE_THREAD_DEBUG_EVENT: pthrd_printf("decodeProcess/decodeThreadEvent\n"); return decodeCreateThread(e, newEvt, proc, events); case EXCEPTION_DEBUG_EVENT: pthrd_printf("decodeException\n"); switch(e.u.Exception.ExceptionRecord.ExceptionCode) { case EXCEPTION_SINGLE_STEP: pthrd_printf("Decoding singleStep event on PID %d, TID %d\n", e.dwProcessId, e.dwThreadId); newEvt = decodeSingleStepEvent(e, proc, thread); break; //fprintf(stderr, "Decoded Single-step event at 0x%lx, PID: %d, TID: %d\n", e.u.Exception.ExceptionRecord.ExceptionAddress, e.dwProcessId, e.dwThreadId); case EXCEPTION_BREAKPOINT: // Case 1: breakpoint is real breakpoint pthrd_printf("Caught breakpoint for pid %d, tid %d, PC 0x%lx\n", e.dwProcessId, e.dwThreadId, e.u.Exception.ExceptionRecord.ExceptionAddress); newEvt = decodeBreakpointEvent(e, proc, thread); if(newEvt) { pthrd_printf("Decoded Breakpoint event at 0x%lx, PID: %d, TID: %d\n", e.u.Exception.ExceptionRecord.ExceptionAddress, e.dwProcessId, e.dwThreadId); //fprintf(stderr, "Decoded Breakpoint event at 0x%lx, PID: %d, TID: %d\n", e.u.Exception.ExceptionRecord.ExceptionAddress, e.dwProcessId, e.dwThreadId); break; } else { if(proc->getState() != int_process::neonatal && proc->getState() != int_process::neonatal_intermediate) { bool didSomething = false; if (windows_proc->pendingDebugBreak()) { pthrd_printf("Decoded Stop event, PID: %d, TID: %d\n", e.dwProcessId, e.dwThreadId); //newEvt = Event::ptr(new EventStop()); windows_proc->setStopThread(e.dwThreadId); return true; } else { pthrd_printf("Decoded unhandled exception (breakpoint) event, PID: %d, TID: %d\n", e.dwProcessId, e.dwThreadId); // Case 3: breakpoint that's not from us, while running. Pass on exception. GeneratorWindows* winGen = static_cast<GeneratorWindows*>(GeneratorWindows::getDefaultGenerator()); newEvt = EventSignal::ptr(new EventSignal(e.u.Exception.ExceptionRecord.ExceptionCode)); } } else { // Case 4: breakpoint in ntdll.dll due to startup. This should be skipped and we should bootstrap. pthrd_printf("Breakpoint due to startup, ignoring\n"); newEvt = Event::ptr(new EventBootstrap()); ntdll_ignore_breakpoint_address = (Address) e.u.Exception.ExceptionRecord.ExceptionAddress; if(!thread) { pthrd_printf("DEBUG: Missing thread on startup breakpoint\n"); } } } break; case EXCEPTION_ILLEGAL_INSTRUCTION: { pthrd_printf("SIGILL in mutatee\n"); GeneratorWindows* winGen = static_cast<GeneratorWindows*>(GeneratorWindows::getDefaultGenerator()); winGen->markUnhandledException(e.dwProcessId); newEvt = EventSignal::ptr(new EventSignal(e.u.Exception.ExceptionRecord.ExceptionCode)); assert(0); } break; case EXCEPTION_ACCESS_VIOLATION: { // check if this is first chance or second chance: if (e.u.Exception.dwFirstChance != 0) { int sig = e.u.Exception.ExceptionRecord.ExceptionCode; int cause = e.u.Exception.ExceptionRecord.ExceptionInformation[0]; Address addr = e.u.Exception.ExceptionRecord.ExceptionInformation[1]; EventSignal* evSig = nullptr; switch (cause) { case 0: evSig = new EventSignal(sig, addr, EventSignal::ReadViolation, true); break; case 1: evSig = new EventSignal(sig, addr, EventSignal::WriteViolation, true); break; case 8: evSig = new EventSignal(sig, addr, EventSignal::ExecuteViolation, true); break; default: evSig = new EventSignal(sig, addr, EventSignal::Unknown, true); break; } newEvt = EventSignal::ptr(evSig); } else { pthrd_printf("segfault in mutatee, thread %d/%d\n", e.dwProcessId, e.dwThreadId); unsigned problemArea = (unsigned int)(e.u.Exception.ExceptionRecord.ExceptionAddress); cerr << "SEGFAULT @ " << hex << problemArea << dec << endl; dumpSurroundingMemory(problemArea, proc); GeneratorWindows* winGen = static_cast<GeneratorWindows*>(GeneratorWindows::getDefaultGenerator()); winGen->markUnhandledException(e.dwProcessId); newEvt = EventSignal::ptr(new EventSignal(e.u.Exception.ExceptionRecord.ExceptionCode)); cerr << "Signal is " << e.u.Exception.ExceptionRecord.ExceptionCode << endl; } } break; // Thread naming exception. Ignore. case EXCEPTION_DEBUGGER_IO: { pthrd_printf("Debugger I/O exception: %lx\n", e.u.Exception.ExceptionRecord.ExceptionInformation[0]); newEvt = EventNop::ptr(new EventNop()); newEvt->setSyncType(Event::async); break; } default: { pthrd_printf("Decoded unhandled exception event, PID: %d, TID: %d, Exception code = 0x%lx, Exception addr = 0x%lx\n", e.dwProcessId, e.dwThreadId, e.u.Exception.ExceptionRecord.ExceptionCode, e.u.Exception.ExceptionRecord.ExceptionAddress); GeneratorWindows* winGen = static_cast<GeneratorWindows*>(GeneratorWindows::getDefaultGenerator()); winGen->markUnhandledException(e.dwProcessId); newEvt = EventSignal::ptr(new EventSignal(e.u.Exception.ExceptionRecord.ExceptionCode)); } break; } break; case EXIT_PROCESS_DEBUG_EVENT: { pthrd_printf("Decoded ProcessExit event, PID: %d, TID: %d\n", e.dwProcessId, e.dwThreadId); if(proc->wasForcedTerminated()) { newEvt = EventForceTerminate::ptr(new EventForceTerminate(e.u.ExitProcess.dwExitCode)); } else { newEvt = EventExit::ptr(new EventExit(EventType::Pre, e.u.ExitProcess.dwExitCode)); } // GeneratorWindows* winGen = static_cast<GeneratorWindows*>(GeneratorWindows::getDefaultGenerator()); // winGen->removeProcess(proc); newEvt->setSyncType(Event::sync_process); newEvt->setProcess(proc->proc()); // Since we're doing thread exit/proc exit, and this means the thread will go away first, // we don't set the thread here if(thread) newEvt->setThread(thread->thread()); // We do this here because the generator thread will exit before updateSyncState otherwise // int_threadPool::iterator i = proc->threadPool()->begin(); // for (; i != proc->threadPool()->end(); i++) { // (*i)->getGeneratorState().setState(int_thread::exited); // (*i)->setExitingInGenerator(true); // } events.push_back(newEvt); return true; } break; case EXIT_THREAD_DEBUG_EVENT: pthrd_printf("Decoded ThreadExit event, PID: %d, TID: %d\n", e.dwProcessId, e.dwThreadId); if(thread) { thread->getGeneratorState().setState(int_thread::exited); thread->setExitingInGenerator(true); if (e.dwThreadId == windows_proc->getStopThread()) { newEvt = EventWinStopThreadDestroy::ptr(new EventWinStopThreadDestroy(EventType::Pre)); windows_proc->clearStopThread(); } else { newEvt = EventLWPDestroy::ptr(new EventLWPDestroy(EventType::Pre)); } } else { // If the thread is NULL, we can't give the user an event with a valid thread object anymore. // So fail the decode. return false; } break; case LOAD_DLL_DEBUG_EVENT: pthrd_printf("Decoded LoadDLL event, PID: %d, TID: %d\n", e.dwProcessId, e.dwThreadId); newEvt = decodeLibraryEvent(e, proc); break; case UNLOAD_DLL_DEBUG_EVENT: pthrd_printf("Decoded UnloadDLL event, PID: %d, TID: %d\n", e.dwProcessId, e.dwThreadId); newEvt = decodeLibraryEvent(e, proc); break; case OUTPUT_DEBUG_STRING_EVENT: { TCHAR buf[1024]; unsigned long bytes_read = 0; windows_process* winProc = dynamic_cast<windows_process*>(proc); BOOL result = ::ReadProcessMemory(winProc->plat_getHandle(), e.u.DebugString.lpDebugStringData, buf, e.u.DebugString.nDebugStringLength, &bytes_read); if(result) { pthrd_printf("Decoded DebugString event, string: %s\n", buf); } else { pthrd_printf("Decoded DebugString event, but string was not readable!\n"); } break; } case RIP_EVENT: pthrd_printf("Decoded RIP event, PID: %d, TID: %d, error = 0x%lx\n", e.dwProcessId, e.dwThreadId, e.u.RipInfo.dwError); newEvt = EventCrash::ptr(new EventCrash(e.u.RipInfo.dwError)); assert(0); break; default: assert(!"invalid event type"); return false; } if(newEvt) { assert(proc); if(newEvt->getSyncType() == Event::unset) newEvt->setSyncType(Event::sync_process); if(thread) { newEvt->setThread(thread->thread()); } newEvt->setProcess(proc->proc()); events.push_back(newEvt); } return true; }