mirror of
https://github.com/gmh5225/awesome-game-security
synced 2026-06-21 13:56:22 +00:00
archive: add 5 repo prompt(s) [skip ci]
This commit is contained in:
@@ -0,0 +1,786 @@
|
||||
Project Path: arc_Krietz7_IDA-DataExportPlus_cznb5gwg
|
||||
|
||||
Source Tree:
|
||||
|
||||
```txt
|
||||
arc_Krietz7_IDA-DataExportPlus_cznb5gwg
|
||||
├── DataExportPlus.py
|
||||
├── LICENSE
|
||||
├── README.md
|
||||
└── README.zh_CN.md
|
||||
|
||||
```
|
||||
|
||||
`DataExportPlus.py`:
|
||||
|
||||
```py
|
||||
from os import getcwd, path
|
||||
from struct import unpack
|
||||
import idc
|
||||
import idaapi
|
||||
import ida_ida
|
||||
from ida_kernwin import add_hotkey
|
||||
from ida_bytes import get_flags
|
||||
|
||||
VERSION = "1.5.3"
|
||||
|
||||
# Notice: Since the selected value of IDA's self.DropdownListControl gets the index of the incoming List object,
|
||||
# the constant definition of key values also needs to follow the 0-index
|
||||
|
||||
# Define constants for data base
|
||||
DATA_BASE_HEX_KEY = 0
|
||||
DATA_BASE_DEC_KEY = 1
|
||||
DATA_BASE_OCT_KEY = 2
|
||||
DATA_BASE_BIN_KEY = 3
|
||||
|
||||
# Define constants for data types
|
||||
DATA_TYPE_BYTE_KEY = 0
|
||||
DATA_TYPE_WORD_KEY = 1
|
||||
DATA_TYPE_DWORD_KEY = 2
|
||||
DATA_TYPE_QWORD_KEY = 3
|
||||
DATA_TYPE_FLOAT_KEY = 4
|
||||
DATA_TYPE_DOUBLE_KEY = 5
|
||||
DATA_TYPE_STRING_LITERAL_KEY = 6
|
||||
DATA_TYPE_ASSEMBLY_CODE_KEY = 7
|
||||
DATA_TYPE_RAW_BYTES_KEY = 8
|
||||
|
||||
# Define constants for export formats
|
||||
EXPORT_FORMAT_STRING_KEY = 0
|
||||
EXPORT_FORMAT_C_VARIABLE_KEY = 1
|
||||
EXPORT_FORMAT_PYTHON_VARIABLE_KEY = 2
|
||||
|
||||
|
||||
class DEP_Conversion:
|
||||
BASE_VALUES = {
|
||||
DATA_BASE_HEX_KEY: 16,
|
||||
DATA_BASE_DEC_KEY: 10,
|
||||
DATA_BASE_OCT_KEY: 8,
|
||||
DATA_BASE_BIN_KEY: 2,
|
||||
}
|
||||
|
||||
C_TYPE_MAP = {
|
||||
DATA_TYPE_BYTE_KEY: "char",
|
||||
DATA_TYPE_WORD_KEY: "short",
|
||||
DATA_TYPE_DWORD_KEY: "int",
|
||||
DATA_TYPE_QWORD_KEY: "long long int",
|
||||
DATA_TYPE_FLOAT_KEY: "float",
|
||||
DATA_TYPE_DOUBLE_KEY: "double"
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def get_list():
|
||||
Data_base_list = {
|
||||
"Hexadecimal": DATA_BASE_HEX_KEY,
|
||||
"Decimal": DATA_BASE_DEC_KEY,
|
||||
"Octal": DATA_BASE_OCT_KEY,
|
||||
"Binary": DATA_BASE_BIN_KEY,
|
||||
}
|
||||
Data_type_list = {
|
||||
"Byte": DATA_TYPE_BYTE_KEY,
|
||||
"Word": DATA_TYPE_WORD_KEY,
|
||||
"Dword": DATA_TYPE_DWORD_KEY,
|
||||
"Qword": DATA_TYPE_QWORD_KEY,
|
||||
"Float": DATA_TYPE_FLOAT_KEY,
|
||||
"Double": DATA_TYPE_DOUBLE_KEY,
|
||||
"String literal": DATA_TYPE_STRING_LITERAL_KEY,
|
||||
"Assembly Code": DATA_TYPE_ASSEMBLY_CODE_KEY,
|
||||
"Raw bytes": DATA_TYPE_RAW_BYTES_KEY,
|
||||
}
|
||||
Data_exported_format_list = {
|
||||
"String": EXPORT_FORMAT_STRING_KEY,
|
||||
"C variable": EXPORT_FORMAT_C_VARIABLE_KEY,
|
||||
"Python variable": EXPORT_FORMAT_PYTHON_VARIABLE_KEY,
|
||||
}
|
||||
return (Data_base_list, Data_type_list, Data_exported_format_list)
|
||||
|
||||
def __init__(self, address, data_bytes, data_type_key=DATA_TYPE_BYTE_KEY,
|
||||
export_as_type_key=EXPORT_FORMAT_STRING_KEY, big_endian=False,
|
||||
base_key=DATA_BASE_HEX_KEY, signed=False, pad_zero=False,
|
||||
delimiter=" ", prefix="", suffix="", keep_comments=True,
|
||||
keep_names=True):
|
||||
self.address = address
|
||||
self.Data_base_list, self.Data_type_list, _ = self.get_list()
|
||||
self.data_bytes = data_bytes
|
||||
self.export_as_type_key = export_as_type_key
|
||||
self.big_endian = big_endian
|
||||
self.signed = signed
|
||||
self.pad_zero = pad_zero
|
||||
self.base_key = base_key
|
||||
self.data_type_key = data_type_key
|
||||
self.delimiter = delimiter
|
||||
self.prefix = prefix
|
||||
self.suffix = suffix
|
||||
self.keep_comments = keep_comments
|
||||
self.keep_names = keep_names
|
||||
|
||||
def activate(self):
|
||||
self.base = self.BASE_VALUES[self.base_key]
|
||||
|
||||
if self.data_type_key == DATA_TYPE_RAW_BYTES_KEY:
|
||||
return "Cannot preview binary data"
|
||||
|
||||
output = self._get_conversion_output()
|
||||
if output is None:
|
||||
return None
|
||||
|
||||
if self.export_as_type_key == EXPORT_FORMAT_STRING_KEY:
|
||||
return output
|
||||
|
||||
if self.export_as_type_key == EXPORT_FORMAT_C_VARIABLE_KEY:
|
||||
return self._format_c_variable(output)
|
||||
|
||||
if self.export_as_type_key == EXPORT_FORMAT_PYTHON_VARIABLE_KEY:
|
||||
return self._format_python_variable(output)
|
||||
|
||||
return None
|
||||
|
||||
def _get_conversion_output(self):
|
||||
if self.data_type_key in [DATA_TYPE_BYTE_KEY, DATA_TYPE_WORD_KEY,
|
||||
DATA_TYPE_DWORD_KEY, DATA_TYPE_QWORD_KEY]:
|
||||
return self._number_conversion()
|
||||
elif self.data_type_key in [DATA_TYPE_FLOAT_KEY, DATA_TYPE_DOUBLE_KEY]:
|
||||
return self._float_conversion()
|
||||
elif self.data_type_key == DATA_TYPE_STRING_LITERAL_KEY:
|
||||
return self._string_literal_conversion()
|
||||
elif self.data_type_key == DATA_TYPE_ASSEMBLY_CODE_KEY:
|
||||
return self.assembly_code_conversion()
|
||||
return None
|
||||
|
||||
def _format_c_variable(self, output):
|
||||
var_name = f"IDA_{hex(self.address)[2:]}"
|
||||
|
||||
if self.data_type_key in self.C_TYPE_MAP:
|
||||
c_type = self.C_TYPE_MAP[self.data_type_key]
|
||||
if not self.signed:
|
||||
c_type = f"unsigned {c_type}"
|
||||
|
||||
if self.data_type_key in [DATA_TYPE_FLOAT_KEY, DATA_TYPE_DOUBLE_KEY]:
|
||||
output = output.replace("nan", "NAN")
|
||||
|
||||
return f"{c_type} {var_name}[] = {{{output}}};"
|
||||
|
||||
elif self.data_type_key == DATA_TYPE_STRING_LITERAL_KEY:
|
||||
c_string = self._strliteral_to_c_string(self.data_bytes)
|
||||
return f'unsigned char {var_name}[] = "{c_string}";'
|
||||
|
||||
elif self.data_type_key == DATA_TYPE_ASSEMBLY_CODE_KEY:
|
||||
escaped_lines = '\\n"\n"'.join(line for line in output.splitlines())[:-1]
|
||||
return f'const char* {var_name} = "{escaped_lines}";'
|
||||
|
||||
return None
|
||||
|
||||
def _format_python_variable(self, output):
|
||||
var_name = f"IDA_{hex(self.address)[2:]}"
|
||||
|
||||
if self.data_type_key in [DATA_TYPE_BYTE_KEY, DATA_TYPE_WORD_KEY,
|
||||
DATA_TYPE_DWORD_KEY, DATA_TYPE_QWORD_KEY]:
|
||||
return f"{var_name} = [{output}]"
|
||||
|
||||
elif self.data_type_key in [DATA_TYPE_FLOAT_KEY, DATA_TYPE_DOUBLE_KEY]:
|
||||
output = output.replace("nan", "float('nan')")
|
||||
return f"{var_name} = [{output}]"
|
||||
|
||||
elif self.data_type_key == DATA_TYPE_STRING_LITERAL_KEY:
|
||||
py_literal = self._bytes_to_py_literal(self.data_bytes)
|
||||
return f"{var_name} = {py_literal}"
|
||||
|
||||
elif self.data_type_key == DATA_TYPE_ASSEMBLY_CODE_KEY:
|
||||
return f"{var_name} = '''{output}'''"
|
||||
|
||||
return None
|
||||
|
||||
def _strliteral_to_c_string(self, data_bytes):
|
||||
result = []
|
||||
is_escape = False
|
||||
|
||||
for byte in data_bytes:
|
||||
if byte == ord('\\'):
|
||||
result.append("\\\\")
|
||||
is_escape = False
|
||||
elif byte == ord('"'):
|
||||
result.append("\\\"")
|
||||
is_escape = False
|
||||
elif byte == ord('\n'):
|
||||
result.append("\\n")
|
||||
is_escape = False
|
||||
elif byte == ord('\r'):
|
||||
result.append("\\r")
|
||||
is_escape = False
|
||||
elif byte == ord('\t'):
|
||||
result.append("\\t")
|
||||
is_escape = False
|
||||
elif 32 <= byte <= 126:
|
||||
c = chr(byte)
|
||||
# To avoid confusion with the previous \xHH, force escape if the current character is a hex letter
|
||||
if c in '0123456789abcdefABCDEF' and is_escape:
|
||||
result.append('""')
|
||||
is_escape = False
|
||||
result.append(c)
|
||||
else:
|
||||
result.append(f'\\x{byte:02X}')
|
||||
is_escape = True
|
||||
|
||||
return ''.join(result)
|
||||
|
||||
def _bytes_to_py_literal(self, data_bytes):
|
||||
return repr(data_bytes)
|
||||
|
||||
# base on Byte/Word/Dword/Qword convert byte stream to number
|
||||
# parameter: base big_endian sign prefix suffix delimiter
|
||||
def _number_conversion(self):
|
||||
type_len_list = {DATA_TYPE_BYTE_KEY:1, DATA_TYPE_WORD_KEY:2,
|
||||
DATA_TYPE_DWORD_KEY:4, DATA_TYPE_QWORD_KEY:8}
|
||||
Number_len = type_len_list[self.data_type_key]
|
||||
number_bytes_array = []
|
||||
|
||||
for i in range(0, len(self.data_bytes), Number_len):
|
||||
chunk = self.data_bytes[i:i+Number_len]
|
||||
if len(chunk) < Number_len:
|
||||
chunk += b'\x00' * (Number_len - len(chunk))
|
||||
number = int.from_bytes(chunk, byteorder='little' if not self.big_endian else 'big', signed=self.signed)
|
||||
number_bytes_array.append(number)
|
||||
|
||||
def format_number(n):
|
||||
base = self.base
|
||||
prefix = self.prefix or ''
|
||||
suffix = self.suffix or ''
|
||||
is_negative = n < 0
|
||||
num_abs = abs(n)
|
||||
|
||||
pad_width = 0
|
||||
if self.pad_zero:
|
||||
if base == 2:
|
||||
pad_width = Number_len * 8
|
||||
elif base == 8:
|
||||
pad_width = Number_len * 3
|
||||
elif base == 16:
|
||||
pad_width = Number_len * 2
|
||||
|
||||
if base == 10:
|
||||
s = str(num_abs)
|
||||
elif base == 2:
|
||||
s = bin(num_abs)[2:]
|
||||
if pad_width:
|
||||
s = s.zfill(pad_width)
|
||||
elif base == 8:
|
||||
s = oct(num_abs)[2:]
|
||||
if pad_width:
|
||||
s = s.zfill(pad_width)
|
||||
elif base == 16:
|
||||
s = hex(num_abs)[2:]
|
||||
if pad_width:
|
||||
s = s.zfill(pad_width)
|
||||
result = prefix + s if not is_negative else '-' + prefix + s
|
||||
return result + suffix
|
||||
|
||||
if self.base > 0 and self.base < 37:
|
||||
return self.delimiter.join(format_number(i) for i in number_bytes_array)
|
||||
return None
|
||||
|
||||
def _float_conversion(self):
|
||||
type_len_list = {DATA_TYPE_FLOAT_KEY:4, DATA_TYPE_DOUBLE_KEY:8}
|
||||
Number_len = type_len_list[self.data_type_key]
|
||||
number_bytes_array = []
|
||||
|
||||
for i in range(0, len(self.data_bytes), Number_len):
|
||||
chunk = self.data_bytes[i:i + Number_len]
|
||||
if len(chunk) == Number_len:
|
||||
number_bytes_array.append(chunk)
|
||||
|
||||
format_char = '<f' if self.data_type_key == DATA_TYPE_FLOAT_KEY else '<d'
|
||||
if self.big_endian:
|
||||
format_char = '>' + format_char[1]
|
||||
|
||||
float_values = []
|
||||
for chunk in number_bytes_array:
|
||||
value = unpack(format_char, chunk)[0]
|
||||
float_values.append(value)
|
||||
|
||||
return self.delimiter.join(f"{self.prefix}{i}{self.suffix}" for i in float_values)
|
||||
|
||||
def _string_literal_conversion(self):
|
||||
return str(self.data_bytes)[2:-1]
|
||||
|
||||
def assembly_code_conversion(self):
|
||||
assembly_code_start = self.address
|
||||
assembly_code_end = self.address + len(self.data_bytes)
|
||||
|
||||
output = ""
|
||||
i = assembly_code_start
|
||||
if(assembly_code_start > ida_ida.inf_get_max_ea() or assembly_code_start < ida_ida.inf_get_min_ea()):
|
||||
return ""
|
||||
while i < assembly_code_end:
|
||||
if(self.keep_names):
|
||||
addr_name = idc.get_name(i)
|
||||
if addr_name:
|
||||
output += "\n" + addr_name + ":\n"
|
||||
if(self.keep_comments):
|
||||
output += idc.generate_disasm_line(i,0)
|
||||
else:
|
||||
output += idc.generate_disasm_line(i,0).split(";")[0]
|
||||
output += "\n"
|
||||
i = idc.find_code(i, 1)
|
||||
return output
|
||||
|
||||
|
||||
class DEP_Form(idaapi.Form):
|
||||
# idaapi information
|
||||
min_ea = ida_ida.inf_get_min_ea()
|
||||
max_ea = ida_ida.inf_get_max_ea()
|
||||
|
||||
# list
|
||||
Data_base_list, Data_type_list, Data_exported_format_list = DEP_Conversion.get_list()
|
||||
|
||||
# We map data_type_key to form field visibility using O(1) time to look
|
||||
# Here True = show field, False = hide field
|
||||
FIELD_VISIBILITY = {
|
||||
DATA_TYPE_BYTE_KEY: {
|
||||
'_endianness': False, '_base': True, '_signed': True, '_pad_zero': True,
|
||||
'_delimiter': True, '_prefix': True, '_suffix': True,
|
||||
'_keep_comments': False, '_keep_names': False
|
||||
},
|
||||
DATA_TYPE_WORD_KEY: {
|
||||
'_endianness': True, '_base': True, '_signed': True, '_pad_zero': True,
|
||||
'_delimiter': True, '_prefix': True, '_suffix': True,
|
||||
'_keep_comments': False, '_keep_names': False
|
||||
},
|
||||
DATA_TYPE_DWORD_KEY: {
|
||||
'_endianness': True, '_base': True, '_signed': True, '_pad_zero': True,
|
||||
'_delimiter': True, '_prefix': True, '_suffix': True,
|
||||
'_keep_comments': False, '_keep_names': False
|
||||
},
|
||||
DATA_TYPE_QWORD_KEY: {
|
||||
'_endianness': True, '_base': True, '_signed': True, '_pad_zero': True,
|
||||
'_delimiter': True, '_prefix': True, '_suffix': True,
|
||||
'_keep_comments': False, '_keep_names': False
|
||||
},
|
||||
DATA_TYPE_FLOAT_KEY: {
|
||||
'_endianness': True, '_base': False, '_signed': False, '_pad_zero': False,
|
||||
'_delimiter': True, '_prefix': True, '_suffix': True,
|
||||
'_keep_comments': False, '_keep_names': False
|
||||
},
|
||||
DATA_TYPE_DOUBLE_KEY: {
|
||||
'_endianness': True, '_base': False, '_signed': False, '_pad_zero': False,
|
||||
'_delimiter': True, '_prefix': True, '_suffix': True,
|
||||
'_keep_comments': False, '_keep_names': False
|
||||
},
|
||||
DATA_TYPE_STRING_LITERAL_KEY: {
|
||||
'_endianness': False, '_base': False, '_signed': False, '_pad_zero': False,
|
||||
'_delimiter': False, '_prefix': False, '_suffix': False,
|
||||
'_keep_comments': False, '_keep_names': False
|
||||
},
|
||||
DATA_TYPE_ASSEMBLY_CODE_KEY: {
|
||||
'_endianness': False, '_base': False, '_signed': False, '_pad_zero': False,
|
||||
'_delimiter': False, '_prefix': False, '_suffix': False,
|
||||
'_keep_comments': True, '_keep_names': True
|
||||
},
|
||||
DATA_TYPE_RAW_BYTES_KEY: {
|
||||
'_endianness': False, '_base': False, '_signed': False, '_pad_zero': False,
|
||||
'_delimiter': False, '_prefix': False, '_suffix': False,
|
||||
'_keep_comments': False, '_keep_names': False
|
||||
}
|
||||
}
|
||||
|
||||
def __init__(self, select_addr_start, select_addr_end, select_len):
|
||||
self.export_address_start = select_addr_start
|
||||
self.export_address_end = select_addr_end
|
||||
self.export_address_len = select_len
|
||||
self.Data_bytes = b""
|
||||
self.export_data_type_key = DATA_TYPE_BYTE_KEY
|
||||
|
||||
# Format Options
|
||||
self.export_big_endian = False
|
||||
self.export_base_key = DATA_BASE_HEX_KEY
|
||||
self.export_signed = False
|
||||
self.export_delimiter = ","
|
||||
self.export_prefix = "0x"
|
||||
self.export_suffix = ""
|
||||
self.export_as_type_key = EXPORT_FORMAT_STRING_KEY
|
||||
self.export_keep_comments = True
|
||||
self.export_keep_names = True
|
||||
self.export_pad_zero = False
|
||||
|
||||
self.export_data = None
|
||||
self.export_file_path = path.join(getcwd(), "export_results.txt")
|
||||
|
||||
data_type_flag = get_flags(self.export_address_start)
|
||||
checks = [
|
||||
(idc.is_byte, DATA_TYPE_BYTE_KEY),
|
||||
(idc.is_word, DATA_TYPE_WORD_KEY),
|
||||
(idc.is_dword, DATA_TYPE_DWORD_KEY),
|
||||
(idc.is_qword, DATA_TYPE_QWORD_KEY),
|
||||
(idc.is_float, DATA_TYPE_FLOAT_KEY),
|
||||
(idc.is_double, DATA_TYPE_DOUBLE_KEY),
|
||||
(idc.is_strlit, DATA_TYPE_STRING_LITERAL_KEY),
|
||||
(idc.is_code, DATA_TYPE_ASSEMBLY_CODE_KEY),
|
||||
]
|
||||
self.export_data_type_key = next((key for func, key in checks if func(data_type_flag)), DATA_TYPE_BYTE_KEY)
|
||||
|
||||
super(DEP_Form, self).__init__(
|
||||
r'''STARTITEM 0
|
||||
BUTTON YES* Export
|
||||
Export Plus: Export Data
|
||||
|
||||
{FormChangeCb}
|
||||
<Start Address :{_address_start}>
|
||||
<End Address :{_address_end}>
|
||||
<Selected Length :{_length}>
|
||||
<~Selected Data~ :{_select_data}>
|
||||
< Data Type :{_data_type}>
|
||||
< Export As :{_export_type}>
|
||||
|
||||
Export Format:
|
||||
<##- Endianness :{_endianness}>
|
||||
<##- Base :{_base}>
|
||||
<##- Signed :{_signed}>
|
||||
<##- Pad Zero :{_pad_zero}>
|
||||
<##- Delimiter :{_delimiter}>
|
||||
<##- Prefix :{_prefix}>
|
||||
<##- Suffix :{_suffix}>
|
||||
|
||||
<##- Keep Comments:{_keep_comments}>
|
||||
<##- Keep Names :{_keep_names}>
|
||||
<~Export Window~: {_export_text}>
|
||||
<Export File Path: {_export_file_path}>''',
|
||||
{
|
||||
"FormChangeCb": self.FormChangeCb(self.OnFormChange),
|
||||
|
||||
"_address_start": self.NumericInput(value = self.export_address_start, tp=self.FT_ADDR, swidth=30),
|
||||
"_address_end": self.NumericInput(value = self.export_address_end, tp=self.FT_ADDR, swidth=30),
|
||||
"_length": self.NumericInput(value = self.export_address_len, swidth = 30),
|
||||
"_select_data": self.StringInput(value = "",swidth = 30),
|
||||
"_data_type": self.DropdownListControl(items = list(self.Data_type_list.keys()), selval = self.export_data_type_key),
|
||||
"_export_type": self.DropdownListControl(items =list(self.Data_exported_format_list.keys())),
|
||||
|
||||
"_endianness": self.DropdownListControl(items = ["Little-endian","Big-endian"]),
|
||||
"_base": self.DropdownListControl(items = list(self.Data_base_list.keys())),
|
||||
"_signed": self.DropdownListControl(items = ["False", "True"]),
|
||||
"_pad_zero": self.DropdownListControl(items = ["False", "True"]),
|
||||
"_delimiter": self.StringInput(value = self.export_delimiter,swidth = 30),
|
||||
"_prefix": self.StringInput(value = self.export_prefix,swidth = 30),
|
||||
"_suffix": self.StringInput(value = self.export_suffix,swidth = 30),
|
||||
"_keep_comments": self.DropdownListControl(items = ["False", "True"], selval = 1),
|
||||
"_keep_names": self.DropdownListControl(items = ["False", "True"], selval = 1),
|
||||
|
||||
|
||||
"_export_text": self.MultiLineTextControl(text="", swidth=48),
|
||||
"_export_file_path": self.FileInput(value=self.export_file_path, save=True, swidth=30),
|
||||
}
|
||||
)
|
||||
self.Compile()
|
||||
|
||||
def OnFormChange(self, fid):
|
||||
if fid == -1:
|
||||
self._initalizate()
|
||||
elif fid in [self._address_start.id, self._address_end.id, self._length.id]:
|
||||
self._update_address_fields(fid)
|
||||
elif fid in [self._data_type.id, self._export_type.id, self._signed.id]:
|
||||
self._update_export_settings()
|
||||
self._update_field_visibility()
|
||||
self._update_field_availability()
|
||||
self._set_controls_default_value()
|
||||
elif fid in [self._endianness.id, self._base.id, self._signed.id, self._pad_zero.id,
|
||||
self._delimiter.id, self._prefix.id, self._suffix.id,
|
||||
self._keep_comments.id, self._keep_names.id]:
|
||||
self._update_format_settings(fid)
|
||||
elif fid == self._export_file_path.id:
|
||||
self.export_file_path = self.GetControlValue(self._export_file_path)
|
||||
|
||||
self._refresh_export_window()
|
||||
return 1
|
||||
|
||||
def _initalizate(self):
|
||||
self.EnableField(self._select_data, False)
|
||||
self._update_address_fields(-1)
|
||||
self._update_export_settings()
|
||||
self._update_field_visibility()
|
||||
self._update_field_availability()
|
||||
self._set_controls_default_value()
|
||||
|
||||
def _update_address_fields(self, fid):
|
||||
input_export_address_start = self.GetControlValue(self._address_start)
|
||||
input_export_address_end = self.GetControlValue(self._address_end)
|
||||
input_export_address_len = self.GetControlValue(self._length)
|
||||
|
||||
self.min_ea = ida_ida.inf_get_min_ea()
|
||||
self.max_ea = ida_ida.inf_get_max_ea()
|
||||
|
||||
try:
|
||||
if fid in (self._address_start.id, self._address_end.id):
|
||||
if not (self.min_ea <= input_export_address_start <= self.max_ea and
|
||||
self.min_ea <= input_export_address_end <= self.max_ea):
|
||||
return 1
|
||||
if input_export_address_start >= input_export_address_end:
|
||||
return 1
|
||||
input_export_address_len = input_export_address_end - input_export_address_start
|
||||
self.SetControlValue(self._length, input_export_address_len)
|
||||
|
||||
elif fid == self._length.id:
|
||||
if not (self.min_ea <= input_export_address_start <= self.max_ea and
|
||||
input_export_address_start + input_export_address_len <= self.max_ea):
|
||||
return 1
|
||||
input_export_address_end = input_export_address_start + input_export_address_len
|
||||
self.SetControlValue(self._address_end, input_export_address_end)
|
||||
|
||||
if (self.min_ea <= input_export_address_start and
|
||||
self.max_ea > input_export_address_start + input_export_address_len):
|
||||
self.Data_bytes, data_str = self._get_ea_data(input_export_address_start, input_export_address_len)
|
||||
self.SetControlValue(self._select_data, data_str)
|
||||
|
||||
self.export_address_start = input_export_address_start
|
||||
self.export_address_len = input_export_address_len
|
||||
except TypeError:
|
||||
return 1
|
||||
|
||||
def _update_export_settings(self):
|
||||
self.export_data_type_key = self.GetControlValue(self._data_type)
|
||||
self.export_as_type_key = self.GetControlValue(self._export_type)
|
||||
self.export_signed = {0: False, 1: True}[self.GetControlValue(self._signed)]
|
||||
|
||||
def _update_field_visibility(self):
|
||||
visibility = self.FIELD_VISIBILITY.get(self.export_data_type_key, {})
|
||||
for field_name, show in visibility.items():
|
||||
self.ShowField(getattr(self, field_name), show)
|
||||
self.ShowField(self._export_type,
|
||||
self.export_data_type_key != DATA_TYPE_RAW_BYTES_KEY)
|
||||
|
||||
def _update_field_availability(self):
|
||||
if self.export_as_type_key == EXPORT_FORMAT_STRING_KEY:
|
||||
self.EnableField(self._delimiter, True)
|
||||
self.EnableField(self._prefix, True)
|
||||
self.EnableField(self._suffix, True)
|
||||
else:
|
||||
self.EnableField(self._delimiter, False)
|
||||
self.EnableField(self._prefix, False)
|
||||
self.EnableField(self._suffix, False)
|
||||
|
||||
def _update_format_settings(self, fid):
|
||||
self.export_big_endian = self.GetControlValue(self._endianness)
|
||||
self.export_base_key = self.GetControlValue(self._base)
|
||||
self.export_signed = {0: False, 1: True}[self.GetControlValue(self._signed)]
|
||||
self.export_pad_zero = {0: False, 1: True}[self.GetControlValue(self._pad_zero)]
|
||||
self.export_delimiter = self.GetControlValue(self._delimiter)
|
||||
self.export_prefix = self.GetControlValue(self._prefix)
|
||||
self.export_suffix = self.GetControlValue(self._suffix)
|
||||
self.export_keep_comments = {0: False, 1: True}[self.GetControlValue(self._keep_comments)]
|
||||
self.export_keep_names = {0: False, 1: True}[self.GetControlValue(self._keep_names)]
|
||||
|
||||
if fid in [self._base.id, self._signed.id]:
|
||||
self._set_controls_default_value()
|
||||
|
||||
def _get_ea_data(self, address, length):
|
||||
data_byte = idc.get_bytes(address, length)
|
||||
data_str = ' '.join([f"{i:02X}" for i in bytearray(data_byte)])
|
||||
return data_byte, data_str.strip()
|
||||
|
||||
def _refresh_export_window(self):
|
||||
t = DEP_Conversion(
|
||||
address=self.export_address_start, data_bytes=self.Data_bytes,
|
||||
data_type_key=self.export_data_type_key, export_as_type_key=self.export_as_type_key,
|
||||
big_endian=self.export_big_endian, base_key=self.export_base_key,
|
||||
signed=self.export_signed, pad_zero=self.export_pad_zero,
|
||||
delimiter=self.export_delimiter, prefix=self.export_prefix, suffix=self.export_suffix,
|
||||
keep_comments=self.export_keep_comments, keep_names=self.export_keep_names
|
||||
)
|
||||
self.export_data = t.activate()
|
||||
self.SetControlValue(self._export_text, idaapi.textctrl_info_t(
|
||||
text=self.export_data, flags=32, tabsize=0))
|
||||
|
||||
def _set_controls_default_value(self):
|
||||
self._set_default_prefix_value()
|
||||
self._set_default_delimiter_value()
|
||||
self._set_default_suffix_value()
|
||||
|
||||
def _set_default_prefix_value(self):
|
||||
if self.export_data_type_key in [DATA_TYPE_BYTE_KEY, DATA_TYPE_WORD_KEY,
|
||||
DATA_TYPE_DWORD_KEY, DATA_TYPE_QWORD_KEY]:
|
||||
Prefix_list = {DATA_BASE_HEX_KEY: "0x", DATA_BASE_DEC_KEY: "",
|
||||
DATA_BASE_OCT_KEY: "0o", DATA_BASE_BIN_KEY: "0b"}
|
||||
self.export_prefix = Prefix_list[self.export_base_key]
|
||||
|
||||
if (self.export_as_type_key == EXPORT_FORMAT_C_VARIABLE_KEY and
|
||||
self.export_base_key == DATA_BASE_OCT_KEY):
|
||||
self.export_prefix = "0"
|
||||
|
||||
elif self.export_data_type_key in [DATA_TYPE_FLOAT_KEY, DATA_TYPE_DOUBLE_KEY]:
|
||||
self.export_prefix = ""
|
||||
|
||||
self.SetControlValue(self._prefix, self.export_prefix)
|
||||
|
||||
def _set_default_delimiter_value(self):
|
||||
if self.export_data_type_key in [DATA_TYPE_BYTE_KEY, DATA_TYPE_WORD_KEY,
|
||||
DATA_TYPE_DWORD_KEY, DATA_TYPE_QWORD_KEY,
|
||||
DATA_TYPE_FLOAT_KEY, DATA_TYPE_DOUBLE_KEY]:
|
||||
self.export_delimiter = ", "
|
||||
self.SetControlValue(self._delimiter, self.export_delimiter)
|
||||
|
||||
def _set_default_suffix_value(self):
|
||||
self.export_suffix = ""
|
||||
|
||||
if self.export_as_type_key == EXPORT_FORMAT_C_VARIABLE_KEY:
|
||||
suffix_map = {
|
||||
DATA_TYPE_BYTE_KEY: "", DATA_TYPE_WORD_KEY: "",
|
||||
DATA_TYPE_DWORD_KEY: "L", DATA_TYPE_QWORD_KEY: "LL",
|
||||
DATA_TYPE_FLOAT_KEY: "", DATA_TYPE_DOUBLE_KEY: ""
|
||||
}
|
||||
suffix = suffix_map.get(self.export_data_type_key, "")
|
||||
|
||||
if not self.export_signed and self.export_data_type_key in [
|
||||
DATA_TYPE_BYTE_KEY, DATA_TYPE_WORD_KEY, DATA_TYPE_DWORD_KEY, DATA_TYPE_QWORD_KEY]:
|
||||
suffix = "U" + suffix
|
||||
self.export_suffix = suffix
|
||||
|
||||
self.SetControlValue(self._suffix, self.export_suffix)
|
||||
|
||||
|
||||
class DataExportPlus(idaapi.plugin_t):
|
||||
flags = idaapi.PLUGIN_DRAW
|
||||
comment = "Export Data"
|
||||
help = ""
|
||||
wanted_name = "Data Export Plus"
|
||||
version = VERSION
|
||||
|
||||
def init(self):
|
||||
print("=" * 80)
|
||||
print("Start Data Export Plus plugin")
|
||||
|
||||
idc.del_idc_hotkey("Shift+E")
|
||||
add_hotkey("Shift-E", self.hotkeystart)
|
||||
|
||||
return idaapi.PLUGIN_OK
|
||||
|
||||
def term(self):
|
||||
return
|
||||
|
||||
def hotkeystart(self):
|
||||
self.run(None)
|
||||
|
||||
def run(self, args):
|
||||
ea_addr_start, ea_addr_end, ea_item_size = self.GetEAItem()
|
||||
form = DEP_Form(ea_addr_start, ea_addr_end, ea_item_size)
|
||||
IsExport = form.Execute()
|
||||
|
||||
if IsExport:
|
||||
if path.exists(form.export_file_path):
|
||||
k = idc.ask_yn(1, "Export file already exists, Do you want to overwrite it?")
|
||||
if k == -1 or k == 0:
|
||||
form.Free()
|
||||
return 1
|
||||
|
||||
try:
|
||||
if form.export_data_type_key == DATA_TYPE_RAW_BYTES_KEY:
|
||||
with open(form.export_file_path, "wb") as file_handle:
|
||||
file_handle.write(form.Data_bytes)
|
||||
else:
|
||||
with open(form.export_file_path, "w", encoding="utf-8") as file_handle:
|
||||
file_handle.write(form.export_data)
|
||||
|
||||
print("Stored export results in", form.export_file_path)
|
||||
|
||||
except:
|
||||
idc.warning("Export file failed")
|
||||
|
||||
form.Free()
|
||||
return 1
|
||||
|
||||
def GetEAItem(self):
|
||||
selection, ea_addr_start, ea_addr_end = idaapi.read_range_selection(None)
|
||||
|
||||
if selection:
|
||||
if ea_addr_start <= ea_addr_end:
|
||||
ea_item_size = ea_addr_end - ea_addr_start
|
||||
else:
|
||||
ea_item_size = ea_addr_start - ea_addr_end
|
||||
else:
|
||||
ea_addr_start = idc.get_screen_ea()
|
||||
ea_item_size = idc.get_item_size(idc.get_screen_ea())
|
||||
ea_addr_end = ea_addr_start + ea_item_size
|
||||
|
||||
return ea_addr_start, ea_addr_end, ea_item_size
|
||||
|
||||
|
||||
|
||||
|
||||
def PLUGIN_ENTRY():
|
||||
return DataExportPlus()
|
||||
|
||||
```
|
||||
|
||||
`LICENSE`:
|
||||
|
||||
```
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 Krietz7
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
```
|
||||
|
||||
`README.md`:
|
||||
|
||||
```md
|
||||
# IDA-DataExportPlus
|
||||
[中文](README.zh_CN.md)
|
||||
|
||||
a IDA Pro plugin for exporting data, it can replace the IDA Pro original Data Export Window.
|
||||
|
||||
The plugin supports export data base on different data type, include integer and floating point types of different sizes. Additional it also supports export assembly code.
|
||||
|
||||
## Installation
|
||||
Copy `DataExportPlus.py` to IDA plugins directory. Requires IDA Pro version 7.6 or above
|
||||
|
||||
## Usage
|
||||
Use hotkey `Shift+E` to Call out the plugin window
|
||||
|
||||
<img width="2560" height="1444" alt="image" src="https://github.com/user-attachments/assets/3d100006-847c-40d9-af1f-629d93f9c0aa" />
|
||||
|
||||
You can change the format of the exported data in this window
|
||||
|
||||
<img width="2559" height="1408" alt="image" src="https://github.com/user-attachments/assets/ca58e48b-8b00-4afc-9839-8b4c9b00d4e3" />
|
||||
|
||||
Exporting assembly code
|
||||
|
||||
<img width="2475" height="1410" alt="image" src="https://github.com/user-attachments/assets/15d4bb43-2b9e-44d0-a9ad-b62f6d5fc841" />
|
||||
|
||||
```
|
||||
|
||||
`README.zh_CN.md`:
|
||||
|
||||
```md
|
||||
# IDA-DataExportPlus
|
||||
一个用于导出数据的IDA PRO插件,它可以替换IDA Pro原始的数据导出窗口。
|
||||
该插件支持基于不同数据类型的数据导出,包括不同大小的整数和浮点类型。除此之外也支持导出汇编代码。
|
||||
|
||||
## 安装
|
||||
复制`dataexportplus.py`到IDA插件目录下。需要IDA Pro版本7.6或以上
|
||||
|
||||
## 使用方法
|
||||
使用快捷键`Shift+E`以唤出插件窗口
|
||||
|
||||
<img width="2560" height="1444" alt="image" src="https://github.com/user-attachments/assets/3d100006-847c-40d9-af1f-629d93f9c0aa" />
|
||||
|
||||
您可以在此窗口中更改导出数据的格式
|
||||
|
||||
<img width="2559" height="1408" alt="image" src="https://github.com/user-attachments/assets/ca58e48b-8b00-4afc-9839-8b4c9b00d4e3" />
|
||||
|
||||
导出汇编代码
|
||||
|
||||
<img width="2475" height="1410" alt="image" src="https://github.com/user-attachments/assets/15d4bb43-2b9e-44d0-a9ad-b62f6d5fc841" />
|
||||
```
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user