def disasm(self, offset=0, processor="intel", mtype=32, lines=1, bsize=512): if processor == "intel": if mtype == 32: decode = Decode32Bits elif mtype == 16: decode = Decode16Bits elif mtype == 64: decode = Decode64Bits else: raise EUnknownDisassemblyType() ret = [] self.calls = [] i = None ilines = 0 try: buf = self.getBytes(offset, bsize) except OverflowError: # OverflowError: long int too large to convert to int return [] for i in Decode(offset, buf, decode): i = self.getDisassembleObject(i, ilines) ret.append(i) ilines += 1 if ilines == lines: break return ret
def disassemble(self, buf, processor="intel", type=32, lines=40, bsize=512, baseoffset=0): """ Disassemble a given buffer using Distorm """ if processor == "intel": if type == 32: decode = Decode32Bits elif type == 16: decode = Decode16Bits elif type == 64: decode = Decode64Bits else: raise EUnknownDisassemblyType() pos = 0 ret = "" index = 0 self.calls = [] offset = 0 i = None for i in Decode(baseoffset, buf, decode): i = self.getDisassembleObject(i) pos += 1 ops = str(i.operands) comment = "" func = "" if str(i.mnemonic).lower().startswith("call") or \ str(i.mnemonic).lower().startswith("j") or \ str(i.mnemonic).lower().startswith("loop"): try: if str(i.operands).startswith("["): ops = str(i.operands).replace("[", "").replace("]", "") else: ops = str(i.operands) ops = int(ops, 16) if self.names.has_key(ops): func = self.names[ops] if self.maxsize >= ops and ops > 0: index += 1 comment = "\t; %d %s" % (index, func) self.calls.append(ops) ops = "0x%08x" % ops else: #comment = "\t; %s" % func if func != "": ops = func else: ops = "0x%08x" % ops comment = "" except: ops = str(i.operands) elif str(i.operands).find("[") > -1: tmp = re.findall("\[(0x[0-9A-F]+)\]", str(i.operands), re.IGNORECASE) if len(tmp) > 0: tmp = int(tmp[0], 16) if self.names.has_key(tmp): if self.imports.has_key(tmp): comment = "\t; %s" % self.names[tmp] else: index += 1 comment = "\t; %d %s" % (index, self.names[tmp]) else: if self.names.has_key(i.offset): mxrefs = [] if self.xrefs_to.has_key(i.offset): tmpidx = 0 for tmp in self.xrefs_to[i.offset]: tmpidx += 1 if self.names.has_key(tmp): mxrefs.append(self.names[tmp]) else: mxrefs.append("sub_%08x" % tmp) if tmpidx == 3: mxrefs.append("...") break pos += 1 if len(mxrefs) > 0: ret += "0x%08x ; FUNCTION %s\t XREFS %s\n" % ( i.offset, self.names[i.offset], ", ".join(mxrefs)) else: ret += "0x%08x ; FUNCTION %s\n" % ( i.offset, self.names[i.offset]) #comment = "\t; Function %s" % self.names[i.offset] else: comment = "" if self.case == 'high': ret += "0x%08x (%02x) %-20s %s%s\n" % ( i.offset, i.size, i.instructionHex, str(i.mnemonic) + " " + str(ops), comment) # if pyew.case is 'low' or wrong else: ret += "0x%08x (%02x) %-20s %s%s\n" % ( i.offset, i.size, i.instructionHex, str(i.mnemonic).lower() + " " + str(ops).lower(), comment) if str(i.mnemonic).lower().startswith("j") or \ str(i.mnemonic).lower() == "ret" or \ str(i.mnemonic).lower().find("loop") > -1: pos += 1 ret += "0x%08x " % i.offset + "-" * 70 + "\n" if pos >= lines: break if i: self.lastasmoffset = i.offset + i.size elif processor == "python": moffset = self.offset self.seek(0) buf = self.f.read() self.log(dis.dis(buf)) self.seek(moffset) ret = "" return ret
def main(args): crash_data_dict = None tr = vtrace.getTrace() global timeout if os.getenv("NIGHTMARE_TIMEOUT"): timeout = float(os.getenv("NIGHTMARE_TIMEOUT")) if args[0] in ["--attach", "-A"]: if len(args) == 1: usage() sys.exit(1) else: pid = int(args[1]) # Schedule a timer to detach from the process after some seconds timer = threading.Timer(timeout, kill_process, ( tr, False, )) timer.start() tr.attach(pid) else: # Schedule a timer to kill the process after 5 seconds timer = threading.Timer(timeout, kill_process, ( tr, True, )) timer.start() tr.execute(" ".join(args)) tr.run() signal = tr.getCurrentSignal() signal_name = signal_to_name(signal) ignore_list = [ "Unknown", "SIGUSR1", "SIGUSR2", "SIGTTIN", "SIGPIPE", "SIGINT" ] while signal is None or signal_name in ignore_list: signal = tr.getCurrentSignal() signal_name = signal_to_name(signal) try: tr.run() except: break timer.cancel() # Don't do anything else, the process is gone if os.name != "nt" and not tr.attached: return None if signal is not None: print tr, hex(signal) print " ".join(sys.argv) crash_name = os.getenv("NFP_INFO_CRASH") # Create the object to store all the crash data crash_data = CCrashData(tr.getProgramCounter(), sig2name(signal)) if crash_name is None: crash_name = "info.crash" #f = open(crash_name, "wb") exploitability_reason = None if os.name != "nt" and signal == 4: # Due to illegal instruction exploitable = EXPLOITABLE exploitability_reason = "Illegal instruction" elif os.name == "nt" and signal in [0xc0000096, 0xc000001d]: # Due to illegal or privileged instruction exploitable = EXPLOITABLE if signal == 0xc000001d: exploitability_reason = "Illegal instruction" else: exploitability_reason = "Privileged instruction" else: exploitable = NOT_EXPLOITABLE crash_data.add_data("process", "pid", tr.getPid()) if os.name == "nt": print "Process %d crashed with exception 0x%x (%s)" % ( tr.getPid(), signal, win32_exc_to_name(signal)) else: print "Process %d crashed with signal %d (%s)" % ( tr.getPid(), signal, signal_to_name(signal)) i = 0 for t in tr.getThreads(): i += 1 crash_data.add_data("threads", "%d" % i, t) stack_trace = tr.getStackTrace() total = len(stack_trace) i = 0 for x in stack_trace: i += 1 sym = tr.getSymByAddr(x[0], exact=False) if sym is None: sym = "" crash_data.add_data("stack trace", "%d" % i, [x[0], str(sym)]) total -= 1 regs = tr.getRegisterContext().getRegisters() for reg in regs: crash_data.add_data("registers", reg, regs[reg]) if reg.startswith("r"): line = reg.ljust(5) + "%016x" % regs[reg] try: mem = tr.readMemory(regs[reg], 32) mem = base64.b64encode(mem) crash_data.add_data("registers memory", reg, mem) line += "\t" + repr(mem) except: pass for reg in COMMON_REGS: if reg in regs: if reg in crash_data.data["registers memory"]: print reg, hex(regs[reg]), repr( base64.b64decode( crash_data.data["registers memory"][reg])) else: print reg, hex(regs[reg]) print total_around = 40 if 'rip' in regs: if len("%08x" % regs['rip']) > 8: decoder = Decode64Bits else: decoder = Decode32Bits else: decoder = Decode32Bits pc = tr.getProgramCounter() pc_line = None try: pc_mem = tr.readMemory(pc - total_around / 2, total_around) offset = regs["rip"] - total_around / 2 ret = [] found = False for x in Decode(0, pc_mem, decoder): line = "%016x %s" % ((offset + x.offset), str(x)) crash_data.add_data("disassembly", offset + x.offset, str(x)) if offset + x.offset == pc: crash_data.disasm = [x.offset + offset, str(x)] line += "\t\t<--------- CRASH" print line pc_line = x found = True ret.append(line) if found: #print "\n".join(ret) pass else: offset = pc = tr.getProgramCounter() pc_mem = tr.readMemory(pc, total_around) for x in Decode(0, pc_mem, decoder): line = "%016x %s" % ((offset + x.offset), str(x)) if offset + x.offset == pc: line += "\t\t<--------- CRASH" pc_line = x print line except: # Due to invalid memory at $PC if signal != 6: exploitable = True exploitability_reason = "Invalid memory at program counter" print "Exception:", sys.exc_info()[1] if pc_line: if str(pc_line.mnemonic) in ["CALL", "JMP"] or \ str(pc_line.mnemonic).startswith("JMP") or \ str(pc_line.mnemonic).startswith("CALL"): if str(pc_line.operands).find("[") > -1: # Due to jump/call with a register that maybe controllable exploitable = EXPLOITABLE exploitability_reason = "Jump or call with a probably controllable register" elif str(pc_line.mnemonic).startswith("DB "): # Due to illegal instruction exploitable = MAYBE_EXPLOITABLE exploitability_reason = "Illegal instruction" elif str(pc_line.mnemonic).startswith("IN") or \ str(pc_line.mnemonic).startswith("OUT") or \ str(pc_line.mnemonic) in ["HLT", "IRET", "CLTS", "LGDT", "LIDT", "LLDT", "LMSW", "LTR", "CLI", "STI"]: if str(pc_line.mnemonic) != "INT": # Due to privileged instruction (which makes no sense in user-land) exploitable = MAYBE_EXPLOITABLE exploitability_reason = "Privileged instruction" #print >>f #print >>f, "Maps:" i = 0 for m in tr.getMemoryMaps(): i += 1 line = "%016x %s %s %s" % (m[0], str( m[1]).rjust(8), get_permision_str(m[2]), m[3]) crash_data.add_data("memory maps", "%d" % i, m) #print >>f, line #print >>f if exploitable > 0: crash_data.exploitable = is_exploitable(exploitable) crash_data.add_data("exploitability", "reason", exploitability_reason) #print >>f, "Exploitable: %s. %s." % (is_exploitable(exploitable), exploitability_reason) else: #print >>f, "Exploitable: Unknown." pass crash_data_buf = crash_data.dump_json() crash_data_dict = crash_data.dump_dict() print "Yep, we got a crash! \o/" print #print "Dumping JSON...." #print crash_data_buf #print if tr.attached: try: tr.kill() except: pass try: tr.release() except: pass return crash_data_dict