├── README.md ├── docs ├── after1.png ├── after2.png ├── after3.png ├── before1.png ├── before2.png ├── before3.png └── menu.png ├── ida-plugin.json └── swift-ida.py /README.md: -------------------------------------------------------------------------------- 1 | # SwiftIDA 2 | 3 | This is a simple plugin for IDA to aid with reverse engineering languages that have multiple return values and non-standard calling conventions. It does this by defining a set of shortcuts to easily change a function's calling convention and make it return a multi-value tuple. Tested on Swift and Golang, but should work on many more languages too. 4 | 5 | ## Usage 6 | 7 | Download `swift-ida.py` and drop it in your IDA's `plugins` folder. After that, simply right-click on a function definition and you will see the `SwiftIDA` menu. 8 | 9 | ![menu](docs/menu.png) 10 | 11 | --- 12 | 13 | #### Before: 14 | 15 | ![before2](docs/before2.png) 16 | 17 | ![before1](docs/before1.png) 18 | 19 | ![before3](docs/before3.png) 20 | 21 | --- 22 | 23 | #### After: 24 | 25 | ![after2](docs/after2.png) 26 | 27 | ![after1](docs/after1.png) 28 | 29 | ![after3](docs/after3.png) 30 | 31 | ## References 32 | 33 | - https://github.com/swiftlang/swift/blob/main/docs/ABI/CallConvSummary.rst 34 | - https://hex-rays.com/blog/igors-tip-of-the-week-107-multiple-return-values 35 | - https://hex-rays.com/blog/igors-tip-of-the-week-51-custom-calling-conventions 36 | - https://docs.hex-rays.com/user-guide/user-interface/menu-bar/edit/functions#set-function-item-type 37 | -------------------------------------------------------------------------------- /docs/after1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ViRb3/swift-ida/88583b6cb6d8be61d372f5cf4f3accc73aebebcb/docs/after1.png -------------------------------------------------------------------------------- /docs/after2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ViRb3/swift-ida/88583b6cb6d8be61d372f5cf4f3accc73aebebcb/docs/after2.png -------------------------------------------------------------------------------- /docs/after3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ViRb3/swift-ida/88583b6cb6d8be61d372f5cf4f3accc73aebebcb/docs/after3.png -------------------------------------------------------------------------------- /docs/before1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ViRb3/swift-ida/88583b6cb6d8be61d372f5cf4f3accc73aebebcb/docs/before1.png -------------------------------------------------------------------------------- /docs/before2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ViRb3/swift-ida/88583b6cb6d8be61d372f5cf4f3accc73aebebcb/docs/before2.png -------------------------------------------------------------------------------- /docs/before3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ViRb3/swift-ida/88583b6cb6d8be61d372f5cf4f3accc73aebebcb/docs/before3.png -------------------------------------------------------------------------------- /docs/menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ViRb3/swift-ida/88583b6cb6d8be61d372f5cf4f3accc73aebebcb/docs/menu.png -------------------------------------------------------------------------------- /ida-plugin.json: -------------------------------------------------------------------------------- 1 | { "IDAMetadataDescriptorVersion": 1, 2 | "plugin": { 3 | "name": "Swift IDA", 4 | "entryPoint": "swift-ida.py" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /swift-ida.py: -------------------------------------------------------------------------------- 1 | import ida_idaapi 2 | import ida_kernwin 3 | import ida_hexrays 4 | import idc 5 | 6 | action_names = [] 7 | call_convs = ["swiftcall", "golang", "fastcall"] 8 | 9 | 10 | class SwiftIDA(ida_idaapi.plugin_t): 11 | flags = ida_idaapi.PLUGIN_HIDE 12 | comment = "SwiftIDA Plugin" 13 | help = "SwiftIDA Plugin" 14 | wanted_name = "SwiftIDA" 15 | wanted_hotkey = "" 16 | dialog = None 17 | 18 | def init(self): 19 | global action_names, call_convs 20 | 21 | for call_conv in call_convs: 22 | action_name = f"SwiftIDA:set_call_conv_{call_conv}" 23 | action = ida_kernwin.action_desc_t( 24 | action_name, 25 | f"Mark {call_conv}", 26 | generic_handler( 27 | lambda ea, c=call_conv: self.convert_to_call_conv(ea, c) 28 | ), 29 | ) 30 | ida_kernwin.register_action(action) 31 | action_names.append(action_name) 32 | 33 | for i in range(2, 9): 34 | action_name = f"SwiftIDA:make_multi_return_{i}" 35 | action = ida_kernwin.action_desc_t( 36 | action_name, 37 | f"Make return tuple{i}", 38 | generic_handler(lambda ea, i=i: self.make_multi_return(ea, i)), 39 | ) 40 | ida_kernwin.register_action(action) 41 | action_names.append(action_name) 42 | 43 | self.ui_hooks = SwiftIDAUIHooks() 44 | self.ui_hooks.hook() 45 | 46 | return ida_idaapi.PLUGIN_KEEP 47 | 48 | def parse_current_func_type(self, ea): 49 | type: str = idc.get_type(ea) 50 | if type is None: 51 | ida_kernwin.warning("The selected item is not a function definition!") 52 | return None, None, None 53 | print(f">>>SwiftIDA: Original type: {type}") 54 | 55 | base, args = type[:-1].split("(", 1) 56 | 57 | base_split = base.split(" ", 1) 58 | ret_type = base_split[0] 59 | 60 | if len(base_split) == 2: 61 | base = base_split[1] 62 | else: 63 | base = "" 64 | 65 | return ret_type, base, args 66 | 67 | def update_current_func_type(self, ea, ret_type: str, base: str, args: str): 68 | 69 | new_type = " ".join( 70 | part for part in [ret_type, base, f"func({args})"] if part != "" 71 | ) 72 | print(f">>>SwiftIDA: New type: {new_type}") 73 | 74 | result = idc.SetType(ea, new_type) 75 | if result != 1: 76 | raise Exception("Failed to set type") 77 | 78 | print(f">>>SwiftIDA: Type changed successfully") 79 | 80 | def convert_to_call_conv(self, ea, call_conv) -> bool: 81 | global call_convs 82 | 83 | ret_type, base, args = self.parse_current_func_type(ea) 84 | if ret_type is None or base is None or args is None: 85 | return False 86 | 87 | for item in call_convs: 88 | base = base.replace("__" + item, "") 89 | 90 | base = " ".join(part for part in ["__" + call_conv, base] if part != "") 91 | 92 | self.update_current_func_type(ea, ret_type, base, args) 93 | return True 94 | 95 | def make_multi_return(self, ea, i: int) -> bool: 96 | struct_name = f"swiftida_tuple{i}" 97 | if idc.get_struc_id(struct_name) == idc.BADADDR: 98 | struct_id = idc.add_struc(-1, struct_name, 0) 99 | for j in range(i): 100 | idc.add_struc_member(struct_id, f"o{j}", -1, idc.FF_QWORD, -1, 8) 101 | print(f">>>SwiftIDA: Created struct {struct_name}") 102 | 103 | ret_type, base, args = self.parse_current_func_type(ea) 104 | if ret_type is None or base is None or args is None: 105 | return False 106 | 107 | ret_type = struct_name 108 | 109 | self.update_current_func_type(ea, ret_type, base, args) 110 | return True 111 | 112 | 113 | class SwiftIDAUIHooks(ida_kernwin.UI_Hooks): 114 | def finish_populating_widget_popup(self, form, popup): 115 | global action_names 116 | if ida_kernwin.get_widget_type(form) not in [ 117 | ida_kernwin.BWN_DISASM, 118 | ida_kernwin.BWN_PSEUDOCODE, 119 | ]: 120 | return 121 | for name in action_names: 122 | ida_kernwin.attach_action_to_popup(form, popup, name, "SwiftIDA/") 123 | 124 | 125 | def generic_handler(callback): 126 | class Handler(ida_kernwin.action_handler_t): 127 | def __init__(self): 128 | ida_kernwin.action_handler_t.__init__(self) 129 | 130 | def activate(self, ctx): 131 | try: 132 | if ctx.widget_type == ida_kernwin.BWN_PSEUDOCODE: 133 | vu = ida_hexrays.get_widget_vdui(ctx.widget) 134 | if vu.item.citype == ida_hexrays.VDI_FUNC: 135 | ea = vu.item.f.entry_ea 136 | else: 137 | ea = vu.item.e.obj_ea 138 | else: 139 | # TODO: Support call operand in disassembly view 140 | ea = ida_kernwin.get_screen_ea() 141 | 142 | result = callback(ea) 143 | 144 | if result: 145 | if ctx.widget_type == ida_kernwin.BWN_PSEUDOCODE: 146 | vu = ida_hexrays.get_widget_vdui(ctx.widget) 147 | vu.refresh_view(True) 148 | except Exception as e: 149 | ida_kernwin.warning("There was an error, check logs") 150 | raise e 151 | return 1 152 | 153 | def update(self, ctx): 154 | return ida_kernwin.AST_ENABLE_ALWAYS 155 | 156 | return Handler() 157 | 158 | 159 | def PLUGIN_ENTRY(): 160 | return SwiftIDA() 161 | --------------------------------------------------------------------------------