diff options
Diffstat (limited to 'python')
| -rw-r--r-- | python/__init__.py | 2277 | ||||
| -rw-r--r-- | python/examples/jump-table.py | 63 | ||||
| -rw-r--r-- | python/examples/nes.py | 33 |
3 files changed, 1528 insertions, 845 deletions
diff --git a/python/__init__.py b/python/__init__.py index e1429bd6..bf2ebfaa 100644 --- a/python/__init__.py +++ b/python/__init__.py @@ -143,7 +143,7 @@ class DataBuffer: return None return DataBuffer(handle = buf) -class NavigationHandler: +class NavigationHandler(object): def _register(self, handle): self._cb = core.BNNavigationHandler() self._cb.context = 0 @@ -174,7 +174,7 @@ class NavigationHandler: log_error(traceback.format_exc()) return False -class FileMetadata: +class FileMetadata(object): def __init__(self, filename = None, handle = None): if handle is not None: self.handle = core.handle_of_type(handle, core.BNFileMetadata) @@ -190,53 +190,72 @@ class FileMetadata: 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 + @property + def filename(self): + """Backing filename""" + return core.BNGetFilename(self.handle) - 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 + @filename.setter + def filename(self, value): + core.BNSetFilename(self.handle, str(value)) + + @property + def modified(self): + """Boolean result of whether the file is modified""" + return core.BNIsFileModified(self.handle) + + @modified.setter + def modified(self, value): + if value: + core.BNMarkFileModified(self.handle) else: - self.__dict__[name] = value + core.BNMarkFileSaved(self.handle) + + @property + def analysis_changed(self): + """Boolean result of whether the auto-analysis results have changed (read-only)""" + return core.BNIsAnalysisChanged(self.handle) - def __dir__(self): - return dir(self.__class__) + ["filename", "modified", "analysis_changed", "has_database", "view", "offset"] + @property + def has_database(self): + """Whether the FileMetadata is backed by a database""" + return core.BNIsBackedByDatabase(self.handle) + + @property + def view(self): + return core.BNGetCurrentView(self.handle) + + @view.setter + def view(self, value): + core.BNNavigate(self.handle, str(value), core.BNGetCurrentOffset(self.handle)) + + @property + def offset(self): + """Current offset into the file""" + return core.BNGetCurrentOffset(self.handle) + + @offset.setter + def offset(self, value): + core.BNNavigate(self.handle, core.BNGetCurrentView(self.handle), value) + + @property + def raw(self): + view = core.BNGetFileViewOfType(self.handle, "Raw") + if view is None: + return None + return BinaryView(self, handle = view) + + @property + def saved(self): + """Set to mark file as saved""" + return not core.BNIsFileModified(self.handle) + + @saved.setter + def saved(self, value): + if value: + core.BNMarkFileSaved(self.handle) + else: + core.BNMarkFileModified(self.handle) def close(self): core.BNCloseFile(self.handle) @@ -279,6 +298,16 @@ class FileMetadata: return None return BinaryView(self, handle = view) + def __setattr__(self, name, value): + if name == "navigation": + value._register(self.handle) + self.__dict__["navigation"] = value + else: + try: + object.__setattr__(self,name,value) + except AttributeError: + raise AttributeError, "attribute '%s' is read only" % name + class FileAccessor: def __init__(self): self._cb = core.BNFileAccessor() @@ -512,26 +541,17 @@ class BinaryDataNotificationCallbacks: 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"] + @property + def list(self): + """List all BinaryView types (read-only)""" + _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 def __getitem__(self, value): _init_plugins() @@ -540,27 +560,27 @@ class _BinaryViewTypeMetaclass(type): raise KeyError, "'%s' is not a valid view type" % str(value) return BinaryViewType(view_type) -class BinaryViewType: + def __setattr__(self, name, value): + try: + type.__setattr__(self,name,value) + except AttributeError: + raise AttributeError, "attribute '%s' is read only" % name + +class BinaryViewType(object): __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 + @property + def name(self): + """Binary View name (read-only)""" + return core.BNGetBinaryViewTypeName(self.handle) - 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"] + @property + def long_name(self): + """ Binary VIew long name (read-only)""" + return core.BNGetBinaryViewTypeLongName(self.handle) def __repr__(self): return "<view type: '%s'>" % self.name @@ -601,12 +621,21 @@ class BinaryViewType: return None return Platform(None, platform) -class BinaryView: + def __setattr__(self, name, value): + try: + object.__setattr__(self,name,value) + except AttributeError: + raise AttributeError, "attribute '%s' is read only" % name + +class BinaryView(object): name = None + """Binary View name""" long_name = None + """Binary View long name""" _registered = False _registered_cb = None view_type = None + """Binary View type""" def __init__(self, file_metadata = None, handle = None): if handle is not None: @@ -719,107 +748,162 @@ class BinaryView: 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 + @property + def modified(self): + return self.file.modified - 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 + @modified.setter + def modified(self, value): + self.file.modified = value + + @property + def analysis_changed(self): + """Whether analysis has changed (read-only)""" + return self.file.analysis_changed + + @property + def has_database(self): + """Whether the BinaryView has a database (read-only)""" + return self.file.has_database + + @property + def view(self): + return self.file.view + + @view.setter + def view(self, value): + self.file.view = value + + @property + def offset(self): + return self.file.offset + + @offset.setter + def offset(self, value): + self.file.offset = value + + @property + def start(self): + """Start offset of the binary (read-only)""" + return core.BNGetStartOffset(self.handle) + + @property + def end(self): + """End offset of the binary (read-only)""" + return core.BNGetEndOffset(self.handle) + + @property + def entry_point(self): + """Entry point of the binary (read-only)""" + return core.BNGetEntryPoint(self.handle) + + @property + def arch(self): + arch = core.BNGetDefaultArchitecture(self.handle) + if arch is None: + return None + return Architecture(handle = arch) + + @arch.setter + def arch(self, value): + if value is None: + core.BNSetDefaultArchitecture(self.handle, None) else: - self.__dict__[name] = value + core.BNSetDefaultArchitecture(self.handle, value.handle) + + @property + def platform(self): + platform = core.BNGetDefaultPlatform(self.handle) + if platform is None: + return None + return Platform(self.arch, handle = platform) - 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"] + @platform.setter + def platform(self, value): + if value is None: + core.BNSetDefaultPlatform(self.handle, None) + else: + core.BNSetDefaultPlatform(self.handle, value.handle) + + @property + def endianness(self): + """Endianness of the binary (read-only)""" + return core.BNGetDefaultEndianness(self.handle) + + @property + def address_size(self): + """Address size of the binary (read-only)""" + return core.BNGetViewAddressSize(self.handle) + + @property + def executable(self): + """Whether the binary is an executable (read-only)""" + return core.BNIsExecutableView(self.handle) + + @property + def functions(self): + """List of functions (read-only)""" + 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 + + @property + def has_functions(self): + """Boolean whether the binary has functions (read-only)""" + return core.BNHasFunctions(self.handle) + + @property + def entry_function(self): + """Entry function (read-only)""" + func = core.BNGetAnalysisEntryPoint(self.handle) + if func is None: + return None + return Function(self, func) + + @property + def symbols(self): + """Dict of symbols (read-only)""" + 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 + + @property + def type(self): + """View type (read-only)""" + return core.BNGetViewType(self.handle) + + @property + def available_types(self): + """Available types (read-only)""" + 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 + + @property + def strings(self): + """List of strings (read-only)""" + return self.get_strings() + + @property + def saved(self): + return self.file.saved + + @saved.setter + def saved(self, value): + self.file.saved = value def __len__(self): return int(core.BNGetViewLength(self.handle)) @@ -1344,7 +1428,13 @@ class BinaryView: core.BNFreeStringList(strings) return result -class BinaryReader: + def __setattr__(self, name, value): + try: + object.__setattr__(self,name,value) + except AttributeError: + raise AttributeError, "attribute '%s' is read only" % name + +class BinaryReader(object): def __init__(self, data, endian = None): self.handle = core.BNCreateBinaryReader(data.handle) if endian is None: @@ -1355,27 +1445,26 @@ class BinaryReader: 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 + @property + def endianness(self): + return core.BNGetBinaryReaderEndianness(self.handle) - 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 + @endianness.setter + def endianness(self, value): + core.BNSetBinaryReaderEndianness(self.handle, value) - def __dir__(self): - return dir(self.__class__) + ["endianness","offset", "eof"] + @property + def offset(self): + return core.BNGetReaderPosition(self.handle) + + @offset.setter + def offset(self, value): + core.BNSeekBinaryReader(self.handle, value) + + @property + def eof(self): + """Boolean, is end of file (read-only)""" + return core.BNIsEndOfFile(self.handle) def read(self, length): dest = ctypes.create_string_buffer(length) @@ -1449,6 +1538,12 @@ class BinaryReader: def seek_relative(self, offset): core.BNSeekBinaryReaderRelative(self.handle, offset) + def __setattr__(self, name, value): + try: + object.__setattr__(self,name,value) + except AttributeError: + raise AttributeError, "attribute '%s' is read only" % name + class BinaryWriter: def __init__(self, data, endian = None): self.handle = core.BNCreateBinaryWriter(data.handle) @@ -1460,23 +1555,21 @@ class BinaryWriter: 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 + @property + def endianness(self): + return core.BNGetBinaryWriterEndianness(self.handle) - 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 + @endianness.setter + def endianness(self, value): + core.BNSetBinaryWriterEndianness(self.handle, value) + + @property + def offset(self): + return core.BNGetWriterPosition(self.handle) - def __dir__(self): - return dir(self.__class__) + ["endianness","offset"] + @offset.setter + def offset(self, value): + core.BNSeekBinaryWriter(self.handle, value) def write(self, value): value = str(value) @@ -1526,7 +1619,7 @@ class BinaryWriter: def seek_relative(self, offset): core.BNSeekBinaryWriterRelative(self.handle, offset) -class Symbol: +class Symbol(object): 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) @@ -1542,105 +1635,163 @@ class Symbol: 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 + @property + def type(self): + """Symbol type (read-only)""" + return core.BNSymbolType_names[core.BNGetSymbolType(self.handle)] - 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 + @property + def name(self): + """Symbol name (read-only)""" + return core.BNGetSymbolRawName(self.handle) + + @property + def short_name(self): + """Symbol short name (read-only)""" + return core.BNGetSymbolShortName(self.handle) + + @property + def full_name(self): + """Symbol full name (read-only)""" + return core.BNGetSymbolFullName(self.handle) + + @property + def raw_name(self): + """Symbol raw name (read-only)""" + return core.BNGetSymbolRawName(self.handle) + + @property + def address(self): + """Symbol address (read-only)""" + return core.BNGetSymbolAddress(self.handle) + + @property + def auto(self): + return core.BNIsSymbolAutoDefined(self.handle) - def __dir__(self): - return dir(self.__class__) + ["type","name", "short_name", "full_name", "raw_name", "address", "auto"] + @auto.setter + def auto(self, value): + core.BNSetSymbolAutoDefined(self.handle, value) def __repr__(self): return "<%s: \"%s\" @ 0x%x>" % (self.type, self.full_name, self.address) -class Type: + def __setattr__(self, name, value): + try: + object.__setattr__(self,name,value) + except AttributeError: + raise AttributeError, "attribute '%s' is read only" % name + +class Type(object): 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 + @property + def type_class(self): + """Type class (read-only)""" + return BNTypeClass_names[core.BNGetTypeClass(self.handle)] - 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 + @property + def width(self): + """Type width (read-only)""" + return core.BNGetTypeWidth(self.handle) + + @property + def alignment(self): + """Type alignment (read-only)""" + return core.BNGetTypeAlignment(self.handle) + + @property + def signed(self): + """Wether type is signed (read-only)""" + return core.BNIsTypeSigned(self.handle) - 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"] + @property + def const(self): + """Whether type is const (read-only)""" + return core.BNIsTypeConst(self.handle) + + @property + def modified(self): + """Whether type is modified (read-only)""" + return core.BNIsTypeFloatingPoint(self.handle) + + @property + def target(self): + """Target (read-only)""" + result = core.BNGetChildType(self.handle) + if result is None: + return None + return Type(result) + + @property + def element_type(self): + """Target (read-only)""" + result = core.BNGetChildType(self.handle) + if result is None: + return None + return Type(result) + + @property + def return_value(self): + """Return value (read-only)""" + result = core.BNGetChildType(self.handle) + if result is None: + return None + return Type(result) + + @property + def calling_convention(self): + """Calling convention (read-only)""" + result = core.BNGetTypeCallingConvention(self.handle) + if result is None: + return None + return CallingConvention(None, result) + + @property + def parameters(self): + """Type parameters list (read-only)""" + 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 + + @property + def has_variable_arguments(self): + """Whether type has variable arguments (read-only)""" + return core.BNTypeHasVariableArguments(self.handle) + + @property + def can_return(self): + """Whether type can return (read-only)""" + return core.BNFunctionTypeCanReturn(self.handle) + + @property + def structure(self): + """Structure of the type (read-only)""" + result = core.BNGetTypeStructure(self.handle) + if result is None: + return None + return Structure(result) + + @property + def enumeration(self): + """Type enumeration (read-only)""" + result = core.BNGetTypeEnumeration(self.handle) + if result is None: + return None + return Enumeration(result) + + @property + def count(self): + """Type count (read-only)""" + return core.BNGetTypeElementCount(self.handle) def __str__(self): return core.BNGetTypeString(self.handle) @@ -1703,6 +1854,12 @@ class Type: return Type(core.BNCreateFunctionType(ret.handle, calling_convention, param_buf, len(params), variable_arguments)) + def __setattr__(self, name, value): + try: + object.__setattr__(self,name,value) + except AttributeError: + raise AttributeError, "attribute '%s' is read only" % name + class StructureMember: def __init__(self, t, name, offset): self.type = t @@ -1715,7 +1872,7 @@ class StructureMember: 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: +class Structure(object): def __init__(self, handle = None): if handle is None: self.handle = core.BNCreateStructure() @@ -1725,42 +1882,57 @@ class Structure: 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 + @property + def name(self): + return core.BNGetStructureName(self.handle) + + @name.setter + def name(self, value): + core.BNSetStructureName(self.handle, value) + + @property + def members(self): + """Structure member list (read-only)""" + 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 + + @property + def width(self): + """Structure width (read-only)""" + return core.BNGetStructureWidth(self.handle) + + @property + def alignment(self): + """Structure alignment (read-only)""" + return core.BNGetStructureAlignment(self.handle) + + @property + def packed(self): + return core.BNIsStructurePacked(self.handle) + + @packed.setter + def packed(self, value): + core.BNSetStructurePacked(self.handle, value) + + @property + def union(self): + return core.BNIsStructureUnion(self.handle) + + @union.setter + def union(self, value): + core.BNSetStructureUnion(self.handle, value) def __setattr__(self, name, value): - if (name == "members") or (name == "width") or (name == "alignment"): + try: + object.__setattr__(self,name,value) + except AttributeError: 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: @@ -1785,7 +1957,7 @@ class EnumerationMember: def __repr__(self): return "<%s = 0x%x>" % (self.name, self.value) -class Enumeration: +class Enumeration(object): def __init__(self, handle = None): if handle is None: self.handle = core.BNCreateEnumeration() @@ -1795,29 +1967,30 @@ class Enumeration: 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 + @property + def name(self): + return core.BNGetEnumerationName(self.handle) + + @name.setter + def name(self, value): + core.BNSetEnumerationName(self.handle, value) + + @property + def members(self): + """Enumeration member list (read-only)""" + 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 def __setattr__(self, name, value): - if name == "members": + try: + object.__setattr__(self,name,value) + except AttributeError: 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: @@ -1885,7 +2058,18 @@ class StackVariableReference: 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: +class IndirectBranchInfo: + def __init__(self, source_arch, source_addr, dest_arch, dest_addr, auto_defined): + self.source_arch = source_arch + self.source_addr = source_addr + self.dest_arch = dest_arch + self.dest_addr = dest_addr + self.auto_defined = auto_defined + + def __repr__(self): + return "<branch %s:0x%x -> %s:0x%x>" % (self.source_arch.name, self.source_addr, self.dest_arch.name, self.dest_addr) + +class Function(object): def __init__(self, view, handle): self._view = view self.handle = core.handle_of_type(handle, core.BNFunction) @@ -1893,78 +2077,146 @@ class Function: 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 + @property + def view(self): + """Function view (read-only)""" + return self._view + + @property + def arch(self): + """Function architecture (read-only)""" + arch = core.BNGetFunctionArchitecture(self.handle) + if arch is None: + return None + return Architecture(arch) + + @property + def start(self): + """Function start (read-only)""" + return core.BNGetFunctionStart(self.handle) + + @property + def symbol(self): + """Function symbol(read-only)""" + sym = core.BNGetFunctionSymbol(self.handle) + if sym is None: + return None + return Symbol(None, None, None, handle = sym) + + @property + def auto(self): + """Whether function was automatically discovered (read-only)""" + return core.BNWasFunctionAutomaticallyDiscovered(self.handle) + + @property + def can_return(self): + """Whether function can return (read-only)""" + return core.BNCanFunctionReturn(self.handle) + + @property + def explicitly_defined_type(self): + """Whether function has explicitly defined types (read-only)""" + return core.BNHasExplicitlyDefinedType(self.handle) + + @property + def basic_blocks(self): + """List of basic blocks (read-only)""" + 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 + + @property + def comments(self): + """Dict of comments (read-only)""" + 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 + + @property + def indirect_branches(self): + count = ctypes.c_ulonglong() + branches = core.BNGetIndirectBranches(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(IndirectBranchInfo(Architecture(branches[i].sourceArch), branches[i].sourceAddr, Architecture(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) + core.BNFreeIndirectBranchList(branches) + return result + + @property + def low_level_il(self): + """Function low level IL (read-only)""" + return LowLevelILFunction(self.arch, core.BNNewLowLevelILFunctionReference( + core.BNGetFunctionLowLevelIL(self.handle))) + + @property + def low_level_il_basic_blocks(self): + """Function low level IL basic blocks (read-only)""" + 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 + + @property + def lifted_il(self): + """Function lifted IL (read-only)""" + return LowLevelILFunction(self.arch, core.BNNewLowLevelILFunctionReference( + core.BNGetFunctionLiftedIL(self.handle))) + + @property + def lifted_il_basic_blocks(self): + """Function lifted IL basic blocks (read-only)""" + count = ctypes.c_ulonglong() + blocks = core.BNGetFunctionLiftedILBasicBlockList(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 + + @property + def type(self): + """Function type (read-only)""" + return Type(core.BNGetFunctionType(self.handle)) + + @property + def stack_layout(self): + """List of function stack (read-only)""" + 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 + + @property + def indirect_branches(self): + """List of indirect branches (read-only)""" + count = ctypes.c_ulonglong() + branches = core.BNGetIndirectBranches(self.handle, count) + result = [] + for i in xrange(0, count.value): + result.append(IndirectBranchInfo(Architecture(branches[i].sourceArch), branches[i].sourceAddr, Architecture(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) + core.BNFreeIndirectBranchList(branches) + return result 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")): + try: + object.__setattr__(self,name,value) + except AttributeError: 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 @@ -2046,6 +2298,49 @@ class Function: core.BNFreeStackVariableReferenceList(refs, count.value) return result + def get_lifted_il_at(self, arch, addr): + return core.BNGetLiftedILForInstruction(self.handle, arch.handle, addr) + + def get_lifted_il_flag_uses_for_definition(self, i, flag): + if isinstance(flag, str): + flag = self.arch._flags[flag] + count = ctypes.c_ulonglong() + instrs = core.BNGetLiftedILFlagUsesForDefinition(self.handle, i, flag, count) + result = [] + for i in xrange(0, count.value): + result.append(instrs[i]) + core.BNFreeLowLevelILInstructionList(instrs) + return result + + def get_lifted_il_flag_definitions_for_use(self, i, flag): + if isinstance(flag, str): + flag = self.arch._flags[flag] + count = ctypes.c_ulonglong() + instrs = core.BNGetLiftedILFlagDefinitionsForUse(self.handle, i, flag, count) + result = [] + for i in xrange(0, count.value): + result.append(instrs[i]) + core.BNFreeLowLevelILInstructionList(instrs) + return result + + def get_flags_read_by_lifted_il_instruction(self, i): + count = ctypes.c_ulonglong() + flags = core.BNGetFlagsReadByLiftedILInstruction(self.handle, i, count) + result = [] + for i in xrange(0, count.value): + result.append(self.arch._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + return result + + def get_flags_written_by_lifted_il_instruction(self, i): + count = ctypes.c_ulonglong() + flags = core.BNGetFlagsWrittenByLiftedILInstruction(self.handle, i, count) + result = [] + for i in xrange(0, count.value): + result.append(self.arch._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + return result + def create_graph(self): return FunctionGraph(self._view, core.BNCreateFunctionGraph(self.handle)) @@ -2055,6 +2350,29 @@ class Function: def apply_auto_discovered_type(self, func_type): core.BNApplyAutoDiscoveredFunctionType(self.handle, func_type.handle) + def set_auto_indirect_branches(self, source_arch, source, branches): + branch_list = (core.BNArchitectureAndAddress * len(branches))() + for i in xrange(len(branches)): + branch_list[i].arch = branches[i][0].handle + branch_list[i].address = branches[i][1] + core.BNSetAutoIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches)) + + def set_user_indirect_branches(self, source_arch, source, branches): + branch_list = (core.BNArchitectureAndAddress * len(branches))() + for i in xrange(len(branches)): + branch_list[i].arch = branches[i][0].handle + branch_list[i].address = branches[i][1] + core.BNSetUserIndirectBranches(self.handle, source_arch.handle, source, branch_list, len(branches)) + + def get_indirect_branches_at(self, arch, addr): + count = ctypes.c_ulonglong() + branches = core.BNGetIndirectBranchesAt(self.handle, arch.handle, addr, count) + result = [] + for i in xrange(0, count.value): + result.append(IndirectBranchInfo(Architecture(branches[i].sourceArch), branches[i].sourceAddr, Architecture(branches[i].destArch), branches[i].destAddr, branches[i].autoDefined)) + core.BNFreeIndirectBranchList(branches) + return result + class BasicBlockEdge: def __init__(self, branch_type, target, arch): self.type = core.BNBranchType_names[branch_type] @@ -2070,7 +2388,7 @@ class BasicBlockEdge: else: return "<%s: 0x%x>" % (self.type, self.target) -class BasicBlock: +class BasicBlock(object): def __init__(self, view, handle): self.view = view self.handle = core.handle_of_type(handle, core.BNBasicBlock) @@ -2078,48 +2396,64 @@ class BasicBlock: 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 + @property + def function(self): + """Basic block function (read-only)""" + func = core.BNGetBasicBlockFunction(self.handle) + if func is None: + return None + return Function(self.view, func) + + @property + def arch(self): + """Basic block architecture (read-only)""" + arch = core.BNGetBasicBlockArchitecture(self.handle) + if arch is None: + return None + return Architecture(arch) + + @property + def start(self): + """Basic block start (read-only)""" + return core.BNGetBasicBlockStart(self.handle) + + @property + def end(self): + """Basic block end (read-only)""" + return core.BNGetBasicBlockEnd(self.handle) + + @property + def length(self): + """Basic block length (read-only)""" + return core.BNGetBasicBlockLength(self.handle) + + @property + def outgoing_edges(self): + """List of basic block outgoing edges (read-only)""" + 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 + + @property + def has_undetermined_outgoing_edges(self): + """Whether basic block has undetermined outgoing edges (read-only)""" + return core.BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) 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")): + try: + object.__setattr__(self,name,value) + except AttributeError: 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)) @@ -2160,73 +2494,93 @@ class FunctionGraphEdge: return "<%s: %s@0x%x>" % (self.type, self.arch.name, self.target) return "<%s: 0x%x>" % (self.type, self.target) -class FunctionGraphBlock: +class FunctionGraphBlock(object): 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 + @property + def arch(self): + """Function graph block architecture (read-only)""" + arch = core.BNGetFunctionGraphBlockArchitecture(self.handle) + if arch is None: + return None + return Architecture(arch) + + @property + def start(self): + """Function graph block start (read-only)""" + return core.BNGetFunctionGraphBlockStart(self.handle) + + @property + def end(self): + """Function graph block end (read-only)""" + return core.BNGetFunctionGraphBlockEnd(self.handle) + + @property + def x(self): + """Function graph block X (read-only)""" + return core.BNGetFunctionGraphBlockX(self.handle) + + @property + def y(self): + """Function graph block Y (read-only)""" + return core.BNGetFunctionGraphBlockY(self.handle) + + @property + def width(self): + """Function graph block width (read-only)""" + return core.BNGetFunctionGraphBlockWidth(self.handle) + + @property + def height(self): + """Function graph block height (read-only)""" + return core.BNGetFunctionGraphBlockHeight(self.handle) + + @property + def lines(self): + """Function graph block list of lines (read-only)""" + 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 + + @property + def outgoing_edges(self): + """Function graph block list of outgoing edges (read-only)""" + 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 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")): + try: + object.__setattr__(self,name,value) + except AttributeError: 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 @@ -2235,7 +2589,7 @@ class FunctionGraphBlock: else: return "<graph block: 0x%x-0x%x>" % (self.start, self.end) -class FunctionGraph: +class FunctionGraph(object): def __init__(self, view, handle): self.view = view self.handle = handle @@ -2246,51 +2600,74 @@ class FunctionGraph: 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 + @property + def function(self): + """Function for a function graph(read-only)""" + func = core.BNGetFunctionForFunctionGraph(self.handle) + if func is None: + return None + return Function(self.view, func) + + @property + def complete(self): + """Whether function graph layout is complete (read-only)""" + return core.BNIsFunctionGraphLayoutComplete(self.handle) + + @property + def type(self): + """Function graph type (read-only)""" + return core.BNFunctionGraphType_names[core.BNGetFunctionGraphType(self.handle)] + + @property + def blocks(self): + """List of basic blocks in function (read-only)""" + 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 + + @property + def width(self): + """Function graph width (read-only)""" + return core.BNGetFunctionGraphWidth(self.handle) + + @property + def height(self): + """Function graph height (read-only)""" + return core.BNGetFunctionGraphHeight(self.handle) + + @property + def horizontal_block_margin(self): + return core.BNGetHorizontalFunctionGraphBlockMargin(self.handle) + + @horizontal_block_margin.setter + def horizontal_block_margin(self, value): + core.BNSetFunctionGraphBlockMargins(self.handle, value, self.vertical_block_margin) + + @property + def vertical_block_margin(self): + return core.BNGetVerticalFunctionGraphBlockMargin(self.handle) + + @vertical_block_margin.setter + def vertical_block_margin(self, value): + core.BNSetFunctionGraphBlockMargins(self.handle, self.horizontal_block_margin, value) + + @property + def max_symbol_width(self): + return core.BNGetFunctionGraphMaximumSymbolWidth(self.handle) + + @max_symbol_width.setter + def max_symbol_width(self, value): + core.BNSetFunctionGraphMaximumSymbolWidth(self.handle, value) def __setattr__(self, name, value): - if ((name == "function") or (name == "complete") or (name == "type") or (name == "blocks") or - (name == "width") or (name == "height")): + try: + object.__setattr__(self,name,value) + except AttributeError: 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) @@ -2407,26 +2784,17 @@ class InstructionTextToken: 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"] + @property + def list(self): + """Architecture list (read-only)""" + _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 def __getitem__(cls, name): _init_plugins() @@ -2443,7 +2811,13 @@ class _ArchitectureMetaClass(type): cls._registered_cb = arch._cb arch.handle = core.BNRegisterArchitecture(cls.name, arch._cb) -class Architecture: + def __setattr__(self, name, value): + try: + type.__setattr__(self,name,value) + except AttributeError: + raise AttributeError, "attribute '%s' is read only" % name + +class Architecture(object): name = None endianness = core.LittleEndian address_size = 8 @@ -2453,6 +2827,9 @@ class Architecture: link_reg = None flags = [] flag_write_types = [] + flag_roles = {} + flags_required_for_flag_condition = {} + flags_written_by_flag_write_type = {} __metaclass__ = _ArchitectureMetaClass def __init__(self, handle = None): @@ -2501,6 +2878,42 @@ class Architecture: self._flag_write_types_by_index[types[i]] = name self.flag_write_types.append(name) core.BNFreeRegisterList(types) + + self._flag_roles = {} + self.__dict__["flag_roles"] = {} + for flag in self.__dict__["flags"]: + role = core.BNGetArchitectureFlagRole(self.handle, self._flags[flag]) + self.__dict__["flag_roles"][flag] = role + self._flag_roles[self._flags[flag]] = role + + self._flags_required_for_flag_condition = {} + self.__dict__["flags_required_for_flag_condition"] = {} + for cond in core.BNLowLevelILFlagCondition_names.keys(): + count = ctypes.c_ulonglong() + flags = core.BNGetArchitectureFlagsRequiredForFlagCondition(self.handle, cond, count) + flag_indexes = [] + flag_names = [] + for i in xrange(0, count.value): + flag_indexes.append(flags[i]) + flag_names.append(self._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + self._flags_required_for_flag_condition[cond] = flag_indexes + self.__dict__["flags_required_for_flag_condition"][cond] = flag_names + + self._flags_written_by_flag_write_type = {} + self.__dict__["flags_written_by_flag_write_type"] = {} + for write_type in self.flag_write_types: + count = ctypes.c_ulonglong() + flags = core.BNGetArchitectureFlagsWrittenByFlagWriteType(self.handle, + self._flag_write_types[write_type], count) + flag_indexes = [] + flag_names = [] + for i in xrange(0, count.value): + flag_indexes.append(flags[i]) + flag_names.append(self._flags_by_index[flags[i]]) + core.BNFreeRegisterList(flags) + self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flag_indexes + self.__dict__["flags_written_by_flag_write_type"][write_type] = flag_names else: _init_plugins() self._cb = core.BNCustomArchitecture() @@ -2521,6 +2934,15 @@ class Architecture: 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.getFlagRole = self._cb.getFlagRole.__class__(self._get_flag_role) + self._cb.getFlagsRequiredForFlagCondition = self._cb.getFlagsRequiredForFlagCondition.__class__( + self._get_flags_required_for_flag_condition) + self._cb.getFlagsWrittenByFlagWriteType = self._cb.getFlagsWrittenByFlagWriteType.__class__( + self._get_flags_written_by_flag_write_type) + self._cb.getFlagWriteLowLevelIL = self._cb.getFlagWriteLowLevelIL.__class__( + self._get_flag_write_low_level_il) + self._cb.getFlagConditionLowLevelIL = self._cb.getFlagConditionLowLevelIL.__class__( + self._get_flag_condition_low_level_il) 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__( @@ -2582,43 +3004,71 @@ class Architecture: self._flag_write_types_by_index[write_type_index] = write_type write_type_index += 1 + self._flag_roles = {} + self.__dict__["flag_roles"] = self.__class__.flag_roles + for flag in self.__class__.flag_roles.keys(): + role = self.__class__.flag_roles[flag] + if isinstance(role, str): + role = core.BNFlagRole_by_name[role] + self._flag_roles[self._flags[flag]] = role + + self._flags_required_for_flag_condition = {} + self.__dict__["flags_required_for_flag_condition"] = self.__class__.flags_required_for_flag_condition + for cond in self.__class__.flags_required_for_flag_condition.keys(): + flags = [] + for flag in self.__class__.flags_required_for_flag_condition[cond]: + flags.append(self._flags[flag]) + self._flags_required_for_flag_condition[cond] = flags + + self._flags_written_by_flag_write_type = {} + self.__dict__["flags_written_by_flag_write_type"] = self.__class__.flags_written_by_flag_write_type + for write_type in self.__class__.flags_written_by_flag_write_type.keys(): + flags = [] + for flag in self.__class__.flags_written_by_flag_write_type[write_type]: + flags.append(self._flags[flag]) + self._flags_written_by_flag_write_type[self._flag_write_types[write_type]] = flags + self._pending_reg_lists = {} self._pending_token_lists = {} + @property + def full_width_regs(self): + """List of full width registers (read-only)""" + 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 - 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) + @property + def calling_conventions(self): + """Dict of calling conventions (read-only)""" + 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 - raise AttributeError, "no attribute '%s'" % name + @property + def standalone_platform(self): + """Architecture standalone platform (read-only)""" + pl = core.BNGetArchitectureStandalonePlatform(self.handle) + return Platform(self, pl) 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")): + (name == "regs")): 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"] + try: + object.__setattr__(self,name,value) + except AttributeError: + raise AttributeError, "attribute '%s' is read only" % name def __repr__(self): return "<arch: %s>" % self.name @@ -2808,6 +3258,79 @@ class Architecture: count[0] = 0 return None + def _get_flag_role(self, ctxt, flag): + try: + if flag in self._flag_roles: + return self._flag_roles[flag] + return core.SpecialFlagRole + except: + log_error(traceback.format_exc()) + return None + + def _get_flags_required_for_flag_condition(self, ctxt, cond, count): + try: + if cond in self._flags_required_for_flag_condition: + flags = self._flags_required_for_flag_condition[cond] + else: + flags = [] + 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_flags_written_by_flag_write_type(self, ctxt, write_type, count): + try: + if write_type in self._flags_written_by_flag_write_type: + flags = self._flags_written_by_flag_write_type[write_type] + else: + flags = [] + 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_flag_write_low_level_il(self, ctxt, op, size, write_type, flag, operands, operand_count, il): + try: + write_type_name = None + if write_type != 0: + write_type_name = self._flag_write_types_by_index[write_type] + flag_name = self._flags_by_index[flag] + operand_list = [] + for i in xrange(operand_count): + if operands[i].constant: + operand_list.append(("const", operands[i].value)) + elif LLIL_REG_IS_TEMP(operands[i].reg): + operand_list.append(("reg", operands[i].reg)) + else: + operand_list.append(("reg", self._regs_by_index[operands[i].reg])) + return self.perform_get_flag_write_low_level_il(op, size, write_type_name, flag_name, operand_list, + LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index + except: + log_error(traceback.format_exc()) + return False + + def _get_flag_condition_low_level_il(self, ctxt, cond, il): + try: + return self.perform_get_flag_condition_low_level_il(cond, + LowLevelILFunction(self, core.BNNewLowLevelILFunctionReference(il))).index + except: + log_error(traceback.format_exc()) + return 0 + def _free_register_list(self, ctxt, regs): try: buf = ctypes.cast(regs, ctypes.c_void_p) @@ -2984,6 +3507,12 @@ class Architecture: def perform_get_instruction_low_level_il(self, data, addr, il): return None + def perform_get_flag_write_low_level_il(self, op, size, write_type, flag, operands, il): + return il.unimplemented() + + def perform_get_flag_condition_low_level_il(self, cond, il): + return il.unimplemented() + def perform_assemble(self, code, addr): return None, "Architecture does not implement an assembler.\n" @@ -3054,7 +3583,6 @@ class Architecture: 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) @@ -3078,6 +3606,33 @@ class Architecture: def get_flag_write_type_by_name(self, write_type): return self._flag_write_types[write_type] + def get_flag_write_low_level_il(self, op, size, write_type, operands, il): + operand_list = (core.BNRegisterOrConstant * len(operands))() + for i in xrange(len(operands)): + if isinstance(operands[i], str): + operand_list[i].constant = False + operand_list[i].reg = self._flags[operands[i]] + else: + operand_list[i].constant = True + operand_list[i].value = operands[i] + return core.BNGetArchitectureFlagWriteLowLevelIL(self.handle, op, size, self._flag_write_types[write_type], + operand_list, len(operand_list), il.handle) + + def get_default_flag_write_low_level_il(self, op, size, write_type, operands, il): + operand_list = (core.BNRegisterOrConstant * len(operands))() + for i in xrange(len(operands)): + if isinstance(operands[i], str): + operand_list[i].constant = False + operand_list[i].reg = self._flags[operands[i]] + else: + operand_list[i].constant = True + operand_list[i].value = operands[i] + return core.BNGetDefaultArchitectureFlagWriteLowLevelIL(self.handle, op, size, self._flag_write_types[write_type], + operand_list, len(operand_list), il.handle) + + def get_flag_condition_low_level_il(self, cond, il): + return core.BNGetArchitectureFlagConditionLowLevelIL(self.handle, cond, il.handle) + def get_modified_regs_on_write(self, reg): reg = core.BNGetArchitectureRegisterByName(self.handle, str(reg)) count = ctypes.c_ulonglong() @@ -3243,7 +3798,7 @@ class LowLevelILLabel: else: self.handle = handle -class LowLevelILInstruction: +class LowLevelILInstruction(object): ILOperations = { core.LLIL_NOP: [], core.LLIL_SET_REG: [("dest", "reg"), ("src", "expr")], @@ -3287,6 +3842,7 @@ class LowLevelILInstruction: core.LLIL_SX: [("src", "expr")], core.LLIL_ZX: [("src", "expr")], core.LLIL_JUMP: [("dest", "expr")], + core.LLIL_JUMP_TO: [("dest", "expr"), ("targets", "int_list")], core.LLIL_CALL: [("dest", "expr")], core.LLIL_RET: [("dest", "expr")], core.LLIL_NORET: [], @@ -3344,6 +3900,13 @@ class LowLevelILInstruction: value = func.arch.get_flag_name(instr.operands[i]) elif operand_type == "cond": value = core.BNLowLevelILFlagCondition_names[instr.operands[i]] + elif operand_type == "int_list": + count = ctypes.c_ulonglong() + operands = core.BNLowLevelILGetOperandList(func.handle, self.index, i, count) + value = [] + for i in xrange(count.value): + value.append(operands[i]) + core.BNLowLevelILFreeOperandList(operands) self.operands.append(value) self.__dict__[name] = value @@ -3359,37 +3922,34 @@ class LowLevelILInstruction: 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 + @property + def tokens(self): + """LLIL tokens (read-only)""" + 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 def __setattr__(self, name, value): - if name == "tokens": + try: + object.__setattr__(self,name,value) + except AttributeError: 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: +class LowLevelILFunction(object): def __init__(self, arch, handle = None): self.arch = arch if handle is not None: @@ -3400,19 +3960,29 @@ class LowLevelILFunction: 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 + @property + def current_address(self): + return core.BNLowLevelILGetCurrentAddress(self.handle) - def __setattr__(self, name, value): - if name == "current_address": - core.BNLowLevelILSetCurrentAddress(self.handle, value) - else: - self.__dict__[name] = value + @current_address.setter + def current_address(self, value): + core.BNLowLevelILSetCurrentAddress(self.handle, value) - def __dir__(self): - return dir(self.__class__) + ["current_address"] + @property + def temp_reg_count(self): + """Number of temporary registers (read-only)""" + return core.BNGetLowLevelILTemporaryRegisterCount(self.handle) + + @property + def temp_flag_count(self): + """Number of temporary flags (read-only)""" + return core.BNGetLowLevelILTemporaryFlagCount(self.handle) + + def __setattr__(self, name, value): + try: + object.__setattr__(self,name,value) + except AttributeError: + raise AttributeError, "attribute '%s' is read only" % name def __len__(self): return int(core.BNGetLowLevelILInstructionCount(self.handle)) @@ -3429,6 +3999,16 @@ class LowLevelILFunction: def __setitem__(self, i): raise IndexError, "instruction modification not implemented" + def clear_indirect_branches(self): + core.BNLowLevelILClearIndirectBranches(self.handle) + + def set_indirect_branches(self, branches): + branch_list = (core.BNArchitectureAndAddress * len(branches))() + for i in xrange(len(branches)): + branch_list[i].arch = branches[i][0].handle + branch_list[i].address = branches[i][1] + core.BNLowLevelILSetIndirectBranches(self.handle, branch_list, len(branches)) + 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] @@ -3649,6 +4229,18 @@ class LowLevelILFunction: def mark_label(self, label): core.BNLowLevelILMarkLabel(self.handle, label.handle) + def add_label_list(self, labels): + label_list = (ctypes.POINTER(BNLowLevelILLabel) * len(labels))() + for i in xrange(len(labels)): + label_list[i] = labels[i].handle + return LowLevelILExpr(core.BNLowLevelILAddLabelList(self.handle, label_list, len(labels))) + + def add_operand_list(self, operands): + operand_list = (ctypes.c_ulonglong * len(operands))() + for i in xrange(len(operands)): + operand_list[i] = operands[i] + return LowLevelILExpr(core.BNLowLevelILAddOperandList(self.handle, operand_list, len(operands))) + def operand(self, n, expr): core.BNLowLevelILSetExprSourceOperand(self.handle, expr.index, n) return expr @@ -3679,26 +4271,22 @@ class TypeParserResult: 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 + @property + def list(self): + _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 - def __setattr__(cls, name, value): - if (name == "list"): + def __setattr__(self, name, value): + try: + type.__setattr__(self,name,value) + except AttributeError: 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() @@ -3901,35 +4489,35 @@ class FunctionRecognizer: 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 + @property + def list(self): + _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 - 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) + @property + def active(self): + return core.BNGetActiveUpdateChannel() - def __dir__(self): - return dir(self.__class__) + ["list","active"] + @active.setter + def active(self, value): + return core.BNSetActiveUpdateChannel(value) + + def __setattr__(self, name, value): + try: + type.__setattr__(self,name,value) + except AttributeError: + raise AttributeError, "attribute '%s' is read only" % name def __getitem__(cls, name): _init_plugins() @@ -3963,7 +4551,7 @@ class UpdateProgressCallback: except: log_error(traceback.format_exc()) -class UpdateChannel: +class UpdateChannel(object): __metaclass__ = _UpdateChannelMetaClass def __init__(self, name, desc, ver): @@ -3971,53 +4559,56 @@ class UpdateChannel: 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 + @property + def versions(self): + """List of versions (read-only)""" + 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 + + @property + def latest_version(self): + """Latest version (read-only)""" + 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 + + @property + def updates_available(self): + """Whether updates are available (read-only)""" + 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 def __setattr__(self, name, value): - if (name == "versions") or (name == "latest_version"): + try: + object.__setattr__(self,name,value) + except AttributeError: 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 @@ -4066,26 +4657,22 @@ class PluginCommandContext: 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 + @property + def list(self): + _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 - def __setattr__(cls, name, value): - if (name == "list"): + def __setattr__(self, name, value): + try: + type.__setattr__(self,name,value) + except AttributeError: 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 = [] @@ -4450,35 +5037,33 @@ class CallingConvention: 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 + @property + def list(self): + _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 - 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) + @property + def os_list(self): + _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 - def __dir__(self): - return dir(self.__class__) + ["list","os_list"] + def __setattr__(self, name, value): + try: + type.__setattr__(self,name,value) + except AttributeError: + raise AttributeError, "attribute '%s' is read only" % name def __getitem__(cls, value): _init_plugins() @@ -4518,61 +5103,77 @@ class Platform: 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 + @property + def default_calling_convention(self): + result = core.BNGetPlatformDefaultCallingConvention(self.handle) + if result is None: + return None + return CallingConvention(None, result) + + @default_calling_convention.setter + def default_calling_convention(self, value): + core.BNRegisterPlatformDefaultCallingConvention(self.handle, value.handle) + + @property + def cdecl_calling_convention(self): + result = core.BNGetPlatformCdeclCallingConvention(self.handle) + if result is None: + return None + return CallingConvention(None, result) + + @cdecl_calling_convention.setter + def cdecl_calling_convention(self, value): + core.BNRegisterPlatformCdeclCallingConvention(self.handle, value.handle) + + @property + def stdcall_calling_convention(self): + result = core.BNGetPlatformStdcallCallingConvention(self.handle) + if result is None: + return None + return CallingConvention(None, result) + + @stdcall_calling_convention.setter + def stdcall_calling_convention(self, value): + core.BNRegisterPlatformStdcallCallingConvention(self.handle, value.handle) + + @property + def fastcall_calling_convention(self): + result = core.BNGetPlatformFastcallCallingConvention(self.handle) + if result is None: + return None + return CallingConvention(None, result) + + @fastcall_calling_convention.setter + def fastcall_calling_convention(self, value): + core.BNRegisterPlatformFastcallCallingConvention(self.handle, value.handle) + + @property + def system_call_convention(self): + result = core.BNGetPlatformSystemCallConvention(self.handle) + if result is None: + return None + return CallingConvention(None, result) + + @system_call_convention.setter + def system_call_convention(self, value): + core.BNSetPlatformSystemCallConvention(self.handle, value.handle) + + @property + def calling_conventions(self): + """List of platform calling conventions (read-only)""" + 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 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"): + try: + object.__setattr__(self,name,value) + except AttributeError: 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 diff --git a/python/examples/jump-table.py b/python/examples/jump-table.py new file mode 100644 index 00000000..f057f52c --- /dev/null +++ b/python/examples/jump-table.py @@ -0,0 +1,63 @@ +# This plugin will attempt to resolve simple jump tables (an array of code pointers) and add the desintations +# as indirect branch targets so that the flow graph reflects the jump table's control flow. +from binaryninja import * +import struct + +def find_jump_table(bv, addr): + for block in bv.get_basic_blocks_at(addr): + func = block.function + arch = func.arch + addrsize = arch.address_size + + # Grab the instruction tokens so that we can look for the table's starting address + tokens, length = arch.get_instruction_text(bv.read(addr, 16), addr) + + # Look for the next jump instruction, which may be the current instruction. Some jump tables will + # compute the address first then jump to the computed address as a separate instruction. + jump_addr = addr + while jump_addr < block.end: + info = arch.get_instruction_info(bv.read(jump_addr, 16), jump_addr) + if len(info.branches) != 0: + break + jump_addr += info.length + if jump_addr >= block.end: + print "Unable to find jump after instruction 0x%x" % addr + continue + print "Jump at 0x%x" % jump_addr + + # Collect the branch targets for any tables referenced by the clicked instruction + branches = [] + for token in tokens: + if token.type == "PossibleAddressToken": # Table addresses will be a "possible address" token + tbl = token.value + print "Found possible table at 0x%x" % tbl + i = 0 + while True: + # Read the next pointer from the table + data = bv.read(tbl + (i * addrsize), addrsize) + if len(data) == addrsize: + if addrsize == 4: + ptr = struct.unpack("<I", data)[0] + else: + ptr = struct.unpack("<Q", data)[0] + + # If the pointer is within the binary, add it as a destination and continue + # to the next entry + if (ptr >= bv.start) and (ptr < bv.end): + print "Found destination 0x%x" % ptr + branches.append((arch, ptr)) + else: + # Once a value that is not a pointer is encountered, the jump table is ended + break + else: + # Reading invalid memory + break + + i += 1 + + # Set the indirect branch targets on the jump instruction to be the list of targets discovered + func.set_user_indirect_branches(arch, jump_addr, branches) + +# Create a plugin command so that the user can right click on an instruction referencing a jump table and +# invoke the command +PluginCommand.register_for_address("Process jump table", "Look for jump table destinations", find_jump_table) diff --git a/python/examples/nes.py b/python/examples/nes.py index 8387ad74..21b02dfa 100644 --- a/python/examples/nes.py +++ b/python/examples/nes.py @@ -231,8 +231,8 @@ OperandIL = [ 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 il[dest].operation == LLIL_CONST: + t = il.get_label_for_address(Architecture['6502'], il[dest].value) if t is None: t = LowLevelILLabel() indirect = True @@ -248,8 +248,8 @@ def cond_branch(il, cond, dest): 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 il[dest].operation == LLIL_CONST: + label = il.get_label_for_address(Architecture['6502'], il[dest].value) if label is None: il.append(il.jump(dest)) else: @@ -291,9 +291,9 @@ InstructionIL = { "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), + "bmi": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_NEG), 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), + "bpl": lambda il, operand: cond_branch(il, il.flag_condition(LLFC_POS), 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), @@ -358,6 +358,26 @@ class M6502(Architecture): stack_pointer = "s" flags = ["c", "z", "i", "d", "b", "v", "s"] flag_write_types = ["*", "czs", "zvs", "zs"] + flag_roles = { + "c": SpecialFlagRole, # Not a normal carry flag, subtract result is inverted + "z": ZeroFlagRole, + "v": OverflowFlagRole, + "s": NegativeSignFlagRole + } + flags_required_for_flag_condition = { + LLFC_UGE: ["c"], + LLFC_ULT: ["c"], + LLFC_E: ["z"], + LLFC_NE: ["z"], + LLFC_NEG: ["s"], + LLFC_POS: ["s"] + } + flags_written_by_flag_write_type = { + "*": ["c", "z", "v", "s"], + "czs": ["c", "z", "s"], + "zvs": ["z", "v", "s"], + "zs": ["z", "s"] + } def decode_instruction(self, data, addr): if len(data) < 1: @@ -678,4 +698,3 @@ for i in xrange(0, 32): NESViewBank.register() M6502.register() - |
