Пример #1
0
    def run(self):
        os.putenv("LANG", "C")
        os.putenv("ASAN_SYMBOLIZER_PATH", self.asan_symbolizer_path)

        cmd = self.program
        print "Running %s" % cmd

        cmd_obj = TimeoutCommand(cmd)
        cmd_obj.run(self.timeout, get_output=True)

        buf = cmd_obj.stderr
        self.asan.parse_buffer(buf)

        if self.asan.reason is not None:
            crash_data = CCrashData(self.asan.pc, self.asan.reason)
            i = 0
            for line in self.asan.stack_trace:
                crash_data.add_data("stack trace", "%d" % i,
                                    (line[0], line[1]))
                i += 1

            crash_data.add_data("registers", "pc", self.asan.pc)
            crash_data.add_data("registers", "bp", self.asan.bp)
            crash_data.add_data("registers", "sp", self.asan.sp)

            crash_data.add_data("disassembly", int(self.asan.pc), "")
            j = 0
            for line in self.asan.additional:
                crash_data.add_data("information", j, line)
                j += 1
            crash_data.disasm = [self.asan.pc, ""]

            if not self.asan.reason.startswith("SIG"):
                crash_data.exploitable = "EXPLOITABLE"
            else:
                crash_data.exploitable = "UNKNOWN"

            crash_data.add_data("exploitability", "reason", self.asan.reason)

            crash_data_buf = crash_data.dump_json()
            crash_data_dict = crash_data.dump_dict()

            line = "Program received %s at PC 0x%x SP 0x%x BP 0x%x"
            print line % (self.asan.reason, self.asan.pc, self.asan.sp,
                          self.asan.bp)
            print
            for i, line in enumerate(self.asan.stack_trace):
                if i > 10:
                    break
                print "0x%08x %s" % (line[0], line[1])

            print
            print "Yep, we got a crash! \o/"
            print

            return crash_data_dict

        return
Пример #2
0
  def run(self):
    os.putenv("LANG", "C")
    os.putenv("ASAN_SYMBOLIZER_PATH", self.asan_symbolizer_path)

    cmd = self.program
    print "Running %s" % cmd

    cmd_obj = TimeoutCommand(cmd)
    cmd_obj.run(self.timeout, get_output=True)

    buf = cmd_obj.stderr
    self.asan.parse_buffer(buf)

    if self.asan.reason is not None:
      crash_data = CCrashData(self.asan.pc, self.asan.reason)
      i = 0
      for line in self.asan.stack_trace:
        crash_data.add_data("stack trace", "%d" % i, (line[0], line[1]))
        i += 1

      crash_data.add_data("registers", "pc", self.asan.pc)
      crash_data.add_data("registers", "bp", self.asan.bp)
      crash_data.add_data("registers", "sp", self.asan.sp)

      crash_data.add_data("disassembly", int(self.asan.pc), "")
      j = 0
      for line in self.asan.additional:
        crash_data.add_data("information", j, line)
        j += 1
      crash_data.disasm = [self.asan.pc, ""]

      if not self.asan.reason.startswith("SIG"):
        crash_data.exploitable = "EXPLOITABLE"
      else:
        crash_data.exploitable = "UNKNOWN"

      crash_data.add_data("exploitability", "reason", self.asan.reason)

      crash_data_buf = crash_data.dump_json()
      crash_data_dict = crash_data.dump_dict()

      line = "Program received %s at PC 0x%x SP 0x%x BP 0x%x"
      print line % (self.asan.reason, self.asan.pc, self.asan.sp, self.asan.bp)
      print
      for i, line in enumerate(self.asan.stack_trace):
        if i > 10:
          break
        print "0x%08x %s" % (line[0], line[1])

      print
      print "Yep, we got a crash! \o/"
      print

      return crash_data_dict

    return
