text: from idaapi import *
  from idc import *
  from idautils import Assemble, Modules, DecodeInstruction
  from payload import Item
  
  
  class FindInstructionsForm(Form):
      def __init__(self):
          Form.__init__(self, r"""STARTITEM {id:iInstructions}
  BUTTON YES* Ok
  BUTTON CANCEL Cancel
  Find instruction(s)
  {FormChangeCb}
  
  Filters:
  <Exclude ASLR modules:{rASLR}>
  <Exclude DEP modules:{rDEP}>
  <Exclude non-executable segments:{rExec}>{cGroup1}>
  
  Options:
  <#Refreshes memory content before starting search process#Sync memory:{rSync}>
  <#Find regex expression (less speed, more flexibility)#Regex:{rRegex}>{cGroup2}>
  
  Find instruction(s):
  <#mov eax, 1; pop; pop; 33 C0; ret#:{iInstructions}>
  """, {
              'cGroup1': Form.ChkGroupControl(("rASLR", "rDEP", "rExec")),
              'cGroup2': Form.ChkGroupControl(("rSync", "rRegex")),
              'iInstructions': Form.StringInput(),
              'FormChangeCb': Form.FormChangeCb(self.OnFormChange)
          })
  
      def OnFormChange(self, fid):
          if GetProcessState() == DSTATE_NOTASK:
              self.SetControlValue(self.rASLR, False)
              self.SetControlValue(self.rDEP, False)
              self.SetControlValue(self.rSync, False)
  
              self.EnableField(self.rASLR, False)
              self.EnableField(self.rDEP, False)
              self.EnableField(self.rSync, False)
              self.SetFocusedField(self.iInstructions)
  
          return 1
  
  def AskInstructionsUsingForm():
      result = (False, "Cancelled")
      f = FindInstructionsForm()
      f.Compile()
  
      f.rASLR.checked = True
      f.rDEP.checked = True
      f.rExec.checked = True
  
      f.rSync.checked = True
      f.rRegex.checked = False
         
      ok = f.Execute()
      f.Free()
      if ok == 1:
          result = (True, (f.iInstructions.value, f.rASLR.checked, f.rDEP.checked, f.rExec.checked, f.rSync.checked, f.rRegex.checked))
      return result
  
   
  
  class SearchResultChoose(Choose2):
      def __init__(self, ealist, title):
          self.list = ealist
          global payload
          global ropviewer
          self.payload = payload
          self.rv = ropviewer
          self.copy_item_cmd_id = self.append_item_cmd_id = None
          Choose2.__init__(self, \
                           title, \
                           [["address", 10 | Choose2.CHCOL_PLAIN], \
                            ["segment", 10 | Choose2.CHCOL_PLAIN], \
                            ["code", 30 | Choose2.CHCOL_PLAIN]], \
                           popup_names = ["Insert", "Delete", "Edit", "Copy item"])
  
      def OnCommand(self, n, cmd_id):
          if cmd_id == self.copy_item_cmd_id:
              ropviewer.set_clipboard((0, "c", Item(self.list[n-1].ea, Item.TYPE_CODE)))
  
          return 0
  
      def OnClose (self):
          pass
  
      def OnGetLine (self, n):
          return self.list[n-1].columns
  
      def OnGetSize (self):
          return len (self.list)
  
      # dbl click / enter
      def OnSelectLine(self, n):
          Jump (self.list[n-1].ea)
  
      def set_copy_item_handler(self, cmd_id):
          self.copy_item_cmd_id = cmd_id
  
  class SearchResult:
      def __init__(self, ea):
          self.ea = ea
          self.columns = []
  
          name = SegName(ea)
          disasm = GetDisasmEx(ea, GENDSM_FORCE_CODE)
  
          self.columns.append ("%X" % ea)
          self.columns.append (name)
          self.columns.append (disasm)
  
  def assemble_code(instructions):
      re_opcode = re.compile('^[0-9a-f]{2} *', re.I)
      lines = instructions.split(";")
      bufs = []
      global payload
  
      for line in lines:
          if re_opcode.match(line):
              # convert from hex string to a character list then join the list to form one string
              buf = ''.join([chr(int(x, 16)) for x in line.split()])
          else:
              # assemble the instruction
              if payload.proc.supports_assemble():
                  ret, buf = Assemble(FirstSeg(), line)
                  if not ret:
                      return (False, "Failed to assemble instruction:"+line)
              else:
                  return (False, "Processor module can't assemble code. Please use regex option.")       
          # add the assembled buffer
          bufs.append(buf)
      buf = ''.join(bufs)
      bin_str = ' '.join(["%02X" % ord(x) for x in buf])
      return (True, bin_str)
  
  def get_disasm(ea, maxinstr=5):
      result = ""
      delim = "\n"
  
      i = 0
      while i<maxinstr:
          ins = DecodeInstruction(ea)
          if not ins:
              break
          
          disasm = GetDisasmEx(ea, GENDSM_FORCE_CODE)
          if not disasm:
              break
          result += disasm + delim
          ea += ins.size
          i += 1
      return result
      
  
  def compile_regex(s):
      try:
          regex = re.compile(s, re.I | re.DOTALL)
      except:
          return (False, "Could not compile regex.")
      return (True, regex)
  
  
  def match_regex(startEA, endEA, regex):
      result = BADADDR
  
      ea = startEA
      while ea < endEA:
          disasm = get_disasm(ea)
          if disasm:
              if regex.match(disasm):
                  result = ea
                  break
          ea += 1
      return result
      
  
  def FindInstructionsInSegments(segments, bin_str, exclASLR, exclDEP, exclNonExec, checkDllChars=False):
      ret = []
      cancelled = False
      isRegex = isinstance(bin_str, type(re.compile('foo')))
      curseg = 0
      maxseg = len(segments)
  
      # thedude had too much coffee
      thedude = ["       "  + "\n" \
                 "  (._.)"  + "\n" \
                 " /(  )\\" + "\n" \
                 "  |  |"   + "\n",
                 "    .  "  + "\n" \
                 " (._.)"   + "\n" \
                 " /(  )\\" + "\n" \
                 "  /  \\"  + "\n",
                 "    o  "  + "\n" \
                 "  (._.)"  + "\n" \
                 " /(  )\\" + "\n" \
                 "  |  |"   + "\n",
                 "    O  "  + "\n" \
                 " (._.)"   + "\n" \
                 " /(  )\\" + "\n" \
                 "  /  \\"  + "\n",
                 "    *  "  + "\n" \
                 "  (._.)"  + "\n" \
                 " /(  )\\" + "\n" \
                 "  |  |"   + "\n"]
  
      show_wait_box("Say hello to thedude!")
  
      nMatches = 0    
      for seg in segments:
          curseg += 1
          if (seg.perm & SEGPERM_EXEC) == 0 and exclNonExec:
              continue
          ea = sea = seg.startEA
          segname = SegName(ea)
          eea = seg.endEA
          if checkDllChars:
              dllchar = get_dll_characteristics(sea, eea-sea)
              if dllchar:
                  dynbase, nx = get_security_flags(dllchar)
                  if dynbase and exclASLR:
                      continue
                  if nx and exclDEP:
                      continue
  
  
          pos = 0
          if isRegex:
              while True:
                  ea = match_regex(ea, eea, bin_str)
                  if ea == BADADDR:
                      break
                  ret.append(ea)
                  ea += 1
                  nMatches += 1
                  if wasBreak():
                      cancelled = True
                      break
                  replace_wait_box("Segment: %d/%d (%s)\n0x%X-0x%X\nMatches: %d\n\n%s" % (curseg, maxseg, segname, ea, eea, nMatches,thedude[pos]))
                  pos += 1
                  pos %= len(thedude)
          else:
              while True:
                  ea = find_binary(ea, eea, bin_str, 16, SEARCH_DOWN)
                  if ea == BADADDR:
                      break
                  ret.append(ea)
                  ea += 1
                  nMatches += 1
                  if wasBreak():
                      cancelled = True
                      break
                  replace_wait_box("Segment: %d/%d (%s)\n0x%X-0x%X\nMatches: %d\n\n%s" % (curseg, maxseg, segname, ea, eea, nMatches,thedude[pos]))
                  pos += 1
                  pos %= len(thedude)
  
          if cancelled:
              break
                  
      hide_wait_box()
      
      if not ret:
          return (False, "Could not match [%s]" % bin_str if not isRegex else "regular expression")
      return (True, ret)   
        
  
  def FindInstructionsInModules(modules, bin_str, exclASLR, exclDEP, exclNonExec):
      segments = []
      for mod in modules:
          if mod.dynbase and exclASLR:
              continue
          if mod.nx and exclDEP:
              continue
  
          segments += get_segments(mod.base, mod.base + mod.size)
      return FindInstructionsInSegments(segments, bin_str, exclASLR, exclDEP, exclNonExec)
      
  
  
  def get_security_flags(dllchars):
      IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x40
      IMAGE_DLLCHARACTERISTICS_NX_COMPAT    = 0x100
  
      dynbase = dllchars & IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE != 0
      nx      = dllchars & IMAGE_DLLCHARACTERISTICS_NX_COMPAT != 0
      return (dynbase, nx)
      
  
  class ModuleInfo(object):   
      def __init__(self, mod):
          self.dynbase = self.nx = None
          self.name = mod.name
          self.base = mod.base
          self.size = mod.size
          self.rebase_to = mod.rebase_to
          self.dll_char = get_dll_characteristics(self.base, self.size)
          if self.dll_char:
              self.dynbase, self.nx = get_security_flags(self.dll_char)
          
          self.columns = []
          aslr = "N/A"
          dep = "N/A"
          if self.dll_char:
              aslr = "X" if self.dynbase else ""
              dep = "X" if self.dynbase else ""
          self.columns.append(self.name)
          self.columns.append("%X" % self.base)
          self.columns.append("%X" % self.size)
          self.columns.append(aslr)
          self.columns.append(dep)
  
  
  # -----------------------------------------------------------------------
  # TODO: add NOSEH
  def get_dll_characteristics(base, size):
      # minimal, bugged pe parser
      result = None
      if size >= 0x40:
          mz = DbgWord(base)
          if mz == 0x5A4D or mz == 0x4D5A:
              offs_pe = DbgDword(base+0x3C)
              if size > offs_pe + 2:
                  pe = DbgWord(base + offs_pe)
                  if pe == 0x4550:
                      if size > offs_pe + 0x5E + 2:
                          result = DbgWord(base + offs_pe + 0x5E)
      return result
                      
  def get_modules():
      results = []
                              
      for mod in Modules():
          results.append(ModuleInfo(mod))
      return results
  
  def get_segments(startEA=MinEA(), endEA=MaxEA()):
      segments = []
  
      seg = getseg(startEA)
      while seg and seg.endEA <= endEA:
          segments.append(seg)
          seg = get_next_seg(seg.startEA)
          
      return segments
  
  payload = None
  ropviewer = None
  
  
  class drgadgetplugin_t:
      def __init__(self, pl, rv):
          global payload
          global ropviewer
          
          payload = pl
          ropviewer = rv
          self.menucallbacks = [("Find gadgets", self.run, "Ctrl-F3")]
  
      # mandatory
      # must return list of tuples
      # (label of menu, callback)
      # or None if no callbacks should be installed
      def get_callback_list(self):
          global payload
          result = self.menucallbacks
          return result
      
      def run(self):
          success, s = AskInstructionsUsingForm()
          if success:
              findstr, excl_aslr, excl_dep, excl_nonexec, sync, regex = s
              if sync:
                  RefreshDebuggerMemory()
  
              if regex:
                  success, s = compile_regex(findstr)
              else:
                  success, s = assemble_code(findstr)
              if not success:
                  Warning(s)
                  return 0
              
              if GetProcessState() == DSTATE_NOTASK:
                  success, ret = FindInstructionsInSegments(get_segments(), s, excl_aslr, excl_dep, excl_nonexec)
              else:
                  success, ret = FindInstructionsInModules(get_modules(), s, excl_aslr, excl_dep, excl_nonexec)
                  
              if success:
                  results = []
                  for ea in ret:
                      results.append(SearchResult(ea))
                      
                  title = "Search result for: [%s]" % findstr
                  close_chooser(title)
                  c = SearchResultChoose(results, title)
                  c.Show()
                  c.set_copy_item_handler(c.AddCommand("Copy item"))
              else:
                  Warning(ret)
          else:
              Warning(s)
  
      def term(self):
          pass
  