summaryrefslogtreecommitdiff
path: root/python/metadata.py
diff options
context:
space:
mode:
authorPeter LaFosse <peter@vector35.com>2017-07-12 16:04:28 -0400
committerPeter LaFosse <peter@vector35.com>2017-07-12 16:04:42 -0400
commit3d3b803d18f0d61b5e36367c9eb660b234cdc66e (patch)
tree1d64b9c63997d6514bc86b12f26c5891a07287c9 /python/metadata.py
parent00961d39ab43a1476b449b858d00f8e43f0928cd (diff)
Metadata enhancements. Metadata objects are now serialized to the DB
Diffstat (limited to 'python/metadata.py')
-rw-r--r--python/metadata.py248
1 files changed, 248 insertions, 0 deletions
diff --git a/python/metadata.py b/python/metadata.py
new file mode 100644
index 00000000..f0e7764d
--- /dev/null
+++ b/python/metadata.py
@@ -0,0 +1,248 @@
+# 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 _binaryninjacore as core
+from enums import MetadataType
+
+
+class Metadata(object):
+ def __init__(self, value=None, signed=None, raw=None, handle=None):
+ if handle is not None:
+ self.handle = handle
+ elif isinstance(value, int):
+ if signed:
+ self.handle = core.BNCreateMetadataSignedIntegerData(value)
+ else:
+ self.handle = core.BNCreateMetadataUnsignedIntegerData(value)
+ elif isinstance(value, bool):
+ self.handle = core.BNCreateMetadataBooleanData(value)
+ elif isinstance(value, str):
+ if raw:
+ buffer = (ctypes.c_ubyte * len(value)).from_buffer_copy(value)
+ self.handle = core.BNCreateMetadataRawData(buffer, len(value))
+ else:
+ self.handle = core.BNCreateMetadataStringData(value)
+ elif isinstance(value, float):
+ self.handle = core.BNCreateMetadataDoubleData(value)
+ elif isinstance(value, list):
+ self.handle = core.BNCreateMetadataOfType(MetadataType.ArrayDataType)
+ for elm in value:
+ md = Metadata(elm, signed, raw)
+ core.BNMetadataArrayAppend(self.handle, md.handle)
+ elif isinstance(value, dict):
+ self.handle = core.BNCreateMetadataOfType(MetadataType.KeyValueDataType)
+ for elm in value:
+ md = Metadata(value[elm], signed, raw)
+ core.BNMetadataSetValueForKey(self.handle, str(elm), md.handle)
+ else:
+ raise ValueError("List doesn't not contain type of: int, bool, str, float, list, dict")
+
+ @property
+ def value(self):
+ if self.is_integer:
+ return int(self)
+ elif self.is_string or self.is_raw:
+ return str(self)
+ elif self.is_float:
+ return float(self)
+ elif self.is_boolean:
+ return bool(self)
+ elif self.is_array:
+ return list(self)
+ elif self.is_dict:
+ return dict(self)
+ raise NotImplementedError()
+
+ @property
+ def type(self):
+ return MetadataType(core.BNMetadataGetType(self.handle))
+
+ @property
+ def is_integer(self):
+ return self.is_signed_integer or self.is_unsigned_integer
+
+ @property
+ def is_signed_integer(self):
+ return core.BNMetadataIsSignedInteger(self.handle)
+
+ @property
+ def is_unsigned_integer(self):
+ return core.BNMetadataIsUnsignedInteger(self.handle)
+
+ @property
+ def is_float(self):
+ return core.BNMetadataIsDouble(self.handle)
+
+ @property
+ def is_boolean(self):
+ return core.BNMetadataIsBoolean(self.handle)
+
+ @property
+ def is_string(self):
+ return core.BNMetadataIsString(self.handle)
+
+ @property
+ def is_raw(self):
+ return core.BNMetadataIsRaw(self.handle)
+
+ @property
+ def is_array(self):
+ return core.BNMetadataIsArray(self.handle)
+
+ @property
+ def is_dict(self):
+ return core.BNMetadataIsKeyValueStore(self.handle)
+
+ def __len__(self):
+ if self.is_array or self.is_dict or self.is_string or self.is_raw:
+ return core.BNMetadataSize(self.handle)
+ raise Exception("Metadata object doesn't support len()")
+
+ def __iter__(self):
+ if self.is_array:
+ for i in xrange(core.BNMetadataSize(self.handle)):
+ yield Metadata(handle=core.BNMetadataGetForIdx(self.handle, i))
+ elif self.is_dict:
+ result = core.BNMetadataGetValueStore(self.handle)
+ try:
+ for i in xrange(result.contents.size):
+ yield result.contents.keys[i]
+ finally:
+ core.BNFreeMetadataValueStore(result)
+ else:
+ raise Exception("Metadata object doesn't support iteration")
+
+ def __getitem__(self, value):
+ if self.is_array:
+ if not isinstance(value, int):
+ raise ValueError("Metadata object only supports integers for indexing")
+ if value >= len(self):
+ raise IndexError("Index value out of range")
+ return Metadata(handle=core.BNMetadataGetForIdx(self.handle, value))
+ if self.is_dict:
+ if not isinstance(value, str):
+ raise ValueError("Metadata object only supports strings for indexing")
+ handle = core.BNMetadataGetForKey(self.handle, value)
+ if handle is None:
+ raise KeyError(value)
+ return Metadata(handle=handle)
+
+ def __str__(self):
+ if self.is_string:
+ return core.BNMetadataGetString(self.handle)
+ if self.is_raw:
+ length = ctypes.c_ulonglong()
+ length.value = 0
+ native_list = core.BNMetadataGetRaw(self.handle, ctypes.byref(length))
+ out_list = []
+ for i in xrange(length.value):
+ out_list.append(native_list[i])
+ core.BNFreeMetadataRaw(native_list)
+ return ''.join(chr(a) for a in out_list)
+
+ raise ValueError("Metadata object not a string or raw type")
+
+ def __int__(self):
+ if self.is_signed_integer:
+ return core.BNMetadataGetSignedInteger(self.handle)
+ if self.is_unsigned_integer:
+ return core.BNMetadataGetUnsignedInteger(self.handle)
+
+ raise ValueError("Metadata object not of integer type")
+
+ def __float__(self):
+ if not self.is_float:
+ raise ValueError("Metadata object is not float type")
+ return core.BNMetadataGetDouble(self.handle)
+
+ def __nonzero__(self):
+ if not self.is_boolean:
+ raise ValueError("Metadata object is not boolean type")
+ return core.BNMetadataGetBoolean(self.handle)
+
+ def __eq__(self, other):
+ if isinstance(other, int) and self.is_integer:
+ return int(self) == other
+ elif isinstance(other, str) and (self.is_string or self.is_raw):
+ return str(self) == other
+ elif isinstance(other, float) and self.is_float:
+ return float(self) == other
+ elif isinstance(other, bool) and self.is_boolean:
+ return bool(self) == other
+ elif self.is_array and ((isinstance(other, Metadata) and other.is_array) or isinstance(other, list)):
+ if len(self) != len(other):
+ return False
+ for a, b in zip(self, other):
+ if a != b:
+ return False
+ return True
+ elif self.is_dict and ((isinstance(other, Metadata) and other.is_dict) or isinstance(other, dict)):
+ if len(self) != len(other):
+ return False
+ for a, b in zip(self, other):
+ if a != b or self[a] != other[b]:
+ return False
+ return True
+ elif isinstance(other, Metadata) and self.is_integer and other.is_integer:
+ return int(self) == int(other)
+ elif isinstance(other, Metadata) and (self.is_string or self.is_raw) and (other.is_string or other.is_raw):
+ return str(self) == str(other)
+ elif isinstance(other, Metadata) and self.is_float and other.is_float:
+ return float(self) == float(other)
+ elif isinstance(other, Metadata) and self.is_boolean and other.is_boolean:
+ return bool(self) == bool(other)
+ raise NotImplementedError()
+
+ def __ne__(self, other):
+ if isinstance(other, int) and self.is_integer:
+ return int(self) != other
+ elif isinstance(other, str) and (self.is_string or self.is_raw):
+ return str(self) != other
+ elif isinstance(other, float) and self.is_float:
+ return float(self) != other
+ elif isinstance(other, bool):
+ return bool(self) != other
+ elif self.is_array and ((isinstance(other, Metadata) and other.is_array) or isinstance(other, list)):
+ if len(self) != len(other):
+ return True
+ areEqual = True
+ for a, b in zip(self, other):
+ if a != b:
+ areEqual = False
+ return not areEqual
+ elif self.is_dict and ((isinstance(other, Metadata) and other.is_dict) or isinstance(other, dict)):
+ if len(self) != len(other):
+ return True
+ for a, b in zip(self, other):
+ if a != b or self[a] != other[b]:
+ return True
+ return False
+ elif isinstance(other, Metadata) and self.is_integer and other.is_integer:
+ return int(self) != int(other)
+ elif isinstance(other, Metadata) and (self.is_string or self.is_raw) and (other.is_string or other.is_raw):
+ return str(self) != str(other)
+ elif isinstance(other, Metadata) and self.is_float and other.is_float:
+ return float(self) != float(other)
+ elif isinstance(other, Metadata) and self.is_boolean and other.is_boolean:
+ return bool(self) != bool(other)