Пример #3
0
    def run(self):
        global buf

        os.putenv("LANG", "C")

        logfile = mkstemp()[1]
        try:
            cmd = '/bin/bash -c "/usr/bin/gdb -q --batch --command=%s --args %s" 2>/dev/null > %s'
            cmd %= (self.gdb_commands, self.program, logfile)
            print cmd
            print "Running %s" % cmd

            cmd_obj = TimeoutCommand(cmd)
            #cmd_obj.shell = True
            cmd_obj.run(self.timeout)

            buf = open(logfile, "rb").readlines()
            self.parse_dump(buf)

            if self.signal:
                crash_data = CCrashData(self.pc, self.signal)
                i = 0
                for stack in self.stack:
                    crash_data.add_data("stack trace", "%d" % i, stack)
                    i += 1

                for reg in self.registers:
                    crash_data.add_data("registers", reg, self.registers[reg])

                crash_data.add_data("disassembly", int(self.pc), self.disasm)
                for dis in self.disasm_around:
                    if type(dis[0]) is int or dis[0].isdigit():
                        crash_data.add_data("disassembly", dis[0], dis[1])
                crash_data.disasm = [self.pc, self.disasm]

                if self.exploitability is not None:
                    crash_data.exploitable = self.exploitability

                if self.exploitability_reason is not None:
                    crash_data.add_data("exploitability", "reason",
                                        self.exploitability_reason)

                crash_data_buf = crash_data.dump_json()
                crash_data_dict = crash_data.dump_dict()

                print
                print "Yep, we got a crash! \o/"
                print

                return crash_data_dict

            return
        finally:
            os.remove(logfile)
Пример #4
0
  def run(self):
    global buf

    os.putenv("LANG", "C")
    
    logfile = mkstemp()[1]
    try:
      cmd = '/bin/bash -c "/usr/bin/gdb -q --batch --command=%s --args %s" 2>/dev/null > %s'
      cmd %= (self.gdb_commands, self.program, logfile)
      print cmd
      print "Running %s" % cmd

      cmd_obj = TimeoutCommand(cmd)
      #cmd_obj.shell = True
      cmd_obj.run(self.timeout)
      
      buf = open(logfile, "rb").readlines()
      self.parse_dump(buf)

      if self.signal:
        crash_data = CCrashData(self.pc, self.signal)
        i = 0
        for stack in self.stack:
          crash_data.add_data("stack trace", "%d" % i, stack)
          i += 1

        for reg in self.registers:
          crash_data.add_data("registers", reg, self.registers[reg])

        crash_data.add_data("disassembly", int(self.pc), self.disasm)
        for dis in self.disasm_around:
          if type(dis[0]) is int or dis[0].isdigit():
            crash_data.add_data("disassembly", dis[0], dis[1])
        crash_data.disasm = [self.pc, self.disasm]
        
        if self.exploitability is not None:
          crash_data.exploitable = self.exploitability
        
        if self.exploitability_reason is not None:
          crash_data.add_data("exploitability", "reason", self.exploitability_reason)

        crash_data_buf = crash_data.dump_json()
        crash_data_dict = crash_data.dump_dict()

        print
        print "Yep, we got a crash! \o/"
        print

        return crash_data_dict

      return
    finally:
      os.remove(logfile)
Пример #5
0
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])
      if timeout != 0:
        # 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:    
    if timeout != 0:
      # 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

  if timeout != 0:
    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 or 'rsp' in regs or 'rbp' in regs:
      if len("%08x" % regs['rip']) > 8 or len("%08x" % regs['rsp']) > 8 or len("%08x" % regs['rbp']) > 8:
        mode = CS_MODE_64
      else:
        mode = CS_MODE_32
    else:
      mode = CS_MODE_32

    md = Cs(CS_ARCH_X86, mode)
    md.skipdata = True
    pc = tr.getProgramCounter()
    crash_mnem = None
    crash_ops = None
    try:
      pc_mem = tr.readMemory(pc-total_around/2, total_around)
      offset = regs["rip"]-total_around/2

      ret = []
      found = False
      for x in md.disasm(pc_mem, 0):
        line = "%016x %s %s" % ((offset + x.address), x.mnemonic, x.op_str)
        crash_data.add_data("disassembly", offset + x.address, "%s %s" %(x.mnemonic, x.op_str))
        if offset + x.address == pc:
          crash_data.disasm = [x.address + offset, "%s %s" %(x.mnemonic, x.op_str)]
          line += "\t\t<--------- CRASH"
          print line
          found = True
        ret.append(line)

      if not found:
        offset = pc = tr.getProgramCounter()
        pc_mem = tr.readMemory(pc, total_around)
        for x in md.disasm(pc_mem, 0):
          line = "%016x %s %s" % ((offset + x.address), x.mnemonic, x.op_str)
          if offset + x.address == pc:
            line += "\t\t<--------- CRASH"
            crash_data.disasm = [x.address + offset, "%s %s" % (x.mnemonic, x.op_str)]
          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 crash_mnem:
      if crash_mnem in ["call", "jmp"] or \
         crash_mnem.startswith("jmp") or \
         crash_mnem.startswith("call"):
        if crash_ops.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 crash_mnem.startswith(".byte"):
        # Due to illegal instruction
        exploitable = MAYBE_EXPLOITABLE
        exploitability_reason = "Illegal instruction"
      elif crash_mnem.startswith("in") or \
           crash_mnem.startswith("out") or \
           crash_mnem in ["hlt", "iret", "clts", "lgdt", "lidt",
                                     "lldt", "lmsw", "ltr", "cli", "sti"]:
        if crash_mnem != "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
