-
Notifications
You must be signed in to change notification settings - Fork 176
/
BasicAPI.java
208 lines (189 loc) · 9.76 KB
/
BasicAPI.java
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package dev.navids.soottutorial.basicapi;
import dev.navids.soottutorial.visual.Visualizer;
import soot.*;
import soot.jimple.*;
import soot.jimple.toolkits.callgraph.CallGraph;
import soot.jimple.toolkits.callgraph.Edge;
import soot.options.Options;
import soot.toolkits.scalar.Pair;
import java.io.File;
import java.util.*;
public class BasicAPI {
public static String sourceDirectory = System.getProperty("user.dir") + File.separator + "demo" + File.separator + "BasicAPI";
public static String circleClassName = "Circle";
public static void setupSoot() {
G.reset();
// Uncomment line below to import essential Java classes
// Options.v().set_prepend_classpath(true);
// Comment the line below to not have phantom refs (you need to uncomment the line above)
Options.v().set_prepend_classpath(true);
Options.v().set_allow_phantom_refs(true);
Options.v().set_soot_classpath(sourceDirectory);
Options.v().set_output_format(Options.output_format_jimple);
Options.v().set_process_dir(Collections.singletonList(sourceDirectory));
Options.v().set_whole_program(true);
Scene.v().loadNecessaryClasses();
PackManager.v().runPacks();
}
public static void main(String[] args) {
setupSoot();
// Access to Classes
SootClass circleClass = reportSootClassInfo();
// Access to Fields
SootField radiusField = reportSootFieldInfo(circleClass);
// Access to Methods
SootMethod areaMethod = reportSootMethodInfo(circleClass);
// Access to Body (units, locals)
System.out.println("-----Body-----");
JimpleBody body = (JimpleBody) areaMethod.getActiveBody();
reportLocalInfo(body);
Stmt firstNonIdentitiyStmt = body.getFirstNonIdentityStmt();
int c = 0;
for (Unit u : body.getUnits()) {
c++;
Stmt stmt = (Stmt) u;
System.out.println(String.format("(%d): %s", c, stmt ));
if(stmt.equals(firstNonIdentitiyStmt))
System.out.println(" This statement is the first non-identity statement!");
if(stmt.containsFieldRef())
reportFieldRefInfo(radiusField, stmt);
if(doesInvokeMethod(stmt, "int area()", circleClassName)){
System.out.println(" This statement invokes 'int area()' method");
}
modifyBody(body, stmt);
}
for(Trap trap : body.getTraps()){
System.out.println(trap);
}
try {
body.validate();
System.out.println("Body is validated! No inconsistency found.");
}
catch (Exception exception){
System.out.println("Body is not validated!");
}
// Call graph
System.out.println("-----CallGraph-----");
CallGraph callGraph = Scene.v().getCallGraph();
for(Iterator<Edge> it = callGraph.edgesOutOf(areaMethod); it.hasNext(); ){
Edge edge = it.next();
System.out.println(String.format("Method '%s' invokes method '%s' through stmt '%s", edge.src(), edge.tgt(), edge.srcUnit()));
}
boolean drawGraph = false;
if (args.length > 0 && args[0].equals("draw"))
drawGraph = true;
if (drawGraph) {
// Visualizer.v().addCallGraph(callGraph);
Visualizer.v().addCallGraph(callGraph,
edge -> edge.src().getDeclaringClass().isApplicationClass(),
sootMethod -> new Pair<>(
sootMethod.getDeclaringClass().isApplicationClass()
? "cg_node, default_color" : "cg_node, cg_lib_class"
, sootMethod.getDeclaringClass().isApplicationClass()
? sootMethod.getSubSignature() : sootMethod.getSignature())
);
Visualizer.v().draw();
}
}
private static void reportLocalInfo(JimpleBody body) {
System.out.println(String.format("Local variables count: %d", body.getLocalCount()));
Local thisLocal = body.getThisLocal();
Type thisType = thisLocal.getType();
Local paramLocal = body.getParameterLocal(0);
}
private static SootMethod reportSootMethodInfo(SootClass circleClass) {
System.out.println("-----Method-----");
System.out.println(String.format("List of %s's methods:", circleClass.getName()));
for(SootMethod sootMethod : circleClass.getMethods())
System.out.println(String.format("- %s",sootMethod.getName()));
SootMethod getCircleCountMethod = circleClass.getMethod("int getCircleCount()");
System.out.println(String.format("Method Signature: %s", getCircleCountMethod.getSignature()));
System.out.println(String.format("Method Subsignature: %s", getCircleCountMethod.getSubSignature()));
System.out.println(String.format("Method Name: %s", getCircleCountMethod.getName()));
System.out.println(String.format("Declaring class: %s", getCircleCountMethod.getDeclaringClass()));
int methodModifers = getCircleCountMethod.getModifiers();
System.out.println(String.format("Method %s is public: %b, is static: %b, is final: %b", getCircleCountMethod.getName(),
Modifier.isPublic(methodModifers),
Modifier.isStatic(methodModifers),
Modifier.isFinal(methodModifers)));
SootMethod constructorMethod = circleClass.getMethodByName("<init>");
try{
SootMethod areaMethod = circleClass.getMethodByName("area");
}
catch (Exception exception){
System.out.println("Th method 'area' is overloaded and Soot cannot retrieve it by name");
}
return circleClass.getMethod("int area(boolean)");
}
private static SootField reportSootFieldInfo(SootClass circleClass) {
SootField radiusField = circleClass.getField("radius", IntType.v());
SootField piField = circleClass.getField("double PI");
System.out.println(String.format("Field %s is final: %b", piField, piField.isFinal()));
return radiusField;
}
private static SootClass reportSootClassInfo() {
System.out.println("-----Class-----");
SootClass circleClass = Scene.v().getSootClass(circleClassName);
System.out.println(String.format("The class %s is an %s class, loaded with %d methods! ",
circleClass.getName(), circleClass.isApplicationClass() ? "Application" : "Library", circleClass.getMethodCount()));
String wrongClassName = "Circrle";
SootClass notExistedClass = Scene.v().getSootClassUnsafe(wrongClassName, false);
System.out.println(String.format("getClassUnsafe: Is the class %s null? %b", wrongClassName, notExistedClass==null));
try{
notExistedClass = Scene.v().getSootClass(wrongClassName);
System.out.println(String.format("getClass creates a phantom class for %s: %b", wrongClassName, notExistedClass.isPhantom()));
}catch (Exception exception){
System.out.println(String.format("getClass throws an exception for class %s.", wrongClassName));
}
Type circleType = circleClass.getType();
System.out.println(String.format("Class '%s' is same as class of type '%s': %b"
, circleClassName, circleType.toString(), circleClass.equals(Scene.v().getSootClass(circleType.toString()))));
return circleClass;
}
private static void modifyBody(JimpleBody body, Stmt stmt) {
stmt.apply(new AbstractStmtSwitch() {
@Override
public void caseIfStmt(IfStmt stmt) {
System.out.println(String.format(" (Before change) if condition '%s' is true goes to stmt '%s'", stmt.getCondition(), stmt.getTarget()));
stmt.setTarget(body.getUnits().getSuccOf(stmt));
System.out.println(String.format(" (After change) if condition '%s' is true goes to stmt '%s'", stmt.getCondition(), stmt.getTarget()));
}
});
}
private static boolean doesInvokeMethod(Stmt stmt, String subsignature, String declaringClass) {
if(!stmt.containsInvokeExpr())
return false;
InvokeExpr invokeExpr = stmt.getInvokeExpr();
invokeExpr.apply(new AbstractJimpleValueSwitch() {
@Override
public void caseStaticInvokeExpr(StaticInvokeExpr v) {
System.out.println(String.format(" StaticInvokeExpr '%s' from class '%s'", v, v.getType()));
}
@Override
public void caseVirtualInvokeExpr(VirtualInvokeExpr v) {
System.out.println(String.format(" VirtualInvokeExpr '%s' from local '%s' with type %s", v, v.getBase(), v.getBase().getType()));
}
@Override
public void defaultCase(Object v) {
super.defaultCase(v);
}
});
return invokeExpr.getMethod().getSubSignature().equals(subsignature)
&& invokeExpr.getMethod().getDeclaringClass().getName().equals(declaringClass);
}
private static void reportFieldRefInfo(SootField radiusField, Stmt stmt) {
FieldRef fieldRef = stmt.getFieldRef();
fieldRef.apply(new AbstractRefSwitch() {
@Override
public void caseStaticFieldRef(StaticFieldRef v) {
// A static field reference
}
@Override
public void caseInstanceFieldRef(InstanceFieldRef v) {
if(v.getField().equals(radiusField)){
System.out.println(String.format(" Field %s is used through FieldRef '%s'. The base local of FieldRef has type '%s'", radiusField, v, v.getBase().getType()));
}
}
});
}
}