package gui;
import com.jgoodies.forms.factories.FormFactory;
import com.jgoodies.forms.layout.ColumnSpec;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.forms.layout.RowSpec;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import parser.apk.APK;
import parser.dex.DEX;
import parser.dex.DexClass;
import parser.utils.FileTypesDetector;
import utils.UtilLocal;
import javax.swing.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created with IntelliJ IDEA.
* User: lai
* Date: 5/29/13
* Time: 10:47 AM
*/
public class APIs extends JPanel {
// private final JTextField jTextFieldFilePath;
JTextArea jTextAreaSMS = null;
JTextArea jTextAreaInternet = null;
JTextArea jTextAreaShell = null;
JTextArea jTextAreaPhoneInfo = null;
JTextArea jTextAreaURI = null;
/**
* 暂时用来做测试特殊代码用.
*/
JTextArea jTextAreaTest = null;
public APIs() {
// ----------------------------------------------- Layout ------------------------------------------------------
setLayout(new FormLayout(new ColumnSpec[]{ColumnSpec.decode("11dlu"),
ColumnSpec.decode("min:grow"),
FormFactory.LABEL_COMPONENT_GAP_COLSPEC,
ColumnSpec.decode("100px"), ColumnSpec.decode("10dlu"),},
new RowSpec[]{RowSpec.decode("15dlu"),
RowSpec.decode("23px"), RowSpec.decode("21px"),
RowSpec.decode("23px"),
FormFactory.RELATED_GAP_ROWSPEC,
RowSpec.decode("default:grow"),
FormFactory.DEFAULT_ROWSPEC,}));
// jTextFieldFilePath = new JTextField();
// add(jTextFieldFilePath, "2, 2, fill, fill");
// -------------------------------- Button -----------------------------------
// final JButton jButtonPath = new JButton("File");
// add(jButtonPath, "4, 2, fill, fill");
final JButton jButtonAnalysis = new JButton("Analysis");
add(jButtonAnalysis, "4, 2, fill, fill");
final JButton jButtonClearAll = new JButton("Clear");
add(jButtonClearAll, "4, 3, fill, fill");
// -------------------------------- Tab -----------------------------------
final JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
add(tabbedPane, "2, 6, 3, 1, fill, fill");
final JScrollPane scrollPaneInfos = new JScrollPane();
tabbedPane.addTab("SMS", null, scrollPaneInfos, null);
jTextAreaSMS = new JTextArea();
scrollPaneInfos.setViewportView(jTextAreaSMS);
final JScrollPane scrollPaneCertificate = new JScrollPane();
tabbedPane.addTab("Internet", null, scrollPaneCertificate, null);
jTextAreaInternet = new JTextArea();
scrollPaneCertificate.setViewportView(jTextAreaInternet);
final JScrollPane scrollPaneReceivers = new JScrollPane();
tabbedPane.addTab("PhoneInfo", null, scrollPaneReceivers, null);
jTextAreaPhoneInfo = new JTextArea();
scrollPaneReceivers.setViewportView(jTextAreaPhoneInfo);
final JScrollPane scrollPaneActivities = new JScrollPane();
tabbedPane.addTab("SHELL", null, scrollPaneActivities, null);
jTextAreaShell = new JTextArea();
scrollPaneActivities.setViewportView(jTextAreaShell);
final JScrollPane scrollPaneServices = new JScrollPane();
tabbedPane.addTab("URI", null, scrollPaneServices, null);
jTextAreaURI = new JTextArea();
scrollPaneServices.setViewportView(jTextAreaURI);
final JScrollPane scrollPanePermissions = new JScrollPane();
tabbedPane.addTab("NONE", null, scrollPanePermissions, null);
jTextAreaTest = new JTextArea();
scrollPanePermissions.setViewportView(jTextAreaTest);
jButtonAnalysis.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final APIsAnalysisTask task = new APIsAnalysisTask(Main.filePath, jTextAreaSMS, jTextAreaInternet,
jTextAreaPhoneInfo, jTextAreaShell, jTextAreaURI, jTextAreaTest, jButtonAnalysis);
task.execute();
jButtonAnalysis.setEnabled(false);
}
});
jButtonClearAll.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
jTextAreaSMS.setText("");
jTextAreaInternet.setText("");
jTextAreaShell.setText("");
jTextAreaPhoneInfo.setText("");
jTextAreaURI.setText("");
jTextAreaTest.setText("");
}
});
}
}
class APIsAnalysisTask extends SwingWorker<HashMap<Byte, String>, HashMap<Byte, String>> {
final Byte FLAG_SMS = 0;
final Byte FLAG_NETWORK = 1;
final Byte FLAG_SHELL = 2;
final Byte FLAG_PHONE_INFO = 3;
final Byte FLAG_URI = 4;
final Byte FLAG_TEST = 5;
final String TAG_API = "api";
final String ATTR_TYPE = "type";
final String ATTR_METHOD = "method";
private final JButton jButtonAnalysis;
HashMap<Byte, String> behaviorType = new HashMap<>();
JTextArea jTextAreaSMS = null;
JTextArea jTextAreaNetwork = null;
JTextArea jTextAreaShell = null;
JTextArea jTextAreaPhoneInfos = null;
JTextArea jTextAreaUri = null;
JTextArea jTextAreaTest = null;
String filePath;
// api configure
String API_PATH = "conf/apis.xml";
/**
* 存放 apis.xml 解析出来的敏感 api :
* <code>
* <pre>
* { API 类型:api 列表 }
* </pre>
* </code>
* 根据不同的类型,放入不同的标签页中显示
*/
HashMap<String, ArrayList<String>> apisMap = new HashMap<>();
public APIsAnalysisTask(String text, JTextArea jTextAreaSMS, JTextArea jTextAreaNetwork,
JTextArea jTextAreaPhoneInfos, JTextArea jTextAreaShell,
JTextArea jTextAreaUri, JTextArea jTextAreaTest, JButton jButtonAnalysis) {
this.filePath = text;
this.jTextAreaSMS = jTextAreaSMS;
this.jTextAreaNetwork = jTextAreaNetwork;
this.jTextAreaShell = jTextAreaShell;
this.jTextAreaPhoneInfos = jTextAreaPhoneInfos;
this.jTextAreaUri = jTextAreaUri;
this.jTextAreaTest = jTextAreaTest;
this.jButtonAnalysis = jButtonAnalysis;
behaviorType.put(FLAG_SMS, "SMS");
behaviorType.put(FLAG_NETWORK, "NETWORK");
behaviorType.put(FLAG_SHELL, "SHELL");
behaviorType.put(FLAG_PHONE_INFO, "PHONE_INFO");
behaviorType.put(FLAG_URI, "URI");
behaviorType.put(FLAG_TEST, "TEST");
initAPIsXML();
if (UtilLocal.DEBUG) {
System.out.println("初始化 apis.xml:");
for (String key : apisMap.keySet()) {
System.out.println(key);
for (String str : apisMap.get(key)) {
System.out.println("\t" + str);
}
}
}
}
@Override
public HashMap<Byte, String> doInBackground() {
HashMap<Byte, String> hashMap = new HashMap<>();
List<DexClass> dexClasses;
File file = new File(filePath);
try {
if (FileTypesDetector.isAPK(file)) {
APK apk = new APK(file);
dexClasses = apk.getDexClasses();
} else if (FileTypesDetector.isDEX(file)) {
DEX dex = new DEX(file);
dexClasses = dex.getDexClasses();
} else {
hashMap.put(FLAG_SMS, "Not a apk or dex/odex.");
return hashMap;
}
// 用来匹配 URI
Pattern pattern = Pattern.compile("[\\w]+://[\\w\\d.:/?&=\\-_%]+", Pattern.CASE_INSENSITIVE);
// 临时存放检索到的 API
HashMap<String, ArrayList<String>> contentMap = new HashMap<>();
// 临时存放检索到的 URI
ArrayList<String> uriList = new ArrayList<>();
String methodBody;
for (DexClass dexClass : dexClasses) {
if (dexClass.methodMap.size() > 0) {
for (String key : dexClass.methodMap.keySet()) {
methodBody = dexClass.methodMap.get(key);
// API 检索
for (String type : apisMap.keySet()) {
for (String api : apisMap.get(type)) {
if (methodBody.contains(api)) {
if (contentMap.keySet().contains(type)) {
contentMap.get(type).add(key + " [ " + api + " ]\n");
} else {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add(key + " [ " + api + " ]\n");
contentMap.put(type, arrayList);
}
}
}
}
Matcher matcher = pattern.matcher(methodBody);
while (matcher.find()) {
uriList.add(key + " [ " + matcher.group() + " ]\n");
}
}
}
}
// 对 field 位置字符串的匹配
for (DexClass dexClass : dexClasses) {
for (DexClass.TField tField : dexClass.fields) {
if (tField.value instanceof String) {
Matcher matcher = pattern.matcher(tField.value.toString());
while (matcher.find()) {
uriList.add(tField.field + " [ " + matcher.group() + " ]\n");
}
}
}
}
if (UtilLocal.DEBUG) {
System.out.println("匹配的结果:");
for (String str : contentMap.keySet()) {
System.out.println(contentMap.get(str));
}
}
// --------------------------------------------- Display --------------------------------------------------
StringBuilder sb = new StringBuilder();
ArrayList<String> tmpList;
for (String key : contentMap.keySet()) {
tmpList = contentMap.get(key);
Collections.sort(tmpList);
for (String str : tmpList) {
sb.append(str);
}
sb.append("\n");
for (Byte type : behaviorType.keySet()) {
if (behaviorType.get(type).equals(key)) {
hashMap.put(type, sb.toString());
sb.delete(0, sb.length());
break;
}
}
}
// -------------------- URI --------------------
Collections.sort(uriList);
for (String item : uriList) {
sb.append(item);
}
sb.append("\n");
hashMap.put(FLAG_URI, sb.toString());
sb.delete(0, sb.length());
} catch (Exception e) {
if (UtilLocal.DEBUG) {
e.printStackTrace();
}
}
return hashMap;
}
/**
* 初始化敏感 API
*/
private void initAPIsXML() {
FileInputStream fileInputStream = null;
try {
if (UtilLocal.DEBUG) {
API_PATH = "/home/lai/Project/android-toolkit/apat/resources/conf/apis.xml";
}
fileInputStream = new FileInputStream(API_PATH);
final DocumentBuilderFactory factory = DocumentBuilderFactory
.newInstance();
factory.setIgnoringElementContentWhitespace(true);
final DocumentBuilder documentBuilder = factory.newDocumentBuilder();
final Document xmlDocument = documentBuilder.parse(fileInputStream);
final Element rootElement = xmlDocument.getDocumentElement();
for (Node node = rootElement.getFirstChild(); node != null; node = node
.getNextSibling()) {
String nodeName = node.getNodeName();
if (TAG_API.equals(nodeName)) {
parseTag(node, apisMap);
}
}
} catch (ParserConfigurationException | IOException | SAXException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null)
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 对标签进行解析,并将解析后的内容保存在 apisMap 中.
*
* @param tag 待解析的标签
* @param apisMap 存放解析结果的MAP
*/
private void parseTag(Node tag, HashMap<String, ArrayList<String>> apisMap) {
NamedNodeMap namedNodeMap = tag.getAttributes();
String type;
String mtd;
Node nodeAttr = namedNodeMap.getNamedItem(ATTR_TYPE);
if (nodeAttr != null) {
type = nodeAttr.getNodeValue();
} else {
return;
}
nodeAttr = namedNodeMap.getNamedItem(ATTR_METHOD);
if (nodeAttr != null) {
mtd = nodeAttr.getNodeValue();
} else {
return;
}
if (apisMap.keySet().contains(type)) {
apisMap.get(type).add(mtd);
} else {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add(mtd);
apisMap.put(type, arrayList);
}
}
@Override
protected void done() {
try {
for (Byte key : get().keySet()) {
if (key.equals(FLAG_SMS)) {
jTextAreaSMS.append(get().get(FLAG_SMS));
} else if (key.equals(FLAG_NETWORK)) {
jTextAreaNetwork.append(get().get(FLAG_NETWORK));
} else if (key.equals(FLAG_SHELL)) {
jTextAreaShell.append(get().get(FLAG_SHELL));
} else if (key.equals(FLAG_PHONE_INFO)) {
jTextAreaPhoneInfos.append(get().get(FLAG_PHONE_INFO));
} else if (key.equals(FLAG_URI)) {
jTextAreaUri.append(get().get(FLAG_URI));
} else if (key.equals(FLAG_TEST)) {
jTextAreaTest.append(get().get(FLAG_TEST));
}
}
} catch (InterruptedException e) {
jTextAreaSMS.append("\nStop!\n");
if (UtilLocal.DEBUG) {
e.printStackTrace();
}
} catch (ExecutionException e) {
jTextAreaSMS.append("\nIs it a android package file?\n");
if (UtilLocal.DEBUG) {
e.printStackTrace();
}
} finally {
jButtonAnalysis.setEnabled(true);
}
}
}