de.rub.syssec.saaf.gui.frame.CfgSelectorFrame.java Source code

Java tutorial

Introduction

Here is the source code for de.rub.syssec.saaf.gui.frame.CfgSelectorFrame.java

Source

/* 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;
        }
    }
}