def memoryFaultInstr(self, instr, fault_address): asm = instr.text # Invalid write (eg. "MOV [...], value") match = re.search(r"^(?:MOV|TEST)[A-Z]* %s," % DEREF_REGEX, asm) if match: if fault_address is None: fault_address = evalFaultAddress(self.process, match) self.reason = InvalidWrite(fault_address, size=findDerefSize(match), instr=instr, process=self.process) return # Invalid read (eg. "CMP BYTE [EAX+EDX-0x1], 0x0") match = re.search(r"^%s %s," % (INSTR_REGEX, DEREF_REGEX), asm) if match: if fault_address is None: fault_address = evalFaultAddress(self.process, match) self.reason = InvalidRead(fault_address, size=findDerefSize(match), instr=instr, process=self.process) return # Invalid read (eg. "MOV reg, [...]") match = re.match(r"%s [^,]+, %s" % (INSTR_REGEX, DEREF_REGEX), asm) if match: if fault_address is None: fault_address = evalFaultAddress(self.process, match) self.reason = InvalidRead(fault_address, size=findDerefSize(match), instr=instr, process=self.process) return # MOVS* and SCAS* instructions (eg. "MOVSB" or "REP SCASD") match = re.search(r"^(?:REP(?:NZ)? )?(?P<operator>MOVS|SCAS)(?P<suffix>[BWD])?", asm) if match: self.reason = self.movsInstr(fault_address, instr, match) return
def childExit(self): siginfo = self.getSignalInfo() if siginfo: child = siginfo._sigchld self.reason = ChildExit(child.pid, child.status, child.uid) else: self.reason = ChildExit()
def mathError(self): instr = self.getInstruction() if not instr: return match = re.match(r"I?DIV (.*)", instr.text) if not match: return self.reason = DivisionByZero(instr=instr, process=self.process)
def memoryFault(self): # Get fault siginfo = self.getSignalInfo() if siginfo: fault_address = siginfo._sigfault._addr if not fault_address: fault_address = 0 else: fault_address = None # Get current instruction instr = self.getInstruction() # Call to invalid address? if fault_address is not None: try: ip = self.process.getInstrPointer() if ip == fault_address: self.reason = InstructionError(ip, process=self.process) return except PtraceError: pass # Stack overflow? stack = self.process.findStack() if stack: sp = self.process.getStackPointer() if not (stack.start <= sp <= stack.end): self.reason = StackOverflow(sp, stack, instr=instr, process=self.process) return # Guess error type using the assembler instruction if instr: self.memoryFaultInstr(instr, fault_address) if self.reason: return # Last chance: use generic invalid memory access error self.reason = InvalidMemoryAccess(fault_address, instr=instr, process=self.process)
def _analyze(self): if self.signum in (SIGSEGV, SIGBUS): self.memoryFault() elif self.signum == SIGFPE: self.mathError() elif self.signum == SIGCHLD: self.childExit() elif self.signum == SIGABRT: self.reason = Abort() return self.reason
def memoryFault(self): # Get fault siginfo = self.getSignalInfo() if siginfo: fault_address = siginfo._sigfault._addr if not fault_address: fault_address = 0 else: fault_address = None # Get current instruction instr = self.getInstruction() # Call to invalid address? if fault_address is not None: try: ip = self.process.getInstrPointer() if ip == fault_address: self.reason = InstructionError(ip, process=self.process) return except PtraceError: pass # Stack overflow? stack = self.process.findStack() if stack: sp = self.process.getStackPointer() if not (stack.start <= sp <= stack.end): self.reason = StackOverflow( sp, stack, instr=instr, process=self.process) return # Guess error type using the assembler instruction if instr: self.memoryFaultInstr(instr, fault_address) if self.reason: return # Last chance: use generic invalid memory access error self.reason = InvalidMemoryAcces( fault_address, instr=instr, process=self.process)
class ProcessSignal(ProcessEvent): def __init__(self, signum, process): # Initialize attributes self.name = signalName(signum) ProcessEvent.__init__(self, process, "Signal %s" % self.name) self.signum = signum self.reason = None def _analyze(self): if self.signum in (SIGSEGV, SIGBUS): self.memoryFault() elif self.signum == SIGFPE: self.mathError() elif self.signum == SIGCHLD: self.childExit() elif self.signum == SIGABRT: self.reason = Abort() return self.reason def getInstruction(self): if not HAS_DISASSEMBLER: return None try: return self.process.disassembleOne() except PtraceError: return None def memoryFaultInstr(self, instr, fault_address): asm = instr.text # Invalid write (eg. "MOV [...], value") match = re.search(r"^(?:MOV|TEST)[A-Z]* %s," % DEREF_REGEX, asm) if match: if fault_address is None: fault_address = evalFaultAddress(self.process, match) self.reason = InvalidWrite(fault_address, size=findDerefSize(match), instr=instr, process=self.process) return # Invalid read (eg. "CMP BYTE [EAX+EDX-0x1], 0x0") match = re.search(r"^%s %s," % (INSTR_REGEX, DEREF_REGEX), asm) if match: if fault_address is None: fault_address = evalFaultAddress(self.process, match) self.reason = InvalidRead(fault_address, size=findDerefSize(match), instr=instr, process=self.process) return # Invalid read (eg. "MOV reg, [...]") match = re.match(r"%s [^,]+, %s" % (INSTR_REGEX, DEREF_REGEX), asm) if match: if fault_address is None: fault_address = evalFaultAddress(self.process, match) self.reason = InvalidRead(fault_address, size=findDerefSize(match), instr=instr, process=self.process) return # MOVS* and SCAS* instructions (eg. "MOVSB" or "REP SCASD") match = re.search(r"^(?:REP(?:NZ)? )?(?P<operator>MOVS|SCAS)(?P<suffix>[BWD])?", asm) if match: self.reason = self.movsInstr(fault_address, instr, match) return def movsInstr(self, fault_address, instr, match): operator = match.group("operator") suffix = match.group("suffix") size = {'B': 1, 'W': 2, 'D': 4}.get(suffix) error_cls = InvalidMemoryAcces try: process = self.process if CPU_64BITS: source_reg = 'rsi' dest_reg = 'rdi' else: source_reg = 'esi' dest_reg = 'edi' source_addr = process.getreg(source_reg) registers = {source_reg: source_addr} write = (operator == 'MOVS') if write: dest_addr = process.getreg(dest_reg) registers[dest_reg] = dest_addr if fault_address is not None: if fault_address == source_addr: error_cls = InvalidRead if write and fault_address == dest_addr: error_cls = InvalidWrite else: if write: fault_address = (source_addr, dest_addr) else: fault_address = (source_addr,) except PtraceError: registers = {} return error_cls(fault_address, size=size, instr=instr, registers=registers, process=self.process) def getSignalInfo(self): if RUNNING_LINUX: return self.process.getsiginfo() else: return None def memoryFault(self): # Get fault siginfo = self.getSignalInfo() if siginfo: fault_address = siginfo._sigfault._addr if not fault_address: fault_address = 0 else: fault_address = None # Get current instruction instr = self.getInstruction() # Call to invalid address? if fault_address is not None: try: ip = self.process.getInstrPointer() if ip == fault_address: self.reason = InstructionError(ip, process=self.process) return except PtraceError: pass # Stack overflow? stack = self.process.findStack() if stack: sp = self.process.getStackPointer() if not (stack.start <= sp <= stack.end): self.reason = StackOverflow(sp, stack, instr=instr, process=self.process) return # Guess error type using the assembler instruction if instr: self.memoryFaultInstr(instr, fault_address) if self.reason: return # Last chance: use generic invalid memory access error self.reason = InvalidMemoryAcces(fault_address, instr=instr, process=self.process) def mathError(self): instr = self.getInstruction() if not instr: return match = re.match(r"I?DIV (.*)", instr.text) if not match: return self.reason = DivisionByZero(instr=instr, process=self.process) def childExit(self): siginfo = self.getSignalInfo() if siginfo: child = siginfo._sigchld self.reason = ChildExit(child.pid, child.status, child.uid) else: self.reason = ChildExit() def display(self, log=None): self._analyze() if not log: log = error log("-" * 60) log("PID: %s" % self.process.pid) log("Signal: %s" % self.name) if self.reason: self.reason.display(log) log("-" * 60)