Difference between revisions of "Identifying Exotic Language Features"

From AtlasWiki
Jump to: navigation, search
(Analyzing Native Code Usage)
(Blanked the page)
 
Line 1: Line 1:
Let's update our [https://github.com/EnSoftCorp/Starter-Toolbox Starter Toolbox] to define a few new <code>Analyzer</code> objects.  Specifically let's write analyzers that detect uses of native code (JNI), Java class loaders, reflection, and native processes.  We want to be able to detect the usage of these language features because these exotic features tend to break naive program analysis implementations.  Without the ability to detect these features we may not be able to tell if our analysis is sound or complete.
 
  
== Analyzing Native Code Usage ==
 
Let's write some analysis logic that detects uses of native code through the [http://docs.oracle.com/javase/7/docs/technotes/guides/jni/ Java Native Interface (JNI)].  Detecting native methods in Java is easy since all native methods use the "native" keyword.  We can create a set of all native methods using the <code>nodesTaggedWithAll(Node.METHOD, Node.IS_NATIVE)</code> query methods.  We then show the "interactions" between the app and the native methods along <code>call</code> edges.  The interactions in this request are just the direct <code>call</code> edges which lay immediately between the app and native methods.
 
 
Most importantly, we document the assumptions we made when writing this analyzer.  For this example, we assumed that uses of native code are limited to native methods and those methods have <code>call</code> edges present in the graph if they are used.  We leave it as an exercise to the reader to determine if these assumptions are fair to make.  To aid your thinking, consider if native methods are the only way to interact with native code.  Also, consider the case of memory mapped files via [https://en.wikipedia.org/wiki/Non-blocking_I/O_%28Java%29 Java NIO].  You might also consider what the Atlas program graph would look like if the native methods were invoked using [http://docs.oracle.com/javase/tutorial/reflect/ Java Reflection].
 
 
<pre>
 
public class NativeCodeUsage extends Analyzer {
 
 
@Override
 
public String getName() {
 
return "Native Code Usage";
 
}
 
 
@Override
 
public String getDescription() {
 
return "Discovers uses of the Java Native Interface (JNI).";
 
}
 
 
@Override
 
public String[] getAssumptions() {
 
return new String[]{"Uses of native code are limited to method calls to app methods with the \"native\" keyword.",
 
    "Uses of native code are only made through direct method calls."};
 
}
 
 
@Override
 
protected Q evaluateEnvelope() {
 
Q nativeMethods = appContext.nodesTaggedWithAll(Node.METHOD, Node.IS_NATIVE);
 
return CommonQueries.interactions(context, appContext, nativeMethods, Edge.CALL);
 
}
 
 
}</pre>
 
 
== Analyzing Class Loader Usage ==
 
 
<pre>
 
public class ClassLoaderUsage extends Analyzer {
 
 
@Override
 
public String getName() {
 
return "Class Loader Usage";
 
}
 
 
@Override
 
public String getDescription() {
 
return "Discovers uses of Java class loaders.";
 
}
 
 
@Override
 
public String[] getAssumptions() {
 
return new String[] { "Uses of class loader APIs are only made through direct method calls." };
 
}
 
 
@Override
 
protected Q evaluateEnvelope() {
 
// get all ClassLoader implementations (getting type hierarchy here
 
// instead of just base types and overrides because the custom
 
// ClassLoader methods may contain valuable contextual information)
 
Q classLoaderAPIs = CommonQueries.typeHierarchy(context,
 
Common.typeSelect("java.lang", "ClassLoader"),
 
TraversalDirection.REVERSE);
 
 
// get the class loader API methods
 
Q classLoaderMethods = CommonQueries.declarations(context,
 
classLoaderAPIs, TraversalDirection.FORWARD)
 
.nodesTaggedWithAny(Node.METHOD);
 
 
// remove the boring class loader methods inherited from Object
 
classLoaderMethods = classLoaderMethods.difference(SetDefinitions
 
.objectMethodOverrides());
 
 
// get the calls to the class loader API methods by the application
 
return CommonQueries.interactions(context, appContext,
 
classLoaderMethods, Edge.CALL);
 
}
 
 
}
 
</pre>
 
 
== Analyzing Reflection Usage ==
 
 
<pre>
 
public class ReflectionUsage extends Analyzer {
 
 
@Override
 
public String getName() {
 
return "Reflection Usage";
 
}
 
 
@Override
 
public String getDescription() {
 
return "Discovers uses of reflection.";
 
}
 
 
@Override
 
public String[] getAssumptions() {
 
return new String[] {
 
"Uses of reflection occur through APIs in the java.lang.reflect package.",
 
"Uses of reflection occur through through various methods in java.lang.Class",
 
"Uses of class loaders are not considered uses of reflection",
 
"Uses of reflection APIs are only made through direct method calls." };
 
}
 
 
@Override
 
protected Q evaluateEnvelope() {
 
// get reflection APIs
 
Q reflectionAPIs = CommonQueries.declarations(context,
 
Common.pkg("java.lang.reflect"));
 
reflectionAPIs = reflectionAPIs.union(Common.typeSelect("java.lang",
 
"Class"));
 
 
// get reflection API's methods
 
Q reflectionAPIMethods = CommonQueries.declarations(context,
 
reflectionAPIs).nodesTaggedWithAny(Node.METHOD);
 
 
// add overridden reflection API methods
 
reflectionAPIMethods = CommonQueries.overrides(context,
 
reflectionAPIMethods, TraversalDirection.FORWARD);
 
 
// remove boring overridden Object methods (like toString())
 
reflectionAPIMethods = reflectionAPIMethods.difference(SetDefinitions
 
.objectMethodOverrides());
 
 
// get the calls to the reflection API methods by the application
 
return CommonQueries.interactions(context, appContext,
 
reflectionAPIMethods, Edge.CALL);
 
}
 
 
}
 
</pre>
 
 
== Analyzing Native Process Usage ==
 
 
<pre>
 
public class NativeProcessUsage extends Analyzer {
 
 
@Override
 
public String getName() {
 
return "Native Process Usage";
 
}
 
 
@Override
 
public String getDescription() {
 
return "Discovers uses of the native processes.";
 
}
 
 
@Override
 
public String[] getAssumptions() {
 
return new String[] {
 
"Native processes can only be launched by the Java Process, ProcessBuilder, and Runtime.exec(...) APIs.",
 
"Uses of native processes APIs are only made through direct method calls." };
 
}
 
 
@Override
 
protected Q evaluateEnvelope() {
 
// select the process APIs
 
Q processAPIs = Common.typeSelect("java.lang", "Process").union(
 
Common.typeSelect("java.lang", "ProcessBuilder"),
 
Common.typeSelect("java.lang", "Runtime"));
 
 
// select all methods declared under the process APIs
 
Q processAPIMethods = CommonQueries.declarations(context, processAPIs)
 
.nodesTaggedWithAny(Node.METHOD);
 
 
// add overridden process API methods
 
processAPIMethods = CommonQueries.overrides(context, processAPIMethods,
 
TraversalDirection.FORWARD);
 
 
// remove boring overridden Object methods (like toString())
 
processAPIMethods = processAPIMethods.difference(SetDefinitions
 
.objectMethodOverrides());
 
 
// get the calls to the process API methods by the application
 
return CommonQueries.interactions(context, appContext,
 
processAPIMethods, Edge.CALL);
 
}
 
 
}
 
</pre>
 
 
<br /><center>Back&nbsp;to&nbsp;[[Learning Atlas]]</center>
 

Latest revision as of 14:57, 9 February 2015