# Dragodis
Dragodis 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 name `Dragodis` comes from the combination of `Dragoman`, a professional
interpreter, and `Disassembler`.
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.
## Install
Use pip to install dragodis:
```console
pip install dragodis
```
Then follow the instructions [here](./docs/install.md) to install a backend disassembler.
## Usage
To use Dragodis, simply pass in the path to your input binary file into either the `IDA` or `Ghidra` class.
This will create an instance of the disassembler with the given input file analyzed.
```python
import dragodis
with dragodis.Ghidra(r"C:\strings.exe") as ghidra:
print(ghidra.get_dword(0x401000))
```
```python
import dragodis
with dragodis.IDA(r"C:\strings.exe") as ida:
print(ida.get_dword(0x401000))
```
A disassembler can also be run without using a context manager using the `start()` and `stop()` functions.
```python
import dragodis
ghidra = dragodis.Ghidra(r"C:\strings.exe")
ghidra.start()
ghidra.get_dword(0x401000)
ghidra.stop()
```
Alternatively, you can use `open_program()` to choose the disassembler more dynamically by providing
the disassembler name in the `disassembler` parameter or by setting the `DRAGODIS_DISASSEMBLER`
environment variable.
```python
import dragodis
with dragodis.open_program(r"C:\strings.exe", disassembler="ida") as ida:
print(ida.get_dword(0x401000))
```
It is highly recommended to use the `DRAGODIS_DISASSEMBLER` environment 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.
```{note}
A "NotInstalledError" will be thrown if the disassembler chosen is not properly installed.
```
```python
import os
os.environ["DRAGODIS_DISASSEMBLER"] = "ida"
import dragodis
with dragodis.open_program(r"C:\strings.exe") as dis:
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:
```python
# If in IDA
import dragodis
dis = dragodis.IDA()
# If in Ghidra
import dragodis
dis = dragodis.Ghidra()
```
We can also directly call scripts using the `open_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 Type
The processor type can be specified during initialization of the `Disassembler` object or through `open_program()`.
This can be useful when loading shellcode.
When using `open_program()` with any backend disassembler supported, use a `dragodis.PROCESSOR_*` flag which will get converted
to a sane default for the respective disassembler.
```python
import dragodis
with dragodis.open_program(r"C:\input.exe", processor=dragodis.PROCESSOR_ARM) as dis:
...
```
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.)
```python
# IDA
import dragodis
ida = dragodis.IDA(r"C:\input", processor="arm:ARMv7-M")
# Ghidra
import dragodis
ghidra = 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.
```python
import dragodis
PROCESSOR = {
dragodis.BACKEND_IDA: "arm:ARMv7-M",
dragodis.BACKEND_GHIDRA: "ARM:LE:32:v7",
}[dragodis.BACKEND_DEFAULT]
with dragodis.open_program(r"C:\input", processor=PROCESSOR) as dis:
...
```
## Disassembler API Translation Map
As 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.*
## Basics
| Dragodis* | IDA | Ghidra |
|--------------------------------------|--------------------------------------------------------|----------------------------------------------------------------------------------|
| dis.processor_name | ida_ida.inf_get_procname() | currentProgram.getLanguage().getProcessor() |
| dis.compiler_name | ida_typeinf.get_compiler_name(ida_ida.inf_get_cc_id()) | currentProgram.getCompiler() |
| dis.bit_size | ida_ida.inf_get_app_bitness() | currentProgram.getDefaultPointerSize() * 8 |
| dis.is_big_endian | ida_ida.inf_is_be() | currentProgram.getLanguage().isBigEndian() |
| dis.min_address | ida_ida.inf_get_min_ea() | currentProgram.getMinAddress() |
| dis.max_address | ida_ida.inf_get_max_ea() | currentProgram.getMaxAddress() |
| dis.base_address | ida_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_point | ida_ida.inf_get_start_ip() | *(export with name "entry" or "_start")* |
### Data
| Dragodis* | IDA | Ghidra |
|-----------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|
| dis.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 code* |
| dis.lines(start_address, end_address)<br>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/A* | code_unit = currentProgram.getListing().getCodeUnitContaining(toAddr(address)) |
| line.address<br>dis.get_line_address(address) | idc.get_item_head(address) | code_unit.getAddress() |
| line.name<br>dis.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.size | ida_bytes.get_item_size(address) | code_unit.getLength() |
| line.type | ida_bytes.get_flags(address) | code_unit.getClass()<br>code_unit.getDataType().getName() |
| line.type = LineType.dword | idc.create_dword(address) | createDWord(address) |
| line.data | ida_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()<br>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.next<br>dis.next_line_address(address) | idc.next_head(address) | currentProgram.getListing().getCodeUnitAfter(code_unit.getAddress()) |
| line.prev<br>dis.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.value | ida_bytes.get_wide_byte(address)<br>ida_bytes.get_wide_word(address)<br>*etc.* | code_unit.getValue() |
| line.value = new_value | ida_bytes.patch_byte(address, new_value)<br>ida_bytes.patch_word(address, new_value)<br>ida_bytes.patch_dword(address, new_value)<br>*etc.* | setByte(code_unit.getAddress(), new_value)<br>setShort(code_unit.getAddress(), new_value)<br>setInt(code_unit.getAddress(), new_value)<br>*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.name | *N/A* | data_type.getName() |
| data_type.size | ida_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-References
| Dragodis* | IDA | Ghidra |
|--------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|
| dis.references_from(address)<br>dis.get_line(address).references_from<br>dis.get_function(address).references_from | idautils.XrefsFrom(address) | getReferencesFrom(toAddr(address)) |
| dis.references_to(address)<br>dis.get_line(address).references_to<br>dis.get_function(address).references_to | idautils.XrefsTo(address) | getReferencesTo(toAddr(address)) |
| ref.from_address | ref.frm | ref.getFromAddress() |
| ref.to_address | ref.to | ref.getToAddress() |
| ref.type | ref.type | ref.getReferenceType() |
| ref.is_code | ref.iscode | not ref.getReferenceType().isData() |
| ref.is_data | not ref.iscode | ref.getReferenceType().isData() |
| dis.create_reference(from_address, to_address, dragodis.ReferenceType.*) | ida_xref.add_cref(from_address, to_address, idc.fl_*)<br>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/Exports
| Dragodis* | IDA | Ghidra |
|-----------------------|----------------------------------------------------------------------------------------------|-----------------------------------------------------------------|
| dis.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_.address | *returned in callback* | symbol.getAddress() |
| import_.name | *returned in callback* | symbol.getName() |
| import_.namespace | ida_nalt.get_import_module_name(i) | symbol.getParentSymbol().getName() |
| import_.references_to | idautils.XrefsTo(address) | symbol.getReferences() |
| export.address | ida_entry.get_entry(ordinal) | symbol.getAddress() |
| export.name | ida_entry.get_entry_name(ordinal) | symbol.getName() |
| export.references_to | idautils.XrefsTo(ida_entry.get_entry(ordinal)) | symbol.getReferences() |
### Functions
| Dragodis* | IDA | Ghidra |
|-------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| dis.functions() | idautils.Functions() | currentProgram.getListing().getFunctions(True) |
| func = dis.get_function(address) | func = ida_funcs.get_func(address) | func = getFunctionContaining(toAddr(address)) |
| func.start | func.start_ea | func.getEntryPoint() |
| func.end | func.end_ea | func.getBody().getMaxAddress() |
| func.name<br>dis.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)<br>func.flowchart | ida_gdl.FlowChart(func) | BasicBlockModel(currentProgram).getCodeBlocksContaining(func.getBody(), monitor) |
| func.stack_frame | ida_frame.get_frame(func) | func.getStackFrame() |
| dis.get_function_signature(address)<br>func.signature | tif = ida_typeinf.tinfo_t()<br>ida_nalt.get_tinfo(tif, address)<br>func_type_data = ida_typeinf.func_type_data_t()<br>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) |
### Instructions
| Dragodis* | IDA | Ghidra |
|--------------------------------------------|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------|
| insn = dis.get_instruction(address) | insn = ida_ua.insn_t(); ida_ua.decode_insn(insn, address) | insn = currentProgram.getListing().getCodeUnitAt(toAddr(address)) |
| insn.is_call | ida_idp.is_call_insn(insn) | insn.getFlowType().isCall() |
| insn.is_jump | *complex: see source code* | insn.getFlowType().isJump() |
| insn.is_return | ida_idp.is_ret_insn(insn) | insn.getFlowType().isTerminal() |
| insn.mnemonic<br>dis.get_mnemonic(address) | ida_ua.ua_mnem(address) | insn.getMnemonicString() |
| insn.text | idc.GetDisasm(address) | str(insn) |
| insn.operands | insn.ops | *N/A: See operands section* |
| insn.stack_depth | idc.get_spd(address) | CallDepthChangeInfo(currentProgram.getListing().getFunctionContaining(insn.getAddress())).getDepth() |
| insn.stack_delta | idc.get_sp_delta(address) | *complex: see source code* |
### Operands
*`insn` pulled as described above*
| Dragodis* | IDA | Ghidra |
|-------------------------------------------------------------------------------|---------------------------------------------------------------------|-------------------------------|
| operand = dis.get_operand(address, index)<br>operand = insn.operands\[index\] | operand = insn.ops\[index\] | *N/A* |
| operand.type<br>dis.get_operand_type(address, index) | idc.get_operand_type(address, index) | insn.getOperandType(index) |
| operand.width | ida_ua.get_dtype_size(operand.dtype) | *complex: see source code* |
| operand.value<br>dis.get_operand_value(address, index) | *(depends on type)*<br>operand.addr<br>operand.reg<br>operand.value | insn.getOpObjects(index) |
| *(phrase operand)*<br>operand.value.base | operand.reg<br>ida_intel.x86_base_reg(insn, operand) | insn.getOpObjects(index)\[0\] |
| *(phrase operand)*<br>operand.value.index | ida_intel.x86_index_reg(insn, operand) | insn.getOpObjects(index)\[1\] |
| *(phrase operand)*<br>operand.value.scale | 1 << ida_intel.sib_scale(operand) | insn.getOpObjects(index)\[2\] |
| *(phrase operand)*<br>operand.value.offset | operand.addr<br>idc.get_operand_value(address, index) | insn.getOpObjects(index)\[3\] |
| *(register operand)*<br>operand.value | operand.reg<br>idc.get_operand_value(address, index) | insn.getOpObjects(index)\[0\] |
| *(immediate operand)*<br>operand.value | operand.value<br>idc.get_operand_value(address, index) | insn.getOpObjects(index)\[0\] |
| *(memory reference operand)*<br>operand.value | operand.addr<br>idc.get_operand_value(address, index) | insn.getOpObjects(index)\[0\] |
### Registers
| Dragodis* | IDA | Ghidra |
|------------------------------------|--------------------------------------------------------------------------|----------------------------------------------|
| register = dis.get_register("eax") | register = ida_idp.reg_info_t(); ida_idp.parse_reg_name(register, "eax") | register = currentProgram.getRegister("eax") |
| register.name | ida_idp.get_reg_name(register.reg, register.size) | register.getName() |
| register.bit_width | register.size * 8 | register.getBitLength() |
### Segments
| Dragodis* | IDA | Ghidra |
|-----------------------------------------------------------------------|---------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|
| dis.segments | \[ida_segment.getnseg(n) for n in range(ida_segment.get_segm_qty())\] | currentProgram.getMemory().getBlocks() |
| segment = dis.get_segment(name)<br>segment = dis.get_segment(address) | segment = ida_segment.get_segm_by_name(name)<br>segment = ida_segment.getseg(address) | memory_block = currentProgram.getMemory().getBlock(name)<br>memory_block = currentProgram.getMemory().getBlock(toAddr(address)) |
| segment.name | ida_segment.get_segm_name(segment) | memory_block.getName() |
| segment.start | segment.start_ea | memory_block.getStart() |
| segment.end | segment.end_ea | memory_block.getEnd() |
| segment.initialized | ida_bytes.is_loaded(segment.start_ea) | memory_block.isInitialized() |
| segment.bit_size | segment.abits() | memory_block.getStart().getSize() |
| segment.permissions | segment.perm | memory_block.isRead()<br>memory_block.isWrite()<br>memory_block.isExecute()<br>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) |
### Strings
| Dragodis* | IDA | Ghidra |
|---------------------------|-------------------------------------------------------------------|---------------------------------------|
| dis.strings(min_length=5) | finder = idautils.Strings(); finder.setup(minlen=5); list(finder) | findStrings(None, 5, 1, False, True) |
\* `dis` in the dragodis column represents the open disassembler object retrieved from `dragodis.open_program()`
Raw data
{
"_id": null,
"home_page": "https://github.com/dod-cyber-crime-center/dragodis",
"name": "dragodis",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "malware, ida, idapro, ghidra, disassembler",
"author": "DC3",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/49/da/e74e7a9ee89b1f30064822afc0c6a2e3f6714e21910717f79a67701d42e3/dragodis-1.0.0.tar.gz",
"platform": null,
"description": "# Dragodis\n\nDragodis is a Python framework which allows for the creation of\nuniversal disassembler scripts. Dragodis currently only supports\nIDA and Ghidra, but has plans to support additional disassemblers\nin the future. Dragodis only supports Python 3.\n\nThe name `Dragodis` comes from the combination of `Dragoman`, a professional\ninterpreter, and `Disassembler`.\n\nDragodis was created due to a need of the ability to run IDA scripts in\nGhidra. Many scripts for automated analysis will work fine in most disassemblers.\nEliminating the need to use disassemblers which require licenses for automated\nanalysis is ideal.\n\nThere are other benefits of a universal disassembler API as well. Many reverse\nengineers have a preferred disassembler. Dragodis allows for simple transfers\nof scripts between users of different disassemblers. Dragodis also aims to provide\na cleaner and easier to use API than those provided by other disassemblers.\n\n\n## Install\n\nUse pip to install dragodis:\n\n```console\npip install dragodis\n```\n\nThen follow the instructions [here](./docs/install.md) to install a backend disassembler.\n\n\n## Usage\n\nTo use Dragodis, simply pass in the path to your input binary file into either the `IDA` or `Ghidra` class.\nThis will create an instance of the disassembler with the given input file analyzed.\n\n```python\nimport dragodis\n\nwith dragodis.Ghidra(r\"C:\\strings.exe\") as ghidra:\n print(ghidra.get_dword(0x401000))\n```\n\n```python\nimport dragodis\n\nwith dragodis.IDA(r\"C:\\strings.exe\") as ida:\n print(ida.get_dword(0x401000))\n```\n\nA disassembler can also be run without using a context manager using the `start()` and `stop()` functions.\n\n```python\nimport dragodis\n\nghidra = dragodis.Ghidra(r\"C:\\strings.exe\")\nghidra.start()\nghidra.get_dword(0x401000)\nghidra.stop()\n```\n\nAlternatively, you can use `open_program()` to choose the disassembler more dynamically by providing\nthe disassembler name in the `disassembler` parameter or by setting the `DRAGODIS_DISASSEMBLER`\nenvironment variable.\n\n```python\nimport dragodis\n\nwith dragodis.open_program(r\"C:\\strings.exe\", disassembler=\"ida\") as ida:\n print(ida.get_dword(0x401000))\n```\n\nIt is highly recommended to use the `DRAGODIS_DISASSEMBLER` environment variable to ensure your scripts\nare cross compatible without any modification. As well, to give the user the power to choose\nwhich disassembler they would like to use.\n\n\n```{note} \nA \"NotInstalledError\" will be thrown if the disassembler chosen is not properly installed.\n```\n\n```python\nimport os\nos.environ[\"DRAGODIS_DISASSEMBLER\"] = \"ida\"\n\nimport dragodis\n\nwith dragodis.open_program(r\"C:\\strings.exe\") as dis:\n print(f\"Disassembler used: {dis.name}\")\n print(dis.get_dword(0x401000))\n```\n\nIf you are locally within the disassembler's interpreter (the output window for IDA or pyhidraw interpreter in Ghidra)\nthen you can initialize a disassembler object by directly acccessing the object:\n\n```python\n# If in IDA\nimport dragodis\ndis = dragodis.IDA()\n\n# If in Ghidra\nimport dragodis\ndis = dragodis.Ghidra()\n```\n\nWe can also directly call scripts using the `open_program()` function locally in the disassembler.\nWhen this happens, the input file path provided must match the detected input file path by the disassembler.\n\n\n### Specifying Processor Type\n\nThe processor type can be specified during initialization of the `Disassembler` object or through `open_program()`. \nThis can be useful when loading shellcode.\n\nWhen using `open_program()` with any backend disassembler supported, use a `dragodis.PROCESSOR_*` flag which will get converted\nto a sane default for the respective disassembler.\n\n```python\nimport dragodis\nwith dragodis.open_program(r\"C:\\input.exe\", processor=dragodis.PROCESSOR_ARM) as dis:\n ...\n```\n\nIf using a specific disassembler, any option that disassembler supports can be passed in.\n(Consult the documentation for the respective disassembler to know how to format the argument.)\n\n```python\n# IDA\nimport dragodis\nida = dragodis.IDA(r\"C:\\input\", processor=\"arm:ARMv7-M\")\n\n# Ghidra\nimport dragodis\nghidra = dragodis.Ghidra(r\"C:\\input\", processor=\"ARM:LE:32:v7\")\n```\n\nAlternatively, we can automatically choose the correct processor for the default disassembler chosen by the user \nwith some initial checks.\n\n```python\nimport dragodis\n\nPROCESSOR = {\n dragodis.BACKEND_IDA: \"arm:ARMv7-M\",\n dragodis.BACKEND_GHIDRA: \"ARM:LE:32:v7\",\n}[dragodis.BACKEND_DEFAULT]\n\nwith dragodis.open_program(r\"C:\\input\", processor=PROCESSOR) as dis:\n ...\n```\n\n\n## Disassembler API Translation Map\n\nAs a reference, the following tables provide a rough mapping between the general equivalent API calls for Dragodis and each supported\ndisassembler. \n\n*NOTE: These are rough translations between equivalent disassembler API functions to help you transition \nfrom using either IDA or Ghidra to using Dragodis.\nThey are not always direct translations.\nFor brevity, some details and differences in results get glossed over here. Please see the source code for more information.*\n\n\n## Basics\n| Dragodis* | IDA | Ghidra | \n|--------------------------------------|--------------------------------------------------------|----------------------------------------------------------------------------------|\n| dis.processor_name | ida_ida.inf_get_procname() | currentProgram.getLanguage().getProcessor() |\n| dis.compiler_name | ida_typeinf.get_compiler_name(ida_ida.inf_get_cc_id()) | currentProgram.getCompiler() |\n| dis.bit_size | ida_ida.inf_get_app_bitness() | currentProgram.getDefaultPointerSize() * 8 |\n| dis.is_big_endian | ida_ida.inf_is_be() | currentProgram.getLanguage().isBigEndian() |\n| dis.min_address | ida_ida.inf_get_min_ea() | currentProgram.getMinAddress() |\n| dis.max_address | ida_ida.inf_get_max_ea() | currentProgram.getMaxAddress() |\n| dis.base_address | ida_nalt.get_imagebase() | currentProgram.getImageBase() |\n| dis.get_virtual_address(file_offset) | ida_loader.get_fileregion_ea(file_offset) | currentProgram.getMemory().locateAddressesForFileOffset(file_offset) |\n| dis.get_file_offset(address) | ida_loader.get_fileregion_offset(address) | currentProgram.getMemory().getAddressSourceInfo(toAddr(address)).getFileOffset() |\n| dis.entry_point | ida_ida.inf_get_start_ip() | *(export with name \"entry\" or \"_start\")* |\n\n\n### Data\n| Dragodis* | IDA | Ghidra | \n|-----------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|\n| dis.get_byte(address) | ida_bytes.get_wide_byte(address) | getByte(toAddr(address)) |\n| dis.get_bytes(address, size) | ida_bytes.get_bytes(address, size) | getBytes(toAddr(address), size) |\n| 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) | \n| 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) |\n| dis.get_word(address) | ida_bytes.get_wide_word(address) | getShort(toAddr(address)) |\n| dis.get_dword(address) | ida_bytes.get_wide_dword(address) | getInt(toAddr(address)) |\n| dis.get_qword(address) | ida_bytes.get_qword(address) | getLong(toAddr(address)) |\n| dis.get_string_bytes(address) | idc.get_strlit_contents(address) | *complex: see source code* |\n| dis.lines(start_address, end_address)<br>dis.line_addresses(start_address, end_address) | idautils.Heads(start_address, end_address) | currentProgram.getListing().getCodeUnits(address_set, True) | \n| line = dis.get_line(address) | *N/A* | code_unit = currentProgram.getListing().getCodeUnitContaining(toAddr(address)) |\n| line.address<br>dis.get_line_address(address) | idc.get_item_head(address) | code_unit.getAddress() |\n| line.name<br>dis.get_name(address) | ida_name.get_name(address) | code_unit.getLabel() |\n| line.name = \"new_name\" | ida_name.set_name(address, \"new_name\") | symbol = code_unit.getPrimarySymbol(); symbol.setName(\"new_name\", symbol.getSource()) | \n| line.size | ida_bytes.get_item_size(address) | code_unit.getLength() |\n| line.type | ida_bytes.get_flags(address) | code_unit.getClass()<br>code_unit.getDataType().getName() |\n| line.type = LineType.dword | idc.create_dword(address) | createDWord(address) |\n| line.data | ida_bytes.get_bytes(address, ida_bytes.get_item_size(address)) | code_unit.getBytes() |\n| line.data = b\"new data\" | ida_bytes.patch_bytes(address, b\"new data\") | setBytes(code_unit.getAddress(), b\"new data\") |\n| line.get_comment()<br>dis.get_comment(address) | ida_bytes.get_cmt(address, 0) | code_unit.getComment(0) |\n| line.set_comment(\"new comment\") | ida_bytes.set_cmt(address, \"new comment\", 0) | code_unit.setComment(0, \"new comment\") |\n| line.next<br>dis.next_line_address(address) | idc.next_head(address) | currentProgram.getListing().getCodeUnitAfter(code_unit.getAddress()) |\n| line.prev<br>dis.prev_line_address(address) | idc.prev_head(address) | currentProgram.getListing().getCodeUnitBefore(code_unit.getAddress()) |\n| line.undefine() | ida_bytes.del_items(address) | clearListing(code_unit.getAddress()) |\n| line.value | ida_bytes.get_wide_byte(address)<br>ida_bytes.get_wide_word(address)<br>*etc.* | code_unit.getValue() |\n| line.value = new_value | ida_bytes.patch_byte(address, new_value)<br>ida_bytes.patch_word(address, new_value)<br>ida_bytes.patch_dword(address, new_value)<br>*etc.* | setByte(code_unit.getAddress(), new_value)<br>setShort(code_unit.getAddress(), new_value)<br>setInt(code_unit.getAddress(), new_value)<br>*etc.* |\n| 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\") |\n| data_type.name | *N/A* | data_type.getName() |\n| data_type.size | ida_bytes.get_data_elsize(address, ida_bytes.get_flags(address) & ida_bytes.DT_TYPE) | data_type.getLength() |\n| dis.undefine(start, end) | ida_bytes.del_items(start, ida_bytes.DELIT_SIMPLE, end - start) | clearListing(toAddr(start), toAddr(end)) |\n\n\n### Cross-References\n| Dragodis* | IDA | Ghidra | \n|--------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------|\n| dis.references_from(address)<br>dis.get_line(address).references_from<br>dis.get_function(address).references_from | idautils.XrefsFrom(address) | getReferencesFrom(toAddr(address)) |\n| dis.references_to(address)<br>dis.get_line(address).references_to<br>dis.get_function(address).references_to | idautils.XrefsTo(address) | getReferencesTo(toAddr(address)) |\n| ref.from_address | ref.frm | ref.getFromAddress() |\n| ref.to_address | ref.to | ref.getToAddress() |\n| ref.type | ref.type | ref.getReferenceType() |\n| ref.is_code | ref.iscode | not ref.getReferenceType().isData() |\n| ref.is_data | not ref.iscode | ref.getReferenceType().isData() |\n| dis.create_reference(from_address, to_address, dragodis.ReferenceType.*) | ida_xref.add_cref(from_address, to_address, idc.fl_*)<br>ida_xref.add_dref(from_address, to_address, idc.dr_*) | currentProgram.getReferenceManager().addMemoryReference(toAddr(from_address), toAddr(to_address), RefType.*, SourceType.USER_DEFINED, 0) |\n\n\n### Imports/Exports\n| Dragodis* | IDA | Ghidra | \n|-----------------------|----------------------------------------------------------------------------------------------|-----------------------------------------------------------------|\n| dis.imports | \\[ida_nalt.enum_import_names(i, callback) for i in range(ida_nalt.get_import_module_qty())\\] | currentProgram.getSymbolTable().getExternalSymbols() |\n| dis.exports | \\[ida_entry.get_entry_ordinal(i) for i in range(ida_entry.get_entry_qty())\\] | currentProgram.getSymbolTable().getExternalEntryPointIterator() |\n| import_.address | *returned in callback* | symbol.getAddress() |\n| import_.name | *returned in callback* | symbol.getName() |\n| import_.namespace | ida_nalt.get_import_module_name(i) | symbol.getParentSymbol().getName() |\n| import_.references_to | idautils.XrefsTo(address) | symbol.getReferences() |\n| export.address | ida_entry.get_entry(ordinal) | symbol.getAddress() |\n| export.name | ida_entry.get_entry_name(ordinal) | symbol.getName() |\n| export.references_to | idautils.XrefsTo(ida_entry.get_entry(ordinal)) | symbol.getReferences() |\n\n\n### Functions\n| Dragodis* | IDA | Ghidra | \n|-------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------|\n| dis.functions() | idautils.Functions() | currentProgram.getListing().getFunctions(True) |\n| func = dis.get_function(address) | func = ida_funcs.get_func(address) | func = getFunctionContaining(toAddr(address)) |\n| func.start | func.start_ea | func.getEntryPoint() |\n| func.end | func.end_ea | func.getBody().getMaxAddress() |\n| func.name<br>dis.get_name(address) | ida_funcs.get_func_name(address) | func.getName() |\n| func.name = \"new_name\" | ida_name.set_name(address, \"new_name\") | func.setName(\"new_name\", SourceType.USER_DEFINED) |\n| func.get_comment() | ida_funcs.get_func_cmt(func, 0) | func.getComment() |\n| func.set_comment(\"new comment\") | ida_funcs.set_func_cmt(func, \"new comment\", 0) | func.setComment(\"new comment\") |\n| dis.get_flowchart(address)<br>func.flowchart | ida_gdl.FlowChart(func) | BasicBlockModel(currentProgram).getCodeBlocksContaining(func.getBody(), monitor) |\n| func.stack_frame | ida_frame.get_frame(func) | func.getStackFrame() |\n| dis.get_function_signature(address)<br>func.signature | tif = ida_typeinf.tinfo_t()<br>ida_nalt.get_tinfo(tif, address)<br>func_type_data = ida_typeinf.func_type_data_t()<br>tif.get_func_details(func_type_data) | func.getSignature() |\n| 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) |\n| func = dis.create_function(start) | func = ida_funcs.add_func(start) | func = CreateFunctionCmd(toAddr(start), True).applyTo(currentProgram).getFunction() |\n| func.undefine() | ida_funcs.del_func(start) | DeleteFunctionCmd(func.getEntryPoint(), True).applyTo(currentProgram) |\n\n\n### Instructions\n| Dragodis* | IDA | Ghidra | \n|--------------------------------------------|-----------------------------------------------------------|------------------------------------------------------------------------------------------------------|\n| insn = dis.get_instruction(address) | insn = ida_ua.insn_t(); ida_ua.decode_insn(insn, address) | insn = currentProgram.getListing().getCodeUnitAt(toAddr(address)) |\n| insn.is_call | ida_idp.is_call_insn(insn) | insn.getFlowType().isCall() |\n| insn.is_jump | *complex: see source code* | insn.getFlowType().isJump() |\n| insn.is_return | ida_idp.is_ret_insn(insn) | insn.getFlowType().isTerminal() |\n| insn.mnemonic<br>dis.get_mnemonic(address) | ida_ua.ua_mnem(address) | insn.getMnemonicString() |\n| insn.text | idc.GetDisasm(address) | str(insn) |\n| insn.operands | insn.ops | *N/A: See operands section* |\n| insn.stack_depth | idc.get_spd(address) | CallDepthChangeInfo(currentProgram.getListing().getFunctionContaining(insn.getAddress())).getDepth() |\n| insn.stack_delta | idc.get_sp_delta(address) | *complex: see source code* |\n\n### Operands\n\n*`insn` pulled as described above*\n\n| Dragodis* | IDA | Ghidra | \n|-------------------------------------------------------------------------------|---------------------------------------------------------------------|-------------------------------|\n| operand = dis.get_operand(address, index)<br>operand = insn.operands\\[index\\] | operand = insn.ops\\[index\\] | *N/A* |\n| operand.type<br>dis.get_operand_type(address, index) | idc.get_operand_type(address, index) | insn.getOperandType(index) |\n| operand.width | ida_ua.get_dtype_size(operand.dtype) | *complex: see source code* |\n| operand.value<br>dis.get_operand_value(address, index) | *(depends on type)*<br>operand.addr<br>operand.reg<br>operand.value | insn.getOpObjects(index) |\n| *(phrase operand)*<br>operand.value.base | operand.reg<br>ida_intel.x86_base_reg(insn, operand) | insn.getOpObjects(index)\\[0\\] |\n| *(phrase operand)*<br>operand.value.index | ida_intel.x86_index_reg(insn, operand) | insn.getOpObjects(index)\\[1\\] |\n| *(phrase operand)*<br>operand.value.scale | 1 << ida_intel.sib_scale(operand) | insn.getOpObjects(index)\\[2\\] |\n| *(phrase operand)*<br>operand.value.offset | operand.addr<br>idc.get_operand_value(address, index) | insn.getOpObjects(index)\\[3\\] |\n| *(register operand)*<br>operand.value | operand.reg<br>idc.get_operand_value(address, index) | insn.getOpObjects(index)\\[0\\] |\n| *(immediate operand)*<br>operand.value | operand.value<br>idc.get_operand_value(address, index) | insn.getOpObjects(index)\\[0\\] |\n| *(memory reference operand)*<br>operand.value | operand.addr<br>idc.get_operand_value(address, index) | insn.getOpObjects(index)\\[0\\] |\n\n\n### Registers\n| Dragodis* | IDA | Ghidra | \n|------------------------------------|--------------------------------------------------------------------------|----------------------------------------------|\n| register = dis.get_register(\"eax\") | register = ida_idp.reg_info_t(); ida_idp.parse_reg_name(register, \"eax\") | register = currentProgram.getRegister(\"eax\") |\n| register.name | ida_idp.get_reg_name(register.reg, register.size) | register.getName() |\n| register.bit_width | register.size * 8 | register.getBitLength() |\n\n\n### Segments\n| Dragodis* | IDA | Ghidra | \n|-----------------------------------------------------------------------|---------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------|\n| dis.segments | \\[ida_segment.getnseg(n) for n in range(ida_segment.get_segm_qty())\\] | currentProgram.getMemory().getBlocks() |\n| segment = dis.get_segment(name)<br>segment = dis.get_segment(address) | segment = ida_segment.get_segm_by_name(name)<br>segment = ida_segment.getseg(address) | memory_block = currentProgram.getMemory().getBlock(name)<br>memory_block = currentProgram.getMemory().getBlock(toAddr(address)) |\n| segment.name | ida_segment.get_segm_name(segment) | memory_block.getName() |\n| segment.start | segment.start_ea | memory_block.getStart() |\n| segment.end | segment.end_ea | memory_block.getEnd() |\n| segment.initialized | ida_bytes.is_loaded(segment.start_ea) | memory_block.isInitialized() |\n| segment.bit_size | segment.abits() | memory_block.getStart().getSize() |\n| segment.permissions | segment.perm | memory_block.isRead()<br>memory_block.isWrite()<br>memory_block.isExecute()<br>memory_block.isVolatile() |\n| 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) |\n\n\n### Strings\n| Dragodis* | IDA | Ghidra |\n|---------------------------|-------------------------------------------------------------------|---------------------------------------|\n| dis.strings(min_length=5) | finder = idautils.Strings(); finder.setup(minlen=5); list(finder) | findStrings(None, 5, 1, False, True) |\n\n\n\\* `dis` in the dragodis column represents the open disassembler object retrieved from `dragodis.open_program()`\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A universal interface for running scripts under multiple disassemblers.",
"version": "1.0.0",
"project_urls": {
"Homepage": "https://github.com/dod-cyber-crime-center/dragodis"
},
"split_keywords": [
"malware",
" ida",
" idapro",
" ghidra",
" disassembler"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "1963296f0f5aa475cb459def3f369f39504afbdb1ac3d9d43c3cd7d6e3e72a0c",
"md5": "74a4534c3573c7a9fa0dbae23c54cf69",
"sha256": "e134b71137e3beede82d71676b72a8d7ade4e1db9829e1c3f86357d3ca91083a"
},
"downloads": -1,
"filename": "dragodis-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "74a4534c3573c7a9fa0dbae23c54cf69",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 126276,
"upload_time": "2024-06-17T18:50:01",
"upload_time_iso_8601": "2024-06-17T18:50:01.741165Z",
"url": "https://files.pythonhosted.org/packages/19/63/296f0f5aa475cb459def3f369f39504afbdb1ac3d9d43c3cd7d6e3e72a0c/dragodis-1.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "49dae74e7a9ee89b1f30064822afc0c6a2e3f6714e21910717f79a67701d42e3",
"md5": "5bbd6fdf8639cf138a42227cccfab068",
"sha256": "3df2ed51b3772ee4cf9792bedfd47758318c4c37d67e0b7b4a7bb86fc41d5d48"
},
"downloads": -1,
"filename": "dragodis-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "5bbd6fdf8639cf138a42227cccfab068",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 127953,
"upload_time": "2024-06-17T18:50:18",
"upload_time_iso_8601": "2024-06-17T18:50:18.411101Z",
"url": "https://files.pythonhosted.org/packages/49/da/e74e7a9ee89b1f30064822afc0c6a2e3f6714e21910717f79a67701d42e3/dragodis-1.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-06-17 18:50:18",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "dod-cyber-crime-center",
"github_project": "dragodis",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "dragodis"
}