Source code for binaryninja.platform

# Copyright (c) 2015-2017 Vector 35 LLC
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

import ctypes

# Binary Ninja components
import binaryninja
from binaryninja import _binaryninjacore as core
from binaryninja import types

# 2-3 compatibility
from binaryninja import range
from binaryninja import with_metaclass


class _PlatformMetaClass(type):

	@property
	def list(self):
		binaryninja._init_plugins()
		count = ctypes.c_ulonglong()
		platforms = core.BNGetPlatformList(count)
		result = []
		for i in range(0, count.value):
			result.append(Platform(None, core.BNNewPlatformReference(platforms[i])))
		core.BNFreePlatformList(platforms, count.value)
		return result

	@property
	def os_list(self):
		binaryninja._init_plugins()
		count = ctypes.c_ulonglong()
		platforms = core.BNGetPlatformOSList(count)
		result = []
		for i in range(0, count.value):
			result.append(str(platforms[i]))
		core.BNFreePlatformOSList(platforms, count.value)
		return result

	def __iter__(self):
		binaryninja._init_plugins()
		count = ctypes.c_ulonglong()
		platforms = core.BNGetPlatformList(count)
		try:
			for i in range(0, count.value):
				yield Platform(None, core.BNNewPlatformReference(platforms[i]))
		finally:
			core.BNFreePlatformList(platforms, count.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, value):
		binaryninja._init_plugins()
		platform = core.BNGetPlatformByName(str(value))
		if platform is None:
			raise KeyError("'%s' is not a valid platform" % str(value))
		return Platform(None, platform)

	def get_list(cls, os = None, arch = None):
		binaryninja._init_plugins()
		count = ctypes.c_ulonglong()
		if os is None:
			platforms = core.BNGetPlatformList(count)
		elif arch is None:
			platforms = core.BNGetPlatformListByOS(os)
		else:
			platforms = core.BNGetPlatformListByArchitecture(os, arch.handle)
		result = []
		for i in range(0, count.value):
			result.append(Platform(None, core.BNNewPlatformReference(platforms[i])))
		core.BNFreePlatformList(platforms, count.value)
		return result


