-
Notifications
You must be signed in to change notification settings - Fork 113
/
soot.py
167 lines (132 loc) · 5.74 KB
/
soot.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import archinfo
from archinfo.arch_soot import SootMethodDescriptor, SootAddressDescriptor
try:
import pysoot
from pysoot.lifter import Lifter
except ImportError:
pysoot = None
from .. import Backend
from ...errors import CLEError
import logging
_l = logging.getLogger("cle.backends.soot")
class Soot(Backend):
"""
The basis loader class for lifting Java code from Jar's and Apk's to Soot.
"""
def __init__(self, path, main_class=None,
additional_jars=None, additional_jar_roots=None,
native_libs_ld_path=None, native_libs=None,
**kwargs):
"""
:param path: Path to the main jar or apk.
The following parameters are optional
:param main_class: Name of class containing the main method, which should be used as entry point.
:param additional_jars: Additional Jars.
:param additional_jar_roots: Additional Jar roots.
:param native_libs: Name(s) if libraries containing native code components (JNI)
:param native_libs_ld_path: Path(s) where to find native libraries. Note: Requires use_system_libs=True
"""
if not pysoot:
raise ImportError('Cannot import PySoot. The Soot backend requires PySoot to function. '
'Please install PySoot first.')
if kwargs.get('has_memory', False):
raise CLEError('The parameter "has_memory" must be False for Soot backend.')
super(Soot, self).__init__(path, has_memory=False, **kwargs)
# load the classes
pysoot_lifter = Lifter(path,
additional_jars=additional_jars,
additional_jar_roots=additional_jar_roots
)
self._classes = pysoot_lifter.classes
# find entry method
try:
main_method_descriptor = SootMethodDescriptor.from_method(next(self.get_method("main", main_class)))
entry = SootAddressDescriptor(main_method_descriptor, 0, 0)
except CLEError:
_l.warning('Failed to identify the entry (the Main method).')
entry = None
self._entry = entry
self.os = 'javavm'
self.rebase_addr = None
self.set_arch(archinfo.arch_from_id('soot'))
if native_libs:
# automatically load nativ libraries (with CLE) by adding them as a dependency of this object
self.deps += [native_libs] if type(native_libs) in (str, unicode) else native_libs
# if available, add additional load path(s)
if native_libs_ld_path:
path_list = [native_libs_ld_path] if type(native_libs_ld_path) in (str, unicode) else native_libs_ld_path
self.extra_load_path += path_list
# JNI support enables to switch between the Java SimOS and the one used by native libs
self.jni_support = True
else:
self.jni_support = False
@property
def max_addr(self):
# FIXME: This is a hack to satisfy checks elsewhere that max_addr must be greater than min_addr
return self.min_addr + 1
@property
def entry(self):
return self._entry
@property
def classes(self):
return self._classes
def get_class(self, cls_name):
"""
Get a Soot class object.
:param str cls_name: Name of the class to get.
:return: The class object.
:rtype: pysoot.soot.SootClass
"""
try:
return self._classes[cls_name]
except KeyError:
raise CLEError('Class "%s" does not exist.' % cls_name)
def get_method(self, thing, cls_name=None):
"""
Get a Soot method object.
:param thing: Descriptor or the method, or name of the method.
:param str class_name: Name of the class. If not specified, class name can be parsed from method_name.
:return: An iterator of all SootMethod objects that satisfy the criteria.
:rtype: iterator
"""
if isinstance(thing, SootMethodDescriptor):
cls_name = thing.class_name
method_name = thing.name
method_params = thing.params
elif isinstance(thing, (str, unicode)):
# parse the method name
method_name = thing
if cls_name is None:
# parse the class name from method_name
last_dot = method_name.rfind('.')
if last_dot >= 0:
cls_name = method_name[ : last_dot ]
method_name = method_name[last_dot + 1 : ]
else:
raise CLEError('Unknown class name for the method.')
method_params = None
else:
raise TypeError('Unsupported type "%s" for the first argument.' % type(thing))
try:
cls = self.get_class(cls_name)
except CLEError:
raise
has_method = False
for method in cls.methods:
if method.name == method_name:
if method_params is None or method_params == method.params:
has_method = True
yield method
if not has_method:
raise CLEError('Method "%s" in class "%s" does not exist.' % (method_name, cls_name))
@property
def main_methods(self):
"""
Find all Main methods in this binary.
:return: All main methods in each class.
:rtype: iterator
"""
for cls in self.classes.values():
for method in cls.methods:
if method.name == "main": # TODO: Check more attributes
yield method