Package: dragodis
DragodisDragodis is a Python framework which allows for the creation of
universal disassembler scripts.  Dragodis currently only supports
IDA and Ghidra, but has plans to support additional disassemblers
in the future.  Dragodis only supports Python 3.The nameDragodiscomes from the combination ofDragoman, a professional
interpreter, andDisassembler.Dragodis was created due to a need of the ability to run IDA scripts in
Ghidra. Many scripts for automated analysis will work fine in most disassemblers.
Eliminating the need to use disassemblers which require licenses for automated
analysis is ideal.There are other benefits of a universal disassembler API as well. Many reverse
engineers have a preferred disassembler. Dragodis allows for simple transfers
of scripts between users of different disassemblers. Dragodis also aims to provide
a cleaner and easier to use API than those provided by other disassemblers.InstallUse pip to install dragodis:pip install dragodisThen follow the instructionshereto install a backend disassembler.UsageTo use Dragodis, simply pass in the path to your input binary file into either theIDAorGhidraclass.
This will create an instance of the disassembler with the given input file analyzed.importdragodiswithdragodis.Ghidra(r"C:\strings.exe")asghidra:print(ghidra.get_dword(0x401000))importdragodiswithdragodis.IDA(r"C:\strings.exe")asida:print(ida.get_dword(0x401000))A disassembler can also be run without using a context manager using thestart()andstop()functions.importdragodisghidra=dragodis.Ghidra(r"C:\strings.exe")ghidra.start()ghidra.get_dword(0x401000)ghidra.stop()Alternatively, you can useopen_program()to choose the disassembler more dynamically by providing
the disassembler name in thedisassemblerparameter or by setting theDRAGODIS_DISASSEMBLERenvironment variable.importdragodiswithdragodis.open_program(r"C:\strings.exe",disassembler="ida")asida:print(ida.get_dword(0x401000))It is highly recommended to use theDRAGODIS_DISASSEMBLERenvironment variable to ensure your scripts
are cross compatible without any modification. As well, to give the user the power to choose
which disassembler they would like to use.A "NotInstalledError" will be thrown if the disassembler chosen is not properly installed.importosos.environ["DRAGODIS_DISASSEMBLER"]="ida"importdragodiswithdragodis.open_program(r"C:\strings.exe")asdis:print(f"Disassembler used:{dis.name}")print(dis.get_dword(0x401000))If you are locally within the disassembler's interpreter (the output window for IDA or pyhidraw interpreter in Ghidra)
then you can initialize a disassembler object by directly acccessing the object:# If in IDAimportdragodisdis=dragodis.IDA()# If in Ghidraimportdragodisdis=dragodis.Ghidra()We can also directly call scripts using theopen_program()function locally in the disassembler.
When this happens, the input file path provided must match the detected input file path by the disassembler.Specifying Processor TypeThe processor type can be specified during initialization of theDisassemblerobject or throughopen_program().
This can be useful when loading shellcode.When usingopen_program()with any backend disassembler supported, use adragodis.PROCESSOR_*flag which will get converted
to a sane default for the respective disassembler.importdragodiswithdragodis.open_program(r"C:\input.exe",processor=dragodis.PROCESSOR_ARM)asdis:...If using a specific disassembler, any option that disassembler supports can be passed in.
(Consult the documentation for the respective disassembler to know how to format the argument.)# IDAimportdragodisida=dragodis.IDA(r"C:\input",processor="arm:ARMv7-M")# Ghidraimportdragodisghidra=dragodis.Ghidra(r"C:\input",processor="ARM:LE:32:v7")Alternatively, we can automatically choose the correct processor for the default disassembler chosen by the user
with some initial checks.importdragodisPROCESSOR={dragodis.BACKEND_IDA:"arm:ARMv7-M",dragodis.BACKEND_GHIDRA:"ARM:LE:32:v7",}[dragodis.BACKEND_DEFAULT]withdragodis.open_program(r"C:\input",processor=PROCESSOR)asdis:...Disassembler API Translation MapAs a reference, the following tables provide a rough mapping between the general equivalent API calls for Dragodis and each supported
disassembler.NOTE: These are rough translations between equivalent disassembler API functions to help you transition
from using either IDA or Ghidra to using Dragodis.
They are not always direct translations.
For brevity, some details and differences in results get glossed over here. Please see the source code for more information.BasicsDragodis*IDAGhidradis.processor_nameida_ida.inf_get_procname()currentProgram.getLanguage().getProcessor()dis.compiler_nameida_typeinf.get_compiler_name(ida_ida.inf_get_cc_id())currentProgram.getCompiler()dis.bit_sizeida_ida.inf_get_app_bitness()currentProgram.getDefaultPointerSize() * 8dis.is_big_endianida_ida.inf_is_be()currentProgram.getLanguage().isBigEndian()dis.min_addressida_ida.inf_get_min_ea()currentProgram.getMinAddress()dis.max_addressida_ida.inf_get_max_ea()currentProgram.getMaxAddress()dis.base_addressida_nalt.get_imagebase()currentProgram.getImageBase()dis.get_virtual_address(file_offset)ida_loader.get_fileregion_ea(file_offset)currentProgram.getMemory().locateAddressesForFileOffset(file_offset)dis.get_file_offset(address)ida_loader.get_fileregion_offset(address)currentProgram.getMemory().getAddressSourceInfo(toAddr(address)).getFileOffset()dis.entry_pointida_ida.inf_get_start_ip()(export with name "entry" or "_start")DataDragodis*IDAGhidradis.get_byte(address)ida_bytes.get_wide_byte(address)getByte(toAddr(address))dis.get_bytes(address, size)ida_bytes.get_bytes(address, size)getBytes(toAddr(address), size)dis.find_bytes(b"\xde\xad\xbe\xef", start)idc.find_binary(start, idc.SEARCH_DOWN, "DE AD BE EF")currentProgram.getMemory().findBytes(start, b"\xde\xad\xbe\xef", None, True, monitor)dis.find_bytes(b"\xde\xad\xbe\xef", start, reverse=True)idc.find_binary(start, idc.SEARCH_UP, "DE AD BE EF")currentProgram.getMemory().findBytes(start, b"\xde\xad\xbe\xef", None, False, monitor)dis.get_word(address)ida_bytes.get_wide_word(address)getShort(toAddr(address))dis.get_dword(address)ida_bytes.get_wide_dword(address)getInt(toAddr(address))dis.get_qword(address)ida_bytes.get_qword(address)getLong(toAddr(address))dis.get_string_bytes(address)idc.get_strlit_contents(address)complex: see source codedis.lines(start_address, end_address)dis.line_addresses(start_address, end_address)idautils.Heads(start_address, end_address)currentProgram.getListing().getCodeUnits(address_set, True)line = dis.get_line(address)N/Acode_unit = currentProgram.getListing().getCodeUnitContaining(toAddr(address))line.addressdis.get_line_address(address)idc.get_item_head(address)code_unit.getAddress()line.namedis.get_name(address)ida_name.get_name(address)code_unit.getLabel()line.name = "new_name"ida_name.set_name(address, "new_name")symbol = code_unit.getPrimarySymbol(); symbol.setName("new_name", symbol.getSource())line.sizeida_bytes.get_item_size(address)code_unit.getLength()line.typeida_bytes.get_flags(address)code_unit.getClass()code_unit.getDataType().getName()line.type = LineType.dwordidc.create_dword(address)createDWord(address)line.dataida_bytes.get_bytes(address, ida_bytes.get_item_size(address))code_unit.getBytes()line.data = b"new data"ida_bytes.patch_bytes(address, b"new data")setBytes(code_unit.getAddress(), b"new data")line.get_comment()dis.get_comment(address)ida_bytes.get_cmt(address, 0)code_unit.getComment(0)line.set_comment("new comment")ida_bytes.set_cmt(address, "new comment", 0)code_unit.setComment(0, "new comment")line.nextdis.next_line_address(address)idc.next_head(address)currentProgram.getListing().getCodeUnitAfter(code_unit.getAddress())line.prevdis.prev_line_address(address)idc.prev_head(address)currentProgram.getListing().getCodeUnitBefore(code_unit.getAddress())line.undefine()ida_bytes.del_items(address)clearListing(code_unit.getAddress())line.valueida_bytes.get_wide_byte(address)ida_bytes.get_wide_word(address)etc.code_unit.getValue()line.value = new_valueida_bytes.patch_byte(address, new_value)ida_bytes.patch_word(address, new_value)ida_bytes.patch_dword(address, new_value)etc.setByte(code_unit.getAddress(), new_value)setShort(code_unit.getAddress(), new_value)setInt(code_unit.getAddress(), new_value)etc.data_type = dis.get_data_type("dword")data_type = ida_typeinf.tinfo_t(); data_type.get_named_type(ida_typeinf.get_idati(), "dword")data_type = DataTypeParser(None, DataTypeParser.AllowedDataTypes.valueOf("ALL")).parse("dword")data_type.nameN/Adata_type.getName()data_type.sizeida_bytes.get_data_elsize(address, ida_bytes.get_flags(address) & ida_bytes.DT_TYPE)data_type.getLength()dis.undefine(start, end)ida_bytes.del_items(start, ida_bytes.DELIT_SIMPLE, end - start)clearListing(toAddr(start), toAddr(end))Cross-ReferencesDragodis*IDAGhidradis.references_from(address)dis.get_line(address).references_fromdis.get_function(address).references_fromidautils.XrefsFrom(address)getReferencesFrom(toAddr(address))dis.references_to(address)dis.get_line(address).references_todis.get_function(address).references_toidautils.XrefsTo(address)getReferencesTo(toAddr(address))ref.from_addressref.frmref.getFromAddress()ref.to_addressref.toref.getToAddress()ref.typeref.typeref.getReferenceType()ref.is_coderef.iscodenot ref.getReferenceType().isData()ref.is_datanot ref.iscoderef.getReferenceType().isData()dis.create_reference(from_address, to_address, dragodis.ReferenceType.*)ida_xref.add_cref(from_address, to_address, idc.fl_)ida_xref.add_dref(from_address, to_address, idc.dr_)currentProgram.getReferenceManager().addMemoryReference(toAddr(from_address), toAddr(to_address), RefType.*, SourceType.USER_DEFINED, 0)Imports/ExportsDragodis*IDAGhidradis.imports[ida_nalt.enum_import_names(i, callback) for i in range(ida_nalt.get_import_module_qty())]currentProgram.getSymbolTable().getExternalSymbols()dis.exports[ida_entry.get_entry_ordinal(i) for i in range(ida_entry.get_entry_qty())]currentProgram.getSymbolTable().getExternalEntryPointIterator()import_.addressreturned in callbacksymbol.getAddress()import_.namereturned in callbacksymbol.getName()import_.namespaceida_nalt.get_import_module_name(i)symbol.getParentSymbol().getName()import_.references_toidautils.XrefsTo(address)symbol.getReferences()export.addressida_entry.get_entry(ordinal)symbol.getAddress()export.nameida_entry.get_entry_name(ordinal)symbol.getName()export.references_toidautils.XrefsTo(ida_entry.get_entry(ordinal))symbol.getReferences()FunctionsDragodis*IDAGhidradis.functions()idautils.Functions()currentProgram.getListing().getFunctions(True)func = dis.get_function(address)func = ida_funcs.get_func(address)func = getFunctionContaining(toAddr(address))func.startfunc.start_eafunc.getEntryPoint()func.endfunc.end_eafunc.getBody().getMaxAddress()func.namedis.get_name(address)ida_funcs.get_func_name(address)func.getName()func.name = "new_name"ida_name.set_name(address, "new_name")func.setName("new_name", SourceType.USER_DEFINED)func.get_comment()ida_funcs.get_func_cmt(func, 0)func.getComment()func.set_comment("new comment")ida_funcs.set_func_cmt(func, "new comment", 0)func.setComment("new comment")dis.get_flowchart(address)func.flowchartida_gdl.FlowChart(func)BasicBlockModel(currentProgram).getCodeBlocksContaining(func.getBody(), monitor)func.stack_frameida_frame.get_frame(func)func.getStackFrame()dis.get_function_signature(address)func.signaturetif = ida_typeinf.tinfo_t()ida_nalt.get_tinfo(tif, address)func_type_data = ida_typeinf.func_type_data_t()tif.get_func_details(func_type_data)func.getSignature()func = dis.create_function(start, end)func = ida_funcs.add_func(start, end)func = currentProgram.getFunctionManager().createFunction(None, toAddr(start), AddressSet(toAddr(start), toAddr(end)), SourceType.USER_DEFINED)func = dis.create_function(start)func = ida_funcs.add_func(start)func = CreateFunctionCmd(toAddr(start), True).applyTo(currentProgram).getFunction()func.undefine()ida_funcs.del_func(start)DeleteFunctionCmd(func.getEntryPoint(), True).applyTo(currentProgram)InstructionsDragodis*IDAGhidrainsn = dis.get_instruction(address)insn = ida_ua.insn_t(); ida_ua.decode_insn(insn, address)insn = currentProgram.getListing().getCodeUnitAt(toAddr(address))insn.is_callida_idp.is_call_insn(insn)insn.getFlowType().isCall()insn.is_jumpcomplex: see source codeinsn.getFlowType().isJump()insn.is_returnida_idp.is_ret_insn(insn)insn.getFlowType().isTerminal()insn.mnemonicdis.get_mnemonic(address)ida_ua.ua_mnem(address)insn.getMnemonicString()insn.textidc.GetDisasm(address)str(insn)insn.operandsinsn.opsN/A: See operands sectioninsn.stack_depthidc.get_spd(address)CallDepthChangeInfo(currentProgram.getListing().getFunctionContaining(insn.getAddress())).getDepth()insn.stack_deltaidc.get_sp_delta(address)complex: see source codeOperandsinsnpulled as described aboveDragodis*IDAGhidraoperand = dis.get_operand(address, index)operand = insn.operands[index]operand = insn.ops[index]N/Aoperand.typedis.get_operand_type(address, index)idc.get_operand_type(address, index)insn.getOperandType(index)operand.widthida_ua.get_dtype_size(operand.dtype)complex: see source codeoperand.valuedis.get_operand_value(address, index)(depends on type)operand.addroperand.regoperand.valueinsn.getOpObjects(index)(phrase operand)operand.value.baseoperand.regida_intel.x86_base_reg(insn, operand)insn.getOpObjects(index)[0](phrase operand)operand.value.indexida_intel.x86_index_reg(insn, operand)insn.getOpObjects(index)[1](phrase operand)operand.value.scale1 << ida_intel.sib_scale(operand)insn.getOpObjects(index)[2](phrase operand)operand.value.offsetoperand.addridc.get_operand_value(address, index)insn.getOpObjects(index)[3](register operand)operand.valueoperand.regidc.get_operand_value(address, index)insn.getOpObjects(index)[0](immediate operand)operand.valueoperand.valueidc.get_operand_value(address, index)insn.getOpObjects(index)[0](memory reference operand)operand.valueoperand.addridc.get_operand_value(address, index)insn.getOpObjects(index)[0]RegistersDragodis*IDAGhidraregister = dis.get_register("eax")register = ida_idp.reg_info_t(); ida_idp.parse_reg_name(register, "eax")register = currentProgram.getRegister("eax")register.nameida_idp.get_reg_name(register.reg, register.size)register.getName()register.bit_widthregister.size * 8register.getBitLength()SegmentsDragodis*IDAGhidradis.segments[ida_segment.getnseg(n) for n in range(ida_segment.get_segm_qty())]currentProgram.getMemory().getBlocks()segment = dis.get_segment(name)segment = dis.get_segment(address)segment = ida_segment.get_segm_by_name(name)segment = ida_segment.getseg(address)memory_block = currentProgram.getMemory().getBlock(name)memory_block = currentProgram.getMemory().getBlock(toAddr(address))segment.nameida_segment.get_segm_name(segment)memory_block.getName()segment.startsegment.start_eamemory_block.getStart()segment.endsegment.end_eamemory_block.getEnd()segment.initializedida_bytes.is_loaded(segment.start_ea)memory_block.isInitialized()segment.bit_sizesegment.abits()memory_block.getStart().getSize()segment.permissionssegment.permmemory_block.isRead()memory_block.isWrite()memory_block.isExecute()memory_block.isVolatile()dis.create_segment(".new_seg", 0x1234, 256)ida_segment.add_segm(0, 0x1234, 0x1334, ".new_seg", "XTRN")currentProgram.getMemory().createUninitializedBlock(".new_seg", toAddr(0x1234), 256, False)StringsDragodis*IDAGhidradis.strings(min_length=5)finder = idautils.Strings(); finder.setup(minlen=5); list(finder)findStrings(None, 5, 1, False, True)*disin the dragodis column represents the open disassembler object retrieved fromdragodis.open_program()