[docs]class Platform(with_metaclass(_PlatformMetaClass, object)): """ ``class Platform`` contains all information releated to the execution environment of the binary, mainly the calling conventions used. """ name = None
[docs] def __init__(self, arch, handle = None): if handle is None: self.arch = arch self.handle = core.BNCreatePlatform(arch.handle, self.__class__.name) else: self.handle = handle self.__dict__["name"] = core.BNGetPlatformName(self.handle) self.arch = binaryninja.architecture.CoreArchitecture._from_cache(core.BNGetPlatformArchitecture(self.handle))
def __del__(self): core.BNFreePlatform(self.handle) def __eq__(self, value): if not isinstance(value, Platform): return False return ctypes.addressof(self.handle.contents) == ctypes.addressof(value.handle.contents) def __ne__(self, value): if not isinstance(value, Platform): return True return ctypes.addressof(self.handle.contents) != ctypes.addressof(value.handle.contents) @property def list(self): """Allow tab completion to discover metaclass list property""" pass @property def default_calling_convention(self): """ Default calling convention. :getter: returns a CallingConvention object for the default calling convention. :setter: sets the default calling convention :type: CallingConvention """ result = core.BNGetPlatformDefaultCallingConvention(self.handle) if result is None: return None return binaryninja.callingconvention.CallingConvention(handle=result) @default_calling_convention.setter def default_calling_convention(self, value): core.BNRegisterPlatformDefaultCallingConvention(self.handle, value.handle) @property def cdecl_calling_convention(self): """ Cdecl calling convention. :getter: returns a CallingConvention object for the cdecl calling convention. :setter sets the cdecl calling convention :type: CallingConvention """ result = core.BNGetPlatformCdeclCallingConvention(self.handle) if result is None: return None return binaryninja.callingconvention.CallingConvention(handle=result) @cdecl_calling_convention.setter def cdecl_calling_convention(self, value): core.BNRegisterPlatformCdeclCallingConvention(self.handle, value.handle) @property def stdcall_calling_convention(self): """ Stdcall calling convention. :getter: returns a CallingConvention object for the stdcall calling convention. :setter sets the stdcall calling convention :type: CallingConvention """ result = core.BNGetPlatformStdcallCallingConvention(self.handle) if result is None: return None return binaryninja.callingconvention.CallingConvention(handle=result) @stdcall_calling_convention.setter def stdcall_calling_convention(self, value): core.BNRegisterPlatformStdcallCallingConvention(self.handle, value.handle) @property def fastcall_calling_convention(self): """ Fastcall calling convention. :getter: returns a CallingConvention object for the fastcall calling convention. :setter sets the fastcall calling convention :type: CallingConvention """ result = core.BNGetPlatformFastcallCallingConvention(self.handle) if result is None: return None return binaryninja.callingconvention.CallingConvention(handle=result) @fastcall_calling_convention.setter def fastcall_calling_convention(self, value): core.BNRegisterPlatformFastcallCallingConvention(self.handle, value.handle) @property def system_call_convention(self): """ System call convention. :getter: returns a CallingConvention object for the system call convention. :setter sets the system call convention :type: CallingConvention """ result = core.BNGetPlatformSystemCallConvention(self.handle) if result is None: return None return binaryninja.callingconvention.CallingConvention(handle=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 CallingConvention objects (read-only) :getter: returns the list of supported CallingConvention objects :type: list(CallingConvention) """ count = ctypes.c_ulonglong() cc = core.BNGetPlatformCallingConventions(self.handle, count) result = [] for i in range(0, count.value): result.append(binaryninja.callingconvention.CallingConvention(handle=core.BNNewCallingConventionReference(cc[i]))) core.BNFreeCallingConventionList(cc, count.value) return result @property def types(self): """List of platform-specific types (read-only)""" count = ctypes.c_ulonglong(0) type_list = core.BNGetPlatformTypes(self.handle, count) result = {} for i in range(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) core.BNFreeTypeList(type_list, count.value) return result @property def variables(self): """List of platform-specific variable definitions (read-only)""" count = ctypes.c_ulonglong(0) type_list = core.BNGetPlatformVariables(self.handle, count) result = {} for i in range(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) core.BNFreeTypeList(type_list, count.value) return result @property def functions(self): """List of platform-specific function definitions (read-only)""" count = ctypes.c_ulonglong(0) type_list = core.BNGetPlatformFunctions(self.handle, count) result = {} for i in range(0, count.value): name = types.QualifiedName._from_core_struct(type_list[i].name) result[name] = types.Type(core.BNNewTypeReference(type_list[i].type), platform = self) core.BNFreeTypeList(type_list, count.value) return result @property def system_calls(self): """List of system calls for this platform (read-only)""" count = ctypes.c_ulonglong(0) call_list = core.BNGetPlatformSystemCalls(self.handle, count) result = {} for i in range(0, count.value): name = types.QualifiedName._from_core_struct(call_list[i].name) t = types.Type(core.BNNewTypeReference(call_list[i].type), platform = self) result[call_list[i].number] = (name, t) core.BNFreeSystemCallList(call_list, count.value) return result def __setattr__(self, name, value): try: object.__setattr__(self, name, value) except AttributeError: raise AttributeError("attribute '%s' is read only" % name) def __repr__(self): return "<platform: %s>" % self.name def __str__(self): return self.name
[docs] def register(self, os): """ ``register`` registers the platform for given OS name. :param str os: OS name to register :rtype: None """ core.BNRegisterPlatform(os, self.handle)
[docs] def register_calling_convention(self, cc): """ ``register_calling_convention`` register a new calling convention. :param CallingConvention cc: a CallingConvention object to register :rtype: None """ core.BNRegisterPlatformCallingConvention(self.handle, cc.handle)
[docs] def get_associated_platform_by_address(self, addr): new_addr = ctypes.c_ulonglong() new_addr.value = addr result = core.BNGetAssociatedPlatformByAddress(self.handle, new_addr) return Platform(None, handle = result), new_addr.value
[docs] def get_type_by_name(self, name): name = types.QualifiedName(name)._get_core_struct() obj = core.BNGetPlatformTypeByName(self.handle, name) if not obj: return None return types.Type(obj, platform = self)
[docs] def get_variable_by_name(self, name): name = types.QualifiedName(name)._get_core_struct() obj = core.BNGetPlatformVariableByName(self.handle, name) if not obj: return None return types.Type(obj, platform = self)
[docs] def get_function_by_name(self, name): name = types.QualifiedName(name)._get_core_struct() obj = core.BNGetPlatformFunctionByName(self.handle, name) if not obj: return None return types.Type(obj, platform = self)
[docs] def get_system_call_name(self, number): return core.BNGetPlatformSystemCallName(self.handle, number)
[docs] def get_system_call_type(self, number): obj = core.BNGetPlatformSystemCallType(self.handle, number) if not obj: return None return types.Type(obj, platform = self)
[docs] def generate_auto_platform_type_id(self, name): name = types.QualifiedName(name)._get_core_struct() return core.BNGenerateAutoPlatformTypeId(self.handle, name)
[docs] def generate_auto_platform_type_ref(self, type_class, name): type_id = self.generate_auto_platform_type_id(name) return types.NamedTypeReference(type_class, type_id, name)
[docs] def get_auto_platform_type_id_source(self): return core.BNGetAutoPlatformTypeIdSource(self.handle)
[docs] def parse_types_from_source(self, source, filename=None, include_dirs=[], auto_type_source=None): """ ``parse_types_from_source`` parses the source string and any needed headers searching for them in the optional list of directories provided in ``include_dirs``. :param str source: source string to be parsed :param str filename: optional source filename :param list(str) include_dirs: optional list of string filename include directories :param str auto_type_source: optional source of types if used for automatically generated types :return: :py:class:`TypeParserResult` (a SyntaxError is thrown on parse error) :rtype: TypeParserResult :Example: >>> platform.parse_types_from_source('int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n') ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions:{'bar': <type: int32_t(int32_t x)>}}, '') >>> """ if filename is None: filename = "input" dir_buf = (ctypes.c_char_p * len(include_dirs))() for i in range(0, len(include_dirs)): dir_buf[i] = include_dirs[i].encode('charmap') parse = core.BNTypeParserResult() errors = ctypes.c_char_p() result = core.BNParseTypesFromSource(self.handle, source, filename, parse, errors, dir_buf, len(include_dirs), auto_type_source) error_str = errors.value core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) if not result: raise SyntaxError(error_str) type_dict = {} variables = {} functions = {} for i in range(0, parse.typeCount): name = types.QualifiedName._from_core_struct(parse.types[i].name) type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self) for i in range(0, parse.variableCount): name = types.QualifiedName._from_core_struct(parse.variables[i].name) variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self) for i in range(0, parse.functionCount): name = types.QualifiedName._from_core_struct(parse.functions[i].name) functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self) core.BNFreeTypeParserResult(parse) return types.TypeParserResult(type_dict, variables, functions)
[docs] def parse_types_from_source_file(self, filename, include_dirs=[], auto_type_source=None): """ ``parse_types_from_source_file`` parses the source file ``filename`` and any needed headers searching for them in the optional list of directories provided in ``include_dirs``. :param str filename: filename of file to be parsed :param list(str) include_dirs: optional list of string filename include directories :param str auto_type_source: optional source of types if used for automatically generated types :return: :py:class:`TypeParserResult` (a SyntaxError is thrown on parse error) :rtype: TypeParserResult :Example: >>> file = "/Users/binja/tmp.c" >>> open(file).read() 'int foo;\\nint bar(int x);\\nstruct bas{int x,y;};\\n' >>> platform.parse_types_from_source_file(file) ({types: {'bas': <type: struct bas>}, variables: {'foo': <type: int32_t>}, functions: {'bar': <type: int32_t(int32_t x)>}}, '') >>> """ dir_buf = (ctypes.c_char_p * len(include_dirs))() for i in range(0, len(include_dirs)): dir_buf[i] = include_dirs[i].encode('charmap') parse = core.BNTypeParserResult() errors = ctypes.c_char_p() result = core.BNParseTypesFromSourceFile(self.handle, filename, parse, errors, dir_buf, len(include_dirs), auto_type_source) error_str = errors.value core.BNFreeString(ctypes.cast(errors, ctypes.POINTER(ctypes.c_byte))) if not result: raise SyntaxError(error_str) type_dict = {} variables = {} functions = {} for i in range(0, parse.typeCount): name = types.QualifiedName._from_core_struct(parse.types[i].name) type_dict[name] = types.Type(core.BNNewTypeReference(parse.types[i].type), platform = self) for i in range(0, parse.variableCount): name = types.QualifiedName._from_core_struct(parse.variables[i].name) variables[name] = types.Type(core.BNNewTypeReference(parse.variables[i].type), platform = self) for i in range(0, parse.functionCount): name = types.QualifiedName._from_core_struct(parse.functions[i].name) functions[name] = types.Type(core.BNNewTypeReference(parse.functions[i].type), platform = self) core.BNFreeTypeParserResult(parse) return types.TypeParserResult(type_dict, variables, functions)