├── IDA ├── README.md └── Renamer │ ├── README.md │ ├── demo.gif │ └── renamer.py ├── LICENSE └── README.md /IDA/README.md: -------------------------------------------------------------------------------- 1 | 2 | # IDA Utilities 3 | IDA utilities we developed to accelerate our security research projects. 4 | 5 | ## Tools 6 | | Tool | Description | 7 | | :---: |-------------| 8 | |Renamer| A plugin which helps in the process of naming functions based on their strings.| -------------------------------------------------------------------------------- /IDA/Renamer/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Renamer 3 | A plugin which helps in the process of naming functions based on their strings. 4 | 5 | ![Alt Text](demo.gif) 6 | 7 | 8 | ## How does it work 9 | The plugin walks you through all the functions in your binary. For each function it shows you what are the available strings in the function and let you choose what is the appropriate name for that function. Upon choosing name you'll jump to the next function. 10 | 11 | The default hot-key to activate the plugin is `Ctrl-Shift-N`. 12 | 13 | ## Installation 14 | Copy `renamer.py` to IDA plugin's directory, usually at: `C:\Program Files\IDA\plugins` 15 | 16 | ## Tested versions 17 | - v7.3 18 | 19 | ## Known Issues 20 | 21 | **AttributeError: ‘module’ object has no attribute ‘MAXSTR’** 22 | 23 | Some users report they receive a traceback related to MAXSTR not being found. It'a a bug in some versions of IDAPython API, specifically in the function `SetControlValue` which seems have broken in some API migration. 24 | > File "C:\Program Files\IDA Pro 7.3\python\ida_kernwin.py", line 25 | > 6924, in SetControlValue 26 | > tid, _ = self.ControlToFieldTypeIdAndSize(ctrl) File "C:\Program Files\IDA Pro 7.3\python\ida_kernwin.py", line 6951, in 27 | > ControlToFieldTypeIdAndSize 28 | > return (3, min(_ida_kernwin.MAXSTR, ctrl.size)) 29 | > AttributeError: 'module' object has no attribute 'MAXSTR' 30 | 31 | The solution is to manually edit `C:\Program Files\IDA Pro 7.x\python\ida_kernwin.py` and replace `_ida_kernwin.MAXSTR` with some big number (e.g. 65536). -------------------------------------------------------------------------------- /IDA/Renamer/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/claroty/ResearchTools/156b26975f1f0e565f58e2496902d90693448c09/IDA/Renamer/demo.gif -------------------------------------------------------------------------------- /IDA/Renamer/renamer.py: -------------------------------------------------------------------------------- 1 | import re 2 | from collections import defaultdict 3 | import string 4 | 5 | import ida_kernwin 6 | from idaapi import * 7 | from idautils import * 8 | from idc import * 9 | 10 | 11 | ############ TODO ########## 12 | # 1. Bug: when selecting a line in the Chooser and moving to the next function, the same line (index wise) will not be clickable (only after double-click). 13 | ############################ 14 | 15 | 16 | def clean_string(st): 17 | st = st.replace("()", "") 18 | st = st.replace("\\", "") 19 | st = st.replace("/", "") 20 | if " " in st: 21 | st = string.capwords(st) 22 | st = st.replace(" ", "") 23 | return st 24 | 25 | 26 | class StringsManager(): 27 | def __init__(self): 28 | # FunctionAddress: ["str1", "str2", "str3"..] 29 | self.dict_strings = defaultdict(list) 30 | self.current_function_index = 0 31 | self.list_keys = [] 32 | self.count_total_strings = 0 33 | self.count_processed_strings = 0 34 | self.build_strings_tree() 35 | 36 | def build_strings_tree(self): 37 | for st in Strings(): 38 | str_addr = st.ea 39 | str_name = str(st) 40 | func_addr_name = self.get_parent_func(str_addr) 41 | func_addr = self.func_name_to_addr(func_addr_name) 42 | if func_addr: 43 | self.dict_strings[func_addr].append(str_name) 44 | self.count_total_strings += 1 45 | self.list_keys = self.dict_strings.keys() 46 | self.list_keys.sort() 47 | 48 | def func_name_to_addr(self, func_name): 49 | for func_addr in Functions(): 50 | curr_func_name = GetFunctionName(func_addr) 51 | if curr_func_name == func_name: 52 | return func_addr 53 | return None 54 | 55 | def get_xref_list(self, addr): 56 | xrefs = [] 57 | for addr in XrefsTo(addr, flags=0): 58 | xrefs.append(addr.frm) 59 | return xrefs 60 | 61 | def get_func_name(self, addr): 62 | try: 63 | return GetFunctionName(addr) 64 | except Exception as e: 65 | return "" 66 | 67 | def get_parent_func(self, addr): 68 | xref_list = self.get_xref_list(addr) 69 | if not xref_list: 70 | return None 71 | fname = self.get_func_name(xref_list[0]) 72 | return fname 73 | 74 | def rename_current_address(self, new_name): 75 | addr = self.get_current_function_addr() 76 | MakeNameEx(addr, new_name, 0x800) 77 | 78 | def get_strings_for_current_function(self): 79 | addr = self.get_current_function_addr() 80 | return self.dict_strings.get(addr) 81 | 82 | def get_current_function_name(self): 83 | return self.get_func_name(self.list_keys[self.current_function_index]) 84 | 85 | def get_current_function_addr(self): 86 | return self.list_keys[self.current_function_index] 87 | 88 | def next_function(self): 89 | self.current_function_index = min(len(self.list_keys)-1, self.current_function_index + 1) 90 | self.claim_strings_of_current_function() 91 | 92 | def prev_function(self): 93 | self.current_function_index = max(0, self.current_function_index - 1) 94 | 95 | def set_function_index(self, index): 96 | self.current_function_index = index 97 | 98 | def claim_strings_of_current_function(self): 99 | self.count_processed_strings += len(self.get_strings_for_current_function()) 100 | 101 | def is_empty(self): 102 | return len(self.list_keys) == 0 103 | 104 | def get_function_names(self): 105 | list_funcs = [] 106 | for key_func_addr in self.list_keys: 107 | func_name = self.get_func_name(key_func_addr) 108 | list_funcs.append("{} ({})".format(hex(key_func_addr), func_name)) 109 | return list_funcs 110 | 111 | # -------------------------------------------------------------------------- 112 | 113 | # https://github.com/idapython/src/blob/master/pywraps/py_kernwin_choose.py 114 | class ChooserStrings(ida_kernwin.Choose): 115 | """ 116 | A simple chooser to be used as an embedded chooser 117 | """ 118 | def __init__(self, title, init_items): 119 | ida_kernwin.Choose.__init__(self, 120 | title, 121 | [ ["Strings", 50 | ida_kernwin.Choose.CHCOL_PLAIN], ], 122 | flags=ida_kernwin.Choose.CH_QFLT | ida_kernwin.Choose.CH_CAN_REFRESH | ida_kernwin.Choose.CHCOL_PLAIN | ida_kernwin.Choose.CH_NOIDB, 123 | embedded=True, # Must be Embedded because it's inside a Form 124 | width=50, 125 | height=8) 126 | self.items = init_items #[ ["teststring %04d" % x] for x in range(1000) ] 127 | self.icon = 0 128 | 129 | def GetItems(self): 130 | return self.items 131 | 132 | def SetItems(self, items): 133 | self.items = [] if items is None else items 134 | 135 | def OnClose(self): 136 | pass 137 | 138 | def OnGetLine(self, n): 139 | return self.items[n] 140 | 141 | def OnGetSize(self): 142 | return len(self.items) 143 | 144 | def OnSelectLine(self, n): 145 | return (ida_kernwin.Choose.ALL_CHANGED, ) 146 | 147 | def OnEditLine(self, n): 148 | return (ida_kernwin.Choose.ALL_CHANGED, ) 149 | 150 | 151 | class RenameForm(ida_kernwin.Form): 152 | def __init__(self): 153 | self.strings_manager = StringsManager() 154 | self.invert = False 155 | F = ida_kernwin.Form 156 | F.__init__( 157 | self, 158 | r"""Form Rename Helper 159 | {FormChangeCb} 160 | This form will help you to rename all the functions in your binary. You can even use ctrl-f to search for strings quickly. 161 | Overall strings: {cStringsProcessed}/{cStringsTotal} ({cStringsPercentage}) strings processed. 162 | 163 | Now working on function: {cCurrentFunction} ({cCurrentFunctionStatus}) 164 | 165 | 166 | 167 |