diff options
Diffstat (limited to 'python')
| -rw-r--r-- | python/__init__.py | 4652 | ||||
| -rw-r--r-- | python/examples/nes.py | 661 | ||||
| -rw-r--r-- | python/generator.cpp | 302 |
3 files changed, 5615 insertions, 0 deletions
diff --git a/python/__init__.py b/python/__init__.py new file mode 100644 index 00000000..432118fa --- /dev/null +++ b/python/__init__.py @@ -0,0 +1,4652 @@ +import _binaryninjacore as core +import ctypes, traceback, json, struct, threading + +_plugin_init = False +def _init_plugins(): + global _plugin_init + if not _plugin_init: + _plugin_init = True + core.BNInitCorePlugins() + core.BNInitUserPlugins() + +class DataBuffer: + def __init__(self, contents = "", handle = None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNDataBuffer) + elif (type(contents) is int) or (type(contents) is long): + self.handle = core.BNCreateDataBuffer(None, contents) + elif isinstance(contents, DataBuffer): + self.handle = core.BNDuplicateDataBuffer(contents.handle) + else: + self.handle = core.BNCreateDataBuffer(contents, len(contents)) + + def __del__(self): + core.BNFreeDataBuffer(self.handle) + + def __len__(self): + return int(core.BNGetDataBufferLength(self.handle)) + + def __getitem__(self, i): + if isinstance(i, tuple): + result = "" + source = str(self) + for s in i: + result += source[s] + return result + elif isinstance(i, slice): + if i.step is not None: + i = i.indices(len(self)) + start = i[0] + stop = i[1] + if stop <= start: + return "" + buf = ctypes.create_string_buffer(stop - start) + ctypes.memmove(buf, core.BNGetDataBufferContentsAt(self.handle, start), stop - start) + return buf.raw + else: + return str(self)[i] + elif i < 0: + if i >= -len(self): + return chr(core.BNGetDataBufferByte(self.handle, int(len(self) + i))) + raise IndexError, "index out of range" + elif i < len(self): + return chr(core.BNGetDataBufferByte(self.handle, int(i))) + else: + raise IndexError, "index out of range" + + def __setitem__(self, i, value): + if isinstance(i, slice): + if i.step is not None: + raise IndexError, "step not supported on assignment" + i = i.indices(len(self)) + start = i[0] + stop = i[1] + if stop < start: + stop = start + if len(value) != (stop - start): + data = str(self) + data = data[0:start] + value + data[stop:] + core.BNSetDataBufferContents(self.handle, data, len(data)) + else: + value = str(value) + buf = ctypes.create_string_buffer(value) + ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, start), buf, len(value)) + elif i < 0: + if i >= -len(self): + if len(value) != 1: + raise ValueError, "expected single byte for assignment" + value = str(value) + buf = ctypes.create_string_buffer(alue) + ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, int(len(self) + i)), buf, 1) + else: + raise IndexError, "index out of range" + elif i < len(self): + if len(value) != 1: + raise ValueError, "expected single byte for assignment" + value = str(value) + buf = ctypes.create_string_buffer(value) + ctypes.memmove(core.BNGetDataBufferContentsAt(self.handle, int(i)), buf, 1) + else: + raise IndexError, "index out of range" + + def __str__(self): + buf = ctypes.create_string_buffer(len(self)) + ctypes.memmove(buf, core.BNGetDataBufferContents(self.handle), len(self)) + return buf.raw + + def __repr__(self): + return repr(str(self)) + + def escape(self): + return core.BNDataBufferToEscapedString(self.handle) + + def unescape(self): + return DataBuffer(handle = core.BNDecodeEscapedString(str(self))) + + def base64_encode(self): + return core.BNDataBufferToBase64(self.handle) + + def base64_decode(self): + return DataBuffer(handle = core.BNDecodeBase64(str(self))) + + def zlib_compress(self): + buf = core.BNZlibCompress(self.handle) + if buf is None: + return None + return DataBuffer(handle = buf) + + def zlib_decompress(self): + buf = core.BNZlibDecompress(self.handle) + if buf is None: + return None + return DataBuffer(handle = buf) + +class NavigationHandler: + def _register(self, handle): + self._cb = core.BNNavigationHandler() + self._cb.context = 0 + self._cb.getCurrentView = self._cb.getCurrentView.__class__(self._get_current_view) + self._cb.getCurrentOffset = self._cb.getCurrentOffset.__class__(self._get_current_offset) + self._cb.navigate = self._cb.navigate.__class__(self._navigate) + core.BNSetFileMetadataNavigationHandler(handle, self._cb) + + def _get_current_view(self, ctxt): + try: + view = self.get_current_view() + except: + log_error(traceback.format_exc()) + view = "" + return core.BNAllocString(view) + + def _get_current_offset(self, ctxt): + try: + return self.get_current_offset() + except: + log_error(traceback.format_exc()) + return 0 + + def _navigate(self, ctxt, view, offset): + try: + return self.navigate(view, offset) + except: + log_error(traceback.format_exc()) + return False + +class FileMetadata: + def __init__(self, filename = None, handle = None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNFileMetadata) + else: + _init_plugins() + self.handle = core.BNCreateFileMetadata() + if filename is not None: + core.BNSetFilename(self.handle, str(filename)) + self.__dict__['navigation'] = None + + def __del__(self): + if self.navigation is not None: + core.BNSetFileMetadataNavigationHandler(self.handle, None) + core.BNFreeFileMetadata(self.handle) + + def __getattr__(self, name): + if name == "filename": + return core.BNGetFilename(self.handle) + elif name == "modified": + return core.BNIsFileModified(self.handle) + elif name == "analysis_changed": + return core.BNIsAnalysisChanged(self.handle) + elif name == "has_database": + return core.BNIsBackedByDatabase(self.handle) + elif name == "view": + return core.BNGetCurrentView(self.handle) + elif name == "offset": + return core.BNGetCurrentOffset(self.handle) + elif name == "raw": + view = core.BNGetFileViewOfType(self.handle, "Raw") + if view is None: + return None + return BinaryView(self, handle = view) + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if name == "filename": + core.BNSetFilename(self.handle, str(value)) + elif name == "navigation": + value._register(self.handle) + self.__dict__[name] = value + elif name == "modified": + if value: + core.BNMarkFileModified(self.handle) + else: + core.BNMarkFileSaved(self.handle) + elif name == "saved": + if value: + core.BNMarkFileSaved(self.handle) + else: + core.BNMarkFileModified(self.handle) + elif name == "view": + core.BNNavigate(self.handle, str(value), core.BNGetCurrentOffset(self.handle)) + elif name == "offset": + core.BNNavigate(self.handle, core.BNGetCurrentView(self.handle), value) + elif (name == "analysis_changed") or (name == "has_database"): + raise AttributeError, "attribute '%s' is read only" % name + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["filename", "modified", "analysis_changed", "has_database", "view", "offset"] + + def close(self): + core.BNCloseFile(self.handle) + + def begin_undo_actions(self): + core.BNBeginUndoActions(self.handle) + + def commit_undo_actions(self): + core.BNCommitUndoActions(self.handle) + + def undo(self): + core.BNUndo(self.handle) + + def redo(self): + core.BNRedo(self.handle) + + def navigate(self, view, offset): + return core.BNNavigate(self.handle, str(view), offset) + + def create_database(self, filename): + return core.BNCreateDatabase(self.raw.handle, str(filename)) + + def open_existing_database(self, filename): + view = core.BNOpenExistingDatabase(self.handle, str(filename)) + if view is None: + return None + return BinaryView(self, handle = view) + + def save_auto_snapshot(self): + return core.BNSaveAutoSnapshot(self.raw.handle) + + def get_view_of_type(self, name): + view = core.BNGetFileViewOfType(self.handle, str(name)) + if view is None: + view_type = core.BNGetBinaryViewTypeByName(str(name)) + if view_type is None: + return None + view = core.BNCreateBinaryViewOfType(view_type, self.raw.handle) + if view is None: + return None + return BinaryView(self, handle = view) + +class FileAccessor: + def __init__(self): + self._cb = core.BNFileAccessor() + self._cb.context = 0 + self._cb.getLength = self._cb.getLength.__class__(self._get_length) + self._cb.read = self._cb.read.__class__(self._read) + self._cb.write = self._cb.write.__class__(self._write) + + def __len__(self): + return self.get_length() + + def _get_length(self, ctxt): + try: + return self.get_length() + except: + log_error(traceback.format_exc()) + return 0 + + def _read(self, ctxt, dest, offset, length): + try: + data = self.read(offset, length) + if data is None: + return 0 + if len(data) > length: + data = data[0:length] + ctypes.memmove(dest, data, len(data)) + return len(data) + except: + log_error(traceback.format_exc()) + return 0 + + def _write(self, ctxt, offset, src, length): + try: + data = ctypes.create_string_buffer(length) + ctypes.memmove(data, src, length) + return self.write(offset, data.raw) + except: + log_error(traceback.format_exc()) + return 0 + +class CoreFileAccessor(FileAccessor): + def __init__(self, accessor): + self._cb.context = accessor.context + self._cb.getLength = accessor.getLength + self._cb.read = accessor.read + self._cb.write = accessor.write + + def get_length(self): + return self._cb.getLength(self._cb.context) + + def read(self, offset, length): + data = ctypes.create_string_buffer(length) + length = self._cb.read(self._cb.context, data, offset, length) + return data.raw[0:length] + + def write(self, offset, value): + value = str(value) + data = ctypes.create_string_buffer(value) + return self._cb.write(self._cb.context, offset, data, len(value)) + +class BinaryDataNotification: + def data_written(self, view, offset, length): + pass + + def data_inserted(self, view, offset, length): + pass + + def data_removed(self, view, offset, length): + pass + + def function_added(self, view, func): + pass + + def function_removed(self, view, func): + pass + + def function_updated(self, view, func): + pass + + def string_found(self, view, string_type, offset, length): + pass + + def string_removed(self, view, string_type, offset, length): + pass + +class UndoAction: + name = None + action_type = None + _registered = False + _registered_cb = None + + def __init__(self, view): + self._cb = core.BNUndoAction() + if not self.__class__._registered: + raise TypeError, "undo action type not registered" + action_type = self.__class__.action_type + if isinstance(action_type, str): + self._cb.type = BNActionType_by_name[action_type] + else: + self._cb.type = action_type + self._cb.context = 0 + self._cb.undo = self._cb.undo.__class__(self._undo) + self._cb.redo = self._cb.redo.__class__(self._redo) + self._cb.serialize = self._cb.serialize.__class__(self._serialize) + self.view = view + + @classmethod + def register(cls): + _init_plugins() + if cls.name is None: + raise ValueError, "undo action 'name' not defined" + if cls.action_type is None: + raise ValueError, "undo action 'action_type' not defined" + cb_type = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_char_p, ctypes.POINTER(core.BNUndoAction)) + cls._registered_cb = cb_type(cls._deserialize) + core.BNRegisterUndoActionType(cls.name, 0, cls._registered_cb) + cls._registered = True + + @classmethod + def _deserialize(cls, ctxt, data, result): + try: + action = cls.deserialize(json.loads(data)) + if action is None: + return False + result.context = action._cb.context + result.undo = action._cb.undo + result.redo = action._cb.redo + result.serialize = action._cb.serialize + return True + except: + log_error(traceback.format_exc()) + return False + + def _undo(self, ctxt, view): + try: + self.undo() + except: + log_error(traceback.format_exc()) + return False + + def _redo(self, ctxt, view): + try: + self.redo() + except: + log_error(traceback.format_exc()) + return False + + def _serialize(self, ctxt): + try: + return json.dumps(self.serialize()) + except: + log_error(traceback.format_exc()) + return "null" + +class StringReference: + def __init__(self, string_type, start, length): + self.type = string_type + self.start = start + self.length = length + + def __repr__(self): + return "<%s: 0x%x, len 0x%x>" % (self.type, self.start, self.length) + +class BinaryDataNotificationCallbacks: + def __init__(self, view, notify): + self.view = view + self.notify = notify + self._cb = core.BNBinaryDataNotification() + self._cb.context = 0 + self._cb.dataWritten = self._cb.dataWritten.__class__(self._data_written) + self._cb.dataInserted = self._cb.dataInserted.__class__(self._data_inserted) + self._cb.dataRemoved = self._cb.dataRemoved.__class__(self._data_removed) + self._cb.functionAdded = self._cb.functionAdded.__class__(self._function_added) + self._cb.functionRemoved = self._cb.functionRemoved.__class__(self._function_removed) + self._cb.functionUpdated = self._cb.functionUpdated.__class__(self._function_updated) + self._cb.stringFound = self._cb.stringFound.__class__(self._string_found) + self._cb.stringRemoved = self._cb.stringRemoved.__class__(self._string_removed) + + def _register(self): + core.BNRegisterDataNotification(self.view.handle, self._cb) + + def _unregister(self): + core.BNUnregisterDataNotification(self.view.handle, self._cb) + + def _data_written(self, ctxt, view, offset, length): + try: + self.notify.data_written(self.view, offset, length) + except: + log_error(traceback.format_exc()) + + def _data_inserted(self, ctxt, view, offset, length): + try: + self.notify.data_inserted(self.view, offset, length) + except: + log_error(traceback.format_exc()) + + def _data_removed(self, ctxt, view, offset, length): + try: + self.notify.data_removed(self.view, offset, length) + except: + log_error(traceback.format_exc()) + + def _function_added(self, ctxt, view, func): + try: + self.notify.function_added(self.view, Function(self.view, core.BNNewFunctionReference(func))) + except: + log_error(traceback.format_exc()) + + def _function_removed(self, ctxt, view, func): + try: + self.notify.function_removed(self.view, Function(self.view, core.BNNewFunctionReference(func))) + except: + log_error(traceback.format_exc()) + + def _function_updated(self, ctxt, view, func): + try: + self.notify.function_updated(self.view, Function(self.view, core.BNNewFunctionReference(func))) + except: + log_error(traceback.format_exc()) + + def _string_found(self, ctxt, view, string_type, offset, length): + try: + self.notify.string_found(self.view, core.BNStringType_names[string_type], offset, length) + except: + log_error(traceback.format_exc()) + + def _string_removed(self, ctxt, view, string_type, offset, length): + try: + self.notify.string_removed(self.view, core.BNStringType_names[string_type], offset, length) + except: + log_error(traceback.format_exc()) + +class _BinaryViewTypeMetaclass(type): + def __getattr__(cls, name): + if name == "list": + _init_plugins() + count = ctypes.c_ulonglong() + types = core.BNGetBinaryViewTypes(count) + result = [] + for i in xrange(0, count.value): + result.append(BinaryViewType(types[i])) + core.BNFreeBinaryViewTypeList(types) + return result + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if name == "list": + raise AttributeError, "attribute '%s' is read only" % name + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["list"] + + def __getitem__(self, value): + _init_plugins() + view_type = core.BNGetBinaryViewTypeByName(str(value)) + if view_type is None: + raise KeyError, "'%s' is not a valid view type" % str(value) + return BinaryViewType(view_type) + +class BinaryViewType: + __metaclass__ = _BinaryViewTypeMetaclass + + def __init__(self, handle): + self.handle = core.handle_of_type(handle, core.BNBinaryViewType) + + def __getattr__(self, name): + if name == "name": + return core.BNGetBinaryViewTypeName(self.handle) + elif name == "long_name": + return core.BNGetBinaryViewTypeLongName(self.handle) + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if (name == "name") or (name == "long_name"): + raise AttributeError, "attribute '%s' is read only" % name + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["name","long_name"] + + def __repr__(self): + return "<view type: '%s'>" % self.name + + def create(self, data): + view = core.BNCreateBinaryViewOfType(self.handle, data.handle) + if view is None: + return None + return BinaryView(data.file, handle = view) + + def open(self, src, file_metadata = None): + data = BinaryView.open(src, file_metadata) + if data is None: + return None + return self.create(data) + + def is_valid_for_data(self, data): + return core.BNIsBinaryViewTypeValidForData(self.handle, data.handle) + + def register_arch(self, ident, arch): + core.BNRegisterArchitectureForViewType(self.handle, ident, arch.handle) + + def get_arch(self, ident): + arch = core.BNGetArchitectureForViewType(self.handle, ident) + if arch is None: + return None + return Architecture(arch) + + def register_platform(self, ident, arch, platform): + core.BNRegisterPlatformForViewType(self.handle, ident, arch.handle, platform.handle) + + def register_default_platform(self, arch, platform): + core.BNRegisterDefaultPlatformForViewType(self.handle, arch.handle, platform.handle) + + def get_platform(self, ident, arch): + platform = core.BNGetPlatformForViewType(self.handle, ident, arch.handle) + if platform is None: + return None + return Platform(None, platform) + +class BinaryView: + name = None + long_name = None + _registered = False + _registered_cb = None + view_type = None + + def __init__(self, file_metadata = None, handle = None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNBinaryView) + if file_metadata is None: + self.file = FileMetadata(handle = core.BNGetFileForView(handle)) + else: + self.file = file_metadata + elif self.__class__ is BinaryView: + _init_plugins() + if file_metadata is None: + file_metadata = FileMetadata() + self.handle = core.BNCreateBinaryDataView(file_metadata.handle) + self.file = FileMetadata(handle = core.BNNewFileReference(file_metadata)) + else: + _init_plugins() + if not self.__class__._registered: + raise TypeError, "view type not registered" + self._cb = core.BNCustomBinaryView() + self._cb.context = 0 + self._cb.init = self._cb.init.__class__(self._init) + self._cb.read = self._cb.read.__class__(self._read) + self._cb.write = self._cb.write.__class__(self._write) + self._cb.insert = self._cb.insert.__class__(self._insert) + self._cb.remove = self._cb.remove.__class__(self._remove) + self._cb.getModification = self._cb.getModification.__class__(self._get_modification) + self._cb.isValidOffset = self._cb.isValidOffset.__class__(self._is_valid_offset) + self._cb.isOffsetReadable = self._cb.isOffsetReadable.__class__(self._is_offset_readable) + self._cb.isOffsetWritable = self._cb.isOffsetWritable.__class__(self._is_offset_writable) + self._cb.isOffsetExecutable = self._cb.isOffsetExecutable.__class__(self._is_offset_executable) + self._cb.getNextValidOffset = self._cb.getNextValidOffset.__class__(self._get_next_valid_offset) + self._cb.getStart = self._cb.getStart.__class__(self._get_start) + self._cb.getLength = self._cb.getLength.__class__(self._get_length) + self._cb.getEntryPoint = self._cb.getEntryPoint.__class__(self._get_entry_point) + self._cb.isExecutable = self._cb.isExecutable.__class__(self._is_executable) + self._cb.getDefaultEndianness = self._cb.getDefaultEndianness.__class__(self._get_default_endianness) + self._cb.getAddressSize = self._cb.getAddressSize.__class__(self._get_address_size) + self._cb.save = self._cb.save.__class__(self._save) + self.file = file_metadata + self.handle = core.BNCreateCustomBinaryView(self.__class__.name, file_metadata.handle, self._cb) + self.notifications = {} + + @classmethod + def register(cls): + _init_plugins() + if cls.name is None: + raise ValueError, "view 'name' not defined" + if cls.long_name is None: + cls.long_name = cls.name + cls._registered_cb = core.BNCustomBinaryViewType() + cls._registered_cb.context = 0 + cls._registered_cb.create = cls._registered_cb.create.__class__(cls._create) + cls._registered_cb.isValidForData = cls._registered_cb.isValidForData.__class__(cls._is_valid_for_data) + cls.view_type = BinaryViewType(core.BNRegisterBinaryViewType(cls.name, cls.long_name, cls._registered_cb)) + cls._registered = True + + @classmethod + def _create(cls, ctxt, data): + try: + file_metadata = FileMetadata(handle = core.BNGetFileForView(data)) + view = cls(BinaryView(file_metadata, handle = core.BNNewViewReference(data))) + if view is None: + return None + return ctypes.cast(core.BNNewViewReference(view.handle), ctypes.c_void_p).value + except: + log_error(traceback.format_exc()) + return None + + @classmethod + def _is_valid_for_data(cls, ctxt, data): + try: + return cls.is_valid_for_data(BinaryView(None, handle = core.BNNewViewReference(data))) + except: + log_error(traceback.format_exc()) + return False + + @classmethod + def open(cls, src, file_metadata = None): + _init_plugins() + if isinstance(src, FileAccessor): + if file_metadata is None: + file_metadata = FileMetadata() + view = core.BNCreateBinaryDataViewFromFile(file_metadata.handle, src._cb) + else: + if file_metadata is None: + file_metadata = FileMetadata(str(src)) + view = core.BNCreateBinaryDataViewFromFilename(file_metadata.handle, str(src)) + if view is None: + return None + result = BinaryView(file_metadata, handle = view) + return result + + @classmethod + def new(cls, data = None, file_metadata = None): + _init_plugins() + if file_metadata is None: + file_metadata = FileMetadata() + if data is None: + view = core.BNCreateBinaryDataView(file_metadata.handle) + else: + buf = DataBuffer(data) + view = core.BNCreateBinaryDataViewFromBuffer(file_metadata.handle, buf.handle) + if view is None: + return None + result = BinaryView(file_metadata, handle = view) + return result + + def __del__(self): + for i in self.notifications.values(): + i._unregister() + core.BNFreeBinaryView(self.handle) + + def __getattr__(self, name): + if name == "modified": + return self.file.modified + elif name == "analysis_changed": + return self.file.analysis_changed + elif name == "has_database": + return self.file.has_database + elif name == "view": + return self.file.view + elif name == "offset": + return self.file.offset + elif name == "start": + return core.BNGetStartOffset(self.handle) + elif name == "end": + return core.BNGetEndOffset(self.handle) + elif name == "entry_point": + return core.BNGetEntryPoint(self.handle) + elif name == "arch": + arch = core.BNGetDefaultArchitecture(self.handle) + if arch is None: + return None + return Architecture(handle = arch) + elif name == "platform": + platform = core.BNGetDefaultPlatform(self.handle) + if platform is None: + return None + return Platform(self.arch, handle = platform) + elif name == "endianness": + return core.BNGetDefaultEndianness(self.handle) + elif name == "address_size": + return core.BNGetViewAddressSize(self.handle) + elif name == "executable": + return core.BNIsExecutableView(self.handle) + elif name == "functions": + count = ctypes.c_ulonglong(0) + funcs = core.BNGetAnalysisFunctionList(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(Function(self, core.BNNewFunctionReference(funcs[i]))) + core.BNFreeFunctionList(funcs, count.value) + return result + elif name == "has_functions": + return core.BNHasFunctions(self.handle) + elif name == "entry_function": + func = core.BNGetAnalysisEntryPoint(self.handle) + if func is None: + return None + return Function(self, func) + elif name == "symbols": + count = ctypes.c_ulonglong(0) + syms = core.BNGetSymbols(self.handle, count) + result = {} + for i in xrange(0, count.value): + sym = Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i])) + result[sym.raw_name] = sym + core.BNFreeSymbolList(syms, count.value) + return result + elif name == "type": + return core.BNGetViewType(self.handle) + elif name == "available_types": + count = ctypes.c_ulonglong(0) + types = core.BNGetBinaryViewTypesForData(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(BinaryViewType(types[i])) + core.BNFreeBinaryViewTypeList(types) + return result + elif name == "strings": + return self.get_strings() + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if name == "modified": + self.file.modified = value + elif name == "saved": + self.file.saved = value + elif name == "view": + self.file.view = value + elif name == "offset": + self.file.offset = value + elif name == "arch": + if value is None: + core.BNSetDefaultArchitecture(self.handle, None) + else: + core.BNSetDefaultArchitecture(self.handle, value.handle) + elif name == "platform": + if value is None: + core.BNSetDefaultPlatform(self.handle, None) + else: + core.BNSetDefaultPlatform(self.handle, value.handle) + elif ((name == "analysis_changed") or (name == "has_database") or (name == "start") or (name == "end") or + (name == "entry_point") or (name == "endianness") or (name == "address_size") or + (name == "executable") or (name == "functions") or (name == "has_functions") or + (name == "entry_function") or (name == "symbols") or (name == "type") or (name == "available_types") or + (name == "strings")): + raise AttributeError, "attribute '%s' is read only" % name + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["modified", "analysis_changed", "has_database", "view", "offset", "start", "end", "entry_point", "arch", "platform", "endianness", "address_size", "executable", "functions", "has_functions", "entry_function", "symbols", "type", "available_types", "strings"] + + def __len__(self): + return int(core.BNGetViewLength(self.handle)) + + def __getitem__(self, i): + if isinstance(i, tuple): + result = "" + for s in i: + result += self.__getitem__(s) + return result + elif isinstance(i, slice): + if i.step is not None: + raise IndexError, "step not implemented" + i = i.indices(self.end) + start = i[0] + stop = i[1] + if stop <= start: + return "" + return str(self.read(start, stop - start)) + elif i < 0: + if i >= -len(self): + value = str(self.read(int(len(self) + i), 1)) + if len(value) == 0: + return IndexError, "index not readable" + return value + raise IndexError, "index out of range" + elif (i >= self.start) and (i < self.end): + value = str(self.read(int(i), 1)) + if len(value) == 0: + return IndexError, "index not readable" + return value + else: + raise IndexError, "index out of range" + + def __setitem__(self, i, value): + if isinstance(i, slice): + if i.step is not None: + raise IndexError, "step not supported on assignment" + i = i.indices(self.end) + start = i[0] + stop = i[1] + if stop < start: + stop = start + if len(value) != (stop - start): + self.remove(start, stop - start) + self.insert(start, value) + else: + self.write(start, value) + elif i < 0: + if i >= -len(self): + if len(value) != 1: + raise ValueError, "expected single byte for assignment" + if self.write(int(len(self) + i), value) != 1: + raise IndexError, "index not writable" + else: + raise IndexError, "index out of range" + elif (i >= self.start) and (i < self.end): + if len(value) != 1: + raise ValueError, "expected single byte for assignment" + if self.write(int(i), value) != 1: + raise IndexError, "index not writable" + else: + raise IndexError, "index out of range" + + def __repr__(self): + start = self.start + length = len(self) + if start != 0: + size = "start 0x%x, len 0x%x" % (start, length) + else: + size = "len 0x%x" % length + filename = self.file.filename + if len(filename) > 0: + return "<%s view: '%s', %s>" % (self.type, filename, size) + return "<%s view: %s>" % (self.type, size) + + def _init(self, ctxt): + try: + return self.init() + except: + log_error(traceback.format_exc()) + return False + + def _read(self, ctxt, dest, offset, length): + try: + data = self.perform_read(offset, length) + if data is None: + return 0 + if len(data) > length: + data = data[0:length] + ctypes.memmove(dest, str(data), len(data)) + return len(data) + except: + log_error(traceback.format_exc()) + return 0 + + def _write(self, ctxt, offset, src, length): + try: + data = ctypes.create_string_buffer(length) + ctypes.memmove(data, src, length) + return self.perform_write(offset, data.raw) + except: + log_error(traceback.format_exc()) + return 0 + + def _insert(self, ctxt, offset, src, length): + try: + data = ctypes.create_string_buffer(length) + ctypes.memmove(data, src, length) + return self.perform_insert(offset, data.raw) + except: + log_error(traceback.format_exc()) + return 0 + + def _remove(self, ctxt, offset, length): + try: + return self.perform_remove(offset, length) + except: + log_error(traceback.format_exc()) + return 0 + + def _get_modification(self, ctxt, offset): + try: + return self.perform_get_modification(offset) + except: + log_error(traceback.format_exc()) + return core.Original + + def _is_valid_offset(self, ctxt, offset): + try: + return self.perform_is_valid_offset(offset) + except: + log_error(traceback.format_exc()) + return False + + def _is_offset_readable(self, ctxt, offset): + try: + return self.perform_is_offset_readable(offset) + except: + log_error(traceback.format_exc()) + return False + + def _is_offset_writable(self, ctxt, offset): + try: + return self.perform_is_offset_writable(offset) + except: + log_error(traceback.format_exc()) + return False + + def _is_offset_executable(self, ctxt, offset): + try: + return self.perform_is_offset_executable(offset) + except: + log_error(traceback.format_exc()) + return False + + def _get_next_valid_offset(self, ctxt, offset): + try: + return self.perform_get_next_valid_offset(offset) + except: + log_error(traceback.format_exc()) + return offset + + def _get_start(self, ctxt): + try: + return self.perform_get_start() + except: + log_error(traceback.format_exc()) + return 0 + + def _get_length(self, ctxt): + try: + return self.perform_get_length() + except: + log_error(traceback.format_exc()) + return 0 + + def _get_entry_point(self, ctxt): + try: + return self.perform_get_entry_point() + except: + log_error(traceback.format_exc()) + return 0 + + def _is_executable(self, ctxt): + try: + return self.perform_is_executable() + except: + log_error(traceback.format_exc()) + return False + + def _get_default_endianness(self, ctxt): + try: + return self.perform_get_default_endianness() + except: + log_error(traceback.format_exc()) + return core.LittleEndian + + def _get_address_size(self, ctxt): + try: + return self.perform_get_address_size() + except: + log_error(traceback.format_exc()) + return 8 + + def _save(self, ctxt, file_accessor): + try: + return self.perform_save(CoreFileAccessor(file_accessor)) + except: + log_error(traceback.format_exc()) + return False + + def init(self): + return True + + def perform_write(self, offset, data): + return 0 + + def perform_insert(self, offset, data): + return 0 + + def perform_remove(self, offset, length): + return 0 + + def perform_get_modification(self, offset): + return core.Original + + def perform_is_valid_offset(self, offset): + data = self.read(offset, 1) + return (data is not None) and (len(data) == 1) + + def perform_is_offset_readable(self, offset): + return self.is_valid_offset(offset) + + def perform_is_offset_writable(self, offset): + return self.is_valid_offset(offset) + + def perform_is_offset_executable(self, offset): + return self.is_valid_offset(offset) + + def perform_get_next_valid_offset(self, offset): + if offset < self.perform_get_start(): + return self.perform_get_start() + return offset + + def perform_get_start(self): + return 0 + + def perform_get_entry_point(self): + return 0 + + def perform_is_executable(self): + return False + + def perform_get_default_endianness(self): + return core.LittleEndian + + def create_database(self, filename): + return self.file.create_database(filename) + + def save_auto_snapshot(self): + return self.file.save_auto_snapshot() + + def get_view_of_type(self, name): + return self.file.get_view_of_type(name) + + def begin_undo_actions(self): + self.file.begin_undo_actions() + + def add_undo_action(self, action): + core.BNAddUndoAction(self.handle, action.__class__.name, action._cb) + + def commit_undo_actions(self): + self.file.commit_undo_actions() + + def undo(self): + self.file.undo() + + def redo(self): + self.file.redo() + + def navigate(self, view, offset): + self.file.navigate(view, offset) + + def read(self, offset, length): + buf = DataBuffer(handle = core.BNReadViewBuffer(self.handle, offset, length)) + return str(buf) + + def write(self, offset, data): + buf = DataBuffer(data) + return core.BNWriteViewBuffer(self.handle, offset, buf.handle) + + def insert(self, offset, data): + buf = DataBuffer(data) + return core.BNInsertViewBuffer(self.handle, offset, buf.handle) + + def remove(self, offset, length): + return core.BNRemoveViewData(self.handle, offset, length) + + def get_modification(self, offset, length = None): + if length is None: + return core.BNGetModification(self.handle, offset) + data = (core.BNModificationStatus * length)() + length = core.BNGetModificationArray(self.handle, offset, data, length); + return data[0:length] + + def is_valid_offset(self, offset): + return core.BNIsValidOffset(self.handle, offset) + + def is_offset_readable(self, offset): + return core.BNIsOffsetReadable(self.handle, offset) + + def is_offset_writable(self, offset): + return core.BNIsOffsetWritable(self.handle, offset) + + def is_offset_executable(self, offset): + return core.BNIsOffsetExecutable(self.handle, offset) + + def save(self, dest): + if isinstance(dest, FileAccessor): + return core.BNSaveToFile(self.handle, dest._cb) + return core.BNSaveToFilename(self.handle, str(dest)) + + def register_notification(self, notify): + cb = BinaryDataNotificationCallbacks(self, notify) + cb._register() + self.notifications[notify] = cb + + def unregister_notification(self, notify): + if notify in self.notifications: + self.notifications[notify]._unregister() + del self.notifications[notify] + + def add_function(self, platform, addr): + core.BNAddFunctionForAnalysis(self.handle, platform.handle, addr) + + def add_entry_point(self, platform, addr): + core.BNAddEntryPointForAnalysis(self.handle, platform.handle, addr) + + def remove_function(self, func): + core.BNRemoveAnalysisFunction(self.handle, func.handle) + + def create_user_function(self, arch, addr): + core.BNCreateUserFunction(self.handle, arch.handle, addr) + + def update_analysis(self): + core.BNUpdateAnalysis(self.handle) + + def abort_analysis(self): + core.BNAbortAnalysis(self.handle) + + def get_function_at(self, arch, addr): + func = core.BNGetAnalysisFunction(self.handle, arch.handle, addr) + if func is None: + return None + return Function(self, func) + + def get_functions_at(self, addr): + count = ctypes.c_ulonglong(0) + funcs = core.BNGetAnalysisFunctionsForAddress(self.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(Function(self, core.BNNewFunctionReference(funcs[i]))) + core.BNFreeFunctionList(funcs, count.value) + return result + + def get_recent_function_at(self, addr): + func = core.BNGetRecentAnalysisFunctionForAddress(self.handle, addr) + if func is None: + return None + return Function(self, func) + + def get_basic_blocks_at(self, addr): + count = ctypes.c_ulonglong(0) + blocks = core.BNGetBasicBlocksForAddress(self.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(BasicBlock(self, core.BNNewBasicBlockReference(blocks[i]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + + def get_recent_basic_block_at(self, addr): + block = core.BNGetRecentBasicBlockForAddress(self.handle, addr) + if block is None: + return None + return BasicBlock(self, block) + + def get_code_refs(self, addr, length = None): + count = ctypes.c_ulonglong(0) + if length is None: + refs = core.BNGetCodeReferences(self.handle, addr, count) + else: + refs = core.BNGetCodeReferencesInRange(self.handle, addr, length, count) + result = [] + for i in xrange(0, count.value): + if refs[i].func: + func = Function(self, core.BNNewFunctionReference(refs[i].func)) + else: + func = None + if refs[i].arch: + arch = Architecture(refs[i].arch) + else: + arch = None + addr = refs[i].addr + result.append(ReferenceSource(func, arch, addr)) + core.BNFreeCodeReferences(refs, count.value) + return result + + def get_symbol_at(self, addr): + sym = core.BNGetSymbolByAddress(self.handle, addr) + if sym is None: + return None + return Symbol(None, None, None, handle = sym) + + def get_symbol_by_raw_name(self, name): + sym = core.BNGetSymbolByRawName(self.handle, name) + if sym is None: + return None + return Symbol(None, None, None, handle = sym) + + def get_symbols_by_name(self, name): + count = ctypes.c_ulonglong(0) + syms = core.BNGetSymbolsByName(self.handle, name, count) + result = [] + for i in xrange(0, count.value): + result.append(Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) + core.BNFreeSymbolList(syms, count.value) + return result + + def get_symbols(self, start = None, length = None): + count = ctypes.c_ulonglong(0) + if start is None: + syms = core.BNGetSymbols(self.handle, count) + else: + syms = core.BNGetSymbolsInRange(self.handle, start, length, count) + result = [] + for i in xrange(0, count.value): + result.append(Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) + core.BNFreeSymbolList(syms, count.value) + return result + + def get_symbols_of_type(self, sym_type, start = None, length = None): + if isinstance(sym_type, str): + sym_type = core.BNSymbolType_by_name[sym_type] + count = ctypes.c_ulonglong(0) + if start is None: + syms = core.BNGetSymbolsOfType(self.handle, sym_type, count) + else: + syms = core.BNGetSymbolsOfTypeInRange(self.handle, sym_type, start, length, count) + result = [] + for i in xrange(0, count.value): + result.append(Symbol(None, None, None, handle = core.BNNewSymbolReference(syms[i]))) + core.BNFreeSymbolList(syms, count.value) + return result + + def define_auto_symbol(self, sym): + core.BNDefineAutoSymbol(self.handle, sym.handle) + + def undefine_auto_symbol(self, sym): + core.BNUndefineAutoSymbol(self.handle, sym.handle) + + def define_user_symbol(self, sym): + core.BNDefineUserSymbol(self.handle, sym.handle) + + def undefine_user_symbol(self, sym): + core.BNUndefineUserSymbol(self.handle, sym.handle) + + def define_imported_function(self, import_addr_sym, func): + core.BNDefineImportedFunction(self.handle, import_addr_sym.handle, func.handle) + + def is_never_branch_patch_available(self, arch, addr): + return core.BNIsNeverBranchPatchAvailable(self.handle, arch.handle, addr) + + def is_always_branch_patch_available(self, arch, addr): + return core.BNIsAlwaysBranchPatchAvailable(self.handle, arch.handle, addr) + + def is_invert_branch_patch_available(self, arch, addr): + return core.BNIsInvertBranchPatchAvailable(self.handle, arch.handle, addr) + + def is_skip_and_return_zero_patch_available(self, arch, addr): + return core.BNIsSkipAndReturnZeroPatchAvailable(self.handle, arch.handle, addr) + + def is_skip_and_return_value_patch_available(self, arch, addr): + return core.BNIsSkipAndReturnValuePatchAvailable(self.handle, arch.handle, addr) + + def convert_to_nop(self, arch, addr): + return core.BNConvertToNop(self.handle, arch.handle, addr) + + def always_branch(self, arch, addr): + return core.BNAlwaysBranch(self.handle, arch.handle, addr) + + def never_branch(self, arch, addr): + return core.BNConvertToNop(self.handle, arch.handle, addr) + + def invert_branch(self, arch, addr): + return core.BNInvertBranch(self.handle, arch.handle, addr) + + def skip_and_return_value(self, arch, addr, value): + return core.BNSkipAndReturnValue(self.handle, arch.handle, addr, value) + + def get_instruction_length(self, arch, addr): + return core.BNGetInstructionLength(self.handle, arch.handle, addr) + + def notify_data_written(self, offset, length): + core.BNNotifyDataWritten(self.handle, offset, length) + + def notify_data_inserted(self, offset, length): + core.BNNotifyDataInserted(self.handle, offset, length) + + def notify_data_removed(self, offset, length): + core.BNNotifyDataRemoved(self.handle, offset, length) + + def get_strings(self, start = None, length = None): + count = ctypes.c_ulonglong(0) + if start is None: + strings = core.BNGetStrings(self.handle, count) + else: + strings = core.BNGetStringsInRange(self.handle, start, length, count) + result = [] + for i in xrange(0, count.value): + result.append(StringReference(core.BNStringType_names[strings[i].type], strings[i].start, strings[i].length)) + core.BNFreeStringList(strings) + return result + +class BinaryReader: + def __init__(self, data, endian = None): + self.handle = core.BNCreateBinaryReader(data.handle) + if endian is None: + core.BNSetBinaryReaderEndianness(self.handle, data.endianness) + else: + core.BNSetBinaryReaderEndianness(self.handle, endian) + + def __del__(self): + core.BNFreeBinaryReader(self.handle) + + def __getattr__(self, name): + if name == "endianness": + return core.BNGetBinaryReaderEndianness(self.handle) + elif name == "offset": + return core.BNGetReaderPosition(self.handle) + elif name == "eof": + return core.BNIsEndOfFile(self.handle) + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if (name == "eof"): + raise AttributeError, "attribute '%s' is read only" % name + elif name == "endianness": + core.BNSetBinaryReaderEndianness(self.handle, value) + elif name == "offset": + core.BNSeekBinaryReader(self.handle, value) + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["endianness","offset", "eof"] + + def read(self, length): + dest = ctypes.create_string_buffer(length) + if not core.BNReadData(self.handle, dest, length): + return None + return dest.raw + + def read8(self): + result = ctypes.c_ubyte() + if not core.BNRead8(self.handle, result): + return None + return result.value + + def read16(self): + result = ctypes.c_ushort() + if not core.BNRead16(self.handle, result): + return None + return result.value + + def read32(self): + result = ctypes.c_uint() + if not core.BNRead32(self.handle, result): + return None + return result.value + + def read64(self): + result = ctypes.c_ulonglong() + if not core.BNRead64(self.handle, result): + return None + return result.value + + def read16le(self): + result = self.read(2) + if (result is None) or (len(result) != 2): + return None + return struct.unpack("<H", result)[0] + + def read32le(self): + result = self.read(4) + if (result is None) or (len(result) != 4): + return None + return struct.unpack("<I", result)[0] + + def read64le(self): + result = self.read(8) + if (result is None) or (len(result) != 8): + return None + return struct.unpack("<Q", result)[0] + + def read16be(self): + result = self.read(2) + if (result is None) or (len(result) != 2): + return None + return struct.unpack(">H", result)[0] + + def read32be(self): + result = self.read(4) + if (result is None) or (len(result) != 4): + return None + return struct.unpack(">I", result)[0] + + def read64be(self): + result = self.read(8) + if (result is None) or (len(result) != 8): + return None + return struct.unpack(">Q", result)[0] + + def seek(self, offset): + core.BNSeekBinaryReader(self.handle, offset) + + def seek_relative(self, offset): + core.BNSeekBinaryReaderRelative(self.handle, offset) + +class BinaryWriter: + def __init__(self, data, endian = None): + self.handle = core.BNCreateBinaryWriter(data.handle) + if endian is None: + core.BNSetBinaryWriterEndianness(self.handle, data.endianness) + else: + core.BNSetBinaryWriterEndianness(self.handle, endian) + + def __del__(self): + core.BNFreeBinaryWriter(self.handle) + + def __getattr__(self, name): + if name == "endianness": + return core.BNGetBinaryWriterEndianness(self.handle) + elif name == "offset": + return core.BNGetWriterPosition(self.handle) + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if name == "endianness": + core.BNSetBinaryWriterEndianness(self.handle, value) + elif name == "offset": + core.BNSeekBinaryWriter(self.handle, value) + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["endianness","offset"] + + def write(self, value): + value = str(value) + buf = ctypes.create_string_buffer(len(value)) + ctypes.memmove(buf, value, len(value)) + return core.BNWriteData(self.handle, buf, len(value)) + + def write8(self, value): + return core.BNWrite8(self.handle, value) + + def write16(self, value): + return core.BNWrite16(self.handle, value) + + def write32(self, value): + return core.BNWrite32(self.handle, value) + + def write64(self, value): + return core.BNWrite64(self.handle, value) + + def write16le(self, value): + value = struct.pack("<H", value) + return self.write(value) + + def write32le(self, value): + value = struct.pack("<I", value) + return self.write(value) + + def write64le(self, value): + value = struct.pack("<Q", value) + return self.write(value) + + def write16be(self, value): + value = struct.pack(">H", value) + return self.write(value) + + def write32be(self, value): + value = struct.pack(">I", value) + return self.write(value) + + def write64be(self, value): + value = struct.pack(">Q", value) + return self.write(value) + + def seek(self, offset): + core.BNSeekBinaryWriter(self.handle, offset) + + def seek_relative(self, offset): + core.BNSeekBinaryWriterRelative(self.handle, offset) + +class Symbol: + def __init__(self, sym_type, addr, short_name, full_name = None, raw_name = None, handle = None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNSymbol) + else: + if isinstance(sym_type, str): + sym_type = core.BNSymbolType_by_name[sym_type] + if full_name is None: + full_name = short_name + if raw_name is None: + raw_name = full_name + self.handle = core.BNCreateSymbol(sym_type, short_name, full_name, raw_name, addr) + + def __del__(self): + core.BNFreeSymbol(self.handle) + + def __getattr__(self, name): + if name == "type": + return core.BNSymbolType_names[core.BNGetSymbolType(self.handle)] + elif name == "name": + return core.BNGetSymbolRawName(self.handle) + elif name == "short_name": + return core.BNGetSymbolShortName(self.handle) + elif name == "full_name": + return core.BNGetSymbolFullName(self.handle) + elif name == "raw_name": + return core.BNGetSymbolRawName(self.handle) + elif name == "address": + return core.BNGetSymbolAddress(self.handle) + elif name == "auto": + return core.BNIsSymbolAutoDefined(self.handle) + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if ((name == "type") or (name == "name") or (name == "short_name") or (name == "full_name") or + (name == "raw_name") or (name == "address")): + raise AttributeError, "attribute '%s' is read only" % name + elif name == "auto": + core.BNSetSymbolAutoDefined(self.handle, value) + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["type","name", "short_name", "full_name", "raw_name", "address", "auto"] + + def __repr__(self): + return "<%s: \"%s\" @ 0x%x>" % (self.type, self.full_name, self.address) + +class Type: + def __init__(self, handle): + self.handle = handle + + def __del__(self): + core.BNFreeType(self.handle) + + def __getattr__(self, name): + if name == "type_class": + return BNTypeClass_names[core.BNGetTypeClass(self.handle)] + elif name == "width": + return core.BNGetTypeWidth(self.handle) + elif name == "alignment": + return core.BNGetTypeAlignment(self.handle) + elif name == "signed": + return core.BNIsTypeSigned(self.handle) + elif name == "const": + return core.BNIsTypeConst(self.handle) + elif name == "float": + return core.BNIsTypeFloatingPoint(self.handle) + elif (name == "target") or (name == "element_type") or (name == "return_value"): + result = core.BNGetChildType(self.handle) + if result is None: + return None + return Type(result) + elif name == "calling_convention": + result = core.BNGetTypeCallingConvention(self.handle) + if result is None: + return None + return CallingConvention(None, result) + elif name == "parameters": + count = ctypes.c_ulonglong() + params = core.BNGetTypeParameters(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append((Type(core.BNNewTypeReference(params[i].type)), params[i].name)) + core.BNFreeTypeParameterList(params, count.value) + return result + elif name == "has_variable_arguments": + return core.BNTypeHasVariableArguments(self.handle) + elif name == "can_return": + return core.BNFunctionTypeCanReturn(self.handle) + elif name == "structure": + result = core.BNGetTypeStructure(self.handle) + if result is None: + return None + return Structure(result) + elif name == "enumeration": + result = core.BNGetTypeEnumeration(self.handle) + if result is None: + return None + return Enumeration(result) + elif name == "count": + return core.BNGetTypeElementCount(self.handle) + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if ((name == "type_class") or (name == "width") or (name == "alignment") or (name == "signed") or + (name == "const") or (name == "float") or (name == "target") or (name == "element_type") or + (name == "return_value") or (name == "parameters") or (name == "has_variable_arguments") or + (name == "can_return") or (name == "structure") or (name == "enumeration") or (name == "count")): + raise AttributeError, "attribute '%s' is read only" % name + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["type_class", "width", "alignment", "signed", "const", "float", "target", "element_type", "return_value", "calling_convention", "parameters", "has_variable_arguments", "can_return", "structure", "enumeration", "count"] + + def __str__(self): + return core.BNGetTypeString(self.handle) + + def __repr__(self): + return "<type: %s>" % str(self) + + def get_string_before_name(self): + return core.BNGetTypeStringBeforeName(self.handle) + + def get_string_after_name(self): + return core.BNGetTypeStringAfterName(self.handle) + + @classmethod + def void(cls): + return Type(core.BNCreateVoidType()) + + @classmethod + def bool(self): + return Type(core.BNCreateBoolType()) + + @classmethod + def int(self, width, sign = True): + return Type(core.BNCreateIntegerType(width, sign)) + + @classmethod + def float(self, width): + return Type(core.BNCreateFloatType(width)) + + @classmethod + def structure_type(self, s): + return Type(core.BNCreateStructureType(s.handle)) + + @classmethod + def enumeration_type(self, arch, e, width = None): + if width is None: + width = arch.default_int_size + return Type(core.BNCreateEnumerationType(e.handle, width)) + + @classmethod + def pointer(self, arch, t, const = False): + return Type(core.BNCreatePointerType(arch.handle, t.handle, const)) + + @classmethod + def array(self, t, count): + return Type(core.BNCreateArrayType(t.handle, count)) + + @classmethod + def function(self, ret, params, calling_convention = None, variable_arguments = False): + param_buf = (core.BNNameAndType * len(params))() + for i in xrange(0, len(params)): + if isinstance(params[i], Type): + param_buf[i].name = ""; + param_buf[i].type = params[i].handle + else: + param_buf[i].name = params[i][1] + param_buf[i].type = params[i][0] + if calling_convention is not None: + calling_convention = calling_convention.handle + return Type(core.BNCreateFunctionType(ret.handle, calling_convention, param_buf, len(params), + variable_arguments)) + +class StructureMember: + def __init__(self, t, name, offset): + self.type = t + self.name = name + self.offset = offset + + def __repr__(self): + if len(name) == 0: + return "<member: %s, offset 0x%x>" % (str(self.type), self.offset) + return "<%s %s%s, offset 0x%x>" % (self.type.get_string_before_name(), self.name, + self.type.get_string_after_name(), self.offset) + +class Structure: + def __init__(self, handle = None): + if handle is None: + self.handle = core.BNCreateStructure() + else: + self.handle = handle + + def __del__(self): + core.BNFreeStructure(self.handle) + + def __getattr__(self, name): + if name == "name": + return core.BNGetStructureName(self.handle) + elif name == "members": + count = ctypes.c_ulonglong() + members = core.BNGetStructureMembers(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(StructureMember(Type(core.BNNewTypeReference(members[i].type)), + members[i].name, members[i].offset)) + core.BNFreeStructureMemberList(members, count.value) + return result + elif name == "width": + return core.BNGetStructureWidth(self.handle) + elif name == "alignment": + return core.BNGetStructureAlignment(self.handle) + elif name == "packed": + return core.BNIsStructurePacked(self.handle) + elif name == "union": + return core.BNIsStructureUnion(self.handle) + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if (name == "members") or (name == "width") or (name == "alignment"): + raise AttributeError, "attribute '%s' is read only" % name + elif name == "name": + core.BNSetStructureName(self.handle, value) + elif name == "packed": + core.BNSetStructurePacked(self.handle, value) + elif name == "union": + core.BNSetStructureUnion(self.handle, value) + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["name","members", "width", "alignment", "packed", "union"] + + def __repr__(self): + if len(self.name) > 0: + return "<struct: %s>" % self.name + return "<struct: size 0x%x>" % self.width + + def append(self, t, name = ""): + core.BNAddStructureMember(self.handle, t.handle, name) + + def insert(self, offset, t, name = ""): + core.BNAddStructureMemberAtOffset(self.handle, t.handle, name, offset) + + def remove(self, i): + core.BNRemoveStructureMember(self.handle, i) + +class EnumerationMember: + def __init__(self, name, value, default): + self.name = name + self.value = value + self.default = default + + def __repr__(self): + return "<%s = 0x%x>" % (self.name, self.value) + +class Enumeration: + def __init__(self, handle = None): + if handle is None: + self.handle = core.BNCreateEnumeration() + else: + self.handle = handle + + def __del__(self): + core.BNFreeEnumeration(self.handle) + + def __getattr__(self, name): + if name == "name": + return core.BNGetEnumerationName(self.handle) + elif name == "members": + count = ctypes.c_ulonglong() + members = core.BNGetEnumerationMembers(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(EnumerationMember(members[i].name, members[i].value, members[i].isDefault)) + core.BNFreeEnumerationMemberList(members, count.value) + return result + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if name == "members": + raise AttributeError, "attribute '%s' is read only" % name + elif name == "name": + core.BNSetEnumerationName(self.handle, value) + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["name", "members"] + + def __repr__(self): + if len(self.name) > 0: + return "<enum: %s>" % self.name + return "<enum: %s>" % repr(self.members) + + def append(self, name, value = None): + if value is None: + core.BNAddEnumerationMember(self.handle, name) + else: + core.BNAddEnumerationMemberWithValue(self.handle, name, value) + +class RegisterValue: + def __init__(self, arch, value_type, reg, value): + self.type = core.BNRegisterValueType_names[value_type] + if value_type == EntryValue: + self.reg = arch.get_reg_name(reg) + elif value_type == OffsetFromEntryValue: + self.reg = arch.get_reg_name(reg) + self.offset = value + elif value_type == ConstantValue: + self.value = value + elif value_type == StackFrameOffset: + self.offset = value + + def __repr__(self): + if self.type == "EntryValue": + return "<entry %s>" % self.reg + if self.type == "OffsetFromEntryValue": + return "<entry %s + 0x%x>" % (self.reg, self.offset) + if self.type == "ConstantValue": + return "<const 0x%x>" % self.value + if self.type == "StackFrameOffset": + return "<stack frame offset 0x%x>" % self.offset + return "<undetermined>" + +class StackVariable: + def __init__(self, ofs, name, t): + self.offset = ofs + self.name = name + self.type = t + + def __repr__(self): + return "<var@%x: %s %s>" % (self.offset, self.type, self.name) + + def __str__(self): + return self.name + +class StackVariableReference: + def __init__(self, src_operand, t, name, start_ofs, ref_ofs): + self.source_operand = src_operand + self.type = t + self.name = name + self.starting_offset = start_ofs + self.referenced_offset = ref_ofs + if self.source_operand == 0xffffffff: + self.source_operand = None + + def __repr__(self): + if self.source_operand is None: + if self.referenced_offset != self.starting_offset: + return "<ref to %s%+x>" % (self.name, self.referenced_offset - self.starting_offset) + return "<ref to %s>" % self.name + if self.referenced_offset != self.starting_offset: + return "<operand %d ref to %s%+x>" % (self.source_operand, self.name, self.referenced_offset) + return "<operand %d ref to %s>" % (self.source_operand, self.name) + +class Function: + def __init__(self, view, handle): + self._view = view + self.handle = core.handle_of_type(handle, core.BNFunction) + + def __del__(self): + core.BNFreeFunction(self.handle) + + def __getattr__(self, name): + if name == "view": + return self._view + elif name == "arch": + arch = core.BNGetFunctionArchitecture(self.handle) + if arch is None: + return None + return Architecture(arch) + elif name == "start": + return core.BNGetFunctionStart(self.handle) + elif name == "symbol": + sym = core.BNGetFunctionSymbol(self.handle) + if sym is None: + return None + return Symbol(None, None, None, handle = sym) + elif name == "auto": + return core.BNWasFunctionAutomaticallyDiscovered(self.handle) + elif name == "can_return": + return core.BNCanFunctionReturn(self.handle) + elif name == "explicitly_defined_type": + return core.BNHasExplicitlyDefinedType(self.handle) + elif name == "basic_blocks": + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionBasicBlockList(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(BasicBlock(self._view, core.BNNewBasicBlockReference(blocks[i]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + elif name == "comments": + count = ctypes.c_ulonglong() + addrs = core.BNGetCommentedAddresses(self.handle, count) + result = {} + for i in xrange(0, count.value): + result[addrs[i]] = self.get_comment_at(addrs[i]) + core.BNFreeAddressList(addrs) + return result + elif name == "low_level_il": + return LowLevelILFunction(self.arch, core.BNNewLowLevelILFunctionReference( + core.BNGetFunctionLowLevelIL(self.handle))) + elif name == "low_level_il_basic_blocks": + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionLowLevelILBasicBlockList(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(BasicBlock(self._view, core.BNNewBasicBlockReference(blocks[i]))) + core.BNFreeBasicBlockList(blocks, count.value) + return result + elif name == "type": + return Type(core.BNGetFunctionType(self.handle)) + elif name == "stack_layout": + count = ctypes.c_ulonglong() + v = core.BNGetStackLayout(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(StackVariable(v[i].offset, v[i].name, Type(handle = core.BNNewTypeReference(v[i].type)))) + result.sort(key = lambda x: x.offset) + core.BNFreeStackLayout(v, count.value) + return result + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if ((name == "view") or (name == "arch") or (name == "start") or (name == "symbol") or (name == "auto") or + (name == "can_return") or (name == "basic_blocks") or (name == "comments") or (name == "low_level_il") or + (name == "low_level_il_basic_blocks") or (name == "type") or (name == "explicitly_defined_type") or + (name == "stack_layout")): + raise AttributeError, "attribute '%s' is read only" % name + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["view", "arch", "start", "symbol", "auto", "can_return", "explicitly_defined_type", "basic_blocks", "comments", "low_level_il", "low_level_il_basic_blocks", "type", "stack_layout"] + + def __repr__(self): + arch = self.arch + if arch: + return "<func: %s@0x%x>" % (arch.name, self.start) + else: + return "<func: 0x%x>" % self.start + + def mark_recent_use(self): + core.BNMarkFunctionAsRecentlyUsed(self.handle) + + def get_comment_at(self, addr): + return core.BNGetCommentForAddress(self.handle, addr) + + def set_comment(self, addr, comment): + core.BNSetCommentForAddress(self.handle, addr, comment) + + def get_low_level_il_at(self, arch, addr): + return core.BNGetLowLevelILForInstruction(self.handle, arch.handle, addr) + + def get_low_level_il_exits_at(self, arch, addr): + count = ctypes.c_ulonglong() + exits = core.BNGetLowLevelILExitsForInstruction(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(exits[i]) + core.BNFreeLowLevelILInstructionList(exits) + return result + + def get_reg_value_at(self, arch, addr, reg): + if isinstance(reg, str): + reg = arch.regs[reg].index + value = core.BNGetRegisterValueAtInstruction(self.handle, arch.handle, addr, reg) + return RegisterValue(arch, value.state, value.reg, value.value) + + def get_reg_value_after(self, arch, addr, reg): + if isinstance(reg, str): + reg = arch.regs[reg].index + value = core.BNGetRegisterValueAfterInstruction(self.handle, arch.handle, addr, reg) + return RegisterValue(arch, value.state, value.reg, value.value) + + def get_reg_value_at_low_level_il_instruction(self, i, reg): + if isinstance(reg, str): + reg = self.arch.regs[reg].index + value = core.BNGetRegisterValueAtInstruction(self.handle, self.arch.handle, i, reg) + return RegisterValue(self.arch, value.state, value.reg, value.value) + + def get_reg_value_after_low_level_il_instruction(self, i, reg): + if isinstance(reg, str): + reg = self.arch.regs[reg].index + value = core.BNGetRegisterValueAfterInstruction(self.handle, self.arch.handle, i, reg) + return RegisterValue(self.arch, value.state, value.reg, value.value) + + def get_regs_read_by(self, arch, addr): + count = ctypes.c_ulonglong() + regs = core.BNGetRegistersReadByInstruction(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + return result + + def get_regs_written_by(self, arch, addr): + count = ctypes.c_ulonglong() + regs = core.BNGetRegistersWrittenByInstruction(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs) + return result + + def get_stack_vars_referenced_by(self, arch, addr): + count = ctypes.c_ulonglong() + refs = core.BNGetStackVariablesReferencedByInstruction(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(StackVariableReference(refs[i].sourceOperand, Type(core.BNNewTypeReference(refs[i].type)), + refs[i].name, refs[i].startingOffset, refs[i].referencedOffset)) + core.BNFreeStackVariableReferenceList(refs, count.value) + return result + + def create_graph(self): + return FunctionGraph(self._view, core.BNCreateFunctionGraph(self.handle)) + + def apply_imported_types(self, sym): + core.BNApplyImportedTypes(self.handle, sym.handle) + + def apply_auto_discovered_type(self, func_type): + core.BNApplyAutoDiscoveredFunctionType(self.handle, func_type.handle) + +class BasicBlockEdge: + def __init__(self, branch_type, target, arch): + self.type = core.BNBranchType_names[branch_type] + if self.type != "UnresolvedBranch": + self.target = target + self.arch = arch + + def __repr__(self): + if self.type == "UnresolvedBranch": + return "<%s>" % self.type + elif self.arch: + return "<%s: %s@0x%x>" % (self.type, self.arch.name, self.target) + else: + return "<%s: 0x%x>" % (self.type, self.target) + +class BasicBlock: + def __init__(self, view, handle): + self.view = view + self.handle = core.handle_of_type(handle, core.BNBasicBlock) + + def __del__(self): + core.BNFreeBasicBlock(self.handle) + + def __getattr__(self, name): + if name == "function": + func = core.BNGetBasicBlockFunction(self.handle) + if func is None: + return None + return Function(self.view, func) + elif name == "arch": + arch = core.BNGetBasicBlockArchitecture(self.handle) + if arch is None: + return None + return Architecture(arch) + elif name == "start": + return core.BNGetBasicBlockStart(self.handle) + elif name == "end": + return core.BNGetBasicBlockEnd(self.handle) + elif name == "outgoing_edges": + count = ctypes.c_ulonglong(0) + edges = core.BNGetBasicBlockOutgoingEdges(self.handle, count) + result = [] + for i in xrange(0, count.value): + branch_type = edges[i].type + target = edges[i].target + if edges[i].arch: + arch = Architecture(edges[i].arch) + else: + arch = None + result.append(BasicBlockEdge(branch_type, target, arch)) + core.BNFreeBasicBlockOutgoingEdgeList(edges) + return result + elif name == "has_undetermined_outgoing_edges": + return core.BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if ((name == "function") or (name == "arch") or (name == "start") or (name == "end") or + (name == "outgoing_edges") or (name == "has_undetermined_outgoing_edges")): + raise AttributeError, "attribute '%s' is read only" % name + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["function", "arch", "start", "end", "outgoing_edges", "has_undetermined_outgoing_edges"] + + def __len__(self): + return int(core.BNGetBasicBlockLength(self.handle)) + + def __repr__(self): + arch = self.arch + if arch: + return "<block: %s@0x%x-0x%x>" % (arch.name, self.start, self.end) + else: + return "<block: 0x%x-0x%x>" % (self.start, self.end) + + def mark_recent_use(): + core.BNMarkBasicBlockAsRecentlyUsed(self.handle) + +class FunctionGraphTextLine: + def __init__(self, addr, tokens): + self.address = addr + self.tokens = tokens + + def __str__(self): + result = "" + for token in self.tokens: + result += token.text + return result + + def __repr__(self): + result = "<0x%x: %s>" % (self.address, str(self)) + +class FunctionGraphEdge: + def __init__(self, branch_type, arch, target, points): + self.type = branch_type + self.arch = arch + self.target = target + self.points = points + + def __repr__(self): + if self.arch: + return "<%s: %s@0x%x>" % (self.type, self.arch.name, self.target) + return "<%s: 0x%x>" % (self.type, self.target) + +class FunctionGraphBlock: + def __init__(self, handle): + self.handle = handle + + def __del__(self): + core.BNFreeFunctionGraphBlock(self.handle) + + def __getattr__(self, name): + if name == "arch": + arch = core.BNGetFunctionGraphBlockArchitecture(self.handle) + if arch is None: + return None + return Architecture(arch) + elif name == "start": + return core.BNGetFunctionGraphBlockStart(self.handle) + elif name == "end": + return core.BNGetFunctionGraphBlockEnd(self.handle) + elif name == "x": + return core.BNGetFunctionGraphBlockX(self.handle) + elif name == "y": + return core.BNGetFunctionGraphBlockY(self.handle) + elif name == "width": + return core.BNGetFunctionGraphBlockWidth(self.handle) + elif name == "height": + return core.BNGetFunctionGraphBlockHeight(self.handle) + elif name == "lines": + count = ctypes.c_ulonglong() + lines = core.BNGetFunctionGraphBlockLines(self.handle, count) + result = [] + for i in xrange(0, count.value): + addr = lines[i].addr + tokens = [] + for j in xrange(0, lines[i].count): + token_type = core.BNInstructionTextTokenType_names[lines[i].tokens[j].type] + text = lines[i].tokens[j].text + value = lines[i].tokens[j].value + tokens.append(InstructionTextToken(token_type, text, value)) + result.append(FunctionGraphTextLine(addr, tokens)) + core.BNFreeFunctionGraphBlockLines(lines, count.value) + return result + elif name == "outgoing_edges": + count = ctypes.c_ulonglong() + edges = core.BNGetFunctionGraphBlockOutgoingEdges(self.handle, count) + result = [] + for i in xrange(0, count.value): + branch_type = core.BNBranchType_names[edges[i].type] + target = edges[i].target + arch = None + if edges[i].arch is not None: + arch = Architecture(edges[i].arch) + points = [] + for j in xrange(0, edges[i].pointCount): + points.append((edges[i].points[j].x, edges[i].points[j].y)) + result.append(FunctionGraphEdge(branch_type, arch, target, points)) + core.BNFreeFunctionGraphBlockOutgoingEdgeList(edges, count.value) + return result + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if ((name == "arch") or (name == "start") or (name == "end") or (name == "x") or (name == "y") or + (name == "width") or (name == "height") or (name == "lines") or (name == "outgoing_edges")): + raise AttributeError, "attribute '%s' is read only" % name + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["arch", "start", "end", "x", "y", "width", "height", "lines", "outgoing_edges"] + + def __repr__(self): + arch = self.arch + if arch: + return "<graph block: %s@0x%x-0x%x>" % (arch.name, self.start, self.end) + else: + return "<graph block: 0x%x-0x%x>" % (self.start, self.end) + +class FunctionGraph: + def __init__(self, view, handle): + self.view = view + self.handle = handle + self._on_complete = None + self._cb = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self._complete) + + def __del__(self): + self.abort() + core.BNFreeFunctionGraph(self.handle) + + def __getattr__(self, name): + if name == "function": + func = core.BNGetFunctionForFunctionGraph(self.handle) + if func is None: + return None + return Function(self.view, func) + elif name == "horizontal_block_margin": + return core.BNGetHorizontalFunctionGraphBlockMargin(self.handle) + elif name == "vertical_block_margin": + return core.BNGetVerticalFunctionGraphBlockMargin(self.handle) + elif name == "max_symbol_width": + return core.BNGetFunctionGraphMaximumSymbolWidth(self.handle) + elif name == "complete": + return core.BNIsFunctionGraphLayoutComplete(self.handle) + elif name == "type": + return core.BNFunctionGraphType_names[core.BNGetFunctionGraphType(self.handle)] + elif name == "blocks": + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionGraphBlocks(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]))) + core.BNFreeFunctionGraphBlockList(blocks, count.value) + return result + elif name == "width": + return core.BNGetFunctionGraphWidth(self.handle) + elif name == "height": + return core.BNGetFunctionGraphHeight(self.handle) + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if ((name == "function") or (name == "complete") or (name == "type") or (name == "blocks") or + (name == "width") or (name == "height")): + raise AttributeError, "attribute '%s' is read only" % name + elif name == "horizontal_block_margin": + core.BNSetFunctionGraphBlockMargins(self.handle, value, self.vertical_block_margin) + elif name == "vertical_block_margin": + core.BNSetFunctionGraphBlockMargins(self.handle, self.horizontal_block_margin, value) + elif name == "max_symbol_width": + core.BNSetFunctionGraphMaximumSymbolWidth(self.handle, value) + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["function", "horizontal_block_margin", "vertical_block_margin", "max_symbol_width", "complete", "type", "blocks", "width", "height"] + + def __repr__(self): + return "<graph of %s>" % repr(self.function) + + def _complete(self, ctxt): + try: + if self._on_complete is not None: + self._on_complete() + except: + log_error(traceback.format_exc()) + + def layout(self, graph_type = core.NormalFunctionGraph): + if isinstance(graph_type, str): + graph_type = core.BNFunctionGraphType_by_name[graph_type] + core.BNStartFunctionGraphLayout(self.handle, graph_type) + + def _wait_complete(self): + self._wait_cond.acquire() + self._wait_cond.notify() + self._wait_cond.release() + + def layout_and_wait(self, graph_type = core.NormalFunctionGraph): + self._wait_cond = threading.Condition() + self.on_complete(self._wait_complete) + self.layout(graph_type) + + self._wait_cond.acquire() + while not self.complete: + self._wait_cond.wait() + self._wait_cond.release() + + def on_complete(self, callback): + self._on_complete = callback + core.BNSetFunctionGraphCompleteCallback(self.handle, None, self._cb) + + def abort(self): + core.BNAbortFunctionGraph(self.handle) + + def get_blocks_in_region(self, left, top, right, bottom): + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionGraphBlocksInRegion(self.handle, left, top, right, bottom, count) + result = [] + for i in xrange(0, count.value): + result.append(FunctionGraphBlock(core.BNNewFunctionGraphBlockReference(blocks[i]))) + core.BNFreeFunctionGraphBlockList(blocks, count.value) + return result + + def is_option_set(self, option): + if isinstance(option, str): + option = core.BNFunctionGraphOption_by_name(option) + return core.BNIsFunctionGraphOptionSet(self.handle, option) + + def set_option(self, option, state = True): + if isinstance(option, str): + option = core.BNFunctionGraphOption_by_name(option) + core.BNSetFunctionGraphOption(self.handle, option, state) + +class RegisterInfo: + def __init__(self, full_width_reg, size, offset = 0, extend = core.NoExtend, index = None): + self.full_width_reg = full_width_reg + self.offset = offset + self.size = size + self.extend = extend + self.index = index + + def __repr__(self): + if (self.extend == core.ZeroExtendToFullWidth) or (self.extend == "ZeroExtendToFullWidth"): + extend = ", zero extend" + elif (self.extend == core.SignExtendToFullWidth) or (self.extend == "SignExtendToFullWidth"): + extend = ", sign extend" + else: + extend = "" + return "<reg: size %d, offset %d in %s%s>" % (self.size, self.offset, self.full_width_reg, extend) + +class InstructionBranch: + def __init__(self, branch_type, target = 0, arch = None): + self.type = branch_type + self.target = target + self.arch = arch + + def __repr__(self): + branch_type = self.type + if not isinstance(branch_type, str): + branch_type = core.BNBranchType_names[branch_type] + if self.arch is not None: + return "<%s: %s@0x%x>" % (branch_type, self.arch.name, self.target) + return "<%s: 0x%x>" % (branch_type, self.target) + +class InstructionInfo: + def __init__(self): + self.length = 0 + self.branch_delay = False + self.branches = [] + + def add_branch(self, branch_type, target = 0, arch = None): + self.branches.append(InstructionBranch(branch_type, target, arch)) + + def __repr__(self): + branch_delay = "" + if self.branch_delay: + branch_delay = ", delay slot" + return "<instr: %d bytes%s, %s>" % (self.length, branch_delay, repr(self.branches)) + +class InstructionTextToken: + def __init__(self, token_type, text, value = 0): + self.type = token_type + self.text = text + self.value = value + + def __str__(self): + return self.text + + def __repr__(self): + return repr(self.text) + +class _ArchitectureMetaClass(type): + def __getattr__(cls, name): + if name == "list": + _init_plugins() + count = ctypes.c_ulonglong() + archs = core.BNGetArchitectureList(count) + result = [] + for i in xrange(0, count.value): + result.append(Architecture(archs[i])) + core.BNFreeArchitectureList(archs) + return result + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(cls, name, value): + if (name == "list"): + raise AttributeError, "attribute '%s' is read only" % name + else: + type.__setattr__(cls, name, value) + + def __dir__(self): + return dir(self.__class__) + ["list"] + + def __getitem__(cls, name): + _init_plugins() + arch = core.BNGetArchitectureByName(name) + if arch is None: + raise KeyError, "'%s' is not a valid architecture" % str(name) + return Architecture(arch) + + def register(cls): + _init_plugins() + if cls.name is None: + raise ValueError, "architecture 'name' is not defined" + arch = cls() + cls._registered_cb = arch._cb + arch.handle = core.BNRegisterArchitecture(cls.name, arch._cb) + +class Architecture: + name = None + endianness = core.LittleEndian + address_size = 8 + default_int_size = 4 + regs = {} + stack_pointer = None + link_reg = None + flags = [] + flag_write_types = [] + __metaclass__ = _ArchitectureMetaClass + + def __init__(self, handle = None): + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNArchitecture) + self.__dict__["name"] = core.BNGetArchitectureName(self.handle) + self.__dict__["endianness"] = core.BNEndianness_names[core.BNGetArchitectureEndianness(self.handle)] + self.__dict__["address_size"] = core.BNGetArchitectureAddressSize(self.handle) + self.__dict__["default_int_size"] = core.BNGetArchitectureDefaultIntegerSize(self.handle) + self.__dict__["stack_pointer"] = core.BNGetArchitectureRegisterName(self.handle, + core.BNGetArchitectureStackPointerRegister(self.handle)) + self.__dict__["link_reg"] = core.BNGetArchitectureRegisterName(self.handle, + core.BNGetArchitectureLinkRegister(self.handle)) + + count = ctypes.c_ulonglong() + regs = core.BNGetAllArchitectureRegisters(self.handle, count) + self.__dict__["regs"] = {} + for i in xrange(0, count.value): + name = core.BNGetArchitectureRegisterName(self.handle, regs[i]) + info = core.BNGetArchitectureRegisterInfo(self.handle, regs[i]) + full_width_reg = core.BNGetArchitectureRegisterName(self.handle, info.fullWidthRegister) + self.regs[name] = RegisterInfo(full_width_reg, info.size, info.offset, + core.BNImplicitRegisterExtend_names[info.extend], regs[i]) + core.BNFreeRegisterList(regs) + + count = ctypes.c_ulonglong() + flags = core.BNGetAllArchitectureFlags(self.handle, count) + self._flags = {} + self._flags_by_index = {} + self.__dict__["flags"] = [] + for i in xrange(0, count.value): + name = core.BNGetArchitectureFlagName(self.handle, flags[i]) + self._flags[name] = flags[i] + self._flags_by_index[flags[i]] = name + self.flags.append(name) + core.BNFreeRegisterList(flags) + + count = ctypes.c_ulonglong() + types = core.BNGetAllArchitectureFlagWriteTypes(self.handle, count) + self._flag_write_types = {} + self._flag_write_types_by_index = {} + self.__dict__["flag_write_types"] = [] + for i in xrange(0, count.value): + name = core.BNGetArchitectureFlagWriteTypeName(self.handle, types[i]) + self._flag_write_types[name] = types[i] + self._flag_write_types_by_index[types[i]] = name + self.flag_write_types.append(name) + core.BNFreeRegisterList(types) + else: + _init_plugins() + self._cb = core.BNCustomArchitecture() + self._cb.context = 0 + self._cb.init = self._cb.init.__class__(self._init) + self._cb.getEndianness = self._cb.getEndianness.__class__(self._get_endianness) + self._cb.getAddressSize = self._cb.getAddressSize.__class__(self._get_address_size) + self._cb.getDefaultIntegerSize = self._cb.getDefaultIntegerSize.__class__(self._get_default_integer_size) + self._cb.getInstructionInfo = self._cb.getInstructionInfo.__class__(self._get_instruction_info) + self._cb.getInstructionText = self._cb.getInstructionText.__class__(self._get_instruction_text) + self._cb.freeInstructionText = self._cb.freeInstructionText.__class__(self._free_instruction_text) + self._cb.getInstructionLowLevelIL = self._cb.getInstructionLowLevelIL.__class__( + self._get_instruction_low_level_il) + self._cb.getRegisterName = self._cb.getRegisterName.__class__(self._get_register_name) + self._cb.getFlagName = self._cb.getFlagName.__class__(self._get_flag_name) + self._cb.getFlagWriteTypeName = self._cb.getFlagWriteTypeName.__class__(self._get_flag_write_type_name) + self._cb.getFullWidthRegisters = self._cb.getFullWidthRegisters.__class__(self._get_full_width_registers) + self._cb.getAllRegisters = self._cb.getAllRegisters.__class__(self._get_all_registers) + self._cb.getAllFlags = self._cb.getAllRegisters.__class__(self._get_all_flags) + self._cb.getAllFlagWriteTypes = self._cb.getAllRegisters.__class__(self._get_all_flag_write_types) + self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list) + self._cb.getRegisterInfo = self._cb.getRegisterInfo.__class__(self._get_register_info) + self._cb.getStackPointerRegister = self._cb.getStackPointerRegister.__class__( + self._get_stack_pointer_register) + self._cb.getLinkRegister = self._cb.getLinkRegister.__class__(self._get_link_register) + self._cb.assemble = self._cb.assemble.__class__(self._assemble) + self._cb.isNeverBranchPatchAvailable = self._cb.isNeverBranchPatchAvailable.__class__( + self._is_never_branch_patch_available) + self._cb.isAlwaysBranchPatchAvailable = self._cb.isAlwaysBranchPatchAvailable.__class__( + self._is_always_branch_patch_available) + self._cb.isInvertBranchPatchAvailable = self._cb.isInvertBranchPatchAvailable.__class__( + self._is_invert_branch_patch_available) + self._cb.isSkipAndReturnZeroPatchAvailable = self._cb.isSkipAndReturnZeroPatchAvailable.__class__( + self._is_skip_and_return_zero_patch_available) + self._cb.isSkipAndReturnValuePatchAvailable = self._cb.isSkipAndReturnValuePatchAvailable.__class__( + self._is_skip_and_return_value_patch_available) + self._cb.convertToNop = self._cb.convertToNop.__class__(self._convert_to_nop) + self._cb.alwaysBranch = self._cb.alwaysBranch.__class__(self._always_branch) + self._cb.invertBranch = self._cb.invertBranch.__class__(self._invert_branch) + self._cb.skipAndReturnValue = self._cb.skipAndReturnValue.__class__(self._skip_and_return_value) + + self._all_regs = {} + self._full_width_regs = {} + self._regs_by_index = {} + self.__dict__["regs"] = self.__class__.regs + reg_index = 0 + for reg in self.regs.keys(): + info = self.regs[reg] + if reg not in self._all_regs: + self._all_regs[reg] = reg_index + self._regs_by_index[reg_index] = reg + self.regs[reg].index = reg_index + reg_index += 1 + if info.full_width_reg not in self._all_regs: + self._all_regs[info.full_width_reg] = reg_index + self._regs_by_index[reg_index] = info.full_width_reg + self.regs[info.full_width_reg].index = reg_index + reg_index += 1 + if info.full_width_reg not in self._full_width_regs: + self._full_width_regs[info.full_width_reg] = self._all_regs[info.full_width_reg] + + self._flags = {} + self._flags_by_index = {} + self.__dict__["flags"] = self.__class__.flags + flag_index = 0 + for flag in self.__class__.flags: + if flag not in self._flags: + self._flags[flag] = flag_index + self._flags_by_index[flag_index] = flag + flag_index += 1 + + self._flag_write_types = {} + self._flag_write_types_by_index = {} + self.__dict__["flag_write_types"] = self.__class__.flag_write_types + write_type_index = 0 + for write_type in self.__class__.flag_write_types: + if write_type not in self._flag_write_types: + self._flag_write_types[write_type] = write_type_index + self._flag_write_types_by_index[write_type_index] = write_type + write_type_index += 1 + + self._pending_reg_lists = {} + self._pending_token_lists = {} + + + def __getattr__(self, name): + if name == "full_width_regs": + count = ctypes.c_ulonglong() + regs = core.BNGetFullWidthArchitectureRegisters(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) + core.BNFreeRegisterList(regs) + return result + elif name == "calling_conventions": + count = ctypes.c_ulonglong() + cc = core.BNGetArchitectureCallingConventions(self.handle, count) + result = {} + for i in xrange(0, count.value): + obj = CallingConvention(None, core.BNNewCallingConventionReference(cc[i])) + result[obj.name] = obj + core.BNFreeCallingConventionList(cc, count) + return result + elif name == "standalone_platform": + pl = core.BNGetArchitectureStandalonePlatform(self.handle) + return Platform(self, pl) + + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if ((name == "name") or (name == "endianness") or (name == "address_size") or (name == "default_int_size") or + (name == "regs") or (name == "full_width_regs") or (name == "calling_conventions")): + raise AttributeError, "attribute '%s' is read only" % name + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["full_width_regs","calling_conventions"] + + def __repr__(self): + return "<arch: %s>" % self.name + + def _init(self, ctxt, handle): + self.handle = handle + + def _get_endianness(self, ctxt): + try: + return self.__class__.endianness + except: + log_error(traceback.format_exc()) + return core.LittleEndian + + def _get_address_size(self, ctxt): + try: + return self.__class__.address_size + except: + log_error(traceback.format_exc()) + return 8 + + def _get_default_integer_size(self, ctxt): + try: + return self.__class__.default_int_size + except: + log_error(traceback.format_exc()) + return 4 + + def _get_instruction_info(self, ctxt, data, addr, max_len, result): + try: + buf = ctypes.create_string_buffer(max_len) + ctypes.memmove(buf, data, max_len) + info = self.perform_get_instruction_info(buf.raw, addr) + if info is None: + return False + result[0].length = info.length + result[0].branchDelay = info.branch_delay + result[0].branchCount = len(info.branches) + for i in xrange(0, len(info.branches)): + if isinstance(info.branches[i].type, str): + result[0].branchType[i] = core.BNBranchType_by_name[info.branches[i].type] + else: + result[0].branchType[i] = info.branches[i].type + result[0].branchTarget[i] = info.branches[i].target + if info.branches[i].arch is None: + result[0].branchArch[i] = None + else: + result[0].branchArch[i] = info.branches[i].arch.handle + return True + except: + log_error(traceback.format_exc()) + return False + + def _get_instruction_text(self, ctxt, data, addr, length, result, count): + try: + buf = ctypes.create_string_buffer(length[0]) + ctypes.memmove(buf, data, length[0]) + info = self.perform_get_instruction_text(buf.raw, addr) + if info is None: + return False + tokens = info[0] + length[0] = info[1] + count[0] = len(tokens) + token_buf = (core.BNInstructionTextToken * len(tokens))() + for i in xrange(0, len(tokens)): + if isinstance(tokens[i].type, str): + token_buf[i].type = BNInstructionTextTokenType_by_name[tokens[i].type] + else: + token_buf[i].type = tokens[i].type + token_buf[i].text = tokens[i].text + token_buf[i].value = tokens[i].value + result[0] = token_buf + ptr = ctypes.cast(token_buf, ctypes.c_void_p) + self._pending_token_lists[ptr.value] = (ptr.value, token_buf) + return True + except: + log_error(traceback.format_exc()) + return False + + def _free_instruction_text(self, tokens, count): + try: + buf = ctypes.cast(tokens, ctypes.c_void_p) + if buf.value not in self._pending_token_lists: + raise ValueError, "freeing token list that wasn't allocated" + del self._pending_token_lists[buf.value] + except: + log_error(traceback.format_exc()) + + def _get_instruction_low_level_il(self, ctxt, data, addr, length, il): + try: + buf = ctypes.create_string_buffer(length[0]) + ctypes.memmove(buf, data, length[0]) + result = self.perform_get_instruction_low_level_il(buf.raw, addr, + LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))) + if result is None: + return False + length[0] = result + return True + except: + log_error(traceback.format_exc()) + return False + + def _get_register_name(self, ctxt, reg): + try: + if reg in self._regs_by_index: + return core.BNAllocString(self._regs_by_index[reg]) + return core.BNAllocString("") + except: + log_error(traceback.format_exc()) + return core.BNAllocString("") + + def _get_flag_name(self, ctxt, flag): + try: + if flag in self._flags_by_index: + return core.BNAllocString(self._flags_by_index[flag]) + return core.BNAllocString("") + except: + log_error(traceback.format_exc()) + return core.BNAllocString("") + + def _get_flag_write_type_name(self, ctxt, write_type): + try: + if write_type in self._flag_write_types_by_index: + return core.BNAllocString(self._flag_write_types_by_index[write_type]) + return core.BNAllocString("") + except: + log_error(traceback.format_exc()) + return core.BNAllocString("") + + def _get_full_width_registers(self, ctxt, count): + try: + regs = self._full_width_regs.values() + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = regs[i] + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except: + log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_all_registers(self, ctxt, count): + try: + regs = self._regs_by_index.keys() + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = regs[i] + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except: + log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_all_flags(self, ctxt, count): + try: + flags = self._flags_by_index.keys() + count[0] = len(flags) + flag_buf = (ctypes.c_uint * len(flags))() + for i in xrange(0, len(flags)): + flag_buf[i] = flags[i] + result = ctypes.cast(flag_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, flag_buf) + return result.value + except: + log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_all_flag_write_types(self, ctxt, count): + try: + types = self._flag_write_types_by_index.keys() + count[0] = len(types) + type_buf = (ctypes.c_uint * len(types))() + for i in xrange(0, len(types)): + type_buf[i] = types[i] + result = ctypes.cast(type_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, type_buf) + return result.value + except: + log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _free_register_list(self, ctxt, regs): + try: + buf = ctypes.cast(regs, ctypes.c_void_p) + if buf.value not in self._pending_reg_lists: + raise ValueError, "freeing register list that wasn't allocated" + del self._pending_reg_lists[buf.value] + except: + log_error(traceback.format_exc()) + + def _get_register_info(self, ctxt, reg, result): + try: + if reg not in self._regs_by_index: + result[0].fullWidthRegister = 0 + result[0].offset = 0 + result[0].size = 0 + result[0].extend = core.NoExtend + return + info = self.__class__.regs[self._regs_by_index[reg]] + result[0].fullWidthRegister = self._all_regs[info.full_width_reg] + result[0].offset = info.offset + result[0].size = info.size + if isinstance(info.extend, str): + result[0].extend = core.BNImplicitRegisterExtend_by_name[info.extend] + else: + result[0].extend = info.extend + except: + log_error(traceback.format_exc()) + result[0].fullWidthRegister = 0 + result[0].offset = 0 + result[0].size = 0 + result[0].extend = core.NoExtend + + def _get_stack_pointer_register(self, ctxt): + try: + return self._all_regs[self.__class__.stack_pointer] + except: + log_error(traceback.format_exc()) + return 0 + + def _get_link_register(self, ctxt): + try: + if self.__class__.link_reg is None: + return 0xffffffff + return self._all_regs[self.__class__.link_reg] + except: + log_error(traceback.format_exc()) + return 0 + + def _assemble(self, ctxt, code, addr, result, errors): + try: + data, error_str = self.perform_assemble(code, addr) + errors[0] = core.BNAllocString(str(error_str)) + if data is None: + return False + data = str(data) + buf = ctypes.create_string_buffer(len(data)) + ctypes.memmove(buf, data, len(data)) + core.BNSetDataBufferContents(result, buf, len(data)) + return True + except: + log_error(traceback.format_exc()) + errors[0] = core.BNAllocString("Unhandled exception during assembly.\n") + return False + + def _is_never_branch_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_never_branch_patch_available(buf.raw, addr) + except: + log_error(traceback.format_exc()) + return False + + def _is_always_branch_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_always_branch_patch_available(buf.raw, addr) + except: + log_error(traceback.format_exc()) + return False + + def _is_invert_branch_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_invert_branch_patch_available(buf.raw, addr) + except: + log_error(traceback.format_exc()) + return False + + def _is_skip_and_return_zero_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_skip_and_return_zero_patch_available(buf.raw, addr) + except: + log_error(traceback.format_exc()) + return False + + def _is_skip_and_return_value_patch_available(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + return self.perform_is_skip_and_return_value_patch_available(buf.raw, addr) + except: + log_error(traceback.format_exc()) + return False + + def _convert_to_nop(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_convert_to_nop(buf.raw, addr) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log_error(traceback.format_exc()) + return False + + def _always_branch(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_always_branch(buf.raw, addr) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log_error(traceback.format_exc()) + return False + + def _invert_branch(self, ctxt, data, addr, length): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_invert_branch(buf.raw, addr) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log_error(traceback.format_exc()) + return False + + def _skip_and_return_value(self, ctxt, data, addr, length, value): + try: + buf = ctypes.create_string_buffer(length) + ctypes.memmove(buf, data, length) + result = self.perform_skip_and_return_value(buf.raw, addr, value) + if result is None: + return False + result = str(result) + if len(result) > length: + result = result[0:length] + ctypes.memmove(data, result, len(result)) + return True + except: + log_error(traceback.format_exc()) + return False + + def perform_get_instruction_low_level_il(self, data, addr, il): + return None + + def perform_assemble(self, code, addr): + return None, "Architecture does not implement an assembler.\n" + + def perform_is_never_branch_patch_available(self, data, addr): + return False + + def perform_is_always_branch_patch_available(self, data, addr): + return False + + def perform_is_invert_branch_patch_available(self, data, addr): + return False + + def perform_is_skip_and_return_zero_patch_available(self, data, addr): + return False + + def perform_is_skip_and_return_value_patch_available(self, data, addr): + return False + + def perform_convert_to_nop(self, data, addr): + return None + + def perform_always_branch(self, data, addr): + return None + + def perform_invert_branch(self, data, addr): + return None + + def perform_skip_and_return_value(self, data, addr, value): + return None + + def get_instruction_info(self, data, addr): + info = core.BNInstructionInfo() + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNGetInstructionInfo(self.handle, buf, addr, len(data), info): + return None + result = InstructionInfo() + result.length = info.length + result.branch_delay = info.branchDelay + for i in xrange(0, info.branchCount): + branch_type = core.BNBranchType_names[info.branchType[i]] + target = info.branchTarget[i] + if info.branchArch[i]: + arch = Architecture(info.branchArch[i]) + else: + arch = None + result.add_branch(branch_type, target, arch) + return result + + def get_instruction_text(self, data, addr): + data = str(data) + count = ctypes.c_ulonglong() + length = ctypes.c_ulonglong() + length.value = len(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + tokens = ctypes.POINTER(core.BNInstructionTextToken)() + if not core.BNGetInstructionText(self.handle, buf, addr, length, tokens, count): + return None, 0 + result = [] + for i in xrange(0, count.value): + token_type = core.BNInstructionTextTokenType_names[tokens[i].type] + text = tokens[i].text + value = tokens[i].value + result.append(InstructionTextToken(token_type, text, value)) + core.BNFreeInstructionText(tokens, count.value) + return result, length.value + + def get_instruction_low_level_il(self, data, addr, il): + info = core.BNInstructionInfo() + data = str(data) + length = ctypes.c_ulonglong() + length.value = len(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + core.BNGetInstructionLowLevelIL(self.handle, buf, addr, length, il.handle) + return length.value + + def get_reg_name(self, reg): + return core.BNGetArchitectureRegisterName(self.handle, reg) + + def get_flag_name(self, flag): + return core.BNGetArchitectureFlagName(self.handle, flag) + + def get_flag_write_type_name(self, write_type): + return core.BNGetArchitectureFlagWriteTypeName(self.handle, write_type) + + def get_flag_by_name(self, flag): + return self._flags[flag] + + def get_flag_write_type_by_name(self, write_type): + return self._flag_write_types[write_type] + + def get_modified_regs_on_write(self, reg): + reg = core.BNGetArchitectureRegisterByName(self.handle, str(reg)) + count = ctypes.c_ulonglong() + regs = core.BNGetModifiedArchitectureRegistersOnWrite(self.handle, reg, count) + result = [] + for i in xrange(0, count.value): + result.append(core.BNGetArchitectureRegisterName(self.handle, regs[i])) + core.BNFreeRegisterList(regs) + return result + + def assemble(self, code, addr = 0): + result = DataBuffer() + errors = ctypes.c_char_p() + if not core.BNAssemble(self.handle, code, addr, result.handle, errors): + return None, errors.value + return str(result), errors.value + + def is_never_branch_patch_available(self, data, addr): + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureNeverBranchPatchAvailable(self.handle, buf, addr, len(data)) + + def is_always_branch_patch_available(self, data, addr): + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureAlwaysBranchPatchAvailable(self.handle, buf, addr, len(data)) + + def is_invert_branch_patch_available(self, data, addr): + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureInvertBranchPatchAvailable(self.handle, buf, addr, len(data)) + + def is_skip_and_return_zero_patch_available(self, data, addr): + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureSkipAndReturnZeroPatchAvailable(self.handle, buf, addr, len(data)) + + def is_skip_and_return_value_patch_available(self, data, addr): + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + return core.BNIsArchitectureSkipAndReturnValuePatchAvailable(self.handle, buf, addr, len(data)) + + def convert_to_nop(self, data, addr): + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureConvertToNop(self.handle, buf, addr, len(data)): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def always_branch(self, data, addr): + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureAlwaysBranch(self.handle, buf, addr, len(data)): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def invert_branch(self, data, addr): + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureInvertBranch(self.handle, buf, addr, len(data)): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def skip_and_return_value(self, data, addr, value): + data = str(data) + buf = (ctypes.c_ubyte * len(data))() + ctypes.memmove(buf, data, len(data)) + if not core.BNArchitectureSkipAndReturnValue(self.handle, buf, addr, len(data), value): + return None + result = ctypes.create_string_buffer(len(data)) + ctypes.memmove(result, buf, len(data)) + return result.raw + + def is_view_type_constant_defined(self, type_name, const_name): + return core.BNIsBinaryViewTypeArchitectureConstantDefined(self.handle, type_name, const_name) + + def get_view_type_constant(self, type_name, const_name, default_value = 0): + return core.BNGetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, default_value) + + def set_view_type_constant(self, type_name, const_name, value): + core.BNSetBinaryViewTypeArchitectureConstant(self.handle, type_name, const_name, value) + + def parse_types_from_source(self, source, filename = None, include_dirs = []): + if filename is None: + filename = "input" + dir_buf = (ctypes.c_char_p * len(include_dirs))() + for i in xrange(0, len(include_dirs)): + dir_buf[i] = str(include_dirs[i]) + parse = core.BNTypeParserResult() + errors = ctypes.c_char_p() + result = core.BNParseTypesFromSource(self.handle, source, filename, parse, errors, dir_buf, len(include_dirs)) + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + if not result: + return (None, error_str) + types = {} + variables = {} + functions = {} + for i in xrange(0, parse.typeCount): + types[parse.types[i].name] = Type(core.BNNewTypeReference(parse.types[i].type)) + for i in xrange(0, parse.variableCount): + variables[parse.variables[i].name] = Type(core.BNNewTypeReference(parse.variables[i].type)) + for i in xrange(0, parse.functionCount): + functions[parse.functions[i].name] = Type(core.BNNewTypeReference(parse.functions[i].type)) + return (TypeParserResult(types, variables, functions), error_str) + + def parse_types_from_source_file(self, filename, include_dirs = []): + dir_buf = (ctypes.c_char_p * len(include_dirs))() + for i in xrange(0, len(include_dirs)): + dir_buf[i] = str(include_dirs[i]) + parse = core.BNTypeParserResult() + errors = ctypes.c_char_p() + result = core.BNParseTypesFromSourceFile(self.handle, filename, parse, errors, dir_buf, len(include_dirs)) + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + if not result: + return (None, error_str) + types = {} + variables = {} + functions = {} + for i in xrange(0, parse.typeCount): + types[parse.types[i].name] = Type(core.BNNewTypeReference(parse.types[i].type)) + for i in xrange(0, parse.variableCount): + variables[parse.variables[i].name] = Type(core.BNNewTypeReference(parse.variables[i].type)) + for i in xrange(0, parse.functionCount): + functions[parse.functions[i].name] = Type(core.BNNewTypeReference(parse.functions[i].type)) + return (TypeParserResult(types, variables, functions), error_str) + + def register_calling_convention(self, cc): + core.BNRegisterCallingConvention(self.handle, cc.handle) + +class ReferenceSource: + def __init__(self, func, arch, addr): + self.function = func + self.arch = arch + self.address = addr + + def __repr__(self): + if self.arch: + return "<ref: %s@0x%x>" % (self.arch.name, self.address) + else: + return "<ref: 0x%x>" % self.address + +class LowLevelILLabel: + def __init__(self, handle = None): + if handle is None: + self.handle = (core.BNLowLevelILLabel * 1)() + core.BNLowLevelILInitLabel(self.handle) + else: + self.handle = handle + +class LowLevelILInstruction: + ILOperations = { + core.LLIL_NOP: [], + core.LLIL_SET_REG: [("dest", "reg"), ("src", "expr")], + core.LLIL_SET_REG_SPLIT: [("hi", "reg"), ("lo", "reg"), ("src", "expr")], + core.LLIL_SET_FLAG: [("dest", "flag"), ("src", "expr")], + core.LLIL_LOAD: [("src", "expr")], + core.LLIL_STORE: [("dest", "expr"), ("src", "expr")], + core.LLIL_PUSH: [("src", "expr")], + core.LLIL_POP: [], + core.LLIL_REG: [("src", "reg")], + core.LLIL_CONST: [("value", "int")], + core.LLIL_FLAG: [("src", "flag")], + core.LLIL_FLAG_BIT: [("src", "flag"), ("bit", "int")], + core.LLIL_ADD: [("left", "expr"), ("right", "expr")], + core.LLIL_ADC: [("left", "expr"), ("right", "expr")], + core.LLIL_SUB: [("left", "expr"), ("right", "expr")], + core.LLIL_SBB: [("left", "expr"), ("right", "expr")], + core.LLIL_AND: [("left", "expr"), ("right", "expr")], + core.LLIL_OR: [("left", "expr"), ("right", "expr")], + core.LLIL_XOR: [("left", "expr"), ("right", "expr")], + core.LLIL_LSL: [("left", "expr"), ("right", "expr")], + core.LLIL_LSR: [("left", "expr"), ("right", "expr")], + core.LLIL_ASR: [("left", "expr"), ("right", "expr")], + core.LLIL_ROL: [("left", "expr"), ("right", "expr")], + core.LLIL_RLC: [("left", "expr"), ("right", "expr")], + core.LLIL_ROR: [("left", "expr"), ("right", "expr")], + core.LLIL_RRC: [("left", "expr"), ("right", "expr")], + core.LLIL_MUL: [("left", "expr"), ("right", "expr")], + core.LLIL_MULU_DP: [("left", "expr"), ("right", "expr")], + core.LLIL_MULS_DP: [("left", "expr"), ("right", "expr")], + core.LLIL_DIVU: [("left", "expr"), ("right", "expr")], + core.LLIL_DIVU_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + core.LLIL_DIVS: [("left", "expr"), ("right", "expr")], + core.LLIL_DIVS_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + core.LLIL_MODU: [("left", "expr"), ("right", "expr")], + core.LLIL_MODU_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + core.LLIL_MODS: [("left", "expr"), ("right", "expr")], + core.LLIL_MODS_DP: [("hi", "expr"), ("lo", "expr"), ("right", "expr")], + core.LLIL_NEG: [("src", "expr")], + core.LLIL_NOT: [("src", "expr")], + core.LLIL_SX: [("src", "expr")], + core.LLIL_ZX: [("src", "expr")], + core.LLIL_JUMP: [("dest", "expr")], + core.LLIL_CALL: [("dest", "expr")], + core.LLIL_RET: [("dest", "expr")], + core.LLIL_NORET: [], + core.LLIL_IF: [("condition", "expr"), ("true", "int"), ("false", "int")], + core.LLIL_GOTO: [("dest", "int")], + core.LLIL_FLAG_COND: [("condition", "cond")], + core.LLIL_CMP_E: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_NE: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_SLT: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_ULT: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_SLE: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_ULE: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_SGE: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_UGE: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_SGT: [("left", "expr"), ("right", "expr")], + core.LLIL_CMP_UGT: [("left", "expr"), ("right", "expr")], + core.LLIL_TEST_BIT: [("left", "expr"), ("right", "expr")], + core.LLIL_SYSCALL: [], + core.LLIL_BP: [], + core.LLIL_TRAP: [("value", "int")], + core.LLIL_UNDEF: [], + core.LLIL_UNIMPL: [], + core.LLIL_UNIMPL_MEM: [("src", "expr")] + } + + def __init__(self, func, i): + instr = core.BNGetLowLevelILByIndex(func.handle, i) + self.function = func + self.index = i + self.operation = instr.operation + self.operation_name = core.BNLowLevelILOperation_names[instr.operation] + self.size = instr.size + self.address = instr.address + self.source_operand = instr.sourceOperand + if instr.flags == 0: + self.flags = None + else: + self.flags = func.arch.get_flag_write_type_name(instr.flags) + if self.source_operand == 0xffffffff: + self.source_operand = None + operands = LowLevelILInstruction.ILOperations[instr.operation] + self.operands = [] + for i in xrange(0, len(operands)): + name, operand_type = operands[i] + if operand_type == "int": + value = instr.operands[i] + elif operand_type == "expr": + value = LowLevelILInstruction(func, instr.operands[i]) + elif operand_type == "reg": + if (instr.operands[i] & 0x80000000) != 0: + value = instr.operands[i] + else: + value = func.arch.get_reg_name(instr.operands[i]) + elif operand_type == "flag": + value = func.arch.get_flag_name(instr.operands[i]) + elif operand_type == "cond": + value = core.BNLowLevelILFlagCondition_names[instr.operands[i]] + self.operands.append(value) + self.__dict__[name] = value + + def __str__(self): + tokens = self.tokens + if tokens is None: + return "invalid" + result = "" + for token in tokens: + result += token.text + return result + + def __repr__(self): + return "<il: %s>" % str(self) + + def __getattr__(self, name): + if name == "tokens": + count = ctypes.c_ulonglong() + tokens = ctypes.POINTER(core.BNInstructionTextToken)() + if not core.BNGetLowLevelILExprText(self.function.handle, self.function.arch.handle, + self.index, tokens, count): + return None + result = [] + for i in xrange(0, count.value): + token_type = core.BNInstructionTextTokenType_names[tokens[i].type] + text = tokens[i].text + value = tokens[i].value + result.append(InstructionTextToken(token_type, text, value)) + core.BNFreeInstructionText(tokens, count.value) + return result + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if name == "tokens": + raise AttributeError, "attribute '%s' is read only" % name + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["tokens"] + +class LowLevelILExpr: + def __init__(self, index): + self.index = index + +class LowLevelILFunction: + def __init__(self, arch, handle = None): + self.arch = arch + if handle is not None: + self.handle = core.handle_of_type(handle, core.BNLowLevelILFunction) + else: + self.handle = core.BNCreateLowLevelILFunction() + + def __del__(self): + core.BNFreeLowLevelILFunction(self.handle) + + def __getattr__(self, name): + if name == "current_address": + return core.BNLowLevelILGetCurrentAddress(self.handle) + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if name == "current_address": + core.BNLowLevelILSetCurrentAddress(self.handle, value) + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["current_address"] + + def __len__(self): + return int(core.BNGetLowLevelILInstructionCount(self.handle)) + + def __getitem__(self, i): + if isinstance(i, slice) or isinstance(i, tuple): + raise IndexError, "expected integer instruction index" + if isinstance(i, LowLevelILExpr): + return LowLevelILInstruction(self, i.index) + if (i < 0) or (i >= len(self)): + raise IndexError, "index out of range" + return LowLevelILInstruction(self, core.BNGetLowLevelILIndexForInstruction(self.handle, i)) + + def __setitem__(self, i): + raise IndexError, "instruction modification not implemented" + + def expr(self, operation, a = 0, b = 0, c = 0, d = 0, size = 0, flags = None): + if isinstance(operation, str): + operation = BNLowLevelILOperation_by_name[operation] + if isinstance(flags, str): + flags = self.arch.get_flag_write_type_by_name(flags) + elif flags is None: + flags = 0 + return LowLevelILExpr(core.BNLowLevelILAddExpr(self.handle, operation, size, flags, a, b, c, d)) + + def append(self, expr): + return core.BNLowLevelILAddInstruction(self.handle, expr.index) + + def nop(self): + return self.expr(core.LLIL_NOP) + + def set_reg(self, size, reg, value, flags = 0): + if isinstance(reg, str): + reg = self.arch.regs[reg].index + return self.expr(core.LLIL_SET_REG, reg, value.index, size = size, flags = flags) + + def set_reg_split(self, size, hi, lo, value, flags = 0): + if isinstance(hi, str): + hi = self.arch.regs[hi].index + if isinstance(lo, str): + lo = self.arch.regs[lo].index + return self.expr(core.LLIL_SET_REG_SPLIT, hi, lo, value.index, size = size, flags = flags) + + def set_flag(self, flag, value): + return self.expr(core.LLIL_SET_FLAG, self.arch.get_flag_by_name(flag), value.index) + + def load(self, size, addr): + return self.expr(core.LLIL_LOAD, addr.index, size = size) + + def store(self, size, addr, value): + return self.expr(core.LLIL_STORE, addr.index, value.index, size = size) + + def push(self, size, value): + return self.expr(core.LLIL_PUSH, value.index, size = size) + + def pop(self, size): + return self.expr(core.LLIL_POP, size = size) + + def reg(self, size, reg): + if isinstance(reg, str): + reg = self.arch.regs[reg].index + return self.expr(core.LLIL_REG, reg, size = size) + + def const(self, size, value): + return self.expr(core.LLIL_CONST, value, size = size) + + def flag(self, reg): + return self.expr(core.LLIL_FLAG, self.arch.get_flag_by_name(reg)) + + def flag_bit(self, size, reg, bit): + return self.expr(core.LLIL_FLAG_BIT, self.arch.get_flag_by_name(reg), bit, size = size) + + def add(self, size, a, b, flags = None): + return self.expr(core.LLIL_ADD, a.index, b.index, size = size, flags = flags) + + def add_carry(self, size, a, b, flags = None): + return self.expr(core.LLIL_ADC, a.index, b.index, size = size, flags = flags) + + def sub(self, size, a, b, flags = None): + return self.expr(core.LLIL_SUB, a.index, b.index, size = size, flags = flags) + + def sub_borrow(self, size, a, b, flags = None): + return self.expr(core.LLIL_SBB, a.index, b.index, size = size, flags = flags) + + def and_expr(self, size, a, b, flags = None): + return self.expr(core.LLIL_AND, a.index, b.index, size = size, flags = flags) + + def or_expr(self, size, a, b, flags = None): + return self.expr(core.LLIL_OR, a.index, b.index, size = size, flags = flags) + + def xor_expr(self, size, a, b, flags = None): + return self.expr(core.LLIL_XOR, a.index, b.index, size = size, flags = flags) + + def shift_left(self, size, a, b, flags = None): + return self.expr(core.LLIL_LSL, a.index, b.index, size = size, flags = flags) + + def logical_shift_right(self, size, a, b, flags = None): + return self.expr(core.LLIL_LSR, a.index, b.index, size = size, flags = flags) + + def arith_shift_right(self, size, a, b, flags = None): + return self.expr(core.LLIL_ASR, a.index, b.index, size = size, flags = flags) + + def rotate_left(self, size, a, b, flags = None): + return self.expr(core.LLIL_ROL, a.index, b.index, size = size, flags = flags) + + def rotate_left_carry(self, size, a, b, flags = None): + return self.expr(core.LLIL_RLC, a.index, b.index, size = size, flags = flags) + + def rotate_right(self, size, a, b, flags = None): + return self.expr(core.LLIL_ROR, a.index, b.index, size = size, flags = flags) + + def rotate_right_carry(self, size, a, b, flags = None): + return self.expr(core.LLIL_RRC, a.index, b.index, size = size, flags = flags) + + def mult(self, size, a, b, flags = None): + return self.expr(core.LLIL_MUL, a.index, b.index, size = size, flags = flags) + + def mult_double_prec_signed(self, size, a, b, flags = None): + return self.expr(core.LLIL_MULS_DP, a.index, b.index, size = size, flags = flags) + + def mult_double_prec_unsigned(self, size, a, b, flags = None): + return self.expr(core.LLIL_MULU_DP, a.index, b.index, size = size, flags = flags) + + def div_signed(self, size, a, b, flags = None): + return self.expr(core.LLIL_DIVS, a.index, b.index, size = size, flags = flags) + + def div_double_prec_signed(self, size, hi, lo, b, flags = None): + return self.expr(core.LLIL_DIVS_DP, hi.index, lo.index, b.index, size = size, flags = flags) + + def div_unsigned(self, size, a, b, flags = None): + return self.expr(core.LLIL_DIVS, a.index, b.index, size = size, flags = flags) + + def div_double_prec_unsigned(self, size, hi, lo, b, flags = None): + return self.expr(core.LLIL_DIVS_DP, hi.index, lo.index, b.index, size = size, flags = flags) + + def mod_signed(self, size, a, b, flags = None): + return self.expr(core.LLIL_MODS, a.index, b.index, size = size, flags = flags) + + def mod_double_prec_signed(self, size, hi, lo, b, flags = None): + return self.expr(core.LLIL_MODS_DP, hi.index, lo.index, b.index, size = size, flags = flags) + + def mod_unsigned(self, size, a, b, flags = None): + return self.expr(core.LLIL_MODS, a.index, b.index, size = size, flags = flags) + + def mod_double_prec_unsigned(self, size, hi, lo, b, flags = None): + return self.expr(core.LLIL_MODS_DP, hi.index, lo.index, b.index, size = size, flags = flags) + + def neg_expr(self, size, value, flags = None): + return self.expr(core.LLIL_NEG, value.index, size = size, flags = flags) + + def not_expr(self, size, value, flags = None): + return self.expr(core.LLIL_NOT, value.index, size = size, flags = flags) + + def sign_extend(self, size, value): + return self.expr(core.LLIL_SX, value.index, size = size) + + def zero_extend(self, size, value): + return self.expr(core.LLIL_ZX, value.index, size = size) + + def jump(self, dest): + return self.expr(core.LLIL_JUMP, dest.index) + + def call(self, dest): + return self.expr(core.LLIL_CALL, dest.index) + + def ret(self, dest): + return self.expr(core.LLIL_RET, dest.index) + + def no_ret(self, dest): + return self.expr(core.LLIL_NORET, dest.index) + + def flag_condition(self, cond): + if isinstance(cond, str): + cond = BNLowLevelILFlagCondition_by_name[cond] + return self.expr(core.LLIL_FLAG_COND, cond) + + def compare_equal(self, size, a, b): + return self.expr(core.LLIL_CMP_E, a.index, b.index, size = size) + + def compare_not_equal(self, size, a, b): + return self.expr(core.LLIL_CMP_NE, a.index, b.index, size = size) + + def compare_signed_less_than(self, size, a, b): + return self.expr(core.LLIL_CMP_SLT, a.index, b.index, size = size) + + def compare_unsigned_less_than(self, size, a, b): + return self.expr(core.LLIL_CMP_ULT, a.index, b.index, size = size) + + def compare_signed_less_equal(self, size, a, b): + return self.expr(core.LLIL_CMP_SLE, a.index, b.index, size = size) + + def compare_unsigned_less_equal(self, size, a, b): + return self.expr(core.LLIL_CMP_ULE, a.index, b.index, size = size) + + def compare_signed_greater_equal(self, size, a, b): + return self.expr(core.LLIL_CMP_SGE, a.index, b.index, size = size) + + def compare_unsigned_greater_equal(self, size, a, b): + return self.expr(core.LLIL_CMP_UGE, a.index, b.index, size = size) + + def compare_signed_greater_than(self, size, a, b): + return self.expr(core.LLIL_CMP_SGT, a.index, b.index, size = size) + + def compare_unsigned_greater_than(self, size, a, b): + return self.expr(core.LLIL_CMP_UGT, a.index, b.index, size = size) + + def test_bit(self, size, a, b): + return self.expr(core.LLIL_TEST_BIT, a.index, b.index, size = size) + + def system_call(self): + return self.expr(core.LLIL_SYSCALL) + + def breakpoint(self): + return self.expr(core.LLIL_BP) + + def trap(self, value): + return self.expr(core.LLIL_TRAP, value) + + def undefined(self): + return self.expr(core.LLIL_UNDEF) + + def unimplemented(self): + return self.expr(core.LLIL_UNIMPL) + + def unimplemented_memory_ref(self, size, addr): + return self.expr(core.LLIL_UNIMPL_MEM, addr.index, size = size) + + def goto(self, label): + return LowLevelILExpr(core.BNLowLevelILGoto(self.handle, label.handle)) + + def if_expr(self, operand, t, f): + return LowLevelILExpr(core.BNLowLevelILIf(self.handle, operand.index, t.handle, f.handle)) + + def mark_label(self, label): + core.BNLowLevelILMarkLabel(self.handle, label.handle) + + def operand(self, n, expr): + core.BNLowLevelILSetExprSourceOperand(self.handle, expr.index, n) + return expr + + def finalize(self): + core.BNFinalizeLowLevelILFunction(self.handle) + + def add_label_for_address(self, arch, addr): + if arch is not None: + arch = arch.handle + core.BNAddLowLevelILLabelForAddress(self.handle, arch, addr) + + def get_label_for_address(self, arch, addr): + if arch is not None: + arch = arch.handle + label = core.BNGetLowLevelILLabelForAddress(self.handle, arch, addr) + if label is None: + return None + return LowLevelILLabel(label) + +class TypeParserResult: + def __init__(self, types, variables, functions): + self.types = types + self.variables = variables + self.functions = functions + + def __repr__(self): + return "{types: %s, variables: %s, functions: %s}" % (self.types, self.variables, self.functions) + +class _TransformMetaClass(type): + def __getattr__(cls, name): + if name == "list": + _init_plugins() + count = ctypes.c_ulonglong() + xforms = core.BNGetTransformTypeList(count) + result = [] + for i in xrange(0, count.value): + result.append(Transform(xforms[i])) + core.BNFreeTransformTypeList(xforms) + return result + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(cls, name, value): + if (name == "list"): + raise AttributeError, "attribute '%s' is read only" % name + else: + type.__setattr__(cls, name, value) + + def __dir__(self): + return dir(self.__class__) + ["list"] + + def __getitem__(cls, name): + _init_plugins() + xform = core.BNGetTransformByName(name) + if xform is None: + raise KeyError, "'%s' is not a valid transform" % str(name) + return Transform(xform) + + def register(cls): + _init_plugins() + if cls.name is None: + raise ValueError, "transform 'name' is not defined" + if cls.long_name is None: + cls.long_name = cls.name + if cls.transform_type is None: + raise ValueError, "transform 'transform_type' is not defined" + if cls.group is None: + cls.group = "" + xform = cls(None) + cls._registered_cb = xform._cb + xform.handle = core.BNRegisterTransformType(cls.transform_type, cls.name, cls.long_name, cls.group, xform._cb) + +class TransformParameter: + def __init__(self, name, long_name = None, fixed_length = 0): + self.name = name + if long_name is None: + self.long_name = name + else: + self.long_name = long_name + self.fixed_length = fixed_length + +class Transform: + transform_type = None + name = None + long_name = None + group = None + parameters = [] + _registered_cb = None + __metaclass__ = _TransformMetaClass + + def __init__(self, handle): + if handle is None: + self._cb = core.BNCustomTransform() + self._cb.context = 0 + self._cb.getParameters = self._cb.getParameters.__class__(self._get_parameters) + self._cb.freeParameters = self._cb.freeParameters.__class__(self._free_parameters) + self._cb.decode = self._cb.decode.__class__(self._decode) + self._cb.encode = self._cb.encode.__class__(self._encode) + self._pending_param_lists = {} + self.type = self.__class__.transform_type + if not isinstance(self.type, str): + self.type = core.BNTransformType_names[self.type] + self.name = self.__class__.name + self.long_name = self.__class__.long_name + self.group = self.__class__.group + self.parameters = self.__class__.parameters + else: + self.handle = handle + self.type = core.BNTransformType_names[core.BNGetTransformType(self.handle)] + self.name = core.BNGetTransformName(self.handle) + self.long_name = core.BNGetTransformLongName(self.handle) + self.group = core.BNGetTransformGroup(self.handle) + count = ctypes.c_ulonglong() + params = core.BNGetTransformParameterList(self.handle, count) + self.parameters = [] + for i in xrange(0, count.value): + self.parameters.append(TransformParameter(params[i].name, params[i].longName, params[i].fixedLength)) + core.BNFreeTransformParameterList(params, count.value) + + def __repr__(self): + return "<transform: %s>" % self.name + + def _get_parameters(self, ctxt, count): + try: + count[0] = len(self.parameters) + param_buf = (core.BNTransformParameterInfo * len(self.parameters))() + for i in xrange(0, len(self.parameters)): + param_buf[i].name = self.parameters[i].name + param_buf[i].longName = self.parameters[i].long_name + param_buf[i].fixedLength = self.parameters[i].fixed_length + result = ctypes.cast(param_buf, ctypes.c_void_p) + self._pending_param_lists[result.value] = (result, param_buf) + return result.value + except: + log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _free_parameters(self, params, count): + try: + buf = ctypes.cast(params, ctypes.c_void_p) + if buf.value not in self._pending_param_lists: + raise ValueError, "freeing parameter list that wasn't allocated" + del self._pending_param_lists[buf.value] + except: + log_error(traceback.format_exc()) + + def _decode(self, ctxt, input_buf, output_buf, params, count): + try: + input_obj = DataBuffer(handle = core.BNDuplicateDataBuffer(input_buf)) + param_map = {} + for i in xrange(0, count): + data = DataBuffer(handle = core.BNDuplicateDataBuffer(params[i].value)) + param_map[params[i].name] = str(data) + result = self.perform_decode(str(input_obj), param_map) + if result is None: + return False + result = str(result) + core.BNSetDataBufferContents(output_buf, result, len(result)) + return True + except: + log_error(traceback.format_exc()) + return False + + def _encode(self, ctxt, input_buf, output_buf, params, count): + try: + input_obj = DataBuffer(handle = core.BNDuplicateDataBuffer(input_buf)) + param_map = {} + for i in xrange(0, count): + data = DataBuffer(handle = core.BNDuplicateDataBuffer(params[i].value)) + param_map[params[i].name] = str(data) + result = self.perform_encode(str(input_obj), param_map) + if result is None: + return False + result = str(result) + core.BNSetDataBufferContents(output_buf, result, len(result)) + return True + except: + log_error(traceback.format_exc()) + return False + + def perform_decode(self, data, params): + if self.type == "InvertingTransform": + return perform_encode(data, params) + return None + + def perform_encode(self, data, params): + return None + + def decode(self, input_buf, params = {}): + input_buf = DataBuffer(input_buf) + output_buf = DataBuffer() + keys = params.keys() + param_buf = (core.BNTransformParameter * len(keys))() + param_data = [] + for i in xrange(0, len(keys)): + data = DataBuffer(params[keys[i]]) + param_buf[i].name = keys[i] + param_buf[i].value = data.handle + if not core.BNDecode(self.handle, input_buf.handle, output_buf.handle, param_buf, len(keys)): + return None + return str(output_buf) + + def encode(self, input_buf, params = {}): + input_buf = DataBuffer(input_buf) + output_buf = DataBuffer() + keys = params.keys() + param_buf = (core.BNTransformParameter * len(keys))() + param_data = [] + for i in xrange(0, len(keys)): + data = DataBuffer(params[keys[i]]) + param_buf[i].name = keys[i] + param_buf[i].value = data.handle + if not core.BNEncode(self.handle, input_buf.handle, output_buf.handle, param_buf, len(keys)): + return None + return str(output_buf) + +class FunctionRecognizer: + _instance = None + + def __init__(self): + self._cb = core.BNFunctionRecognizer() + self._cb.context = 0 + self._cb.recognizeLowLevelIL = self._cb.recognizeLowLevelIL.__class__(self._recognize_low_level_il) + + @classmethod + def register_global(cls): + if cls._instance is None: + cls._instance = cls() + core.BNRegisterGlobalFunctionRecognizer(cls._instance._cb) + + @classmethod + def register_arch(cls, arch): + if cls._instance is None: + cls._instance = cls() + core.BNRegisterArchitectureFunctionRecognizer(arch.handle, cls._instance._cb) + + def _recognize_low_level_il(self, ctxt, data, func, il): + try: + file_metadata = FileMetadata(handle = core.BNGetFileForView(data)) + view = BinaryView(file_metadata, handle = core.BNNewViewReference(data)) + func = Function(view, handle = core.BNNewFunctionReference(func)) + il = LowLevelILFunction(func.arch, handle = core.BNNewLowLevelILFunctionReference(il)) + return self.recognize_low_level_il(view, func, il) + except: + log_error(traceback.format_exc()) + return False + + def recognize_low_level_il(self, data, func, il): + return False + +class _UpdateChannelMetaClass(type): + def __getattr__(cls, name): + if name == "list": + _init_plugins() + count = ctypes.c_ulonglong() + errors = ctypes.c_char_p() + channels = core.BNGetUpdateChannels(count, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError, error_str + result = [] + for i in xrange(0, count.value): + result.append(UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion)) + core.BNFreeUpdateChannelList(channels, count.value) + return result + elif name == "active": + return core.BNGetActiveUpdateChannel() + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(cls, name, value): + if (name == "list"): + raise AttributeError, "attribute '%s' is read only" % name + elif name == "active": + return core.BNSetActiveUpdateChannel(value) + else: + type.__setattr__(cls, name, value) + + def __dir__(self): + return dir(self.__class__) + ["list","active"] + + def __getitem__(cls, name): + _init_plugins() + count = ctypes.c_ulonglong() + errors = ctypes.c_char_p() + channels = core.BNGetUpdateChannels(count, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError, error_str + result = None + for i in xrange(0, count.value): + if channels[i].name == str(name): + result = UpdateChannel(channels[i].name, channels[i].description, channels[i].latestVersion) + break + core.BNFreeUpdateChannelList(channels, count.value) + if result is None: + raise KeyError, "'%s' is not a valid channel" % str(name) + return result + +class UpdateProgressCallback: + def __init__(self, func): + self.cb = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(self.callback) + self.func = func + + def callback(self, ctxt, progress, total): + try: + if self.func is not None: + return self.func(progress, total) + return True + except: + log_error(traceback.format_exc()) + +class UpdateChannel: + __metaclass__ = _UpdateChannelMetaClass + + def __init__(self, name, desc, ver): + self.name = name + self.description = desc + self.latest_version_num = ver + + def __getattr__(self, name): + if name == "versions": + count = ctypes.c_ulonglong() + errors = ctypes.c_char_p() + versions = core.BNGetUpdateChannelVersions(self.name, count, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError, error_str + result = [] + for i in xrange(0, count.value): + result.append(UpdateVersion(self, versions[i].version, versions[i].notes, versions[i].time)) + core.BNFreeUpdateChannelVersionList(versions, count.value) + return result + elif name == "latest_version": + count = ctypes.c_ulonglong() + errors = ctypes.c_char_p() + versions = core.BNGetUpdateChannelVersions(self.name, count, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError, error_str + result = None + for i in xrange(0, count.value): + if versions[i].version == self.latest_version_num: + result = UpdateVersion(self, versions[i].version, versions[i].notes, versions[i].time) + break + core.BNFreeUpdateChannelVersionList(versions, count.value) + return result + elif name == "updates_available": + errors = ctypes.c_char_p() + result = core.BNAreUpdatesAvailable(self.name, errors) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError, error_str + return result + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if (name == "versions") or (name == "latest_version"): + raise AttributeError, "attribute '%s' is read only" % name + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["versions","latest_version", "updates_available"] + + def __repr__(self): + return "<channel: %s>" % self.name + + def __str__(self): + return self.name + + def update_to_latest(self, progress = None): + cb = UpdateProgressCallback(progress) + errors = ctypes.c_char_p() + result = core.BNUpdateToLatestVersion(self.name, errors, cb.cb, None) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError, error_str + return core.BNUpdateResult_names[result] + +class UpdateVersion: + def __init__(self, channel, ver, notes, t): + self.channel = channel + self.version = ver + self.notes = notes + self.time = t + + def __repr__(self): + return "<version: %s>" % self.version + + def __str__(self): + return self.version + + def update(self, progress = None): + cb = UpdateProgressCallback(progress) + errors = ctypes.c_char_p() + result = core.BNUpdateToVersion(self.channel.name, self.version, errors, cb.cb, None) + if errors: + error_str = errors.value + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + raise IOError, error_str + return core.BNUpdateResult_names[result] + +class PluginCommandContext: + def __init__(self, view): + self.view = view + self.address = 0 + self.length = 0 + self.function = None + +class _PluginCommandMetaClass(type): + def __getattr__(cls, name): + if name == "list": + _init_plugins() + count = ctypes.c_ulonglong() + commands = core.BNGetAllPluginCommands(count) + result = [] + for i in xrange(0, count.value): + result.append(PluginCommand(commands[i])) + core.BNFreePluginCommandList(commands) + return result + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(cls, name, value): + if (name == "list"): + raise AttributeError, "attribute '%s' is read only" % name + else: + type.__setattr__(cls, name, value) + + def __dir__(self): + return dir(self.__class__) + ["list"] + +class PluginCommand: + _registered_commands = [] + __metaclass__ = _PluginCommandMetaClass + + def __init__(self, cmd): + self.command = core.BNPluginCommand() + ctypes.memmove(ctypes.byref(self.command), ctypes.byref(cmd), ctypes.sizeof(core.BNPluginCommand)) + self.name = str(cmd.name) + self.description = str(cmd.description) + self.type = core.BNPluginCommandType_names[cmd.type] + + @classmethod + def _default_action(cls, view, action): + try: + file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = BinaryView(file_metadata, handle = core.BNNewViewReference(view)) + action(view_obj) + except: + log_error(traceback.format_exc()) + + @classmethod + def _address_action(cls, view, addr, action): + try: + file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = BinaryView(file_metadata, handle = core.BNNewViewReference(view)) + action(view_obj, addr) + except: + log_error(traceback.format_exc()) + + @classmethod + def _range_action(cls, view, addr, length, action): + try: + file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = BinaryView(file_metadata, handle = core.BNNewViewReference(view)) + action(view_obj, addr, length) + except: + log_error(traceback.format_exc()) + + @classmethod + def _function_action(cls, view, func, action): + try: + file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = BinaryView(file_metadata, handle = core.BNNewViewReference(view)) + func_obj = Function(view_obj, core.BNNewFunctionReference(func)) + action(view_obj, func_obj) + except: + log_error(traceback.format_exc()) + + @classmethod + def _default_is_valid(cls, view, is_valid): + try: + if is_valid is None: + return True + file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = BinaryView(file_metadata, handle = core.BNNewViewReference(view)) + return is_valid(view_obj) + except: + log_error(traceback.format_exc()) + return False + + @classmethod + def _address_is_valid(cls, view, addr, is_valid): + try: + if is_valid is None: + return True + file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = BinaryView(file_metadata, handle = core.BNNewViewReference(view)) + return is_valid(view_obj, addr) + except: + log_error(traceback.format_exc()) + return False + + @classmethod + def _range_is_valid(cls, view, addr, length, is_valid): + try: + if is_valid is None: + return True + file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = BinaryView(file_metadata, handle = core.BNNewViewReference(view)) + return is_valid(view_obj, addr, length) + except: + log_error(traceback.format_exc()) + return False + + @classmethod + def _function_is_valid(cls, view, func, is_valid): + try: + if is_valid is None: + return True + file_metadata = FileMetadata(handle = core.BNGetFileForView(view)) + view_obj = BinaryView(file_metadata, handle = core.BNNewViewReference(view)) + func_obj = Function(view_obj, core.BNNewFunctionReference(func)) + return is_valid(view_obj, func_obj) + except: + log_error(traceback.format_exc()) + return False + + @classmethod + def register(cls, name, description, action, is_valid = None): + _init_plugins() + action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView))(lambda ctxt, view: cls._default_action(view, action)) + is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView))(lambda ctxt, view: cls._default_is_valid(view, is_valid)) + cls._registered_commands.append((action_obj, is_valid_obj)) + core.BNRegisterPluginCommand(name, description, action_obj, is_valid_obj, None) + + @classmethod + def register_for_address(cls, name, description, action, is_valid = None): + _init_plugins() + action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong)(lambda ctxt, view, addr: cls._address_action(view, addr, action)) + is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong)(lambda ctxt, view, addr: cls._address_is_valid(view, addr, is_valid)) + cls._registered_commands.append((action_obj, is_valid_obj)) + core.BNRegisterPluginCommandForAddress(name, description, action_obj, is_valid_obj, None) + + @classmethod + def register_for_range(cls, name, description, action, is_valid = None): + _init_plugins() + action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, view, addr, length: cls._range_action(view, addr, length, action)) + is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, view, addr, length: cls._range_is_valid(view, addr, length, is_valid)) + cls._registered_commands.append((action_obj, is_valid_obj)) + core.BNRegisterPluginCommandForRange(name, description, action_obj, is_valid_obj, None) + + @classmethod + def register_for_function(cls, name, description, action, is_valid = None): + _init_plugins() + action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNFunction))(lambda ctxt, view, func: cls._function_action(view, func, action)) + is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.POINTER(core.BNBinaryView), ctypes.POINTER(core.BNFunction))(lambda ctxt, view, func: cls._function_is_valid(view, func, is_valid)) + cls._registered_commands.append((action_obj, is_valid_obj)) + core.BNRegisterPluginCommandForFunction(name, description, action_obj, is_valid_obj, None) + + @classmethod + def get_valid_list(cls, context): + commands = cls.list + result = [] + for cmd in commands: + if cmd.is_valid(context): + result.append(cmd) + return result + + def is_valid(self, context): + if context.view is None: + return False + if self.command.type == core.DefaultPluginCommand: + if not self.command.defaultIsValid: + return True + return self.command.defaultIsValid(self.command.context, context.view.handle) + elif self.command.type == core.AddressPluginCommand: + if not self.command.addressIsValid: + return True + return self.command.addressIsValid(self.command.context, context.view.handle, context.address) + elif self.command.type == core.RangePluginCommand: + if context.length == 0: + return False + if not self.command.rangeIsValid: + return True + return self.command.rangeIsValid(self.command.context, context.view.handle, context.address, context.length) + elif self.command.type == core.FunctionPluginCommand: + if context.function is None: + return False + if not self.command.functionIsValid: + return True + return self.command.functionIsValid(self.command.context, context.view.handle, context.function.handle) + return False + + def execute(self, context): + if not self.is_valid(context): + return + if self.command.type == core.DefaultPluginCommand: + self.command.defaultCommand(self.command.context, context.view.handle) + elif self.command.type == core.AddressPluginCommand: + self.command.addressCommand(self.command.context, context.view.handle, context.address) + elif self.command.type == core.RangePluginCommand: + self.command.rangeCommand(self.command.context, context.view.handle, context.address, context.length) + elif self.command.type == core.FunctionPluginCommand: + self.command.functionCommand(self.command.context, context.view.handle, context.function.handle) + + def __repr__(self): + return "<PluginCommand: %s>" % self.name + +class CallingConvention: + name = None + caller_saved_regs = [] + int_arg_regs = [] + float_arg_regs = [] + arg_regs_share_index = False + stack_reserved_for_arg_regs = False + int_return_reg = None + high_int_return_reg = None + float_return_reg = None + + _registered_calling_conventions = [] + + def __init__(self, arch, handle = None): + if handle is None: + self.arch = arch + self._pending_reg_lists = {} + self._cb = core.BNCustomCallingConvention() + self._cb.context = 0 + self._cb.getCallerSavedRegisters = self._cb.getCallerSavedRegisters.__class__(self._get_caller_saved_regs) + self._cb.getIntegerArgumentRegisters = self._cb.getIntegerArgumentRegisters.__class__(self._get_int_arg_regs) + self._cb.getFloatArgumentRegisters = self._cb.getFloatArgumentRegisters.__class__(self._get_float_arg_regs) + self._cb.freeRegisterList = self._cb.freeRegisterList.__class__(self._free_register_list) + self._cb.areArgumentRegistersSharedIndex = self._cb.areArgumentRegistersSharedIndex.__class__(self._arg_regs_share_index) + self._cb.isStackReservedForArgumentRegisters = self._cb.isStackReservedForArgumentRegisters.__class__(self._stack_reserved_for_arg_regs) + self._cb.getIntegerReturnValueRegister = self._cb.getIntegerReturnValueRegister.__class__(self._get_int_return_reg) + self._cb.getHighIntegerReturnValueRegister = self._cb.getHighIntegerReturnValueRegister.__class__(self._get_high_int_return_reg) + self._cb.getFloatReturnValueRegister = self._cb.getFloatReturnValueRegister.__class__(self._get_float_return_reg) + self.handle = core.BNCreateCallingConvention(arch.handle, self.__class__.name, self._cb) + self.__class__._registered_calling_conventions.append(self) + else: + self.handle = handle + self.arch = Architecture(core.BNGetCallingConventionArchitecture(self.handle)) + self.__dict__["name"] = core.BNGetCallingConventionName(self.handle) + self.__dict__["arg_regs_share_index"] = core.BNAreArgumentRegistersSharedIndex(self.handle) + self.__dict__["stack_reserved_for_arg_regs"] = core.BNIsStackReservedForArgumentRegisters(self.handle) + + count = ctypes.c_ulonglong() + regs = core.BNGetCallerSavedRegisters(self.handle, count) + result = [] + arch = self.arch + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs, count.value) + self.__dict__["caller_saved_regs"] = result + + count = ctypes.c_ulonglong() + regs = core.BNGetIntegerArgumentRegisters(self.handle, count) + result = [] + arch = self.arch + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs, count.value) + self.__dict__["int_arg_regs"] = result + + count = ctypes.c_ulonglong() + regs = core.BNGetFloatArgumentRegisters(self.handle, count) + result = [] + arch = self.arch + for i in xrange(0, count.value): + result.append(arch.get_reg_name(regs[i])) + core.BNFreeRegisterList(regs, count.value) + self.__dict__["float_arg_regs"] = result + + reg = core.BNGetIntegerReturnValueRegister(self.handle) + if reg == 0xffffffff: + self.__dict__["int_return_reg"] = None + else: + self.__dict__["int_return_reg"] = self.arch.get_reg_name(reg) + + reg = core.BNGetHighIntegerReturnValueRegister(self.handle) + if reg == 0xffffffff: + self.__dict__["high_int_return_reg"] = None + else: + self.__dict__["high_int_return_reg"] = self.arch.get_reg_name(reg) + + reg = core.BNGetFloatReturnValueRegister(self.handle) + if reg == 0xffffffff: + self.__dict__["float_return_reg"] = None + else: + self.__dict__["float_return_reg"] = self.arch.get_reg_name(reg) + + def __del__(self): + core.BNFreeCallingConvention(self.handle) + + def _get_caller_saved_regs(self, ctxt, count): + try: + regs = self.__class__.caller_saved_regs + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = self.arch.regs[regs[i]].index + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except: + log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_int_arg_regs(self, ctxt, count): + try: + regs = self.__class__.int_arg_regs + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = self.arch.regs[regs[i]].index + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except: + log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _get_float_arg_regs(self, ctxt, count): + try: + regs = self.__class__.float_arg_regs + count[0] = len(regs) + reg_buf = (ctypes.c_uint * len(regs))() + for i in xrange(0, len(regs)): + reg_buf[i] = self.arch.regs[regs[i]].index + result = ctypes.cast(reg_buf, ctypes.c_void_p) + self._pending_reg_lists[result.value] = (result, reg_buf) + return result.value + except: + log_error(traceback.format_exc()) + count[0] = 0 + return None + + def _free_register_list(self, ctxt, regs): + try: + buf = ctypes.cast(regs, ctypes.c_void_p) + if buf.value not in self._pending_reg_lists: + raise ValueError, "freeing register list that wasn't allocated" + del self._pending_reg_lists[buf.value] + except: + log_error(traceback.format_exc()) + + def _arg_regs_share_index(self, ctxt): + try: + return self.__class__.arg_regs_share_index + except: + log_error(traceback.format_exc()) + return False + + def _stack_reserved_for_arg_regs(self, ctxt): + try: + return self.__class__.stack_reserved_for_arg_regs + except: + log_error(traceback.format_exc()) + return False + + def _get_int_return_reg(self, ctxt): + try: + return self.arch.regs[self.__class__.int_return_reg].index + except: + log_error(traceback.format_exc()) + return False + + def _get_high_int_return_reg(self, ctxt): + try: + if self.__class__.high_int_return_reg is None: + return 0xffffffff + return self.arch.regs[self.__class__.high_int_return_reg].index + except: + log_error(traceback.format_exc()) + return False + + def _get_float_return_reg(self, ctxt): + try: + if self.__class__.float_return_reg is None: + return 0xffffffff + return self.arch.regs[self.__class__.float_int_return_reg].index + except: + log_error(traceback.format_exc()) + return False + + def __repr__(self): + return "<calling convention: %s %s>" % (self.arch.name, self.name) + + def __str__(self): + return self.name + +class _PlatformMetaClass(type): + def __getattr__(cls, name): + if name == "list": + _init_plugins() + count = ctypes.c_ulonglong() + platforms = core.BNGetPlatformList(count) + result = [] + for i in xrange(0, count.value): + result.append(Platform(None, core.BNNewPlatformReference(platforms[i]))) + core.BNFreePlatformList(platforms, count.value) + return result + elif name == "os_list": + _init_plugins() + count = ctypes.c_ulonglong() + platforms = core.BNGetPlatformOSList(count) + result = [] + for i in xrange(0, count.value): + result.append(str(platforms[i])) + core.BNFreePlatformOSList(platforms, count.value) + return result + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(cls, name, value): + if (name == "list") or (name == "os_list"): + raise AttributeError, "attribute '%s' is read only" % name + else: + type.__setattr__(cls, name, value) + + def __dir__(self): + return dir(self.__class__) + ["list","os_list"] + + def __getitem__(cls, value): + _init_plugins() + platform = core.BNGetPlatformByName(str(value)) + if platform is None: + raise KeyError, "'%s' is not a valid platform" % str(value) + return Platform(None, platform) + + def get_list(cls, os = None, arch = None): + _init_plugins() + count = ctypes.c_ulonglong() + if os is None: + platforms = core.BNGetPlatformList(count) + elif arch is None: + platforms = core.BNGetPlatformListByOS(os) + else: + platforms = core.BNGetPlatformListByArchitecture(os, arch.handle) + result = [] + for i in xrange(0, count.value): + result.append(Platform(None, core.BNNewPlatformReference(platforms[i]))) + core.BNFreePlatformList(platforms, count.value) + return result + +class Platform: + __metaclass__ = _PlatformMetaClass + name = None + + def __init__(self, arch, handle = None): + if handle is None: + self.arch = arch + self.handle = core.BNCreatePlatform(arch.handle, self.__class__.name) + else: + self.handle = handle + self.__dict__["name"] = core.BNGetPlatformName(self.handle) + self.arch = Architecture(core.BNGetPlatformArchitecture(self.handle)) + + def __del__(self): + core.BNFreePlatform(self.handle) + + def __getattr__(self, name): + if name == "default_calling_convention": + result = core.BNGetPlatformDefaultCallingConvention(self.handle) + if result is None: + return None + return CallingConvention(None, result) + elif name == "cdecl_calling_convention": + result = core.BNGetPlatformCdeclCallingConvention(self.handle) + if result is None: + return None + return CallingConvention(None, result) + elif name == "stdcall_calling_convention": + result = core.BNGetPlatformStdcallCallingConvention(self.handle) + if result is None: + return None + return CallingConvention(None, result) + elif name == "fastcall_calling_convention": + result = core.BNGetPlatformFastcallCallingConvention(self.handle) + if result is None: + return None + return CallingConvention(None, result) + elif name == "system_call_convention": + result = core.BNGetPlatformSystemCallConvention(self.handle) + if result is None: + return None + return CallingConvention(None, result) + elif name == "calling_conventions": + count = ctypes.c_ulonglong() + cc = core.BNGetPlatformCallingConventions(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(CallingConvention(None, core.BNNewCallingConventionReference(cc[i]))) + core.BNFreeCallingConventionList(cc, count.value) + return result + raise AttributeError, "no attribute '%s'" % name + + def __setattr__(self, name, value): + if name == "default_calling_convention": + core.BNRegisterPlatformDefaultCallingConvention(self.handle, value.handle) + elif name == "cdecl_calling_convention": + core.BNRegisterPlatformCdeclCallingConvention(self.handle, value.handle) + elif name == "stdcall_calling_convention": + core.BNRegisterPlatformStdcallCallingConvention(self.handle, value.handle) + elif name == "fastcall_calling_convention": + core.BNRegisterPlatformFastcallCallingConvention(self.handle, value.handle) + elif name == "system_call_convention": + core.BNSetPlatformSystemCallConvention(self.handle, value.handle) + elif (name == "calling_conventions"): + raise AttributeError, "attribute '%s' is read only" % name + else: + self.__dict__[name] = value + + def __dir__(self): + return dir(self.__class__) + ["default_calling_convention", "cdecl_calling_convention", + "stdcall_calling_convention", "fastcall_calling_convention", "system_call_convention", "calling_conventions"] + + def __repr__(self): + return "<platform: %s>" % self.name + + def __str__(self): + return self.name + + def register(self, os): + core.BNRegisterPlatform(os, self.handle) + + def register_calling_convention(self, cc): + core.BNRegisterPlatformCallingConvention(self.handle, cc.handle) + +def LLIL_TEMP(n): + return n | 0x80000000 + +def LLIL_REG_IS_TEMP(n): + return (n & 0x80000000) != 0 + +def LLIL_GET_TEMP_REG_INDEX(n): + return n & 0x7fffffff + +def shutdown(): + core.BNShutdown() + +def log(level, text): + core.BNLog(level, "%s", str(text)) + +def log_debug(text): + core.BNLogDebug("%s", str(text)) + +def log_info(text): + core.BNLogInfo("%s", str(text)) + +def log_warn(text): + core.BNLogWarn("%s", str(text)) + +def log_error(text): + core.BNLogError("%s", str(text)) + +def log_alert(text): + core.BNLogAlert("%s", str(text)) + +def log_to_stdout(min_level): + core.BNLogToStdout(min_level) + +def log_to_stderr(min_level): + core.BNLogToStderr(min_level) + +def log_to_file(min_level, path, append = False): + core.BNLogToFile(min_level, str(path), append) + +def close_logs(): + core.BNCloseLogs() + +def escape_string(text): + return DataBuffer(text).escape() + +def unescape_string(text): + return DataBuffer(text).unescape() + +def preprocess_source(source, filename = None, include_dirs = []): + if filename is None: + filename = "input" + dir_buf = (ctypes.c_char_p * len(include_dirs))() + for i in xrange(0, len(include_dirs)): + dir_buf[i] = str(include_dirs[i]) + output = ctypes.c_char_p() + errors = ctypes.c_char_p() + result = core.BNPreprocessSource(source, filename, output, errors, dir_buf, len(include_dirs)) + output_str = output.value + error_str = errors.value + core.BNFreeString(ctypes.cast(output, ctypes.POINTER(ctypes.c_byte))) + core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) + if result: + return (output_str, error_str) + return (None, error_str) + +def are_auto_updates_enabled(): + return core.BNAreAutoUpdatesEnabled() + +def set_auto_updates_enabled(enabled): + core.BNSetAutoUpdatesEnabled(enabled) + +def get_time_since_last_update_check(): + return core.BNGetTimeSinceLastUpdateCheck() + +def updates_checked(): + core.BNUpdatesChecked() + +bundled_plugin_path = core.BNGetBundledPluginDirectory() +user_plugin_path = core.BNGetUserPluginDirectory() + +core_version = core.BNGetVersionString() +core_build_id = core.BNGetBuildId() + +# Ensure all enumeration constants from the core are exposed by this module +for name in core.all_enum_values: + globals()[name] = core.all_enum_values[name] diff --git a/python/examples/nes.py b/python/examples/nes.py new file mode 100644 index 00000000..bce3336b --- /dev/null +++ b/python/examples/nes.py @@ -0,0 +1,661 @@ +from binaryninja import * +import struct +import traceback +import os + +InstructionNames = [ + "brk", "ora", None, None, None, "ora", "asl", None, # 0x00 + "php", "ora", "asl@", None, None, "ora", "asl", None, # 0x08 + "bpl", "ora", None, None, None, "ora", "asl", None, # 0x10 + "clc", "ora", None, None, None, "ora", "asl", None, # 0x18 + "jsr", "and", None, None, "bit", "and", "rol", None, # 0x20 + "plp", "and", "rol@", None, "bit", "and", "rol", None, # 0x28 + "bmi", "and", None, None, None, "and", "rol", None, # 0x30 + "sec", "and", None, None, None, "and", "rol", None, # 0x38 + "rti", "eor", None, None, None, "eor", "lsr", None, # 0x40 + "pha", "eor", "lsr@", None, "jmp", "eor", "lsr", None, # 0x48 + "bvc", "eor", None, None, None, "eor", "lsr", None, # 0x50 + "cli", "eor", None, None, None, "eor", "lsr", None, # 0x58 + "rts", "adc", None, None, None, "adc", "ror", None, # 0x60 + "pla", "adc", "ror@", None, "jmp", "adc", "ror", None, # 0x68 + "bvs", "adc", None, None, None, "adc", "ror", None, # 0x70 + "sei", "adc", None, None, None, "adc", "ror", None, # 0x78 + None, "sta", None, None, "sty", "sta", "stx", None, # 0x80 + "dey", None, "txa", None, "sty", "sta", "stx", None, # 0x88 + "bcc", "sta", None, None, "sty", "sta", "stx", None, # 0x90 + "tya", "sta", "txs", None, None, "sta", None, None, # 0x98 + "ldy", "lda", "ldx", None, "ldy", "lda", "ldx", None, # 0xa0 + "tay", "lda", "tax", None, "ldy", "lda", "ldx", None, # 0xa8 + "bcs", "lda", None, None, "ldy", "lda", "ldx", None, # 0xb0 + "clv", "lda", "tsx", None, "ldy", "lda", "ldx", None, # 0xb8 + "cpy", "cmp", None, None, "cpy", "cmp", "dec", None, # 0xc0 + "iny", "cmp", "dex", None, "cpy", "cmp", "dec", None, # 0xc8 + "bne", "cmp", None, None, None, "cmp", "dec", None, # 0xd0 + "cld", "cmp", None, None, None, "cmp", "dec", None, # 0xd8 + "cpx", "sbc", None, None, "cpx", "sbc", "inc", None, # 0xe0 + "inx", "sbc", "nop", None, "cpx", "sbc", "inc", None, # 0xe8 + "beq", "sbc", None, None, None, "sbc", "inc", None, # 0xf0 + "sed", "sbc", None, None, None, "sbc", "inc", None # 0xf8 +] + +NONE = 0 +ABS = 1 +ABS_DEST = 2 +ABS_X = 3 +ABS_X_DEST = 4 +ABS_Y = 5 +ABS_Y_DEST = 6 +ACCUM = 7 +ADDR = 8 +IMMED = 9 +IND = 10 +IND_X = 11 +IND_X_DEST = 12 +IND_Y = 13 +IND_Y_DEST = 14 +REL = 15 +ZERO = 16 +ZERO_DEST = 17 +ZERO_X = 18 +ZERO_X_DEST = 19 +ZERO_Y = 20 +ZERO_Y_DEST = 21 +InstructionOperandTypes = [ + NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x00 + NONE, IMMED, ACCUM, NONE, NONE, ABS, ABS_DEST, NONE, # 0x08 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x10 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x18 + ADDR, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0x20 + NONE, IMMED, ACCUM, NONE, ABS, ABS, ABS_DEST, NONE, # 0x28 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x30 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x38 + NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x40 + NONE, IMMED, ACCUM, NONE, ADDR, ABS, ABS_DEST, NONE, # 0x48 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x50 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x58 + NONE, IND_X, NONE, NONE, NONE, ZERO, ZERO_DEST, NONE, # 0x60 + NONE, IMMED, ACCUM, NONE, IND, ABS, ABS_DEST, NONE, # 0x68 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0x70 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0x78 + NONE, IND_X_DEST, NONE, NONE, ZERO_DEST, ZERO_DEST, ZERO_DEST, NONE, # 0x80 + NONE, NONE, NONE, NONE, ABS_DEST, ABS_DEST, ABS_DEST, NONE, # 0x88 + REL, IND_Y_DEST, NONE, NONE, ZERO_X_DEST, ZERO_X_DEST, ZERO_Y_DEST, NONE, # 0x90 + NONE, ABS_Y_DEST, NONE, NONE, NONE, ABS_X_DEST, NONE, NONE, # 0x98 + IMMED, IND_X, IMMED, NONE, ZERO, ZERO, ZERO, NONE, # 0xa0 + NONE, IMMED, NONE, NONE, ABS, ABS, ABS, NONE, # 0xa8 + REL, IND_Y, NONE, NONE, ZERO_X, ZERO_X, ZERO_Y, NONE, # 0xb0 + NONE, ABS_Y, NONE, NONE, ABS_X, ABS_X, ABS_Y, NONE, # 0xb8 + IMMED, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0xc0 + NONE, IMMED, NONE, NONE, ABS, ABS, ABS_DEST, NONE, # 0xc8 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0xd0 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE, # 0xd8 + IMMED, IND_X, NONE, NONE, ZERO, ZERO, ZERO_DEST, NONE, # 0xe0 + NONE, IMMED, NONE, NONE, ABS, ABS, ABS_DEST, NONE, # 0xe8 + REL, IND_Y, NONE, NONE, NONE, ZERO_X, ZERO_X_DEST, NONE, # 0xf0 + NONE, ABS_Y, NONE, NONE, NONE, ABS_X, ABS_X_DEST, NONE # 0xf8 +] + +OperandLengths = [ + 0, # NONE + 2, # ABS + 2, # ABS_DEST + 2, # ABS_X + 2, # ABS_X_DEST + 2, # ABS_Y + 2, # ABS_Y_DEST + 0, # ACCUM + 2, # ADDR + 1, # IMMED + 2, # IND + 1, # IND_X + 1, # IND_X_DEST + 1, # IND_Y + 1, # IND_Y_DEST + 1, # REL + 1, # ZERO + 1, # ZREO_DEST + 1, # ZERO_X + 1, # ZERO_X_DEST + 1, # ZERO_Y + 1 # ZERO_Y_DEST +] + +OperandTokens = [ + lambda value: [], # NONE + lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value)], # ABS + lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value)], # ABS_DEST + lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x")], # ABS_X + lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x")], # ABS_X_DEST + lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "y")], # ABS_Y + lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "y")], # ABS_Y_DEST + lambda value: [InstructionTextToken(RegisterToken, "a")], # ACCUM + lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value)], # ADDR + lambda value: [InstructionTextToken(TextToken, "#"), InstructionTextToken(IntegerToken, "$%.2x" % value, value)], # IMMED + lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value), + InstructionTextToken(TextToken, "]")], # IND + lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x"), + InstructionTextToken(TextToken, "]")], # IND_X + lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x"), + InstructionTextToken(TextToken, "]")], # IND_X_DEST + lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(TextToken, "], "), InstructionTextToken(RegisterToken, "y")], # IND_Y + lambda value: [InstructionTextToken(TextToken, "["), InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(TextToken, "], "), InstructionTextToken(RegisterToken, "y")], # IND_Y_DEST + lambda value: [InstructionTextToken(PossibleAddressToken, "$%.4x" % value, value)], # REL + lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value)], # ZERO + lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value)], # ZERO_DEST + lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x")], # ZERO_X + lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "x")], # ZERO_X_DEST + lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "y")], # ZERO_Y + lambda value: [InstructionTextToken(PossibleAddressToken, "$%.2x" % value, value), + InstructionTextToken(TextToken, ", "), InstructionTextToken(RegisterToken, "y")] # ZERO_Y_DEST +] + +def indirect_load(il, value): + if (value & 0xff) == 0xff: + lo_addr = il.const(2, value) + hi_addr = il.const(2, (value & 0xff00) | ((value + 1) & 0xff)) + lo = il.zero_extend(2, il.load(1, lo_addr)) + hi = il.shift_left(2, il.zero_extend(2, il.load(1, hi_addr)), il.const(2, 8)) + return il.or_expr(2, lo, hi) + return il.load(2, il.const(2, value)) + +def load_zero_page_16(il, value): + if il[value].operation == "LLIL_CONST": + if il[value].value == 0xff: + lo = il.zero_extend(2, il.load(1, il.const(2, 0xff))) + hi = il.shift_left(2, il.zero_extend(2, il.load(1, il.const(2, 0)), il.const(2, 8))) + return il.or_expr(2, lo, hi) + return il.load(2, il.const(2, il[value].value)) + il.append(il.set_reg(1, LLIL_TEMP(0), value)) + value = il.reg(1, LLIL_TEMP(0)) + lo_addr = value + hi_addr = il.add(1, value, il.const(1, 1)) + lo = il.zero_extend(2, il.load(1, lo_addr)) + hi = il.shift_left(2, il.zero_extend(2, il.load(1, hi_addr)), il.const(2, 8)) + return il.or_expr(2, lo, hi) + +OperandIL = [ + lambda il, value: None, # NONE + lambda il, value: il.load(1, il.const(2, value)), # ABS + lambda il, value: il.const(2, value), # ABS_DEST + lambda il, value: il.load(1, il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "x")))), # ABS_X + lambda il, value: il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "x"))), # ABS_X_DEST + lambda il, value: il.load(1, il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "y")))), # ABS_Y + lambda il, value: il.add(2, il.const(2, value), il.zero_extend(2, il.reg(1, "y"))), # ABS_Y_DEST + lambda il, value: il.reg(1, "a"), # ACCUM + lambda il, value: il.const(2, value), # ADDR + lambda il, value: il.const(1, value), # IMMED + lambda il, value: indirect_load(il, value), # IND + lambda il, value: il.load(1, load_zero_page_16(il, il.add(1, il.const(1, value), il.reg(1, "x")))), # IND_X + lambda il, value: load_zero_page_16(il, il.add(1, il.const(1, value), il.reg(1, "x"))), # IND_X_DEST + lambda il, value: il.load(1, il.add(2, load_zero_page_16(il, il.const(1, value)), il.reg(1, "y"))), # IND_Y + lambda il, value: il.add(2, load_zero_page_16(il, il.const(1, value)), il.reg(1, "y")), # IND_Y_DEST + lambda il, value: il.const(2, value), # REL + lambda il, value: il.load(1, il.const(2, value)), # ZERO + lambda il, value: il.const(2, value), # ZERO_DEST + lambda il, value: il.load(1, il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "x")))), # ZERO_X + lambda il, value: il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "x"))), # ZERO_X_DEST + lambda il, value: il.load(1, il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "y")))), # ZERO_Y + lambda il, value: il.zero_extend(2, il.add(1, il.const(1, value), il.reg(1, "y"))) # ZERO_Y_DEST +] + +def cond_branch(il, cond, dest): + t = None + if il[dest].operation == "LLIL_CONST": + t = il.get_label_for_address(Architecture['6502'].standalone_platform, il[dest].value) + if t is None: + t = LowLevelILLabel() + indirect = True + else: + indirect = False + f = LowLevelILLabel() + il.append(il.if_expr(cond, t, f)) + if indirect: + il.mark_label(t) + il.append(il.jump(dest)) + il.mark_label(f) + return None + +def jump(il, dest): + label = None + if il[dest].operation == "LLIL_CONST": + label = il.get_label_for_address(Architecture['6502'].standalone_platform, il[dest].value) + if label is None: + il.append(il.jump(dest)) + else: + il.append(il.goto(label)) + return None + +def get_p_value(il): + c = il.flag_bit(1, "c", 0) + z = il.flag_bit(1, "z", 1) + i = il.flag_bit(1, "i", 2) + d = il.flag_bit(1, "d", 3) + b = il.flag_bit(1, "b", 4) + v = il.flag_bit(1, "v", 6) + s = il.flag_bit(1, "s", 7) + return il.or_expr(1, il.or_expr(1, il.or_expr(1, il.or_expr(1, il.or_expr(1, + il.or_expr(1, c, z), i), d), b), v), s) + +def set_p_value(il, value): + il.append(il.set_reg(1, LLIL_TEMP(0), value)) + il.append(il.set_flag("c", il.test_bit(1, il.reg(1, LLIL_TEMP(0)), il.const(1, 0x01)))) + il.append(il.set_flag("z", il.test_bit(1, il.reg(1, LLIL_TEMP(0)), il.const(1, 0x02)))) + il.append(il.set_flag("i", il.test_bit(1, il.reg(1, LLIL_TEMP(0)), il.const(1, 0x04)))) + il.append(il.set_flag("d", il.test_bit(1, il.reg(1, LLIL_TEMP(0)), il.const(1, 0x08)))) + il.append(il.set_flag("b", il.test_bit(1, il.reg(1, LLIL_TEMP(0)), il.const(1, 0x10)))) + il.append(il.set_flag("v", il.test_bit(1, il.reg(1, LLIL_TEMP(0)), il.const(1, 0x40)))) + il.append(il.set_flag("s", il.test_bit(1, il.reg(1, LLIL_TEMP(0)), il.const(1, 0x80)))) + return None + +def rti(il): + set_p_value(il, il.pop(1)) + return il.ret(il.pop(2)) + +InstructionIL = { + "adc": lambda il, operand: il.set_reg(1, "a", il.add_carry(1, il.reg(1, "a"), operand, flags = "*")), + "asl": lambda il, operand: il.store(1, operand, il.shift_left(1, il.load(1, operand), il.const(1, 1), flags = "czs")), + "asl@": lambda il, operand: il.set_reg(1, "a", il.shift_left(1, operand, il.const(1, 1), flags = "czs")), + "and": lambda il, operand: il.set_reg(1, "a", il.and_expr(1, il.reg(1, "a"), operand, flags = "zs")), + "bcc": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_UGE), operand), + "bcs": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_ULT), operand), + "beq": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_E), operand), + "bit": lambda il, operand: il.and_expr(1, il.reg(1, "a"), operand, flags = "czs"), + "bmi": lambda il, operand: cond_branch(il, il.flag("s"), operand), + "bne": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_NE), operand), + "bpl": lambda il, operand: cond_branch(il, il.not_expr(0, il.flag("s")), operand), + "brk": lambda il, operand: il.system_call(), + "bvc": lambda il, operand: cond_branch(il, il.not_expr(0, il.flag("v")), operand), + "bvs": lambda il, operand: cond_branch(il, il.flag("v"), operand), + "clc": lambda il, operand: il.set_flag("c", il.const(0, 0)), + "cld": lambda il, operand: il.set_flag("d", il.const(0, 0)), + "cli": lambda il, operand: il.set_flag("i", il.const(0, 0)), + "clv": lambda il, operand: il.set_flag("v", il.const(0, 0)), + "cmp": lambda il, operand: il.sub(1, il.reg(1, "a"), operand, flags = "czs"), + "cpx": lambda il, operand: il.sub(1, il.reg(1, "x"), operand, flags = "czs"), + "cpy": lambda il, operand: il.sub(1, il.reg(1, "y"), operand, flags = "czs"), + "dec": lambda il, operand: il.store(1, operand, il.sub(1, il.load(1, operand), il.const(1, 1), flags = "zs")), + "dex": lambda il, operand: il.set_reg(1, "x", il.sub(1, il.reg(1, "x"), il.const(1, 1), flags = "zs")), + "dey": lambda il, operand: il.set_reg(1, "y", il.sub(1, il.reg(1, "y"), il.const(1, 1), flags = "zs")), + "eor": lambda il, operand: il.set_reg(1, "a", il.xor_expr(1, il.reg(1, "a"), operand, flags = "zs")), + "inc": lambda il, operand: il.store(1, operand, il.add(1, il.load(1, operand), il.const(1, 1), flags = "zs")), + "inx": lambda il, operand: il.set_reg(1, "x", il.add(1, il.reg(1, "x"), il.const(1, 1), flags = "zs")), + "iny": lambda il, operand: il.set_reg(1, "y", il.add(1, il.reg(1, "y"), il.const(1, 1), flags = "zs")), + "jmp": lambda il, operand: jump(il, operand), + "jsr": lambda il, operand: il.call(operand), + "lda": lambda il, operand: il.set_reg(1, "a", operand, flags = "zs"), + "ldx": lambda il, operand: il.set_reg(1, "x", operand, flags = "zs"), + "ldy": lambda il, operand: il.set_reg(1, "y", operand, flags = "zs"), + "lsr": lambda il, operand: il.store(1, operand, il.logical_shift_right(1, il.load(1, operand), il.const(1, 1), flags = "czs")), + "lsr@": lambda il, operand: il.set_reg(1, "a", il.logical_shift_right(1, il.reg(1, "a"), il.const(1, 1), flags = "czs")), + "nop": lambda il, operand: il.nop(), + "ora": lambda il, operand: il.set_reg(1, "a", il.or_expr(1, il.reg(1, "a"), operand, flags = "zs")), + "pha": lambda il, operand: il.push(1, il.reg(1, "a")), + "php": lambda il, operand: il.push(1, get_p_value(il)), + "pla": lambda il, operand: il.set_reg(1, "a", il.pop(1), flags = "zs"), + "plp": lambda il, operand: set_p_value(il, il.pop(1)), + "rol": lambda il, operand: il.store(1, operand, il.rotate_left_carry(1, il.load(1, operand), il.const(1, 1), flags = "czs")), + "rol@": lambda il, operand: il.set_reg(1, "a", il.rotate_left_carry(1, il.reg(1, "a"), il.const(1, 1), flags = "czs")), + "ror": lambda il, operand: il.store(1, operand, il.rotate_right_carry(1, il.load(1, operand), il.const(1, 1), flags = "czs")), + "ror@": lambda il, operand: il.set_reg(1, "a", il.rotate_right_carry(1, il.reg(1, "a"), il.const(1, 1), flags = "czs")), + "rti": lambda il, operand: rti(il), + "rts": lambda il, operand: il.ret(il.add(2, il.pop(2), il.const(2, 1))), + "sbc": lambda il, operand: il.set_reg(1, "a", il.sub_borrow(1, il.reg(1, "a"), operand, flags = "*")), + "sec": lambda il, operand: il.set_flag("c", il.const(0, 1)), + "sed": lambda il, operand: il.set_flag("d", il.const(0, 1)), + "sei": lambda il, operand: il.set_flag("i", il.const(0, 1)), + "sta": lambda il, operand: il.store(1, operand, il.reg(1, "a")), + "stx": lambda il, operand: il.store(1, operand, il.reg(1, "x")), + "sty": lambda il, operand: il.store(1, operand, il.reg(1, "y")), + "tax": lambda il, operand: il.set_reg(1, "x", il.reg(1, "a"), flags = "zs"), + "tay": lambda il, operand: il.set_reg(1, "y", il.reg(1, "a"), flags = "zs"), + "tsx": lambda il, operand: il.set_reg(1, "x", il.reg(1, "s"), flags = "zs"), + "txa": lambda il, operand: il.set_reg(1, "a", il.reg(1, "x"), flags = "zs"), + "txs": lambda il, operand: il.set_reg(1, "s", il.reg(1, "x")), + "tya": lambda il, operand: il.set_reg(1, "a", il.reg(1, "y"), flags = "zs") +} + +class M6502(Architecture): + name = "6502" + address_size = 2 + default_int_size = 1 + regs = { + "a": RegisterInfo("a", 1), + "x": RegisterInfo("x", 1), + "y": RegisterInfo("y", 1), + "s": RegisterInfo("s", 1) + } + stack_pointer = "s" + flags = ["c", "z", "i", "d", "b", "v", "s"] + flag_write_types = ["*", "czs", "zvs", "zs"] + + def decode_instruction(self, data, addr): + if len(data) < 1: + return None, None, None, None + opcode = ord(data[0]) + instr = InstructionNames[opcode] + if instr is None: + return None, None, None, None + + operand = InstructionOperandTypes[opcode] + length = 1 + OperandLengths[operand] + if len(data) < length: + return None, None, None, None + + if OperandLengths[operand] == 0: + value = None + elif operand == REL: + value = (addr + 2 + struct.unpack("b", data[1])[0]) & 0xffff + elif OperandLengths[operand] == 1: + value = ord(data[1]) + else: + value = struct.unpack("<H", data[1:3])[0] + + return instr, operand, length, value + + def perform_get_instruction_info(self, data, addr): + instr, operand, length, value = self.decode_instruction(data, addr) + if instr is None: + return None + + result = InstructionInfo() + result.length = length + if instr == "jmp": + if operand == ADDR: + result.add_branch(UnconditionalBranch, struct.unpack("<H", data[1:3])[0]) + else: + result.add_branch(UnresolvedBranch) + elif instr == "jsr": + result.add_branch(CallDestination, struct.unpack("<H", data[1:3])[0]) + elif instr in ["rti", "rts"]: + result.add_branch(FunctionReturn) + if instr in ["bcc", "bcs", "beq", "bmi", "bne", "bpl", "bvc", "bvs"]: + dest = (addr + 2 + struct.unpack("b", data[1])[0]) & 0xffff + result.add_branch(TrueBranch, dest) + result.add_branch(FalseBranch, addr + 2) + return result + + def perform_get_instruction_text(self, data, addr): + instr, operand, length, value = self.decode_instruction(data, addr) + if instr is None: + return None + + tokens = [] + tokens.append(InstructionTextToken(TextToken, "%-7s " % instr.replace("@", ""))) + tokens += OperandTokens[operand](value) + return tokens, length + + def perform_get_instruction_low_level_il(self, data, addr, il): + instr, operand, length, value = self.decode_instruction(data, addr) + if instr is None: + return None + + operand = OperandIL[operand](il, value) + instr = InstructionIL[instr](il, operand) + if isinstance(instr, list): + for i in instr: + il.append(i) + elif instr is not None: + il.append(instr) + + return length + + def perform_is_never_branch_patch_available(self, data, addr): + if (data[0] == "\x10") or (data[0] == "\x30") or (data[0] == "\x50") or (data[0] == "\x70") or (data[0] == "\x90") or (data[0] == "\xb0") or (data[0] == "\xd0") or (data[0] == "\xf0"): + return True + return False + + def perform_is_invert_branch_patch_available(self, data, addr): + if (data[0] == "\x10") or (data[0] == "\x30") or (data[0] == "\x50") or (data[0] == "\x70") or (data[0] == "\x90") or (data[0] == "\xb0") or (data[0] == "\xd0") or (data[0] == "\xf0"): + return True + return False + + def perform_is_always_branch_patch_available(self, data, addr): + return False + + def perform_is_skip_and_return_zero_patch_available(self, data, addr): + return (data[0] == "\x20") and (len(data) == 3) + + def perform_is_skip_and_return_value_patch_available(self, data, addr): + return (data[0] == "\x20") and (len(data) == 3) + + def perform_convert_to_nop(self, data, addr): + return "\xea" * len(data) + + def perform_never_branch(self, data, addr): + if (data[0] == "\x10") or (data[0] == "\x30") or (data[0] == "\x50") or (data[0] == "\x70") or (data[0] == "\x90") or (data[0] == "\xb0") or (data[0] == "\xd0") or (data[0] == "\xf0"): + return "\xea" * len(data) + return None + + def perform_invert_branch(self, data, addr): + if (data[0] == "\x10") or (data[0] == "\x30") or (data[0] == "\x50") or (data[0] == "\x70") or (data[0] == "\x90") or (data[0] == "\xb0") or (data[0] == "\xd0") or (data[0] == "\xf0"): + return chr(ord(data[0]) ^ 0x20) + data[1:] + return None + + def perform_skip_and_return_value(self, data, addr, value): + if (data[0] != "\x20") or (len(data) != 3): + return None + return "\xa9" + chr(value & 0xff) + "\xea" + +class NESViewUpdateNotification(BinaryDataNotification): + def __init__(self, view): + self.view = view + + def data_written(self, view, offset, length): + addr = offset - self.view.rom_offset + while length > 0: + bank_ofs = addr & 0x3fff + if (bank_ofs + length) > 0x4000: + to_read = 0x4000 - bank_ofs + else: + to_read = length + if length < to_read: + to_read = length + if (addr >= (bank_ofs + (self.view.__class__.bank * 0x4000))) and (addr < (bank_ofs + ((self.view.__class__.bank + 1) * 0x4000))): + self.view.notify_data_written(0x8000 + bank_ofs, to_read) + elif (addr >= (bank_ofs + (self.view.rom_length - 0x4000))) and (addr < (bank_ofs + self.view.rom_length)): + self.view.notify_data_written(0xc000 + bank_ofs, to_read) + length -= to_read + addr += to_read + + def data_inserted(self, view, offset, length): + self.view.notify_data_written(0x8000, 0x8000) + + def data_removed(self, view, offset, length): + self.view.notify_data_written(0x8000, 0x8000) + +class NESView(BinaryView): + name = "NES" + long_name = "NES ROM" + + def __init__(self, data): + BinaryView.__init__(self, data.file) + self.data = data + self.notification = NESViewUpdateNotification(self) + self.data.register_notification(self.notification) + + @classmethod + def is_valid_for_data(self, data): + hdr = data.read(0, 16) + if len(hdr) < 16: + return False + if hdr[0:4] != "NES\x1a": + return False + rom_banks = struct.unpack("B", hdr[4])[0] + if rom_banks < (self.bank + 1): + return False + return True + + def init(self): + try: + hdr = self.data.read(0, 16) + self.rom_banks = struct.unpack("B", hdr[4])[0] + self.vrom_banks = struct.unpack("B", hdr[5])[0] + self.rom_flags = struct.unpack("B", hdr[6])[0] + self.mapper_index = struct.unpack("B", hdr[7])[0] | (self.rom_flags >> 4) + self.ram_banks = struct.unpack("B", hdr[8])[0] + self.rom_offset = 16 + if self.rom_flags & 4: + self.rom_offset += 512 + self.rom_length = self.rom_banks * 0x4000 + + nmi = struct.unpack("<H", self.read(0xfffa, 2))[0] + start = struct.unpack("<H", self.read(0xfffc, 2))[0] + irq = struct.unpack("<H", self.read(0xfffe, 2))[0] + self.define_auto_symbol(Symbol(FunctionSymbol, nmi, "_nmi")) + self.define_auto_symbol(Symbol(FunctionSymbol, start, "_start")) + self.define_auto_symbol(Symbol(FunctionSymbol, irq, "_irq")) + self.add_function(Architecture['6502'].standalone_platform, nmi) + self.add_function(Architecture['6502'].standalone_platform, irq) + self.add_entry_point(Architecture['6502'].standalone_platform, start) + + # Hardware registers + self.define_auto_symbol(Symbol(DataSymbol, 0x2000, "PPUCTRL")) + self.define_auto_symbol(Symbol(DataSymbol, 0x2001, "PPUMASK")) + self.define_auto_symbol(Symbol(DataSymbol, 0x2002, "PPUSTATUS")) + self.define_auto_symbol(Symbol(DataSymbol, 0x2003, "OAMADDR")) + self.define_auto_symbol(Symbol(DataSymbol, 0x2004, "OAMDATA")) + self.define_auto_symbol(Symbol(DataSymbol, 0x2005, "PPUSCROLL")) + self.define_auto_symbol(Symbol(DataSymbol, 0x2006, "PPUADDR")) + self.define_auto_symbol(Symbol(DataSymbol, 0x2007, "PPUDATA")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4000, "SQ1_VOL")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4001, "SQ1_SWEEP")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4002, "SQ1_LO")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4003, "SQ1_HI")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4004, "SQ2_VOL")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4005, "SQ2_SWEEP")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4006, "SQ2_LO")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4007, "SQ2_HI")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4008, "TRI_LINEAR")) + self.define_auto_symbol(Symbol(DataSymbol, 0x400a, "TRI_LO")) + self.define_auto_symbol(Symbol(DataSymbol, 0x400b, "TRI_HI")) + self.define_auto_symbol(Symbol(DataSymbol, 0x400c, "NOISE_VOL")) + self.define_auto_symbol(Symbol(DataSymbol, 0x400e, "NOISE_LO")) + self.define_auto_symbol(Symbol(DataSymbol, 0x400f, "NOISE_HI")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4010, "DMC_FREQ")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4011, "DMC_RAW")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4012, "DMC_START")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4013, "DMC_LEN")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4014, "OAMDMA")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4015, "SND_CHN")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4016, "JOY1")) + self.define_auto_symbol(Symbol(DataSymbol, 0x4017, "JOY2")) + + sym_files = [self.data.file.filename + ".%x.nl" % self.__class__.bank, + self.data.file.filename + ".ram.nl", + self.data.file.filename + ".%x.nl" % (self.rom_banks - 1)] + for f in sym_files: + if os.path.exists(f): + sym_contents = open(f, "r").read() + lines = sym_contents.split('\n') + for line in lines: + sym = line.split('#') + if len(sym) < 3: + break + addr = int(sym[0][1:], 16) + name = sym[1] + self.define_auto_symbol(Symbol(FunctionSymbol, addr, name)) + if addr >= 0x8000: + self.add_function(Architecture['6502'].standalone_platform, addr) + + return True + except: + log_error(traceback.format_exc()) + return False + + def perform_is_valid_offset(self, addr): + if (addr >= 0x8000) and (addr < 0x10000): + return True + return False + + def perform_read(self, addr, length): + if addr < 0x8000: + return None + if addr >= (0x8000 + self.rom_length): + return None + if (addr + length) > 0x10000: + length = 0x10000 - addr + result = "" + while length > 0: + bank_ofs = addr & 0x3fff + if (bank_ofs + length) > 0x4000: + to_read = 0x4000 - bank_ofs + else: + to_read = length + if addr < 0xc000: + data = self.data.read(self.rom_offset + bank_ofs + (self.__class__.bank * 0x4000), to_read) + else: + data = self.data.read(self.rom_offset + bank_ofs + self.rom_length - 0x4000, to_read) + result += data + if len(data) < to_read: + break + length -= to_read + addr += to_read + return result + + def perform_write(self, addr, value): + if addr < 0x8000: + return 0 + if addr >= (0x8000 + self.rom_length): + return 0 + if (addr + len(value)) > (0x8000 + self.rom_length): + length = (0x8000 + self.rom_length) - addr + else: + length = len(value) + if (addr + length) > 0x10000: + length = 0x10000 - addr + offset = 0 + while length > 0: + bank_ofs = addr & 0x3fff + if (bank_ofs + length) > 0x4000: + to_write = 0x4000 - bank_ofs + else: + to_write = length + if addr < 0xc000: + written = self.data.write(self.rom_offset + bank_ofs + (self.__class__.bank * 0x4000), value[offset : offset + to_write]) + else: + written = self.data.write(self.rom_offset + bank_ofs + self.rom_length - 0x4000, value[offset : offset + to_write]) + if written < to_write: + break + length -= to_write + addr += to_write + offset += to_write + return offset + + def perform_get_start(self): + return 0 + + def perform_get_length(self): + return 0x10000 + + def perform_is_executable(self): + return True + + def perform_get_entry_point(self): + return struct.unpack("<H", str(self.perform_read(0xfffc, 2)))[0] + +banks = [] +for i in xrange(0, 32): + class NESViewBank(NESView): + bank = i + name = "NES Bank %X" % i + long_name = "NES ROM (bank %X)" % i + + def __init__(self, data): + NESView.__init__(self, data) + + banks.append(NESViewBank) + NESViewBank.register() + +M6502.register() + diff --git a/python/generator.cpp b/python/generator.cpp new file mode 100644 index 00000000..bd9242d8 --- /dev/null +++ b/python/generator.cpp @@ -0,0 +1,302 @@ +#include <stdio.h> +#include <inttypes.h> +#include "binaryninjaapi.h" + +using namespace BinaryNinja; +using namespace std; + + +class GeneratorArchitecture: public Architecture +{ +public: + GeneratorArchitecture(): Architecture("generator") + { + } + + virtual bool GetInstructionInfo(const uint8_t*, uint64_t, size_t, InstructionInfo&) override + { + return false; + } + + virtual bool GetInstructionText(const uint8_t*, uint64_t, size_t&, vector<InstructionTextToken>&) override + { + return false; + } + + virtual BNEndianness GetEndianness() const override + { + return LittleEndian; + } + + virtual size_t GetAddressSize() const override + { + return 8; + } +}; + + +void OutputType(FILE* out, Type* type, bool isReturnType = false, bool isCallback = false) +{ + switch (type->GetClass()) + { + case BoolTypeClass: + fprintf(out, "ctypes.c_bool"); + break; + case IntegerTypeClass: + switch (type->GetWidth()) + { + case 1: + if (type->IsSigned()) + fprintf(out, "ctypes.c_byte"); + else + fprintf(out, "ctypes.c_ubyte"); + break; + case 2: + if (type->IsSigned()) + fprintf(out, "ctypes.c_short"); + else + fprintf(out, "ctypes.c_ushort"); + break; + case 4: + if (type->IsSigned()) + fprintf(out, "ctypes.c_int"); + else + fprintf(out, "ctypes.c_uint"); + break; + default: + if (type->IsSigned()) + fprintf(out, "ctypes.c_longlong"); + else + fprintf(out, "ctypes.c_ulonglong"); + break; + } + break; + case FloatTypeClass: + if (type->GetWidth() == 4) + fprintf(out, "ctypes.c_float"); + else + fprintf(out, "ctypes.c_double"); + break; + case StructureTypeClass: + fprintf(out, "%s", type->GetStructure()->GetName().c_str()); + break; + case EnumerationTypeClass: + fprintf(out, "%s", type->GetEnumeration()->GetName().c_str()); + break; + case PointerTypeClass: + if (isCallback || (type->GetChildType()->GetClass() == VoidTypeClass)) + { + fprintf(out, "ctypes.c_void_p"); + break; + } + else if ((type->GetChildType()->GetClass() == IntegerTypeClass) && + (type->GetChildType()->GetWidth() == 1) && (type->GetChildType()->IsSigned())) + { + if (isReturnType) + fprintf(out, "ctypes.POINTER(ctypes.c_byte)"); + else + fprintf(out, "ctypes.c_char_p"); + break; + } + else if (type->GetChildType()->GetClass() == FunctionTypeClass) + { + fprintf(out, "ctypes.CFUNCTYPE("); + OutputType(out, type->GetChildType()->GetChildType(), true, true); + for (auto& i : type->GetChildType()->GetParameters()) + { + fprintf(out, ", "); + OutputType(out, i.type); + } + fprintf(out, ")"); + break; + } + fprintf(out, "ctypes.POINTER("); + OutputType(out, type->GetChildType()); + fprintf(out, ")"); + break; + case ArrayTypeClass: + OutputType(out, type->GetChildType()); + fprintf(out, " * %" PRId64, type->GetElementCount()); + break; + default: + fprintf(out, "None"); + break; + } +} + + +int main(int argc, char* argv[]) +{ + if (argc < 3) + { + fprintf(stderr, "Usage: generator <header> <output>\n"); + return 1; + } + + Architecture::Register(new GeneratorArchitecture()); + + // Parse API header to get type and function information + map<string, Ref<Type>> types, vars, funcs; + string errors; + bool ok = Architecture::GetByName("generator")->ParseTypesFromSourceFile(argv[1], types, vars, funcs, errors); + fprintf(stderr, "%s", errors.c_str()); + if (!ok) + return 1; + + FILE* out = fopen(argv[2], "w"); + + fprintf(out, "import ctypes, os\n\n"); + + fprintf(out, "# Load core module\n"); +#if defined(__APPLE__) + fprintf(out, "_base_path = os.path.join(os.path.dirname(__file__), \"..\", \"..\", \"..\", \"MacOS\")\n"); +#else + fprintf(out, "_base_path = os.path.join(os.path.dirname(__file__), \"..\", \"..\")\n"); +#endif + +#ifdef WIN32 + fprintf(out, "core = ctypes.CDLL(os.path.join(_base_path, \"binaryninjacore.dll\"))\n\n"); +#elif defined(__APPLE__) + fprintf(out, "core = ctypes.CDLL(os.path.join(_base_path, \"libbinaryninjacore.dylib\"))\n\n"); +#else + fprintf(out, "core = ctypes.CDLL(os.path.join(_base_path, \"libbinaryninjacore.so.1\"))\n\n"); +#endif + + // Create type objects + fprintf(out, "# Type definitions\n"); + map<string, int64_t> enumMembers; + for (auto& i : types) + { + if (i.second->GetClass() == StructureTypeClass) + { + fprintf(out, "class %s(ctypes.Structure):\n", i.first.c_str()); + fprintf(out, " pass\n"); + } + else if (i.second->GetClass() == EnumerationTypeClass) + { + fprintf(out, "%s = ctypes.c_int\n", i.first.c_str()); + for (auto& j : i.second->GetEnumeration()->GetMembers()) + fprintf(out, "%s = %" PRId64 "\n", j.name.c_str(), j.value); + fprintf(out, "%s_names = {\n", i.first.c_str()); + for (auto& j : i.second->GetEnumeration()->GetMembers()) + fprintf(out, " %" PRId64 ": \"%s\",\n", j.value, j.name.c_str()); + fprintf(out, "}\n"); + fprintf(out, "%s_by_name = {\n", i.first.c_str()); + for (auto& j : i.second->GetEnumeration()->GetMembers()) + fprintf(out, " \"%s\": %" PRId64 ",\n", j.name.c_str(), j.value); + fprintf(out, "}\n"); + for (auto& j : i.second->GetEnumeration()->GetMembers()) + enumMembers[j.name] = j.value; + } + else if ((i.second->GetClass() == BoolTypeClass) || (i.second->GetClass() == IntegerTypeClass) || + (i.second->GetClass() == FloatTypeClass) || (i.second->GetClass() == ArrayTypeClass)) + { + fprintf(out, "%s = ", i.first.c_str()); + OutputType(out, i.second); + fprintf(out, "\n"); + } + } + + fprintf(out, "all_enum_values = {\n"); + for (auto& i : enumMembers) + fprintf(out, " \"%s\": %" PRId64 ",\n", i.first.c_str(), i.second); + fprintf(out, "}\n"); + + fprintf(out, "\n# Structure definitions\n"); + for (auto& i : types) + { + if ((i.second->GetClass() == StructureTypeClass) && (i.second->GetStructure()->GetMembers().size() != 0)) + { + fprintf(out, "%s._fields_ = [\n", i.first.c_str()); + for (auto& j : i.second->GetStructure()->GetMembers()) + { + fprintf(out, " (\"%s\", ", j.name.c_str()); + OutputType(out, j.type); + fprintf(out, "),\n"); + } + fprintf(out, " ]\n"); + } + } + + fprintf(out, "\n# Function definitions\n"); + for (auto& i : funcs) + { + // Check for a string result, these will be automatically wrapped to free the string + // memory and return a Python string + bool stringResult = (i.second->GetChildType()->GetClass() == PointerTypeClass) && + (i.second->GetChildType()->GetChildType()->GetWidth() == 1) && + (i.second->GetChildType()->GetChildType()->IsSigned()); + // Pointer returns will be automatically wrapped to return None on null pointer + bool pointerResult = (i.second->GetChildType()->GetClass() == PointerTypeClass); + bool callbackConvention = false; + if (i.first == "BNAllocString") + { + // Don't perform automatic wrapping of string allocation, and return a void + // pointer so that callback functions (which is the only valid use of BNAllocString) + // can properly return the result + stringResult = false; + callbackConvention = true; + } + + string funcName = i.first; + if (stringResult || pointerResult) + funcName = string("_") + funcName; + + fprintf(out, "%s = core.%s\n", funcName.c_str(), i.first.c_str()); + fprintf(out, "%s.restype = ", funcName.c_str()); + OutputType(out, i.second->GetChildType(), true, callbackConvention); + fprintf(out, "\n"); + if (!i.second->HasVariableArguments()) + { + fprintf(out, "%s.argtypes = [\n", funcName.c_str()); + for (auto& j : i.second->GetParameters()) + { + fprintf(out, " "); + if (i.first == "BNFreeString") + { + // BNFreeString expects a pointer to a string allocated by the core, so do not use + // a c_char_p here, as that would be allocated by the Python runtime. This can + // be enforced by outputting like a return value. + OutputType(out, j.type, true); + } + else + { + OutputType(out, j.type); + } + fprintf(out, ",\n"); + } + fprintf(out, " ]\n"); + } + + if (stringResult) + { + // Emit wrapper to get Python string and free native memory + fprintf(out, "def %s(*args):\n", i.first.c_str()); + fprintf(out, " result = %s(*args)\n", funcName.c_str()); + fprintf(out, " string = ctypes.cast(result, ctypes.c_char_p).value\n"); + fprintf(out, " BNFreeString(result)\n"); + fprintf(out, " return string\n"); + } + else if (pointerResult) + { + // Emit wrapper to return None on null pointer + fprintf(out, "def %s(*args):\n", i.first.c_str()); + fprintf(out, " result = %s(*args)\n", funcName.c_str()); + fprintf(out, " if not result:\n"); + fprintf(out, " return None\n"); + fprintf(out, " return result\n"); + } + } + + fprintf(out, "\n# Helper functions\n"); + fprintf(out, "def handle_of_type(value, handle_type):\n"); + fprintf(out, " if isinstance(value, ctypes.POINTER(handle_type)) or isinstance(value, ctypes.c_void_p):\n"); + fprintf(out, " return ctypes.cast(value, ctypes.POINTER(handle_type))\n"); + fprintf(out, " raise ValueError, 'expected pointer to %%s' %% str(handle_type)\n"); + + fprintf(out, "\n# Set path for core plugins\n"); + fprintf(out, "BNSetBundledPluginDirectory(os.path.join(_base_path, \"plugins\"))\n"); + + fclose(out); + return 0; +} |
