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 parser.apk.APK;
import parser.utils.FileTypesDetector;
import parser.utils.HashTool;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.*;
import java.util.Date;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 这个界面主要是样本的数据导入到数据库中.
*/
public class AppDataBaseGUI extends JPanel {
private final JTextField filePath;
JTextArea jTextAreaSMS = null;
JTextArea jTextAreaInternet = null;
JTextArea jTextAreaShell = null;
JTextArea jTextAreaPhoneInfo = null;
JTextArea jTextAreaURI = null;
/**
* 暂时用来做测试特殊代码用.
*/
JTextArea jTextAreaTest = null;
public AppDataBaseGUI() {
// ----------------------------------------------- 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,}));
filePath = new JTextField();
// filePath.setText("");
add(filePath, "2, 2, fill, fill");
// -------------------------------- Button -----------------------------------
final JButton jButtonPath = new JButton("File");
add(jButtonPath, "4, 2, fill, fill");
final JButton jButtonAnalysis = new JButton("Import");
add(jButtonAnalysis, "4, 3, fill, fill");
final JButton jButtonClearAll = new JButton("Clear");
add(jButtonClearAll, "4, 4, 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("Log1", null, scrollPaneInfos, null);
jTextAreaSMS = new JTextArea();
scrollPaneInfos.setViewportView(jTextAreaSMS);
final JScrollPane scrollPaneCertificate = new JScrollPane();
tabbedPane.addTab("Log2", null, scrollPaneCertificate, null);
jTextAreaInternet = new JTextArea();
scrollPaneCertificate.setViewportView(jTextAreaInternet);
final JScrollPane scrollPaneReceivers = new JScrollPane();
tabbedPane.addTab("Log3", null, scrollPaneReceivers, null);
jTextAreaPhoneInfo = new JTextArea();
scrollPaneReceivers.setViewportView(jTextAreaPhoneInfo);
final JScrollPane scrollPaneActivities = new JScrollPane();
tabbedPane.addTab("Log4", null, scrollPaneActivities, null);
jTextAreaShell = new JTextArea();
scrollPaneActivities.setViewportView(jTextAreaShell);
final JScrollPane scrollPaneServices = new JScrollPane();
tabbedPane.addTab("Log5", null, scrollPaneServices, null);
jTextAreaURI = new JTextArea();
scrollPaneServices.setViewportView(jTextAreaURI);
final JScrollPane scrollPanePermissions = new JScrollPane();
tabbedPane.addTab("Log6", null, scrollPanePermissions, null);
jTextAreaTest = new JTextArea();
scrollPanePermissions.setViewportView(jTextAreaTest);
// ----------------------------------------------- Event -------------------------------------------------------
new FileDrop(System.out, filePath,
new FileDrop.Listener() {
@Override
public void filesDropped(File[] files) {
File f = files[0];
filePath.setText(f.getAbsolutePath());
}
});
jButtonPath.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
final JFileChooser fc = new JFileChooser();
final int returnVal = fc.showOpenDialog(null);
if (returnVal == JFileChooser.APPROVE_OPTION) {
filePath.setText(fc.getSelectedFile().getAbsolutePath());
}
}
});
jButtonAnalysis.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final APIsAnalysisTask task = new APIsAnalysisTask(filePath.getText(), 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<List<String>, 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 Connection conn;
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 path;
// Scanner scanner;
// ------- ---- 线程池相关参数 ---------------
ThreadPoolExecutor threadPoolExecutor;
int maximumPoolSize = Runtime.getRuntime().availableProcessors() / 2;
int corePoolSize = maximumPoolSize / 2;
long keepAliveTime = 3L;
public APIsAnalysisTask(String text, JTextArea jTextAreaSMS, JTextArea jTextAreaNetwork,
JTextArea jTextAreaPhoneInfos, JTextArea jTextAreaShell,
JTextArea jTextAreaUri, JTextArea jTextAreaTest, JButton jButtonAnalysis) {
this.path = text;
this.jTextAreaSMS = jTextAreaSMS;
this.jTextAreaNetwork = jTextAreaNetwork;
this.jTextAreaShell = jTextAreaShell;
this.jTextAreaPhoneInfos = jTextAreaPhoneInfos;
this.jTextAreaUri = jTextAreaUri;
this.jTextAreaTest = jTextAreaTest;
this.jButtonAnalysis = jButtonAnalysis;
conn = prepareDb();
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");
initThreadPoolExecutor();
// scanner = new Scanner();
}
private void initThreadPoolExecutor() {
threadPoolExecutor =
new ThreadPoolExecutor(
corePoolSize,
maximumPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(maximumPoolSize),
new ThreadPoolExecutor.CallerRunsPolicy());
}
/**
* 分析APK,并将其信息存入数据库
*
* @param file file
*/
public void printAppInfo(File file) {
try {
if (FileTypesDetector.getType(new FileInputStream(file)).contains("ELF")){
String fileSHA256 = HashTool.getSHA256(file);
String varNames = file.getParentFile().getName();
byte result = insert_elf_info(conn, fileSHA256, file.getName(), varNames);
if (result == 1) {
publish(file.getName() + " 导入成功\n");
} else if (result == -1) {
publish(file.getName() + " 导入失败\n");
} else {
publish(file.getName() + " 数据已存在\n");
}
return;
}
} catch (IOException e) {
e.printStackTrace();
}
try {
APK apk = new APK(file);
String fileSHA256 = HashTool.getSHA256(file);
HashMap<String, String> certMap = apk.getCertificateInfos();
String permissions = apk.getPermissions().toString();
String services = apk.getServices().toString();
String receivers = apk.getReceivers().toString();
String varNames = file.getParentFile().getName();
// ArrayList<String> elfHash256List = apk.getelfHash256List();
byte result = insert_sample_info(conn, fileSHA256, file.getName(), varNames, apk.getDexSHA256(),
apk.getPackageName(), apk.getLabel(), certMap.toString(), permissions, receivers, services);
if (result == 1) {
publish(file.getName() + " 导入成功\n");
} else if (result == -1) {
publish(file.getName() + " 导入失败\n");
} else {
publish(file.getName() + " 数据已存在\n");
}
} catch (Exception e) {
if (e.getMessage().contains("IT IS NOT A APK FILE")) {
publish(file.getName() + " 非APK文件\n");
} else {
publish(file.getName() + " 导入异常!!!!\n");
e.printStackTrace();
}
}
}
@Override
public List<String> doInBackground() {
final File pathFile = new File(path);
if (pathFile.isFile()) {
publish(new Date().toString());
publish("路径 : " + pathFile.getParentFile().getName() + "/" + pathFile.getName());
printAppInfo(pathFile);
} else if (pathFile.isDirectory()) {
int count = 0;
publish(new Date() + " > 开始导入数据:目录(多线程, 非递归):" + pathFile.getName());
LinkedList<File> linkedList = new LinkedList<>();
linkedList.addLast(pathFile);
while (linkedList.size() > 0) {
File file = linkedList.removeFirst();
File[] files = file.listFiles();
if (files == null) {
continue;
}
for (final File subFile : files) {
if (subFile.isDirectory()) {
linkedList.addLast(subFile);
} else {
count++;
final int finalCount = count;
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
publish(String.valueOf(finalCount) + " : " + subFile.getName() + "...................");
printAppInfo(subFile);
}
});
while (threadPoolExecutor.getActiveCount() >= maximumPoolSize) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (count % 1000 == 0) {
try {
conn.commit();
} catch (SQLException e) {
if (e.getMessage().contains("database is locked")) {
publish("database is locked");
}
e.printStackTrace();
}
}
}
}
}
}
while (true) {
if (threadPoolExecutor.getActiveCount() == 0
&& threadPoolExecutor.getQueue().size() == 0) {
break;
}
publish("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
publish("Active Count : " + threadPoolExecutor.getActiveCount());
publish("Core Pool Size : " + threadPoolExecutor.getCorePoolSize());
publish("Maximum Pool Size : " + threadPoolExecutor.getMaximumPoolSize());
publish("Pool Size : " + threadPoolExecutor.getPoolSize());
publish("Completed Task Count : " + threadPoolExecutor.getCompletedTaskCount());
publish("Queue Size : " + threadPoolExecutor.getQueue().size());
publish("Task Count : " + threadPoolExecutor.getTaskCount());
publish("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
publish("=========================================================");
publish("Active Count : " + threadPoolExecutor.getActiveCount());
publish("Core Pool Size : " + threadPoolExecutor.getCorePoolSize());
publish("Maximum Pool Size : " + threadPoolExecutor.getMaximumPoolSize());
publish("Pool Size : " + threadPoolExecutor.getPoolSize());
publish("Completed Task Count : " + threadPoolExecutor.getCompletedTaskCount());
publish("Queue Size : " + threadPoolExecutor.getQueue().size());
publish("Task Count : " + threadPoolExecutor.getTaskCount());
publish("=========================================================");
try {
conn.commit();
} catch (SQLException e) {
if (e.getMessage().contains("database is locked")) {
publish("database is locked");
}
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
return null;
}
@Override
protected void done() {
releaseDb(conn);
threadPoolExecutor.shutdown();
jButtonAnalysis.setEnabled(true);
publish(new Date() + " 全部操作完毕 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
}
@Override
protected void process(List<String> chunks) {
for (String str : chunks) {
jTextAreaSMS.append(str + "\n");
}
}
// -----------------------------------数据库操作------------------------------------------------
//
/**
* 初始化数据库
*
* @return 返回数据库连接器
*/
private Connection prepareDb() {
Connection conn = null;
try {
// load the sqlite-JDBC driver using the current class loader
Class.forName("org.sqlite.JDBC");
// conn = DriverManager.getConnection("jdbc:sqlite::memory:");
conn = DriverManager.getConnection("jdbc:sqlite:samples.db");
final Statement statement = conn.createStatement();
// 恶意软件表(恶意软件ID,恶意软件名,恶意软件描述)
// statement.executeUpdate("CREATE TABLE IF NOT EXISTS MAL_INFO (" +
// "\"mal_id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
// "\"mal_name\" TEXT(20) NOT NULL, " +
// "\"mal_dst\" TEXT(300)" +
// ")");
// statement.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_MAL_INFO\" ON \"MAL_INFO\" (\"mal_name\" ASC)");
// 变种表(变种ID,变种名,恶意软件ID)
// statement.executeUpdate("CREATE TABLE IF NOT EXISTS VARIETY_INFO (" +
// "\"var_id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " +
// "\"var_name\" TEXT(20) NOT NULL," +
// "\"mal_id\" INTEGER NOT NULL" +
// ")");
// statement.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_VARIETY_INFO\" ON \"VARIETY_INFO\" (\"var_name\" ASC)");
// 样本表(样本hash, 变种ID)
statement.executeUpdate("CREATE TABLE IF NOT EXISTS SAMPLE_INFO (" +
"\"file_sha256\" TEXT(32) PRIMARY KEY NOT NULL, " +
"\"file_name\" TEXT(100) NOT NULL, " +
"\"var_name\" TEXT(100) NOT NULL, " +
"\"dex_sha256\" TEXT(32) NOT NULL, " +
"\"pkg_name\" TEXT(50) NOT NULL, " +
"\"label\" TEXT(100) NOT NULL, " +
"\"cert\" TEXT(100), " +
"\"permissions\" TEXT(1000), " +
"\"receivers\" TEXT(2000), " +
"\"services\" TEXT(1000), " +
"\"feature_code\" TEXT(50), " +
"\"sub_file_sha256\" TEXT(64)" +
")");
statement.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_SAMPLE_INFO\" ON \"SAMPLE_INFO\" (\"file_sha256\" ASC)");
statement.executeUpdate("CREATE TABLE IF NOT EXISTS ELF_INFO (" +
"\"file_sha256\" TEXT(32) PRIMARY KEY NOT NULL, " +
"\"file_name\" TEXT(100) NOT NULL, " +
"\"var_name\" TEXT(100) NOT NULL, " +
"\"feature_code\" TEXT(100)" +
")");
statement.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_SAMPLE_INFO\" ON \"ELF_INFO\" (\"file_sha256\" ASC)");
// 证书表(证书MD5, 证书信息,证书类型[暂时没用])
// statement.executeUpdate("CREATE TABLE IF NOT EXISTS CERT_INFO (" +
// "\"md5\" TEXT(32) PRIMARY KEY NOT NULL, " +
// "\"owner\" TEXT(100)" +
// ")");
// statement.executeUpdate("CREATE UNIQUE INDEX IF NOT EXISTS [IDX_CERT_INFO] ON [CERT_INFO]([cert_md5] ASC)");
// 清单信息表
// statement.executeUpdate("CREATE TABLE IF NOT EXISTS AM_INFO (" +
// "\"file_md5\" TEXT, " +
// "\"pkg_name\" TEXT, " +
// "\"receivers\" TEXT, " +
// "\"services\" TEXT)");
// statement.executeUpdate("CREATE TABLE IF NOT EXISTS DEX_INFO (file_md5 TEXT, dex_md5 TEXT)");
statement.close();
// 让其不自动提交
conn.setAutoCommit(false);
} catch (final ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return conn;
}
/**
* 插入恶意软件信息
*
* @param mal_name 恶意软件名
*/
@SuppressWarnings("UnusedDeclaration")
private boolean insert_mal_info(String mal_name) {
boolean result = false;
if (conn != null) {
PreparedStatement st;
try {
st = conn.prepareStatement("insert into mal_info(mal_id, mal_name, mal_dst) values(?, ?, ?)");
st.setString(2, mal_name);
st.setString(3, mal_name);
st.execute();
result = true;
} catch (final SQLException e) {
System.out.println(e.getLocalizedMessage());
}
}
return result;
}
/**
* 插入变种信息
*
* @param varName 变种名
*/
@SuppressWarnings("UnusedDeclaration")
private boolean insert_var_info(String varName) {
boolean result = false;
if (conn != null) {
PreparedStatement st;
try {
st = conn.prepareStatement("INSERT INTO VARIETY_INFO(var_id, var_name, mal_id) values(?, ?, ?)");
st.setString(2, varName);
st.setInt(3, 1);
st.execute();
result = true;
} catch (final SQLException e) {
System.out.println(e.getLocalizedMessage());
}
}
return result;
}
/**
* 插入样本信息
*
* @param conn database connection
* @param fileSHA256 file md5
* @param fileName file name
* @param varName variety name
* @param label apk label
* @param cert certificate info
* @param perms permissions
* @param recs receivers
* @param services services
* @return 1 插入成功,0 数据已存在, -1 插入异常。
*/
private byte insert_sample_info(Connection conn, String fileSHA256, String fileName, String varName,
String dexSHA256, String pkgName, String label, String cert, String perms,
String recs, String services) {
byte result = -1;
if (conn != null) {
PreparedStatement st;
try {
st = conn.prepareStatement("insert into sample_info(file_sha256, file_name, var_name, dex_sha256, " +
"pkg_name, label, cert, permissions, receivers, services) " +
"values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
st.setString(1, fileSHA256);
st.setString(2, fileName);
st.setString(3, varName);
st.setString(4, dexSHA256);
st.setString(5, pkgName);
st.setString(6, label);
st.setString(7, cert);
st.setString(8, perms);
st.setString(9, recs);
st.setString(10, services);
st.execute();
result = 1;
} catch (final SQLException e) {
if (e.getMessage().contains("column file_sha256 is not unique")) {
result = 0;
} else {
result = -1;
e.printStackTrace();
}
}
}
return result;
}
private byte insert_elf_info(Connection conn, String fileSHA256, String fileName, String varName) {
byte result = -1;
if (conn != null) {
PreparedStatement st;
try {
st = conn.prepareStatement("insert into elf_info(file_sha256, file_name, var_name) values(?, ?, ?)");
st.setString(1, fileSHA256);
st.setString(2, fileName);
st.setString(3, varName);
st.execute();
result = 1;
} catch (final SQLException e) {
if (e.getMessage().contains("column file_sha256 is not unique")) {
result = 0;
} else {
result = -1;
e.printStackTrace();
}
}
}
return result;
}
/**
* 插入证书信息
*
* @param conn database connection
* @param md5 certificate md5
* @param owner owner
*/
@SuppressWarnings("UnusedDeclaration")
private boolean insert_cert_info(Connection conn, String md5, String owner) {
boolean result = false;
if (conn != null) {
PreparedStatement st;
try {
st = conn.prepareStatement("insert into cert_info(cert_md5, owner) values(?, ?)");
st.setString(1, md5);
st.setString(2, owner);
st.execute();
result = true;
} catch (final SQLException e) {
System.out.println(e.getLocalizedMessage());
}
}
return result;
}
private void releaseDb(Connection conn) {
try {
if (conn != null)
conn.close();
} catch (final SQLException e) {
e.printStackTrace();
}
}
}