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 PCEventHandler::handleSignal(EventSignal::const_ptr ev, PCProcess *evProc) const { proccontrol_printf("%s[%d]: thread %d/%d received signal %d\n", FILE__, __LINE__, ev->getProcess()->getPid(), ev->getThread()->getLWP(), ev->getSignal()); // Check whether it is a signal from the RT library (note: this will internally // handle any entry/exit to syscalls and make the necessary up calls as appropriate) RTSignalResult result = handleRTSignal(ev, evProc); if( result == ErrorInDecoding ) { proccontrol_printf("%s[%d]: failed to determine whether signal came from RT library\n", FILE__, __LINE__); return false; } if( result == IsRTSignal ) { // handleRTSignal internally does all handling for the events in order to keep // related logic in one place proccontrol_printf("%s[%d]: signal came from RT library\n", FILE__, __LINE__); return true; } // check if windows access violation, defensive mode, and write permissions. // unprotect pages if necessary. if (evProc->getHybridMode() == BPatch_defensiveMode && ev->isFirst() && ev->getCause() == EventSignal::WriteViolation) { malware_cerr << "Write to protected address 0x" << std::hex << ev->getAddress() << std::dec << std::endl; Address addr = ev->getAddress(); mapped_object* obj = evProc->findObject(addr); // retry finding object by its original address. if (obj == NULL && evProc->isMemoryEmulated()) { std::pair<bool, Address> trans = evProc->getMemEm()->translateBackwards(addr); if (trans.first) { addr = trans.second; obj = evProc->findObject(addr); } } // change permissions if we can find this originally writable region if (obj != NULL) { SymtabAPI::Region* reg = obj->parse_img()->getObject()->findEnclosingRegion(addr - obj->codeBase()); if (reg != NULL && (reg->getRegionPermissions() == SymtabAPI::Region::RP_RW || reg->getRegionPermissions() == SymtabAPI::Region::RP_RWX)) { // change back permissions. PCProcess::PCMemPerm rights(true, true, true); evProc->changeMemoryProtections( addr - (addr % evProc->getMemoryPageSize()), evProc->getMemoryPageSize(), rights /* PAGE_EXECUTE_READWRITE */ , false); return true; } } // else fall through to forwarding. } bool shouldForwardSignal = true; BPatch_process *bpproc = BPatch::bpatch->getProcessByPid(evProc->getPid()); if( bpproc == NULL ) { proccontrol_printf("%s[%d]: failed to locate BPatch_process for process %d\n", FILE__, __LINE__, evProc->getPid()); } if( shouldStopForSignal(ev->getSignal()) ) { proccontrol_printf("%s[%d]: signal %d is stop signal, leaving process stopped\n", FILE__, __LINE__, ev->getSignal()); evProc->setDesiredProcessState(PCProcess::ps_stopped); shouldForwardSignal = false; } // Tell the BPatch layer we received a signal if( bpproc ) bpproc->setLastSignal(ev->getSignal()); // Debugging only if( (dyn_debug_proccontrol || dyn_debug_crash) && isCrashSignal(ev->getSignal()) ) { fprintf(stderr, "Caught crash signal %d for thread %d/%d\n", ev->getSignal(), ev->getProcess()->getPid(), ev->getThread()->getLWP()); RegisterPool regs; if( !ev->getThread()->getAllRegisters(regs) ) { fprintf(stderr, "%s[%d]: Failed to get registers for crash\n", FILE__, __LINE__); }else{ fprintf(stderr, "Registers at crash:\n"); for(RegisterPool::iterator i = regs.begin(); i != regs.end(); i++) { fprintf(stderr, "\t%s = 0x%lx\n", (*i).first.name().c_str(), (*i).second); } } // Dump the stacks pdvector<pdvector<Frame> > stackWalks; evProc->walkStacks(stackWalks); for (unsigned walk_iter = 0; walk_iter < stackWalks.size(); walk_iter++) { fprintf(stderr, "Stack for pid %d, lwpid %d\n", stackWalks[walk_iter][0].getProc()->getPid(), stackWalks[walk_iter][0].getThread()->getLWP()); for( unsigned i = 0; i < stackWalks[walk_iter].size(); i++ ) { cerr << stackWalks[walk_iter][i] << endl; } } // User specifies the action, defaults to core dump // (which corresponds to standard Dyninst behavior) if(dyn_debug_crash_debugger) { if( string(dyn_debug_crash_debugger).find("gdb") != string::npos ) { evProc->launchDebugger(); // If for whatever reason this fails, fall back on sleep dyn_debug_crash_debugger = const_cast<char *>("sleep"); } if( string(dyn_debug_crash_debugger) == string("sleep") ) { static volatile int spin = 1; while(spin) sleep(1); } } } if(shouldForwardSignal ) { // Now, explicitly set the signal to be delivered to the process ev->setThreadSignal(ev->getSignal()); } return true; }