├── README.md ├── binja_export.py ├── binja_import.py ├── ida_export.py └── ida_import.py /README.md: -------------------------------------------------------------------------------- 1 | # New version 2 | 3 | A rewrite/fork of this with more features that's better maintained is available at https://zznop.github.io/bnida/ 4 | 5 | We're still leaving this plugin around and will accept PRs but expect that most future development on this feature will occur on that plugin. 6 | 7 | Additionally, for a plugin that supports fewer types of data synchronized but can do it live, you may also be interested in [revsync](https://github.com/lunixbochs/revsync) 8 | 9 | # Binary Ninja Importer 10 | 11 | Utility for transfering analysis information between Binary Ninja and IDA pro. This utility has four parts: 12 | 1. An IDA plugin that exports analysis data to a json file 13 | 2. A Binary Ninja plugin/script that imports the analysis data that has been exported from IDA 14 | 3. A Binary Ninja plugin/script that exports analysis data to a json file 15 | 4. An IDA plugin that imports analysis data that has been exported from Binary Ninja 16 | 17 | ## Usage 18 | 19 | Install the `binja_import.py` and `binja_export.py` scripts into your Binary Ninja user directory. https://docs.binary.ninja/getting-started/index.html#user-folder 20 | 21 | ### IDA->Binary Ninja 22 | 23 | 1. Open your IDA database 24 | 2. Click `File->Script File` and select the `ida_export.py` 25 | 3. Input the filename of the file you want to write the exported data to 26 | 4. Click Ok. Analysis data will be written to the json file. 27 | 5. Open your BN database for the same binary 28 | 6. Click `tools->Import data to BN` 29 | 7. Enter the file path to the json file 30 | 8. Click ok. Your database will then be updated with the analysis data from IDA. 31 | 32 | #### Supported Analysis data 33 | 34 | 1. Function 35 | start address 36 | line comment, function comment 37 | no-return status 38 | 2. Strings 39 | string objects (start, length, type) 40 | string cross-references 41 | 42 | #### Unsupported Analysis data 43 | 44 | 1. Segments 45 | 2. Global non-string data 46 | 3. Local variable names and types 47 | 4. Standard types and enumerations 48 | 5. Non-function comments (currently unsupported by Binary Ninja) 49 | 50 | ### Binary Ninja->IDA 51 | 52 | 1. Open your Binary Ninja database 53 | 2. Click `tools->Export data from BN` 54 | 3. Enter the filename of the file you want to write the exported data to 55 | 4. Click Ok. Analysis data will be written to the json file 56 | 5. Open your IDA database for the same binary 57 | 6. Click `File->Script File` and select the `ida_import.py` 58 | 7. Select the json file 59 | 8. Click ok. Your database will then be updated with the analysis data from BN. 60 | 61 | Analysis data for IDA to Binary Ninja is currently limited to comments and symbol names. 62 | 63 | ## Notes 64 | 65 | Currently this tool does *NOT* do any deconfliction. Thus if you have data you don't wish to import you must edit the intermediate json file directly. Future revisions of the tool we hope will have interactive deconfliction and allow for regular expression based inclusion/exclusion. 66 | 67 | -------------------------------------------------------------------------------- /binja_export.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (c) 2018 zznop 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy 6 | # of this software and associated documentation files (the "Software"), to 7 | # deal in the Software without restriction, including without limitation the 8 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 | # sell copies of the Software, and to permit persons to whom the Software is 10 | # furnished to do so, subject to the following conditions: 11 | # 12 | # The above copyright notice and this permission notice shall be included in 13 | # all copies or substantial portions of the Software. 14 | # 15 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21 | # IN THE SOFTWARE. 22 | 23 | import json 24 | from optparse import OptionParser 25 | from binaryninja import * 26 | 27 | def get_functions(bv): 28 | """Populate dictionary of function names and offsets 29 | """ 30 | functions = {} 31 | for func in bv.functions: 32 | functions[func.start] = func.name 33 | return functions 34 | 35 | def get_symbols(bv): 36 | """Populate dictionary of symbol names 37 | """ 38 | symbols = {} 39 | for symbol in bv.get_symbols(): 40 | symbols[symbol.address] = symbol.name 41 | return symbols 42 | 43 | def get_comments(bv): 44 | """Populate dictionary of comments 45 | """ 46 | comments = {} 47 | for func in bv: 48 | for addr in func.comments: 49 | comments[addr] = func.get_comment_at(addr) 50 | return comments 51 | 52 | def get_sections(bv): 53 | """Populate dictionary of sections 54 | """ 55 | sections = {} 56 | for section_name in bv.sections: 57 | section = bv.get_section_by_name(section_name) 58 | sections[section.name] = { 59 | 'start' : section.start, 60 | 'end' : section.end 61 | } 62 | 63 | return sections 64 | 65 | def export_bn(json_file, bv): 66 | """Construct json array of everything we want to export 67 | """ 68 | json_array = {} 69 | json_array["sections"] = get_sections(bv) 70 | json_array["names"] = get_symbols(bv) 71 | json_array["comments"] = get_comments(bv) 72 | 73 | try: 74 | with open(json_file, "wb") as f: 75 | f.write(json.dumps(json_array, indent=4)) 76 | except Exception as ex: 77 | return False, "Failed to create JSON file {} {}".format(json_file, ex) 78 | 79 | return True, None 80 | 81 | class GetOptions: 82 | def __init__(self, interactive=False): 83 | # from BN UI 84 | if interactive: 85 | json_file = OpenFileNameField("Export json file") 86 | get_form_input([json_file], "BN Export Options") 87 | if json_file.result == '': 88 | self.json_file = None 89 | else: 90 | self.json_file = json_file.result 91 | return 92 | 93 | # headless 94 | usage = "usage: %prog " 95 | parser = OptionParser(usage=usage) 96 | (options, args) = parser.parse_args() 97 | self.bn_database = args[0] 98 | self.json_file = args[1] 99 | self.usage = parser.get_usage() 100 | 101 | class ExportBNInBackground(BackgroundTaskThread): 102 | def __init__(self, bv, options): 103 | global task 104 | BackgroundTaskThread.__init__(self, "Exporting data from BN", False) 105 | self.json_file = options.json_file 106 | self.options = options 107 | self.bv = bv 108 | task = self 109 | 110 | def run(self): 111 | (success, error_message) = export_bn(self.options.json_file, self.bv) 112 | if not success: 113 | log_error(error_message) 114 | 115 | def export_bn_headless(): 116 | """Export data running as headless script 117 | """ 118 | options = GetOptions(False) 119 | bv = BinaryViewType.get_view_of_file(options.bn_database) 120 | bv.update_analysis_and_wait() 121 | (success, error_message) = export_bn(options.json_file, bv) 122 | if not success: 123 | print "Error: {}".format(error_message) 124 | 125 | def export_bn_in_background(bv): 126 | """Export data in background from BN UI 127 | """ 128 | options = GetOptions(True) 129 | background_task = ExportBNInBackground(bv, options) 130 | background_task.start() 131 | 132 | if __name__ == '__main__': 133 | export_bn_headless() 134 | else: 135 | PluginCommand.register("Export data from BN", "Export data from BN", export_bn_in_background) 136 | -------------------------------------------------------------------------------- /binja_import.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2015-2017 Vector 35 LLC 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to 6 | # deal in the Software without restriction, including without limitation the 7 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | # sell copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | # IN THE SOFTWARE. 21 | 22 | import json 23 | from optparse import OptionParser 24 | 25 | from binaryninja.binaryview import BinaryViewType 26 | from binaryninja.types import (Type, Symbol) 27 | from binaryninja.enums import (SymbolType, IntegerDisplayType, InstructionTextTokenType) 28 | from binaryninja.plugin import PluginCommand 29 | from binaryninja.plugin import BackgroundTaskThread 30 | from binaryninja.interaction import (ChoiceField, OpenFileNameField, get_form_input) 31 | from binaryninja.log import log_error 32 | 33 | 34 | def log(message, verbose): 35 | if task is None: 36 | if verbose: 37 | print message 38 | else: 39 | task.progress = message 40 | 41 | 42 | def import_ida(json_file, bv, options): 43 | if json_file is None: 44 | return False, "No json file specified" 45 | 46 | imported = None 47 | 48 | try: 49 | f = open(json_file, "rb") 50 | imported = json.load(f) 51 | except Exception as e: 52 | return False, "Failed to parse json file {} {}".format(json_file, e) 53 | 54 | resolved_functions = imported["functions"] 55 | resolved_strings = imported["strings"] 56 | 57 | # TODO: import segments 58 | # TODO: Handle Conflicts 59 | 60 | if options.import_functions: 61 | log("Applying import data", options.verbose) 62 | for name, rec in resolved_functions.items(): 63 | bv.add_function(rec["start"]) 64 | func = bv.get_function_at(rec["start"]) 65 | if name != ("sub_%x" % rec["start"]): 66 | func.name = name 67 | 68 | if options.import_comments: 69 | if "comment" in rec: 70 | func.comment = rec["comment"] 71 | 72 | if "comments" in rec: 73 | for comment, addr in rec["comments"].items(): 74 | func.set_comment_at(addr, comment) 75 | 76 | if "can_return" in rec: 77 | func.can_return = rec["can_return"] 78 | 79 | bv.update_analysis_and_wait() 80 | 81 | if options.import_strings: 82 | log("Importing string types", options.verbose) 83 | for addr, (name, length, t, data_refs) in resolved_strings.items(): 84 | bv.define_user_data_var(int(addr), Type.array(Type.int(1, None, "char"), length)) 85 | if options.import_strings_names: 86 | bv.define_user_symbol(Symbol(SymbolType.DataSymbol, int(addr), name)) 87 | for ref in data_refs: # references to this data 88 | for block in bv.get_basic_blocks_at(ref): # find any references in code 89 | for i in block.get_disassembly_text(): # go through all the instructions in the block 90 | if i.address == ref: 91 | for token in i.tokens: 92 | if token.type == InstructionTextTokenType.PossibleAddressToken: 93 | print "setting token", i.address, token.value, token.operand, IntegerDisplayType.PointerDisplayType, block.arch 94 | block.function.set_int_display_type(i.address, token.value, token.operand, IntegerDisplayType.PointerDisplayType, block.arch) 95 | break 96 | 97 | log("Updating Analysis", options.verbose) 98 | bv.update_analysis_and_wait() 99 | return True, None 100 | 101 | 102 | class GetOptions: 103 | def __init__(self, interactive=False): 104 | if interactive: 105 | import_strings_choice = ChoiceField("Import Strings", ["Yes", "No"]) 106 | import_string_name_choice = ChoiceField("Import String Names", ["Yes", "No"]) 107 | import_function_choice = ChoiceField("Import Functions", ["Yes", "No"]) 108 | import_comment_choice = ChoiceField("Import Comments", ["Yes", "No"]) 109 | json_file = OpenFileNameField("Import json file") 110 | get_form_input([json_file, import_strings_choice, import_string_name_choice, import_function_choice, 111 | import_comment_choice], "IDA Import Options") 112 | 113 | self.import_strings = import_strings_choice.result == 0 114 | self.import_strings_names = import_string_name_choice.result == 0 115 | self.import_functions = import_function_choice.result == 0 116 | self.import_comments = import_comment_choice.result == 0 117 | self.verbose = True 118 | if json_file.result == '': 119 | self.json_file = None 120 | else: 121 | self.json_file = json_file.result 122 | self.output_name = None 123 | else: 124 | usage = "usage: %prog [options] " 125 | parser = OptionParser(usage=usage) 126 | 127 | parser.add_option("-q", "--quiet", 128 | dest="verbose", 129 | action="store_false", 130 | default=True, 131 | help="Don't display automatic actions") 132 | parser.add_option("-s", "--no-strings", 133 | dest="import_strings", 134 | action="store_false", 135 | default=True, 136 | help="Don't import string data") 137 | parser.add_option("-n", "--no-string-names", 138 | dest="import_strings_names", 139 | action="store_false", 140 | default=True, 141 | help="Don't import string names") 142 | parser.add_option("-o", "--output-name", 143 | dest="output_name", 144 | action="store", 145 | default=None, 146 | help="Specify output name of bndb. Defaults to .bndb") 147 | parser.add_option("-f", "--no-functions", 148 | dest="import_functions", 149 | action="store_false", 150 | default=True, 151 | help="Don't import function starts") 152 | parser.add_option("-c", "--no-comments", 153 | dest="import_comments", 154 | action="store_false", 155 | default=True, 156 | help="Don't import comments") 157 | 158 | (options, args) = parser.parse_args() 159 | self.import_strings = options.import_strings 160 | self.import_strings_names = options.import_strings_names 161 | self.import_functions = options.import_functions 162 | self.import_comments = options.import_comments 163 | self.verbose = options.verbose 164 | self.json_file = args[0] 165 | self.input_file = args[0] 166 | self.output_name = options.output_name 167 | if self.output_name is None: 168 | self.output_name = self.input_file + ".bndb" 169 | self.usage = parser.get_usage() 170 | 171 | 172 | def main(): 173 | options = GetOptions() 174 | if options.json_file is None or options.output_name is None: 175 | print options.usage 176 | return 177 | 178 | log("Loading the binary: {}".format(options.input_file), options) 179 | 180 | bv = BinaryViewType.get_view_of_file(options.input_file) 181 | if bv is None: 182 | print "Could not open {}".format(options.input_file) 183 | return False 184 | 185 | (success, error_message) = import_ida(options.json_file, bv, options.output_name, options) 186 | if not success: 187 | print "Error:", error_message 188 | print options.usage 189 | return 190 | 191 | log("Writing out {}".format(options.output_name), options.verbose) 192 | bv.create_database(options.output_name) 193 | return 194 | 195 | 196 | class ImportIDAInBackground(BackgroundTaskThread): 197 | def __init__(self, bv, options): 198 | global task 199 | BackgroundTaskThread.__init__(self, "Importing data from IDA", False) 200 | self.json_file = options.json_file 201 | self.options = options 202 | self.bv = bv 203 | task = self 204 | 205 | def run(self): 206 | (success, error_message) = import_ida(self.options.json_file, self.bv, self.options) 207 | if not success: 208 | log_error(error_message) 209 | 210 | 211 | def import_ida_in_background(bv): 212 | options = GetOptions(True) 213 | background_task = ImportIDAInBackground(bv, options) 214 | background_task.start() 215 | 216 | 217 | if __name__ == "__main__": 218 | main() 219 | else: 220 | PluginCommand.register("Import data from IDA", "Import data from IDA", import_ida_in_background) 221 | -------------------------------------------------------------------------------- /ida_export.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2015-2017 Vector 35 LLC 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to 6 | # deal in the Software without restriction, including without limitation the 7 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | # sell copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | # IN THE SOFTWARE. 21 | 22 | import idc 23 | import idautils 24 | import idaapi 25 | import json 26 | 27 | DefaultSectionSemantics = 0 28 | ReadOnlyCodeSectionSemantics = 1 29 | ReadOnlyDataSectionSemantics = 2 30 | ReadWriteDataSectionSemantics = 3 31 | 32 | 33 | def linearize_comment(ea, function_comment=False): 34 | regular_comment = "" 35 | repeatable_comment = "" 36 | 37 | if function_comment: 38 | regular_comment = idc.GetFunctionCmt(ea, 0) 39 | repeatable_comment = idc.GetFunctionCmt(ea, 1) 40 | else: 41 | regular_comment = idc.Comment(ea) 42 | repeatable_comment = idc.RptCmt(ea) 43 | 44 | if regular_comment is None and repeatable_comment is None: 45 | return None 46 | elif regular_comment is not None and repeatable_comment is None: 47 | return regular_comment 48 | elif repeatable_comment is not None and regular_comment is None: 49 | return repeatable_comment 50 | else: 51 | if len(regular_comment) == 0: 52 | return repeatable_comment 53 | if len(repeatable_comment) == 0: 54 | return repeatable_comment 55 | return regular_comment + "\n" + repeatable_comment 56 | return None 57 | 58 | 59 | def main(fileName): 60 | if fileName is None: 61 | return 62 | jsonValue = {} 63 | jsonValue["names"] = {} 64 | jsonValue["functions"] = {} 65 | jsonValue["segments"] = [] 66 | jsonValue["strings"] = {} 67 | 68 | for addr, name in idautils.Names(): 69 | jsonValue["names"][addr] = name 70 | 71 | # Record segment details 72 | for ea in idautils.Segments(): 73 | cur_seg = {} 74 | cur_seg["start"] = idc.SegStart(ea) 75 | cur_seg["end"] = idc.SegEnd(ea) 76 | cur_seg["name"] = idc.SegName(ea) 77 | seg = idaapi.getseg(ea) 78 | cur_seg["r"] = (seg.perm & idaapi.SEGPERM_READ) != 0 79 | cur_seg["w"] = (seg.perm & idaapi.SEGPERM_WRITE) != 0 80 | cur_seg["x"] = (seg.perm & idaapi.SEGPERM_EXEC) != 0 81 | cur_seg["semantics"] = DefaultSectionSemantics 82 | if seg.type == idaapi.SEG_CODE: 83 | cur_seg["semantics"] = ReadOnlyCodeSectionSemantics 84 | elif seg.type == idaapi.SEG_DATA or seg.type == idaapi.SEG_BSS: 85 | if cur_seg["w"]: 86 | cur_seg["semantics"] = ReadWriteDataSectionSemantics 87 | else: 88 | cur_seg["semantics"] = ReadOnlyDataSectionSemantics 89 | 90 | # Record function details 91 | for ea in idautils.Functions(): 92 | cur_func = {} 93 | cur_func["start"] = ea 94 | cur_func["end"] = idc.GetFunctionAttr(ea, idc.FUNCATTR_END) 95 | cur_func["comment"] = linearize_comment(ea, True) 96 | cur_func["comments"] = {} 97 | for line_ea in idautils.Heads(ea, cur_func["end"]): 98 | line_comment = linearize_comment(line_ea) 99 | if line_comment is not None: 100 | cur_func["comments"][line_comment] = line_ea 101 | 102 | flags = idc.GetFunctionFlags(ea) 103 | cur_func["can_return"] = (flags & idc.FUNC_NORET) != idc.FUNC_NORET 104 | cur_func["thunk"] = False 105 | f = idaapi.get_func(ea) 106 | blocks =[] 107 | for block in idaapi.FlowChart(f): 108 | blocks.append([block.startEA, block.endEA]) 109 | 110 | # IDA treats thunks as being part of the function they are tunking to 111 | # Binary Ninja doesn't so only add the first basic block for all thunks 112 | if flags & idc.FUNC_THUNK != 0: 113 | cur_func["thunk"] = True 114 | break 115 | cur_func["basic_blocks"] = blocks 116 | jsonValue["functions"][idc.GetFunctionName(ea)] = cur_func 117 | 118 | # Record string details 119 | for string in idautils.Strings(): 120 | name = "" 121 | if string.ea in jsonValue["names"]: 122 | name = jsonValue["names"][string.ea] 123 | 124 | xrefs = list(idautils.DataRefsTo(string.ea)) 125 | if idaapi.IDA_SDK_VERSION < 700: 126 | jsonValue["strings"][string.ea] = (name, string.length, string.type, xrefs) 127 | else: 128 | jsonValue["strings"][string.ea] = (name, string.length, string.strtype, xrefs) 129 | 130 | # TODO: global variable names and types 131 | # TODO: stack local variable names and types 132 | # TODO: types and enumerations 133 | # TODO: non-function comments 134 | 135 | with open(fileName, "wb") as f: 136 | f.write(json.dumps(jsonValue, indent=4)) 137 | 138 | print("Exported idb to {}".format(fileName)) 139 | 140 | 141 | if __name__ == "__main__": 142 | main(idc.AskFile(1, "*.json", "Export file name")) 143 | -------------------------------------------------------------------------------- /ida_import.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2018 zznop 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to 6 | # deal in the Software without restriction, including without limitation the 7 | # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | # sell copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | # IN THE SOFTWARE. 21 | 22 | import idc 23 | import idautils 24 | import idaapi 25 | import json 26 | 27 | def sanitize_name(name): 28 | """Remove characters from BN names that IDA doesn't like 29 | """ 30 | name = name.replace("!", "_") 31 | name = name.replace("@", "_") 32 | return name 33 | 34 | def import_comments(comments): 35 | """Import BN comments 36 | """ 37 | for addr, comment in comments.items(): 38 | addr = int(addr) 39 | comment = comment.encode("utf-8") 40 | current_comment = idc.Comment(addr) 41 | 42 | # make a new comment 43 | if not current_comment: 44 | idc.MakeComm(addr, comment) 45 | continue 46 | 47 | # ensure comments hasn't already been imported 48 | if comment in current_comment: 49 | continue 50 | 51 | # append to comment 52 | idc.MakeComm(addr, current_comment + " " + comment) 53 | 54 | def import_symbols(names): 55 | """Import BN symbol names 56 | """ 57 | for addr, name in names.items(): 58 | addr = int(addr) 59 | name = sanitize_name(name).encode("utf-8") 60 | idc.MakeName(addr, name) 61 | 62 | def get_json(json_file): 63 | """Read JSON data file 64 | """ 65 | json_array = None 66 | if json_file is None: 67 | print("JSON file not specified") 68 | return json_array 69 | 70 | try: 71 | f = open(json_file, "rb") 72 | json_array = json.load(f) 73 | except Exception as e: 74 | print("Failed to parse json file {} {}".format(json_file, e)) 75 | return json_array 76 | 77 | def main(json_file): 78 | """Import data from BN 79 | """ 80 | json_array = get_json(json_file) 81 | if not json_array: 82 | return 83 | 84 | import_symbols(json_array["names"]) 85 | import_comments(json_array["comments"]) 86 | 87 | if __name__ == "__main__": 88 | main(idc.AskFile(1, "*.json", "Import file name")) 89 | --------------------------------------------------------------------------------