396 lines
16 KiB
Java
396 lines
16 KiB
Java
/*
|
|
* This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.eclipse.org/legal/epl-v10.html.
|
|
*
|
|
* This file is a derivative of code released under the terms listed below.
|
|
*
|
|
*/
|
|
/**
|
|
*
|
|
* Copyright (c) 2009-2012,
|
|
*
|
|
* Galois, Inc. (Aaron Tomb <atomb@galois.com>,
|
|
* Rogan Creswick <creswick@galois.com>,
|
|
* Adam Foltzer <acfoltzer@galois.com>)
|
|
* Steve Suh <suhsteve@gmail.com>
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3. The names of the contributors may not be used to endorse or promote
|
|
* products derived from this software without specific prior written
|
|
* permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*
|
|
*/
|
|
|
|
package org.scandroid.util;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.io.InputStreamReader;
|
|
import java.util.ArrayList;
|
|
import java.util.LinkedList;
|
|
import java.util.List;
|
|
import java.util.StringTokenizer;
|
|
|
|
import javax.xml.parsers.DocumentBuilder;
|
|
import javax.xml.parsers.DocumentBuilderFactory;
|
|
|
|
import org.scandroid.spec.AndroidSpecs;
|
|
import org.scandroid.spec.MethodNamePattern;
|
|
import org.w3c.dom.Document;
|
|
import org.w3c.dom.Element;
|
|
import org.w3c.dom.Node;
|
|
import org.w3c.dom.NodeList;
|
|
|
|
import com.ibm.wala.classLoader.IMethod;
|
|
import com.ibm.wala.ipa.callgraph.Entrypoint;
|
|
import com.ibm.wala.ipa.callgraph.impl.DefaultEntrypoint;
|
|
import com.ibm.wala.ipa.cha.ClassHierarchy;
|
|
import com.ibm.wala.types.ClassLoaderReference;
|
|
import com.ibm.wala.types.MethodReference;
|
|
import com.ibm.wala.util.strings.StringStuff;
|
|
|
|
|
|
public class EntryPoints {
|
|
|
|
private String pathToApkFile;
|
|
private String pathToApkTool;
|
|
private String pathToJava;
|
|
private String tempFolder;
|
|
private ArrayList<String[]> ActivityIntentList;
|
|
private ArrayList<String[]> ReceiverIntentList;
|
|
private ArrayList<String[]> ServiceIntentList;
|
|
|
|
private LinkedList<Entrypoint> entries;
|
|
|
|
public void listenerEntryPoints(ClassHierarchy cha, AndroidAnalysisContext loader) {
|
|
ArrayList<MethodReference> entryPointMRs = new ArrayList<MethodReference>();
|
|
|
|
// onLocation
|
|
entryPointMRs.add(StringStuff.makeMethodReference("android.location.LocationListener.onLocationChanged(Landroid/location/Location;)V"));
|
|
for(MethodReference mr:entryPointMRs)
|
|
for(IMethod im:cha.getPossibleTargets(mr))
|
|
{
|
|
|
|
|
|
// limit to functions defined within the application
|
|
if(im.getReference().getDeclaringClass().getClassLoader().
|
|
equals(ClassLoaderReference.Application)) {
|
|
|
|
entries.add(new DefaultEntrypoint(im, cha));
|
|
}
|
|
}
|
|
}
|
|
|
|
public static List<Entrypoint> defaultEntryPoints(ClassHierarchy cha) {
|
|
List<Entrypoint> entries = new ArrayList<Entrypoint>();
|
|
for (MethodNamePattern mnp:new AndroidSpecs().getEntrypointSpecs()) {
|
|
for (IMethod im: mnp.getPossibleTargets(cha)) {
|
|
|
|
// limit to functions defined within the application
|
|
if(LoaderUtils.fromLoader(im, ClassLoaderReference.Application))
|
|
{
|
|
|
|
entries.add(new DefaultEntrypoint(im, cha));
|
|
}
|
|
}
|
|
}
|
|
return entries;
|
|
}
|
|
|
|
public void activityModelEntry(ClassHierarchy cha, AndroidAnalysisContext loader) {
|
|
String[] methodReferences = {
|
|
"android.app.Activity.ActivityModel()V",
|
|
// find all onActivityResult functions and add them as entry points
|
|
// "android.app.Activity.onActivityResult(IILandroid/content/Intent;)V",
|
|
//
|
|
// // SERVICE ENTRY POINTS
|
|
// "android.app.Service.onCreate()V",
|
|
// "android.app.Service.onStart(Landroid/content/Intent;I)V",
|
|
// "android.app.Service.onBind(Landroid/content/Intent;)Landroid/os/IBinder;",
|
|
// "android.app.Service.onTransact(ILandroid/os/Parcel;Landroid/os/Parcel;I)B"
|
|
};
|
|
|
|
for (int i = 0; i < methodReferences.length; i++) {
|
|
MethodReference mr =
|
|
StringStuff.makeMethodReference(methodReferences[i]);
|
|
|
|
for (IMethod im : cha.getPossibleTargets(mr)) {
|
|
|
|
|
|
// limit to functions defined within the application
|
|
if (im.getReference().getDeclaringClass().getClassLoader()
|
|
.equals(ClassLoaderReference.Application)) {
|
|
|
|
entries.add(new DefaultEntrypoint(im, cha));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unused")
|
|
private void systemEntry(ClassHierarchy cha, AndroidAnalysisContext loader) {
|
|
String[] systemEntyPoints = {
|
|
// "android.app.ActivityThread.main([Ljava/lang/String;)V"
|
|
// , "com.android.server.ServerThread.run()V"
|
|
//"android.location.LocationManager$ListenerTransport._handleMessage(Landroid/os/Message;)V"
|
|
// "android.location.LocationManager$ListenerTransport$1.handleMessage(Landroid/os/Message;)V"
|
|
// "android.os.Handler.handleMessage(Landroid/os/Message;)V",
|
|
// "android.os.Handler$Callback.handleMessage(Landroid/os/Message;)Z",
|
|
// "com.android.internal.os.HandlerCaller$Callback.executeMessage(Landroid/os/Message;)V"
|
|
// "android.os.Handler.dispatchMessage(Landroid/os/Message;)V",
|
|
// "android.view.View.dispatchTouchEvent(Landroid/view/MotionEvent;)Z",
|
|
// "android.view.View.onTouchEvent(Landroid/view/MotionEvent;)Z",
|
|
// "android.view.View.setOnClickListener(Landroid/view/View$OnClickListener;)V",
|
|
"com.android.server.ServerThread.run()V"
|
|
//"android.app.ActivityThread.main([Ljava/lang/String;)V"
|
|
};
|
|
|
|
for (int i = 0; i < systemEntyPoints.length; i++) {
|
|
MethodReference methodRef =
|
|
StringStuff.makeMethodReference(systemEntyPoints[i]);
|
|
|
|
for (IMethod im : cha.getPossibleTargets(methodRef)) {
|
|
|
|
entries.add(new DefaultEntrypoint(im, cha));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public void addTestEntry(ClassHierarchy cha, AndroidAnalysisContext loader) {
|
|
String[] methodReferences = {
|
|
// "Test.Apps.Outer$PrivateInnerClass.printNum()V",
|
|
//"Test.Apps.Outer$PublicInnerClass.printNum()V"
|
|
//"Test.Apps.Outer.<init>()V"
|
|
//"Test.Apps.Outer.getNum()I"
|
|
//"Test.Apps.FixpointSolver.someMethod(LTest/Apps/GenericSink;LTest/Apps/GenericSource;)V"
|
|
//"Test.Apps.Outer$PrivateInnerClass.testParameters(LTest/Apps/GenericSink;LTest/Apps/GenericSource;)V"
|
|
"android.view.View.setOnClickListener(Landroid/view/View$OnClickListener;)V",
|
|
};
|
|
|
|
for (int i = 0; i < methodReferences.length; i++) {
|
|
MethodReference mr =
|
|
StringStuff.makeMethodReference(methodReferences[i]);
|
|
|
|
for (IMethod im : cha.getPossibleTargets(mr)) {
|
|
|
|
entries.add(new DefaultEntrypoint(im, cha));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public void unpackApk(String classpath){
|
|
StringTokenizer st = new StringTokenizer(classpath, File.pathSeparator);
|
|
pathToApkFile = st.nextToken();
|
|
//String pathToApkTool = new String(System.getProperty("user.dir").replace(" ", "\\ ") + File.separator + "apktool" +File.separator);
|
|
pathToApkTool = System.getProperty("user.dir") + File.separator + "apktool" +File.separator;
|
|
//String pathToJava = new String(System.getProperty("java.home").replace(" ", "\\ ") + File.separator + "bin" + File.separator);
|
|
pathToJava = System.getProperty("java.home") + File.separator + "bin" + File.separator;
|
|
String s = null;
|
|
|
|
//String command = new String(pathToJava + "java -jar " + pathToApkTool + "apktool.jar d -f " + pathToApkFile + " " + pathToApkTool + tempFolder);
|
|
|
|
//System.out.println("command: " + command);
|
|
|
|
ProcessBuilder pb = new ProcessBuilder(pathToJava + "java", "-jar", pathToApkTool + "apktool.jar", "d", "-f", pathToApkFile, pathToApkTool+tempFolder);
|
|
|
|
|
|
try {
|
|
//Process p = Runtime.getRuntime().exec(command);
|
|
Process p = pb.start();
|
|
BufferedReader stdInput = new BufferedReader(new
|
|
InputStreamReader(p.getInputStream()));
|
|
|
|
BufferedReader stdError = new BufferedReader(new
|
|
InputStreamReader(p.getErrorStream()));
|
|
|
|
// read the output from the command
|
|
|
|
while ((s = stdInput.readLine()) != null) {
|
|
|
|
}
|
|
|
|
// read any errors from the attempted command
|
|
while ((s = stdError.readLine()) != null) {
|
|
System.err.println(s);
|
|
}
|
|
|
|
} catch (IOException e) {
|
|
// TODO Auto-generated catch block
|
|
e.printStackTrace();
|
|
}
|
|
//System.out.println( System.getProperty("user.dir") );
|
|
//System.out.println("classpath: " + st.nextToken());
|
|
}
|
|
|
|
public void readXMLFile() {
|
|
try {
|
|
|
|
File fXmlFile = new File(pathToApkTool + tempFolder + File.separator + "AndroidManifest.xml");
|
|
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
|
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
|
|
Document doc = dBuilder.parse(fXmlFile);
|
|
doc.getDocumentElement().normalize();
|
|
|
|
//System.out.println("Root element :" + doc.getDocumentElement().getNodeName());
|
|
|
|
String basePackage = doc.getDocumentElement().getAttribute("package");
|
|
NodeList iList = doc.getElementsByTagName("intent-filter");
|
|
System.out.println("-----------------------");
|
|
|
|
|
|
for (int i = 0; i < iList.getLength(); i++) {
|
|
Node nNode = iList.item(i);
|
|
|
|
if (nNode.getNodeType() == Node.ELEMENT_NODE) {
|
|
Element eElement = (Element) nNode;
|
|
// System.out.println(eElement.getNodeName());
|
|
populateIntentList(basePackage, eElement);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
} catch (Exception e) {
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unused")
|
|
private static String getTagValue(String sTag, Element eElement) {
|
|
NodeList nlList = eElement.getElementsByTagName(sTag).item(0).getChildNodes();
|
|
|
|
Node nValue = (Node) nlList.item(0);
|
|
|
|
return nValue.getNodeValue();
|
|
}
|
|
|
|
private void populateIntentList(String basePackage, Element eElement) {
|
|
ArrayList<String[]> IntentList;
|
|
NodeList actionList = eElement.getElementsByTagName("action");
|
|
Node parent = eElement.getParentNode();
|
|
IntentList = chooseIntentList(parent.getNodeName());
|
|
|
|
String IntentClass = parent.getAttributes().getNamedItem("android:name").getTextContent();
|
|
|
|
for (int i = 0; i < actionList.getLength(); i++)
|
|
{
|
|
Node nNode = actionList.item(i);
|
|
if (nNode.getNodeType() == Node.ELEMENT_NODE) {
|
|
IntentList.add(new String[2]);
|
|
IntentList.get(IntentList.size()-1)[0] = actionList.item(i).getAttributes().getNamedItem("android:name").getTextContent();
|
|
|
|
if (IntentClass.startsWith(basePackage))
|
|
IntentList.get(IntentList.size()-1)[1] = IntentClass;
|
|
else {
|
|
if (IntentClass.startsWith("."))
|
|
IntentList.get(IntentList.size()-1)[1] = basePackage + IntentClass;
|
|
else {
|
|
IntentList.get(IntentList.size()-1)[1] = basePackage + "." + IntentClass;
|
|
IntentList.add(new String[2]);
|
|
IntentList.get(IntentList.size()-1)[0] = actionList.item(i).getAttributes().getNamedItem("android:name").getTextContent();
|
|
IntentList.get(IntentList.size()-1)[1] = IntentClass;
|
|
}
|
|
|
|
//IntentList.get(IntentList.size()-1)[1] = basePackage + (IntentClass.startsWith(".") ? IntentClass : "." + IntentClass);
|
|
}
|
|
|
|
//System.out.println(IntentList.get(IntentList.size()-1)[0] + " ~> " + IntentList.get(IntentList.size()-1)[1]);
|
|
}
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unused")
|
|
private void populateEntryPoints(ClassHierarchy cha) {
|
|
String method = null;
|
|
IMethod im = null;
|
|
for (String[] intent: ActivityIntentList) {
|
|
//method = IntentToMethod(intent[0]);
|
|
method = "onCreate(Landroid/os/Bundle;)V";
|
|
|
|
if (method != null)
|
|
im = cha.resolveMethod(StringStuff.makeMethodReference(intent[1]+"."+method));
|
|
if (im!=null)
|
|
entries.add(new DefaultEntrypoint(im,cha));
|
|
|
|
}
|
|
for (String[] intent: ReceiverIntentList) {
|
|
//Seems that every broadcast receiver can be an entrypoints?
|
|
// method = IntentToMethod(intent[0]);
|
|
method = "onReceive(Landroid/content/Context;Landroid/content/Intent;)V";
|
|
|
|
if (method != null)
|
|
im = cha.resolveMethod(StringStuff.makeMethodReference(intent[1]+"."+method));
|
|
if (im!=null)
|
|
entries.add(new DefaultEntrypoint(im,cha));
|
|
}
|
|
//IMethod im = cha.resolveMethod(StringStuff.makeMethodReference("android.app.Activity.onCreate(Landroid/os/Bundle;)V"));
|
|
//entries.add(new DefaultEntrypoint(im, cha));
|
|
}
|
|
|
|
@SuppressWarnings("unused")
|
|
private String IntentToMethod(String intent) {
|
|
if (intent.contentEquals("android.intent.action.MAIN") ||
|
|
intent.contentEquals("android.media.action.IMAGE_CAPTURE") ||
|
|
intent.contentEquals("android.media.action.VIDEO_CAPTURE") ||
|
|
intent.contentEquals("android.media.action.STILL_IMAGE_CAMERA") ||
|
|
intent.contentEquals("android.intent.action.MUSIC_PLAYER") ||
|
|
intent.contentEquals("android.media.action.VIDEO_CAMERA"))
|
|
return "onCreate(Landroid/os/Bundle;)V";
|
|
|
|
// else if (intent.contentEquals("android.intent.action.BOOT_COMPLETED") ||
|
|
// intent.contentEquals("android.appwidget.action.APPWIDGET_UPDATE") ||
|
|
// intent.contentEquals("android.provider.Telephony.SECRET_CODE") )
|
|
// return "onReceive(Landroid/content/Context;Landroid/content/Intent;)V";
|
|
|
|
|
|
else return null;
|
|
}
|
|
|
|
private ArrayList<String[]> chooseIntentList(String name) {
|
|
if (name.equals("activity"))
|
|
return ActivityIntentList;
|
|
else if (name.equals("receiver"))
|
|
return ReceiverIntentList;
|
|
else if (name.equals("service"))
|
|
return ServiceIntentList;
|
|
else {
|
|
return ActivityIntentList;
|
|
// throw new UnimplementedError("EntryPoints intent category not yet covered: " + name);
|
|
}
|
|
}
|
|
|
|
public LinkedList<Entrypoint> getEntries() {
|
|
return entries;
|
|
}
|
|
|
|
}
|