Пример #6
0
class CWinDbgInterface(object):
  def __init__(self, program, mode=32, windbg_path=None, exploitable_path=None):
    global timeout

    self.id = None
    self.mode = mode
    self.program = program
    self.exploitable_path = None
    self.windbg_path = windbg_path
    self.exploitable_path = exploitable_path
    try:
      self.handler = ExceptionHandler()
    except:
      self.handler = None
    self.minidump_path = None
    #self.minidump_path = r"C:\minidumps\\"

    if windbg_path is None:
      self.resolve_windbg_path()

    self.regs = {}
    self.stack = []
    self.pc_register = None
    self.disassembly = None
    self.exploitability = "Unknown"
    
    self.do_stop = False
    self.timer = None
    if os.getenv("NIGHTMARE_TIMEOUT"):
      timeout = float(os.getenv("NIGHTMARE_TIMEOUT"))
    self.timeout = timeout

  def resolve_windbg_path(self):
    try:
      reg = ConnectRegistry(None,HKEY_LOCAL_MACHINE)
      key = OpenKey(reg, r"SOFTWARE\Microsoft\Microsoft SDKs\Windows")
      if key:
        for i in range(QueryInfoKey(key)[0]):
          value = EnumKey(key, i)
          if value:
            full_key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\\" + value
            key2 = OpenKey(reg, full_key)
            if key2:
              name = QueryValueEx(key2, "ProductName")
              name = name[0]
              if name and name.startswith("Microsoft Windows SDK for Windows"):
                vals = QueryValueEx(key2, "InstallationFolder")
                val = vals[0]
                if val is not None:
                  log("Found installation path at %s" % val)
                  self.windbg_path = val
                  break
              CloseKey(key2)
      CloseKey(key)
    except WindowsError:
      print "Cannot resolve Windows SDKs path:", sys.exc_info()[1]
      print "Did you install Windows SDKs for Windows?"
    except:
      print "Cannot resolve Windows SDKs path:", sys.exc_info()[1]

  def parse_registers(self, regs):
    ret = re.findall("([a-z]{1}[a-z0-9]{1,})=([a-f0-9]+)", regs)
    tmp_regs = []
    for reg in ret:
      reg, val = reg
      self.regs[reg] = val
      tmp_regs.append([reg, val])

    if "rip" in self.regs:
      self.pc = int(self.regs["rip"], 16)
      self.pc_register = "rip"
    elif "eip" in self.regs:
      self.pc = int(self.regs["eip"], 16)
      self.pc_register = "eip"
    elif "pc" in self.regs:
      self.pc = int(self.regs["pc"], 16)
      self.pc_register = "pc"
    else:
      raise Exception("Cannot find the program counter register!")

    # Create the CCrashData object and fill registers and disassembly
    # data
    self.crash_data = CCrashData(self.pc, self.signal)

    # Extract the disassembly line from the "r" command
    lines = regs.split("\n")
    last_line = lines[len(lines)-2]
    dis = re.findall("[a-z0-9]{1,} [a-z0-9]{2,} (.*)", last_line)
    self.disasm = dis[0].strip(" ")
    # ...and add it to the CCrashData object
    self.crash_data.disasm = [self.pc, self.disasm]

    # Add the registers in the order shown by WinDbg
    for reg in tmp_regs:
      reg, val = reg
      self.crash_data.add_data("registers", reg, int(val, 16))

  def parse_stack(self, stack):
    lines = stack.split("\n")
    i = 0
    for line in lines:
      fields = re.findall("([a-f0-9]+) ([a-f0-9]+) (.*)", line)
      if len(fields) > 0:
        addr, func = fields[0][1], fields[0][2]
        if addr.strip("-") == "":
          addr = "FFFFFFFF"
          if self.mode == 64:
            addr += addr
          addr = "0x%s" % addr
        self.stack.append([addr, func])
        self.crash_data.add_data("stack trace", "%d" %i, [int(addr, 16), func])
        i += 1

  def disasm_around(self):
    lines = pykd.dbgCommand("u %s-c L12" % self.pc_register)
    for line in lines.split("\n"):
      tmp = re.findall("([a-f0-9]{1,}) ([a-f0-9]{2,}) (.*)", line)
      if len(tmp) > 0:
        line = tmp[0]

        addr = line[0]
        dis = line[2]
        self.crash_data.add_data("disassembly", int(addr, 16), dis)

  def create_crash_data(self, regs, stack, exploitable):
    regs = regs.replace("`", "")
    stack = stack.replace("`", "")

    self.signal = self.handler.exception_info[1]
    self.parse_registers(regs)
    self.parse_stack(stack)
    self.parse_exploitable(exploitable)
    self.disasm_around()
  
  def parse_exploitable(self, exploitable):
    self.exploitability = "Unknown"
    if exploitable is not None:
      s = "Exploitability Classification: "
      l = exploitable.split("\n")
      for line in l:
        pos = line.find(s)
        if pos > -1:
          self.exploitability = line[pos+len(s):]
          self.crash_data.exploitable = self.exploitability
          break
      
      last_line = l[len(l)-2]
      self.exploitability_reason = last_line
      self.crash_data.add_data("exploitability", "reason", \
                               self.exploitability_reason)

  def timeout_func(self):
    log("Timeout (%d seconds), killing the target..." % self.timeout)
    self.do_stop = True
    pykd.breakin()

  def run(self):
    if self.timeout != 0:
      self.timer = Timer(self.timeout, self.timeout_func)
      self.timer.start()

    self.do_stop = False
    self.id = pykd.startProcess(self.program, debugChildren=True)
    if self.handler is None:
      self.handler = ExceptionHandler()

    while not self.handler.exception_occurred and not self.do_stop:
      try:
        pykd.go()
      except:
        break

    if self.do_stop:
      try:
        pykd.dbgCommand(".kill")
      except:
        log("Exception killing target: %s" % str(sys.exc_info()[1]))
      return None

    if self.timer is not None:
      self.timer.cancel()

    ret = None
    if self.handler.exception_occurred:
      tmp = pykd.dbgCommand("k 1")
      if tmp.find("Wow64NotifyDebugger") > -1:
        pykd.dbgCommand(".effmach x86")

      stack_trace = pykd.dbgCommand("k")
      registers = pykd.dbgCommand("r")

      exploitable = None
      msec_path = None
      if self.exploitable_path is None:
        if self.mode == 32:
          msec_path = os.path.join(self.windbg_path, r"Debuggers\x86\winext")
        elif self.mode == 64:
          msec_path = os.path.join(self.windbg_path, r"Debuggers\x64\winext")
        elif self.mode == "arm":
          msec_path = os.path.join(self.windbg_path, r"Debuggers\arm\winext")
        else:
          raise Exception("Unknown mode %s, known ones are 32, 64 or 'arm'." % self.mode)
      else:
        msec_path = self.exploitable_path

      if msec_path is not None:
        full_msec_path = os.path.join(msec_path, r"msec.dll")
        if os.path.exists(full_msec_path):
          try:
            msec_handle = pykd.loadExt(full_msec_path)
            commandOutput = pykd.callExt(msec_handle, "exploitable", "")
            exploitable = commandOutput
          except:
            log("Error loading extension: " + str(sys.exc_info()[1]))

      try:
        if self.minidump_path is not None:
          pykd.dbgCommand(r".dump /m /u %s\\" % self.minidump_path)
          log("*** Minidump written at %s" % self.minidump_path)
      except:
        log("!!! Error saving minidump:" + str(sys.exc_info()[1]))

      ret = self.create_crash_data(registers, stack_trace, exploitable)
      
      print pykd.dbgCommand("k 10")
      print pykd.dbgCommand("r")
      print exploitable

      crash_data_buf = self.crash_data.dump_json()
      ret = self.crash_data.dump_dict()

      print
      print "Yep, we got a crash! \o/"
      print

    return ret
