├── rsrc └── pyhelper.gif ├── README.md └── idapyhelper.py /rsrc/pyhelper.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patois/IDAPyHelper/HEAD/rsrc/pyhelper.gif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IDAPyHelper 2 | 3 | IDAPyHelper is a script for the Interactive Disassembler that helps writing IDAPython scripts and plugins. 4 | 5 | It does so by acquiring all names accessible via IDAPython and makes them available in a browsable list that can be sorted, scanned (Alt-T) and filtered (Ctrl-F) arbitrarily. Double clicking a list entry opens a separate view that displays the entry's docstring, if available. Pressing Alt-E opens the entire module for viewing. 6 | 7 | This IDAPython project is compatible with Python3. For compatibility with older versions of IDA, you may want to check out the Python2 branch of this project. 8 | 9 | ![IDAPyHelper animated gif](/rsrc/pyhelper.gif?raw=true) -------------------------------------------------------------------------------- /idapyhelper.py: -------------------------------------------------------------------------------- 1 | import ida_kernwin, ida_diskio, ida_pro 2 | import os, inspect, sys 3 | 4 | __author__ = "Dennis Elser" 5 | 6 | DBG = False 7 | 8 | # ----------------------------------------------------------------------------- 9 | def is_ida_version(min_ver_required): 10 | return ida_pro.IDA_SDK_VERSION >= min_ver_required 11 | 12 | # -------------------------------------------------------------------------- 13 | class FileViewer(ida_kernwin.Form): 14 | """A form that displays a text file's content.""" 15 | def __init__(self, title, content): 16 | ida_kernwin.Form.__init__(self, 17 | ("BUTTON YES NONE\n" 18 | "BUTTON NO NONE\n" 19 | "BUTTON CANCEL NONE\n" 20 | "%s\n\n" 21 | "<##Docstring##:{cbEditable}>" 22 | ) % title, 23 | {'cbEditable': ida_kernwin.Form.MultiLineTextControl(text=content, 24 | flags=ida_kernwin.textctrl_info_t.TXTF_READONLY | 25 | ida_kernwin.textctrl_info_t.TXTF_FIXEDFONT)}) 26 | 27 | # -------------------------------------------------------------------------- 28 | class DocstringViewer(ida_kernwin.Form): 29 | """A form that displays a docstring.""" 30 | def __init__(self, title, docstr): 31 | ida_kernwin.Form.__init__(self, 32 | ("BUTTON YES NONE\n" 33 | "BUTTON NO NONE\n" 34 | "BUTTON CANCEL NONE\n" 35 | "%s\n\n" 36 | "<##Docstring##:{cbEditable}>" 37 | ) % title, 38 | {'cbEditable': ida_kernwin.Form.MultiLineTextControl(text=docstr, 39 | flags=ida_kernwin.textctrl_info_t.TXTF_READONLY | 40 | ida_kernwin.textctrl_info_t.TXTF_FIXEDFONT)}) 41 | 42 | # -------------------------------------------------------------------------- 43 | class ChooserData: 44 | """Structure that holds information for the chooser to display.""" 45 | icon_ids = {"str": 80, 46 | "int": 8, 47 | "class": 89, 48 | "function": 81, 49 | "method": 99} 50 | def __init__(self, mod_name, sym_name, file_name): 51 | self.mod_name = mod_name 52 | self.sym_name = sym_name 53 | self.file_name = file_name 54 | self.doc_str = "" 55 | self.sym_type = "" 56 | self.sym_value = "" 57 | self.line_no = "" 58 | 59 | def get_icon(self): 60 | return self.icon_ids[self.sym_type] 61 | 62 | # -------------------------------------------------------------------------- 63 | class PyHelperChooser(ida_kernwin.Choose): 64 | """A chooser filled with information about IDAPython bindings. 65 | Output is supposed to be filtered with Ctrl-F.""" 66 | def __init__(self, title, nb=5): 67 | ida_kernwin.Choose.__init__(self, 68 | title, 69 | [ ["Module", 10 | ida_kernwin.Choose.CHCOL_PLAIN], 70 | ["Symbol", 20 | ida_kernwin.Choose.CHCOL_PLAIN], 71 | ["Documentation", 10 | ida_kernwin.Choose.CHCOL_PLAIN], 72 | ["Type", 10 | ida_kernwin.Choose.CHCOL_PLAIN], 73 | ["Value", 10 | ida_kernwin.Choose.CHCOL_HEX], 74 | ["Line number", 10 | ida_kernwin.Choose.CHCOL_DEC],], 75 | flags=ida_kernwin.Choose.CH_QFLT | ida_kernwin.Choose.CH_NOIDB) 76 | self.items = [] 77 | self.icon = 0 78 | self.build_items() 79 | 80 | def build_items(self): 81 | subdir = "" 82 | if is_ida_version(740): 83 | subdir, _, _, _, _ = sys.version_info 84 | pydir = ida_diskio.idadir(os.path.join("python", str(subdir))) 85 | for mod_name in os.listdir(pydir): 86 | if mod_name.endswith(".py"): 87 | mod_name, _ = os.path.splitext(mod_name) 88 | if mod_name not in ["init", "idaapi"]: 89 | mod = __import__(mod_name) 90 | file_name = mod.__file__ 91 | for sym_name, obj in inspect.getmembers(mod): 92 | 93 | if inspect.isfunction(obj): 94 | data = ChooserData(mod_name, sym_name, file_name) 95 | data.sym_type = "function" 96 | data.line_no = "%d" % obj.__code__.co_firstlineno 97 | data.doc_str = inspect.getdoc(obj) 98 | self.items.append(data) 99 | 100 | elif inspect.isclass(obj): 101 | data = ChooserData(mod_name, sym_name, file_name) 102 | data.sym_type = "class" 103 | data.doc_str = inspect.getdoc(obj) 104 | self.items.append(data) 105 | 106 | elif inspect.ismethod(obj): 107 | data = ChooserData(mod_name, sym_name, file_name) 108 | data.sym_type = "method" 109 | data.line_no = "%d" % obj.im_func.__code__.co_firstlineno 110 | data.doc_str = inspect.getdoc(obj) 111 | self.items.append(data) 112 | 113 | elif type(obj) == int: 114 | data = ChooserData(mod_name, sym_name, file_name) 115 | data.sym_type = "int" 116 | data.sym_value = "0x%x" % (obj) 117 | self.items.append(data) 118 | 119 | elif type(obj) == str: 120 | data = ChooserData(mod_name, sym_name, file_name) 121 | data.sym_type = "str" 122 | data.sym_value = str(obj) 123 | self.items.append(data) 124 | else: 125 | if DBG: 126 | ida_kernwin.msg("%s: %s" % (type(obj), sym_name)) 127 | 128 | def OnGetLine(self, n): 129 | data = self.items[n] 130 | return [data.mod_name, 131 | data.sym_name, 132 | "%s" % data.doc_str, 133 | data.sym_type, 134 | data.sym_value, 135 | data.line_no] 136 | 137 | def OnGetIcon(self, n): 138 | return self.items[n].get_icon() 139 | 140 | def OnGetSize(self): 141 | return len(self.items) 142 | 143 | def OnSelectLine(self, n): 144 | data = self.items[n] 145 | postfix = " (%s)" % data.mod_name if len(data.mod_name) else "" 146 | if not data.doc_str: 147 | ida_kernwin.msg("No documentation available for \"%s\"\n" % data.sym_name) 148 | else: 149 | f = DocstringViewer("%s%s" % (data.sym_name, postfix), data.doc_str) 150 | f.modal = False 151 | f.openform_flags = ida_kernwin.PluginForm.WOPN_TAB 152 | f, args = f.Compile() 153 | f.Open() 154 | return (ida_kernwin.Choose.NOTHING_CHANGED, ) 155 | 156 | def OnEditLine(self, n): 157 | fn = self.items[n].file_name 158 | if fn: 159 | # ghetto 160 | if fn.endswith(".pyc"): 161 | fn = fn[:-1] 162 | with open(fn) as fin: 163 | f = FileViewer("%s" % (os.path.basename(fn)), fin.read()) 164 | f.modal = False 165 | f.openform_flags = ida_kernwin.PluginForm.WOPN_TAB 166 | f, args = f.Compile() 167 | f.Open() 168 | return (ida_kernwin.Choose.NOTHING_CHANGED, ) 169 | 170 | try: 171 | pyhelper 172 | except: 173 | pyhelper=PyHelperChooser("IDAPyHelper") 174 | else: 175 | if DBG: 176 | del pyhelper 177 | pyhelper=PyHelperChooser("IDAPyHelper") 178 | pyhelper.Show() 179 | --------------------------------------------------------------------------------