-
Notifications
You must be signed in to change notification settings - Fork 1k
/
javavm_classloader.py
132 lines (110 loc) · 5.39 KB
/
javavm_classloader.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
import logging
from archinfo.arch_soot import (SootAddressDescriptor, SootAddressTerminator,
SootClassDescriptor)
from ..engines.soot.method_dispatcher import resolve_method
from ..sim_state import SimState
from .plugin import SimStatePlugin
l = logging.getLogger("angr.state_plugins.javavm_classloader")
class SimJavaVmClassloader(SimStatePlugin):
"""
JavaVM Classloader is used as an interface for resolving and initializing
Java classes.
"""
def __init__(self, initialized_classes=None):
super(SimJavaVmClassloader, self).__init__()
self._initialized_classes = set() if initialized_classes is None else initialized_classes
def get_class(self, class_name, init_class=False, step_func=None):
"""
Get a class descriptor for the class.
:param str class_name: Name of class.
:param bool init_class: Whether the class initializer <clinit> should be
executed.
:param func step_func: Callback function executed at every step of the simulation manager during
the execution of the main <clinit> method
"""
# try to get the soot class object from CLE
java_binary = self.state.javavm_registers.load('ip_binary')
soot_class = java_binary.get_soot_class(class_name, none_if_missing=True)
# create class descriptor
class_descriptor = SootClassDescriptor(class_name, soot_class)
# load/initialize class
if init_class:
self.init_class(class_descriptor, step_func=step_func)
return class_descriptor
def get_superclass(self, class_):
"""
Get the superclass of the class.
"""
if not class_.is_loaded or class_.superclass_name is None:
return None
return self.get_class(class_.superclass_name)
def get_class_hierarchy(self, base_class):
"""
Walks up the class hierarchy and returns a list of all classes between
base class (inclusive) and java.lang.Object (exclusive).
"""
classes = [base_class]
while classes[-1] is not None and classes[-1] != "java.lang.Object":
classes.append(self.get_superclass(classes[-1]))
return classes[:-1]
def is_class_initialized(self, class_):
"""
Indicates whether the classes initializing method <clinit> was already
executed on the state.
"""
return class_ in self.initialized_classes
def init_class(self, class_, step_func=None):
"""
This method simulates the loading of a class by the JVM, during which
parts of the class (e.g. static fields) are initialized. For this, we
run the class initializer method <clinit> (if available) and update
the state accordingly.
Note: Initialization is skipped, if the class has already been
initialized (or if it's not loaded in CLE).
"""
if self.is_class_initialized(class_):
l.debug("Class %r already initialized.", class_)
return
l.debug("Initialize class %r.", class_)
self.initialized_classes.add(class_)
if not class_.is_loaded:
l.warning("Class %r is not loaded in CLE. Skip initializiation.", class_)
return
clinit_method = resolve_method(self.state, '<clinit>', class_.name,
include_superclasses=False, init_class=False)
if clinit_method.is_loaded:
javavm_simos = self.state.project.simos
clinit_state = javavm_simos.state_call(addr=SootAddressDescriptor(clinit_method, 0, 0),
base_state=self.state,
ret_addr=SootAddressTerminator())
simgr = self.state.project.factory.simgr(clinit_state)
l.info(">"*15 + " Run class initializer %r ... " + ">"*15, clinit_method)
simgr.run(step_func=step_func)
l.debug("<"*15 + " Run class initializer %r ... done " + "<"*15, clinit_method)
# The only thing that can be updated during initialization are
# static or rather global information, which are either stored on
# the heap or in the vm_static_table
self.state.memory.vm_static_table = simgr.deadended[-1].memory.vm_static_table.copy()
self.state.memory.heap = simgr.deadended[-1].memory.heap.copy()
else:
l.debug("Class initializer <clinit> is not loaded in CLE. Skip initializiation.")
@property
def initialized_classes(self):
"""
List of all initialized classes.
"""
return self._initialized_classes
@SimStatePlugin.memo
def copy(self, memo): # pylint: disable=unused-argument
return SimJavaVmClassloader(
initialized_classes=self.initialized_classes.copy()
)
def merge(self, others, merge_conditions, common_ancestor=None): # pylint: disable=unused-argument
l.warning("Merging is not implemented for JavaVM classloader!")
return False
def widen(self, others): # pylint: disable=unused-argument
l.warning("Widening is not implemented for JavaVM classloader!")
return False
# TODO use a default JavaVM preset
# see for reference: angr/engines/__init__.py
SimState.register_default('javavm_classloader', SimJavaVmClassloader)