Пример #7
0
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
Пример #8
0
class CWinDbgInterface(object):
    def __init__(self,
                 program,
                 mode=32,
                 windbg_path=None,
                 exploitable_path=None):
        global timeout

        self.id = None
        self.mode = mode
        self.program = program
        self.exploitable_path = None
        self.windbg_path = windbg_path
        self.exploitable_path = exploitable_path
        try:
            self.handler = ExceptionHandler()
        except:
            self.handler = None
        self.minidump_path = None
        #self.minidump_path = r"C:\minidumps\\"

        if windbg_path is None:
            self.resolve_windbg_path()

        self.regs = {}
        self.stack = []
        self.pc_register = None
        self.disassembly = None
        self.exploitability = "Unknown"

        self.do_stop = False
        self.timer = None
        if os.getenv("NIGHTMARE_TIMEOUT"):
            timeout = float(os.getenv("NIGHTMARE_TIMEOUT"))
        self.timeout = timeout

    def resolve_windbg_path(self):
        try:
            reg = ConnectRegistry(None, HKEY_LOCAL_MACHINE)
            key = OpenKey(reg, r"SOFTWARE\Microsoft\Microsoft SDKs\Windows")
            if key:
                for i in range(QueryInfoKey(key)[0]):
                    value = EnumKey(key, i)
                    if value:
                        full_key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\\" + value
                        key2 = OpenKey(reg, full_key)
                        if key2:
                            name = QueryValueEx(key2, "ProductName")
                            name = name[0]
                            if name and name.startswith(
                                    "Microsoft Windows SDK for Windows"):
                                vals = QueryValueEx(key2, "InstallationFolder")
                                val = vals[0]
                                if val is not None:
                                    log("Found installation path at %s" % val)
                                    self.windbg_path = val
                                    break
                            CloseKey(key2)
            CloseKey(key)
        except WindowsError:
            print "Cannot resolve Windows SDKs path:", sys.exc_info()[1]
            print "Did you install Windows SDKs for Windows?"
        except:
            print "Cannot resolve Windows SDKs path:", sys.exc_info()[1]

    def parse_registers(self, regs):
        ret = re.findall("([a-z]{1}[a-z0-9]{1,})=([a-f0-9]+)", regs)
        tmp_regs = []
        for reg in ret:
            reg, val = reg
            self.regs[reg] = val
            tmp_regs.append([reg, val])

        if "rip" in self.regs:
            self.pc = int(self.regs["rip"], 16)
            self.pc_register = "rip"
        elif "eip" in self.regs:
            self.pc = int(self.regs["eip"], 16)
            self.pc_register = "eip"
        elif "pc" in self.regs:
            self.pc = int(self.regs["pc"], 16)
            self.pc_register = "pc"
        else:
            raise Exception("Cannot find the program counter register!")

        # Create the CCrashData object and fill registers and disassembly
        # data
        self.crash_data = CCrashData(self.pc, self.signal)

        # Extract the disassembly line from the "r" command
        lines = regs.split("\n")
        last_line = lines[len(lines) - 2]
        dis = re.findall("[a-z0-9]{1,} [a-z0-9]{2,} (.*)", last_line)
        self.disasm = dis[0].strip(" ")
        # ...and add it to the CCrashData object
        self.crash_data.disasm = [self.pc, self.disasm]

        # Add the registers in the order shown by WinDbg
        for reg in tmp_regs:
            reg, val = reg
            self.crash_data.add_data("registers", reg, int(val, 16))

    def parse_stack(self, stack):
        lines = stack.split("\n")
        i = 0
        for line in lines:
            fields = re.findall("([a-f0-9]+) ([a-f0-9]+) (.*)", line)
            if len(fields) > 0:
                addr, func = fields[0][1], fields[0][2]
                if addr.strip("-") == "":
                    addr = "FFFFFFFF"
                    if self.mode == 64:
                        addr += addr
                    addr = "0x%s" % addr
                self.stack.append([addr, func])
                self.crash_data.add_data("stack trace", "%d" % i,
                                         [int(addr, 16), func])
                i += 1

    def disasm_around(self):
        lines = pykd.dbgCommand("u %s-c L12" % self.pc_register)
        for line in lines.split("\n"):
            tmp = re.findall("([a-f0-9]{1,}) ([a-f0-9]{2,}) (.*)", line)
            if len(tmp) > 0:
                line = tmp[0]

                addr = line[0]
                dis = line[2]
                self.crash_data.add_data("disassembly", int(addr, 16), dis)

    def create_crash_data(self, regs, stack, exploitable):
        regs = regs.replace("`", "")
        stack = stack.replace("`", "")

        self.signal = self.handler.exception_info[1]
        self.parse_registers(regs)
        self.parse_stack(stack)
        self.parse_exploitable(exploitable)
        self.disasm_around()

    def parse_exploitable(self, exploitable):
        self.exploitability = "Unknown"
        if exploitable is not None:
            s = "Exploitability Classification: "
            l = exploitable.split("\n")
            for line in l:
                pos = line.find(s)
                if pos > -1:
                    self.exploitability = line[pos + len(s):]
                    self.crash_data.exploitable = self.exploitability
                    break

            last_line = l[len(l) - 2]
            self.exploitability_reason = last_line
            self.crash_data.add_data("exploitability", "reason", \
                                     self.exploitability_reason)

    def timeout_func(self):
        log("Timeout (%d seconds), killing the target..." % self.timeout)
        self.do_stop = True
        pykd.breakin()

    def run(self):
        if self.timeout != 0:
            self.timer = Timer(self.timeout, self.timeout_func)
            self.timer.start()

        self.do_stop = False
        self.id = pykd.startProcess(self.program, debugChildren=True)
        if self.handler is None:
            self.handler = ExceptionHandler()

        while not self.handler.exception_occurred and not self.do_stop:
            try:
                pykd.go()
            except:
                break

        if self.do_stop:
            try:
                pykd.dbgCommand(".kill")
            except:
                log("Exception killing target: %s" % str(sys.exc_info()[1]))
            return None

        if self.timer is not None:
            self.timer.cancel()

        ret = None
        if self.handler.exception_occurred:
            tmp = pykd.dbgCommand("k 1")
            if tmp.find("Wow64NotifyDebugger") > -1:
                pykd.dbgCommand(".effmach x86")

            stack_trace = pykd.dbgCommand("k")
            registers = pykd.dbgCommand("r")

            exploitable = None
            msec_path = None
            if self.exploitable_path is None:
                if self.mode == 32:
                    msec_path = os.path.join(self.windbg_path,
                                             r"Debuggers\x86\winext")
                elif self.mode == 64:
                    msec_path = os.path.join(self.windbg_path,
                                             r"Debuggers\x64\winext")
                elif self.mode == "arm":
                    msec_path = os.path.join(self.windbg_path,
                                             r"Debuggers\arm\winext")
                else:
                    raise Exception(
                        "Unknown mode %s, known ones are 32, 64 or 'arm'." %
                        self.mode)
            else:
                msec_path = self.exploitable_path

            if msec_path is not None:
                full_msec_path = os.path.join(msec_path, r"msec.dll")
                if os.path.exists(full_msec_path):
                    try:
                        msec_handle = pykd.loadExt(full_msec_path)
                        commandOutput = pykd.callExt(msec_handle,
                                                     "exploitable", "")
                        exploitable = commandOutput
                    except:
                        log("Error loading extension: " +
                            str(sys.exc_info()[1]))

            try:
                if self.minidump_path is not None:
                    pykd.dbgCommand(r".dump /m /u %s\\" % self.minidump_path)
                    log("*** Minidump written at %s" % self.minidump_path)
            except:
                log("!!! Error saving minidump:" + str(sys.exc_info()[1]))

            ret = self.create_crash_data(registers, stack_trace, exploitable)

            print pykd.dbgCommand("k 10")
            print pykd.dbgCommand("r")
            print exploitable

            crash_data_buf = self.crash_data.dump_json()
            ret = self.crash_data.dump_dict()

            print
            print "Yep, we got a crash! \o/"
            print

        return ret
