Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/archinfo/arch_soot.py: 51%
211 statements
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:15 +0000
« prev ^ index » next coverage.py v7.3.1, created at 2023-09-25 06:15 +0000
1import logging
2import re
4from .arch import Arch, Endness, register_arch
6log = logging.getLogger("archinfo.arch_soot")
9class SootMethodDescriptor:
10 __slots__ = ["class_name", "name", "params", "_soot_method", "ret"]
12 def __init__(self, class_name, name, params, soot_method=None, ret_type=None):
13 self.class_name = class_name
14 self.name = name
15 self.params = params
16 self._soot_method = soot_method
17 self.ret = ret_type
19 def __repr__(self):
20 return "{}.{}({})".format(self.class_name, self.name, ", ".join(self.params))
22 def __hash__(self):
23 return hash((self.class_name, self.name, self.params))
25 def __eq__(self, other):
26 return (
27 isinstance(other, SootMethodDescriptor)
28 and self.class_name == other.class_name
29 and self.name == other.name
30 and self.params == other.params
31 )
33 def __ne__(self, other):
34 return not self == other
36 def __lt__(self, other):
37 return self.__repr__() < other.__repr__()
39 def __gt__(self, other):
40 return self.__repr__() > other.__repr__()
42 def __le__(self, other):
43 return self.__repr__() <= other.__repr__()
45 def __ge__(self, other):
46 return self.__repr__() >= other.__repr__()
48 def address(self, block_idx=0, stmt_idx=0):
49 """
50 :return Address of the method.
51 :rtype: SootAddressDescriptor
52 """
53 return SootAddressDescriptor(self, block_idx, stmt_idx)
55 @property
56 def fullname(self):
57 """
58 :return the full name of the method (class name + method name)
59 """
60 return f"{self.class_name}.{self.name}"
62 @property
63 def symbolic(self):
64 return False
66 @property
67 def is_loaded(self):
68 """
69 :return: True, if the method is loaded in CLE and thus infos about attrs,
70 ret and exceptions are available.
71 """
72 return self._soot_method is not None
74 @property
75 def attrs(self):
76 return self._soot_method.attrs if self.is_loaded else []
78 @property
79 def exceptions(self):
80 return self._soot_method.exceptions if self.is_loaded else []
82 @property
83 def block_by_label(self):
84 return self._soot_method.block_by_label if self.is_loaded else None
86 # @property
87 # def ret(self):
88 # return self._soot_method.ret if self.is_loaded else []
90 @property
91 def addr(self):
92 """
93 :return: the soot address description of the entry point of the method
94 """
95 return SootAddressDescriptor(self, 0, 0)
97 def matches_with_native_name(self, native_method):
98 """
99 The name of native methods are getting encoded, s.t. they translate into
100 valid C function names. This method indicates if the name of the given
101 native method matches the name of the soot method.
103 :return: True, if name of soot method matches the mangled native name.
104 """
106 if "__" in native_method:
107 # if native methods are overloaded, two underscores are used
108 native_method, params_sig = native_method.split("__")
109 params = ArchSoot.decode_parameter_list_signature(params_sig)
110 # check function signature
111 if params != self.params:
112 return False
114 # demangle native name
115 native_method = native_method.replace("_1", "_")
116 # TODO unicode escaping
118 method_native_name = "Java_{class_name}_{method_name}".format(
119 class_name=self.class_name.replace(".", "_"), method_name=self.name
120 )
122 return native_method == method_native_name
124 @classmethod
125 def from_string(cls, tstr):
126 # this should be the opposite of repr
127 tstr = tstr.strip()
128 class_and_method, tparams = tstr.split("(")
129 params_str = tparams.split(")")[0]
130 if params_str == "":
131 params = ()
132 else:
133 params = tuple(t.strip() for t in params_str.split(","))
134 class_name, _, method = class_and_method.rpartition(".")
135 return cls(class_name, method, params)
137 @classmethod
138 def from_soot_method(cls, soot_method):
139 return cls(
140 class_name=soot_method.class_name,
141 name=soot_method.name,
142 params=soot_method.params,
143 soot_method=soot_method,
144 ret_type=soot_method.ret,
145 )
148class SootAddressDescriptor:
149 __slots__ = ["method", "block_idx", "stmt_idx"]
151 def __init__(self, method, block_idx, stmt_idx):
152 if not isinstance(method, SootMethodDescriptor):
153 raise ValueError('The parameter "method" must be an ' "instance of SootMethodDescriptor.")
155 self.method = method
156 self.block_idx = block_idx
157 self.stmt_idx = stmt_idx
159 def __repr__(self):
160 return "<{!r}+({}:{})>".format(
161 self.method, self.block_idx, "%d" % self.stmt_idx if self.stmt_idx is not None else "[0]"
162 )
164 def __hash__(self):
165 return hash((self.method, self.stmt_idx))
167 def __eq__(self, other):
168 return (
169 isinstance(other, SootAddressDescriptor)
170 and self.method == other.method
171 and self.block_idx == other.block_idx
172 and self.stmt_idx == other.stmt_idx
173 )
175 def __ne__(self, other):
176 return not self == other
178 def __lt__(self, other):
179 return self.__repr__() < other.__repr__()
181 def __gt__(self, other):
182 return self.__repr__() > other.__repr__()
184 def __le__(self, other):
185 return self.__repr__() <= other.__repr__()
187 def __ge__(self, other):
188 return self.__repr__() >= other.__repr__()
190 def __add__(self, stmts_offset):
191 if not isinstance(stmts_offset, int):
192 raise TypeError("The stmts_offset must be an int or a long.")
193 s = self.copy()
194 s.stmt_idx += stmts_offset
195 return s
197 def copy(self):
198 return SootAddressDescriptor(method=self.method, block_idx=self.block_idx, stmt_idx=self.stmt_idx)
200 @property
201 def symbolic(self):
202 return False
205class SootAddressTerminator(SootAddressDescriptor):
206 __slots__ = []
208 def __init__(self):
209 dummy_method = SootMethodDescriptor("dummy", "dummy", tuple())
210 super().__init__(dummy_method, 0, 0)
212 def __repr__(self):
213 return "<Terminator>"
216class SootFieldDescriptor:
217 __slots__ = ["class_name", "name", "type"]
219 def __init__(self, class_name, name, type_):
220 self.class_name = class_name
221 self.name = name
222 self.type = type_
224 def __repr__(self):
225 return f"{self.class_name}.{self.name}"
227 def __hash__(self):
228 return hash((self.class_name, self.name, self.type))
230 def __eq__(self, other):
231 return (
232 isinstance(other, SootFieldDescriptor)
233 and self.class_name == other.class_name
234 and self.name == other.name
235 and self.type == other.type
236 )
238 def __ne__(self, other):
239 return not self == other
242class SootClassDescriptor:
243 __slots__ = ["name", "_soot_class"]
245 def __init__(self, name, soot_class=None):
246 self.name = name
247 self._soot_class = soot_class
249 def __repr__(self):
250 return self.name
252 def __hash__(self):
253 return hash(self.name)
255 def __eq__(self, other):
256 return isinstance(other, SootClassDescriptor) and self.name == other.name
258 def __ne__(self, other):
259 return not self == other
261 @property
262 def is_loaded(self):
263 """
264 :return: True, if the class is loaded in CLE and thus info about field,
265 methods, ... are available.
266 """
267 return self._soot_class is not None
269 @property
270 def fields(self):
271 return self._soot_class.fields if self.is_loaded else None
273 @property
274 def methods(self):
275 return self._soot_class.methods if self.is_loaded else None
277 @property
278 def superclass_name(self):
279 return self._soot_class.super_class if self.is_loaded else None
281 @property
282 def type(self):
283 return "java.lang.Class"
286class SootNullConstant:
287 def __init__(self):
288 pass
290 def __repr__(self):
291 return "null"
293 def __hash__(self):
294 return hash("null")
296 def __eq__(self, other):
297 return isinstance(other, SootNullConstant)
299 def __ne__(self, other):
300 return not self == other
303class SootArgument:
304 """
305 Typed Java argument.
306 """
308 __slots__ = ["value", "type", "is_this_ref"]
310 def __init__(self, value, type_, is_this_ref=False):
311 """
312 :param value: Value of the argument
313 :param type_: Type of the argument
314 :param is_this_ref: Indicates whether the argument represents the
315 'this' reference, i.e. the object on which an
316 instance method is invoked.
317 """
318 self.value = value
319 self.type = type_
320 self.is_this_ref = is_this_ref
322 def __repr__(self):
323 return f"{self.value} ({self.type})"
326class ArchSoot(Arch):
327 def __init__(self, endness=Endness.LE):
328 super().__init__(endness)
330 name = "Soot"
332 vex_arch = None # No VEX support
333 qemu_name = None # No Qemu/Unicorn-engine support
334 bits = 64
335 address_types = (SootAddressDescriptor,)
336 function_address_types = (SootMethodDescriptor,)
338 # Size of native counterparts of primitive Java types
339 sizeof = {"boolean": 8, "byte": 8, "char": 16, "short": 16, "int": 32, "long": 64, "float": 32, "double": 64}
341 primitive_types = ["boolean", "byte", "char", "short", "int", "long", "float", "double"]
343 sig_dict = {
344 "Z": "boolean",
345 "B": "byte",
346 "C": "char",
347 "S": "short",
348 "I": "int",
349 "J": "long",
350 "F": "float",
351 "D": "double",
352 "V": "void",
353 }
355 @staticmethod
356 def decode_type_signature(type_sig):
357 if not type_sig:
358 return None
359 # try to translate signature as a primitive type
360 if type_sig in ArchSoot.sig_dict:
361 return ArchSoot.sig_dict[type_sig]
362 # java classes are encoded as 'Lclass_name;'
363 if type_sig.startswith("L") and type_sig.endswith(";"):
364 return type_sig[1:-1]
365 raise ValueError(type_sig)
367 @staticmethod
368 def decode_parameter_list_signature(param_sig):
369 return tuple(
370 ArchSoot.decode_type_signature(param) for param in re.findall(r"([\[]*[ZBCSIJFDV]|[\[]*L.+;)", param_sig)
371 )
373 @staticmethod
374 def decode_method_signature(method_sig):
375 # signature format follows the pattern: (param_sig)ret_sig
376 match = re.search(r"\((.*)\)(.*)", method_sig)
377 param_sig, ret_sig = match.group(1), match.group(2)
378 # decode types
379 params_types = ArchSoot.decode_parameter_list_signature(param_sig)
380 ret_type = ArchSoot.decode_type_signature(ret_sig)
381 log.debug("Decoded method signature '%s' as params=%s and ret=%s", method_sig, params_types, ret_type)
382 return params_types, ret_type
384 def library_search_path(self, pedantic=False):
385 """
386 Since Java is mostly system independent, we cannot return system
387 specific paths.
389 :return: empty list
390 """
391 return []
394register_arch(["soot"], 8, Endness.LE, ArchSoot)