Java tutorial
/* SAAF: A static analyzer for APK files. * Copyright (C) 2013 syssec.rub.de * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package de.rub.syssec.saaf.gui.frame; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import java.util.LinkedList; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JInternalFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; import org.apache.commons.io.FileUtils; import de.rub.syssec.saaf.analysis.steps.cfg.CFGGraph; import de.rub.syssec.saaf.analysis.steps.cfg.ExportCFG; import de.rub.syssec.saaf.application.methods.Method; import de.rub.syssec.saaf.gui.MainWindow; import de.rub.syssec.saaf.gui.editor.MethodViewer; import de.rub.syssec.saaf.misc.config.Config; import de.rub.syssec.saaf.misc.config.ConfigKeys; import de.rub.syssec.saaf.model.application.ClassInterface; import de.rub.syssec.saaf.model.application.MethodInterface; /** * This frame shows all methods for one smali class for which CFGs can * be created and exported. */ public class CfgSelectorFrame extends JInternalFrame { private static final long serialVersionUID = -1922752821043510095L; private final ClassInterface smaliClass; private JTable list = new JTable(); private final AbstractTableModel listModel; private JButton generateButton; private JButton showButton; private JCheckBox saveCfgCheckBox; private static final String GENERATE_OPERATION = "Generate"; private static final String SHOW_OPERATION = "Show"; /** * A frame to generate and open Control Flow Graphs. CFGs are created using mxGraph * and are saved as PNGs. * * @param smaliClass the smali class to select methods from */ public CfgSelectorFrame(ClassInterface smaliClass) { super(smaliClass.getFullClassName(true), true, true, true, true); this.smaliClass = smaliClass; listModel = new MethodTableModel(smaliClass); list.setModel(listModel); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.getSelectionModel().addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent event) { int viewRow = list.getSelectedRow(); if (viewRow <= 0) { showButton.setEnabled(false); } else { showButton.setEnabled(true); } } }); JScrollPane listScrollPane = new JScrollPane(list); ActionListener al = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals(SHOW_OPERATION)) { generateAndShowCfg(true); } else if (e.getActionCommand().equals(GENERATE_OPERATION)) { generateAndShowCfg(false); } } }; generateButton = new JButton(GENERATE_OPERATION); generateButton.setActionCommand(GENERATE_OPERATION); generateButton.addActionListener(al); showButton = new JButton(SHOW_OPERATION); showButton.setEnabled(false); showButton.addActionListener(al); saveCfgCheckBox = new JCheckBox("Save a copy"); saveCfgCheckBox.setToolTipText("Save a copy in the configured CFG folder upon generation."); JPanel buttonPane = new JPanel(); buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.LINE_AXIS)); buttonPane.add(generateButton); buttonPane.add(saveCfgCheckBox); buttonPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); JPanel filler = new JPanel(); filler.setPreferredSize(new Dimension(10, 10)); buttonPane.add(filler); buttonPane.add(showButton); add(listScrollPane, BorderLayout.CENTER); add(buttonPane, BorderLayout.PAGE_END); this.setPreferredSize(new Dimension(500, 250)); this.pack(); this.setVisible(true); } /** * Show a CFG in external viewer. CFG will be created on-the-fly if it * does not exists. This method will determine the selected method(s) * from the shown table. * * @param showCfg Generate AND show CFG? */ private void generateAndShowCfg(boolean showCfg) { boolean save = saveCfgCheckBox.isSelected(); int index = list.getSelectedRow(); if (index == 0) { // Generate all for (MethodInterface method : smaliClass.getMethods()) { File cfg = generateAndShowCfg(method, false); if (save) { safeCopy(cfg); } } } else if (index > 0) { Method method = (Method) listModel.getValueAt(index, 0); // one-dimensional File cfg = generateAndShowCfg(method, showCfg); if (save) { safeCopy(cfg); } } } /** * generates a cfg and may show it * * @param showCfg Generate AND show CFG? * @return the file representing the CFG as PNG file */ private File generateAndShowCfg(MethodInterface method, boolean showCfg) { String targetDir = smaliClass.getApplication().getBytecodeDirectory().getAbsolutePath(); CFGGraph c = new CFGGraph(method); ExportCFG ex = new ExportCFG(c.getGraph(), targetDir); ex.export(method); File cfg = new File(ex.getLastExportedFile()); if (showCfg) { MethodViewer cfgViewer = new MethodViewer(method); MainWindow.getDesktopPane().add(cfgViewer); try { cfgViewer.setSelected(true); } catch (java.beans.PropertyVetoException e) { } } return cfg; } /** * Copies the generated PNG to the specified directory. The directory must be * specified in the config. * * @param cfg the CFG to copy */ private void safeCopy(File cfg) { String cfgValue = Config.getInstance().getConfigValue(ConfigKeys.DIRECTORY_CFGS); File externalDir = null; boolean error = false; if (cfgValue != null) { externalDir = new File(cfgValue); if (!externalDir.exists() || !externalDir.isDirectory() || !externalDir.canWrite()) { error = true; } } else { error = true; } if (error) { MainWindow.showErrorDialog("Could not save CFG(s). Option " + ConfigKeys.DIRECTORY_CFGS.toString() + " not properly set in saaf.conf.", "Error"); return; } String name = smaliClass.getApplication().getApplicationDirectory().getName(); String bytecodeDir = smaliClass.getApplication().getBytecodeDirectory().getName(); int posByteCodeName = cfg.getAbsolutePath().lastIndexOf(bytecodeDir); String path = cfg.getAbsolutePath().substring(posByteCodeName + bytecodeDir.length() + 1); File targetFile = new File( externalDir + File.separator + name + File.separator + path.replace(File.separator, ".")); try { if (!targetFile.exists() || (cfg.length() != targetFile.length())) { FileUtils.copyFile(cfg, targetFile); } } catch (IOException e) { e.printStackTrace(); MainWindow.showErrorDialog("Could not save CFG(s): " + e.getMessage(), "Error"); } } /** * An internal helper class to make use of a JTable. */ private class MethodTableModel extends AbstractTableModel { private static final long serialVersionUID = 8834298385480526192L; private final LinkedList<MethodInterface> methods = new LinkedList<MethodInterface>(); private final String[] columnNames = { "Shortened method names" }; public MethodTableModel(ClassInterface smaliClass) { /* * First one is the 'All' list item, it is no real method but starts * the CFG creation for all methods in one class. */ for (MethodInterface method : smaliClass.getMethods()) { methods.add(method); } } @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return false; } @Override public Object getValueAt(int row, int column) { if (row == 0) { return "Create CFGs for all methods"; } return methods.get(row - 1); // first one is null/"All" } @Override public int getRowCount() { return methods.size() + 1; } @Override public int getColumnCount() { return columnNames.length; } @Override public String getColumnName(int columnIndex) { return columnNames[columnIndex]; } @Override public Class<?> getColumnClass(int columnIndex) { return Object.class; } } }