Пример #9
0
class CWinDbgInterface(object):
  def __init__(self, program, timeout, mode=32, windbg_path=None, exploitable_path=None):
    reload(pykd)
    self.id = None
    self.mode = mode
    self.program = program
    self.exploitable_path = None
    self.windbg_path = windbg_path
    self.exploitable_path = exploitable_path

    try:
      self.handler = ExceptionHandler()
    except:
      self.handler = None
    self.minidump_path = None
    #self.minidump_path = r"C:\minidumps\\"

    if windbg_path is None:
      self.resolve_windbg_path()

    self.regs = {}
    self.stack = []
    self.pc_register = None
    self.disassembly = None
    self.exploitability = "Unknown"
    
    self.do_stop = False
    self.timer = None

    if str(timeout).lower() == "auto":
      self.timeout = timeout
    else:
      self.timeout = int(timeout)
    
    self.pykd_version = self.get_pykd_version()

  def resolve_windbg_path(self):
    try:
      reg = ConnectRegistry(None,HKEY_LOCAL_MACHINE)
      key = OpenKey(reg, r"SOFTWARE\Microsoft\Microsoft SDKs\Windows")
      if key:
        for i in range(QueryInfoKey(key)[0]):
          value = EnumKey(key, i)
          if value:
            full_key = r"SOFTWARE\Microsoft\Microsoft SDKs\Windows\\" + value
            key2 = OpenKey(reg, full_key)
            if key2:
              name = QueryValueEx(key2, "ProductName")
              name = name[0]
              if name and name.startswith("Microsoft Windows SDK for Windows"):
                vals = QueryValueEx(key2, "InstallationFolder")
                val = vals[0]
                if val is not None:
                  log("Found installation path at %s" % val)
                  self.windbg_path = val
                  break
              CloseKey(key2)
      CloseKey(key)
    except WindowsError:
      print "Cannot resolve Windows SDKs path:", sys.exc_info()[1]
      print "Did you install Windows SDKs for Windows?"
    except:
      print "Cannot resolve Windows SDKs path:", sys.exc_info()[1]

  def parse_registers(self, regs):
    ret = re.findall("([a-z]{1}[a-z0-9]{1,})=([a-f0-9]+)", regs)
    tmp_regs = []
    for reg in ret:
      reg, val = reg
      self.regs[reg] = val
      tmp_regs.append([reg, val])

    if "rip" in self.regs:
      self.pc = int(self.regs["rip"], 16)
      self.pc_register = "rip"
    elif "eip" in self.regs:
      self.pc = int(self.regs["eip"], 16)
      self.pc_register = "eip"
    elif "pc" in self.regs:
      self.pc = int(self.regs["pc"], 16)
      self.pc_register = "pc"
    else:
      raise Exception("Cannot find the program counter register!")

    # Create the CCrashData object and fill registers and disassembly
    # data
    self.crash_data = CCrashData(self.pc, self.signal)

    # Extract the disassembly line from the "r" command
    lines = regs.split("\n")
    last_line = lines[len(lines)-2]
    dis = re.findall("[a-z0-9]{1,} [a-z0-9]{2,} (.*)", last_line)
    self.disasm = None
    if dis is not None and len(dis) > 0:
      self.disasm = dis[0].strip(" ")

    # ...and add it to the CCrashData object
    self.crash_data.disasm = [self.pc, self.disasm]

    # Add the registers in the order shown by WinDbg
    for reg in tmp_regs:
      reg, val = reg
      self.crash_data.add_data("registers", reg, int(val, 16))

  def parse_stack(self, stack):
    lines = stack.split("\n")
    i = 0
    for line in lines:
      fields = re.findall("([a-f0-9]+) ([a-f0-9]+) (.*)", line)
      if len(fields) > 0:
        addr, func = fields[0][1], fields[0][2]
        if addr.strip("-") == "":
          addr = "FFFFFFFF"
          if self.mode == 64:
            addr += addr
          addr = "0x%s" % addr
        self.stack.append([addr, func])
        self.crash_data.add_data("stack trace", "%d" %i, [int(addr, 16), func])
        i += 1

  def disasm_around(self):
    try:
      lines = pykd.dbgCommand("u %s-c L12" % self.pc_register)
      for line in lines.split("\n"):
        tmp = re.findall("([a-f0-9]{1,}) ([a-f0-9]{2,}) (.*)", line)
        if len(tmp) > 0:
          line = tmp[0]

          addr = line[0]
          dis = line[2]
          self.crash_data.add_data("disassembly", int(addr, 16), dis)
    except:
      log("Error in disasm_around: %s" % str(sys.exc_info()[1]))

  def create_crash_data(self, regs, stack, exploitable):
    regs = regs.replace("`", "")
    stack = stack.replace("`", "")

    self.signal = self.handler.exception_info[1]
    self.parse_registers(regs)
    self.parse_stack(stack)
    self.parse_exploitable(exploitable)
    self.disasm_around()
  
  def parse_exploitable(self, exploitable):
    self.exploitability = "Unknown"
    if exploitable is not None:
      s = "Exploitability Classification: "
      l = exploitable.split("\n")
      for line in l:
        pos = line.find(s)
        if pos > -1:
          self.exploitability = line[pos+len(s):]
          self.crash_data.exploitable = self.exploitability
          break
      
      last_line = l[len(l)-2]
      self.exploitability_reason = last_line
      self.crash_data.add_data("exploitability", "reason", \
                               self.exploitability_reason)

  def timeout_func(self):
    log("Timeout (%d seconds), killing the target..." % self.timeout)
    self.do_stop = True
    try:
      pykd.breakin()
    except:
      # A race condition might happen in the timeout function and in 
      # such cases we must ignore the error.
      pass

  def check_cpu(self):
    while True:
      try:
        if self.pid is None:
          time.sleep(0.2)
          continue

        proc = psutil.Process(self.pid)
        if proc is None:
          break

        cpu = 0
        l = []
        for x in xrange(20):
          tmp = int(proc.cpu_percent(interval=0.1))
          cpu += tmp
          l.append(tmp)

        if cpu is not None and (cpu <= 100 or l.count(0) > 10):
          log("CPU at 0%, killing")
          self.do_stop = True
          pykd.breakin()
          break
        else:
          time.sleep(0.5)
      except psutil.NoSuchProcess:
        self.do_stop = True
        break

  def get_pykd_version(self):
    """  
    Gets the pykd version number 2 or 3.
    Returns: pykd version number
    """
    version = pykd.version  
    version_number = int(version.replace(',', '.').replace(' ', '').split('.')[1])  
    if version_number == 3:  
      return PYKD3
    elif version_number == 2:  
      return PYKD2
    return None

  def get_pid(self):
    if self.pykd_version == PYKD3:
      return pykd.getProcessSystemID()
    return pykd.getCurrentProcessId()

  def start_process(self):
    if not "ProcessDebugOptions" in dir(pykd):
      self.id = pykd.startProcess(self.program, debugChildren=True)
    else:
      self.id = pykd.startProcess(self.program, pykd.ProcessDebugOptions.DebugChildren)
    return self.id

  def run(self):
    self.do_stop = False
    try:
      self.id = self.start_process()
      self.pid = self.get_pid()
    except:
      log("Error launching process! %s" % str(sys.exc_info()[1]))
      return None

    if self.handler is None:
      self.handler = ExceptionHandler()

    if self.timeout is not None:
      if str(self.timeout).lower() == "auto":
        self.thread = Thread(target=self.check_cpu)
        self.thread.start()
      else:
        self.timer = Timer(self.timeout, self.timeout_func)
        self.timer.start()

    while not self.handler.exception_occurred and not self.do_stop:
      try:
        pykd.go()
      except:
        break

    if self.do_stop:
      try:
        pykd.dbgCommand(".kill")
      except:
        log("Exception killing target: %s" % str(sys.exc_info()[1]))
      return None

    if self.timer is not None:
      self.timer.cancel()

    ret = None
    if self.handler.exception_occurred:
      try:
        pykd.breakin()
        pykd.breakin()
      except:
        pass

      tmp = pykd.dbgCommand("k 1")
      if tmp.find("Wow64NotifyDebugger") > -1:
        pykd.dbgCommand(".effmach x86")

      registers = pykd.dbgCommand("r")
      stack_trace = pykd.dbgCommand("k")

      exploitable = None
      msec_path = None
      if self.exploitable_path is None:
        if self.mode == 32:
          msec_path = os.path.join(self.windbg_path, r"Debuggers\x86\winext")
        elif self.mode == 64:
          msec_path = os.path.join(self.windbg_path, r"Debuggers\x64\winext")
        elif self.mode == "arm":
          msec_path = os.path.join(self.windbg_path, r"Debuggers\arm\winext")
        else:
          raise Exception("Unknown mode %s, known ones are 32, 64 or 'arm'." % self.mode)
      else:
        msec_path = self.exploitable_path

      if msec_path is not None:
        full_msec_path = os.path.join(msec_path, r"msec.dll")
        if os.path.exists(full_msec_path):
          try:
            msec_handle = pykd.loadExt(full_msec_path)
            commandOutput = pykd.callExt(msec_handle, "exploitable", "")
            exploitable = commandOutput
          except:
            log("Error loading extension: " + str(sys.exc_info()[1]))

      try:
        if self.minidump_path is not None:
          pykd.dbgCommand(r".dump /m /u %s\\" % self.minidump_path)
          log("*** Minidump written at %s" % self.minidump_path)
      except:
        log("!!! Error saving minidump:" + str(sys.exc_info()[1]))

      ret = self.create_crash_data(registers, stack_trace, exploitable)
      
      print pykd.dbgCommand("k 10")
      print pykd.dbgCommand("r")
      print exploitable
      try:
        pykd.killAllProcesses()
      except:
        log("Error killing processes: " + str(sys.exc_info()[1]))

      crash_data_buf = self.crash_data.dump_json()
      ret = self.crash_data.dump_dict()

      print
      print "Yep, we got a crash! \o/"
      print

    return ret