/*
* Copyright, Aspect Security, Inc.
*
* This file is part of JavaSnoop.
*
* JavaSnoop 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.
*
* JavaSnoop 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 JavaSnoop. If not, see <http://www.gnu.org/licenses/>.
*/
package com.aspect.snoop.ui.canary;
import com.aspect.snoop.FunctionHook;
import com.aspect.snoop.MethodWrapper;
import com.aspect.snoop.agent.AgentCommunicationException;
import com.aspect.snoop.agent.AgentLogger;
import com.aspect.snoop.agent.SnoopAgent;
import com.aspect.snoop.agent.manager.InstrumentationException;
import com.aspect.snoop.agent.manager.InstrumentationManager;
import com.aspect.snoop.agent.manager.MethodChanges;
import com.aspect.snoop.util.CanaryUtil;
import com.aspect.snoop.util.ClasspathUtil;
import com.aspect.snoop.util.StringUtil;
import com.aspect.snoop.util.UIUtil;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;
import javax.swing.table.DefaultTableModel;
import org.jdesktop.application.Action;
public class StartCanaryModeView extends javax.swing.JDialog {
final DefaultTableModel model;
SwingWorker canaryWorker = null;
public StartCanaryModeView(final java.awt.Frame parent, boolean modal) {
super(parent, modal);
initComponents();
this.model = new DefaultTableModel(new Object[]{"Method",""},0);
tblCanaries.setRowHeight(25);
tblCanaries.setModel(model);
/*
* The action to be performed when the user hits one
* of the "Add Hook" buttons.
*/
javax.swing.Action addHook = new AbstractAction() {
public void actionPerformed(ActionEvent e) {
int i = Integer.valueOf(e.getActionCommand()).intValue();
Chirp c = chirps.get(i);
FunctionHook hook = new FunctionHook(c.getMethod());
SnoopAgent.getMainView().addHook(hook);
JOptionPane.showMessageDialog(parent, "Function hook added to " + hook.getClazz().getName() + "." + hook.getMethodName() + "()");
}
};
new ButtonColumn(tblCanaries, addHook, 1);
tblCanaries.getColumnModel().getColumn(0).setPreferredWidth(450);
}
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
btnStartCanaryMode = new javax.swing.JButton();
txtCanary = new javax.swing.JTextField();
jLabel2 = new javax.swing.JLabel();
lstDataTypes = new javax.swing.JComboBox();
btnStopCanaryMode = new javax.swing.JButton();
jScrollPane1 = new javax.swing.JScrollPane();
tblCanaries = new javax.swing.JTable();
txtPackage = new javax.swing.JTextField();
jLabel3 = new javax.swing.JLabel();
prgCanary = new javax.swing.JProgressBar();
chkIncludeJava = new javax.swing.JCheckBox();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
org.jdesktop.application.ResourceMap resourceMap = org.jdesktop.application.Application.getInstance(com.aspect.snoop.JavaSnoop.class).getContext().getResourceMap(StartCanaryModeView.class);
setTitle(resourceMap.getString("Form.title")); // NOI18N
setName("Form"); // NOI18N
addComponentListener(new java.awt.event.ComponentAdapter() {
public void componentShown(java.awt.event.ComponentEvent evt) {
viewShown(evt);
}
});
javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(com.aspect.snoop.JavaSnoop.class).getContext().getActionMap(StartCanaryModeView.class, this);
btnStartCanaryMode.setAction(actionMap.get("beginCanaryMode")); // NOI18N
btnStartCanaryMode.setFont(resourceMap.getFont("btnStartCanaryMode.font")); // NOI18N
btnStartCanaryMode.setText(resourceMap.getString("btnStartCanaryMode.text")); // NOI18N
btnStartCanaryMode.setToolTipText(resourceMap.getString("btnStartCanaryMode.toolTipText")); // NOI18N
btnStartCanaryMode.setName("btnStartCanaryMode"); // NOI18N
txtCanary.setText(resourceMap.getString("txtCanary.text")); // NOI18N
txtCanary.setToolTipText(resourceMap.getString("txtCanary.toolTipText")); // NOI18N
txtCanary.setName("txtCanary"); // NOI18N
jLabel2.setText(resourceMap.getString("jLabel2.text")); // NOI18N
jLabel2.setName("jLabel2"); // NOI18N
lstDataTypes.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "String", "short", "int", "long", "double", "float" }));
lstDataTypes.setName("lstDataTypes"); // NOI18N
btnStopCanaryMode.setAction(actionMap.get("endCanaryMode")); // NOI18N
btnStopCanaryMode.setFont(resourceMap.getFont("btnStopCanaryMode.font")); // NOI18N
btnStopCanaryMode.setText(resourceMap.getString("btnStopCanaryMode.text")); // NOI18N
btnStopCanaryMode.setToolTipText(resourceMap.getString("btnStopCanaryMode.toolTipText")); // NOI18N
btnStopCanaryMode.setEnabled(false);
btnStopCanaryMode.setName("btnStopCanaryMode"); // NOI18N
jScrollPane1.setName("jScrollPane1"); // NOI18N
tblCanaries.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
{null, null, null, null},
{null, null, null, null},
{null, null, null, null},
{null, null, null, null}
},
new String [] {
"Title 1", "Title 2", "Title 3", "Title 4"
}
));
tblCanaries.setName("tblCanaries"); // NOI18N
jScrollPane1.setViewportView(tblCanaries);
txtPackage.setText(resourceMap.getString("txtPackage.text")); // NOI18N
txtPackage.setToolTipText(resourceMap.getString("txtPackage.toolTipText")); // NOI18N
txtPackage.setName("txtPackage"); // NOI18N
jLabel3.setText(resourceMap.getString("jLabel3.text")); // NOI18N
jLabel3.setName("jLabel3"); // NOI18N
prgCanary.setName("prgCanary"); // NOI18N
chkIncludeJava.setText(resourceMap.getString("chkIncludeJava.text")); // NOI18N
chkIncludeJava.setToolTipText(resourceMap.getString("chkIncludeJava.toolTipText")); // NOI18N
chkIncludeJava.setName("chkIncludeJava"); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 625, Short.MAX_VALUE)
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
.addComponent(jLabel3)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(txtPackage, javax.swing.GroupLayout.PREFERRED_SIZE, 125, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(chkIncludeJava))
.addComponent(prgCanary, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 625, Short.MAX_VALUE)
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(txtCanary)
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel2)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(lstDataTypes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnStartCanaryMode, javax.swing.GroupLayout.PREFERRED_SIZE, 211, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnStopCanaryMode)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(txtCanary, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel2)
.addComponent(lstDataTypes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btnStartCanaryMode, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(btnStopCanaryMode, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(txtPackage, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel3)
.addComponent(chkIncludeJava))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(prgCanary, javax.swing.GroupLayout.PREFERRED_SIZE, 26, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(7, 7, 7)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 160, Short.MAX_VALUE))
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void viewShown(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_viewShown
repaint();
}//GEN-LAST:event_viewShown
@Action
public void beginCanaryMode() throws AgentCommunicationException {
final String canary = txtCanary.getText();
final String pkg = txtPackage.getText();
if (canary.length() <= 0) {
UIUtil.showErrorMessage(this, "Please enter a value to track through the application");
return;
}
// setup background action to find canaries.
setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
final String type = (String) lstDataTypes.getSelectedItem();
try {
final InstrumentationManager manager = SnoopAgent.getAgentManager();
manager.resetAllClasses();
CanaryUtil.currentCanary = canary;
String cType = null;
if ("String".equals(type)) {
cType = "java.lang.String";
} else if ("short".equals(type)) {
cType = "java.lang.Short";
} else if ("int".equals(type)) {
cType = "java.lang.Integer";
} else if ("long".equals(type)) {
cType = "java.lang.Long";
} else if ("double".equals(type)) {
cType = "java.lang.Double";
} else if ("float".equals(type)) {
cType = "java.lang.Float";
}
final String canaryType = cType;
if (canary == null) {
throw new InstrumentationException("Can't apply canary to unknown type: " + type);
}
final javax.swing.JDialog parent = this;
canaryWorker = new SwingWorker() {
int clsCount = 0;
int mtdCount = 0;
String finalMsg = null;
String nl = System.getProperty("line.separator");
@Override
protected void done() {
prgCanary.setString(finalMsg);
prgCanary.setEnabled(false);
try {
final String methodList = (String) get();
prgCanary.addMouseListener(new MouseListener() {
public void mouseClicked(MouseEvent e) {
if ( e.getClickCount() != 2 )
return;
ShowMethodsView view = new ShowMethodsView(parent, true, methodList);
view.setVisible(true);
}
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
});
} catch (InterruptedException ex) {
AgentLogger.error(ex);
} catch (ExecutionException ex) {
AgentLogger.error(ex);
}
}
@Override
protected Object doInBackground() throws Exception {
StringBuilder sb = new StringBuilder();
prgCanary.setString("Starting canary mode...");
prgCanary.setStringPainted(true);
prgCanary.setEnabled(true);
List<Class> loadedClasses = manager.getLoadedClasses();
List<Class> targetClasses = new ArrayList<Class>();
for(Class c : loadedClasses) {
if (c == null || c.isInterface() || c.isArray())
continue;
String clsName = c.getName();
if (pkg != null && pkg.length() > 0 && !clsName.startsWith(pkg))
continue;
if (ClasspathUtil.isJavaSnoopClass(clsName))
continue;
// FIXME: run into errors here on applets. should work.
if ( ! chkIncludeJava.isSelected() && ClasspathUtil.isJavaOrSunClass(clsName) )
continue;
targetClasses.add(c);
}
prgCanary.setMinimum(0);
prgCanary.setMaximum(targetClasses.size());
AgentLogger.debug("Applying canaries to " + targetClasses.size() + " classes...");
for (int i=0;i<targetClasses.size();i++) {
if ( isCancelled() )
return null;
Class c = targetClasses.get(i);
String clsName = c.getName();
prgCanary.setValue(i);
prgCanary.setString("Adding canaries to class " + c.getName() + " (" + (i+1) + "/" + (targetClasses.size()+1) + ")");
List<MethodChanges> classChanges = new ArrayList<MethodChanges>();
Method[] methods = null;
try {
methods = c.getDeclaredMethods();
} catch (Throwable t) {
AgentLogger.warn("Problem analyzing method for canary purposes",t);
continue;
}
Constructor[] constructors = null;
try {
constructors = c.getDeclaredConstructors();
} catch (Throwable t) {
AgentLogger.trace("Failed to canary " + c.getName() + ": " + t.getMessage());
}
List<Member> members = new ArrayList<Member>();
if (methods != null) {
members.addAll(Arrays.asList(methods));
}
if (constructors != null) {
members.addAll(Arrays.asList(constructors));
}
for (Member m : members) {
if (Modifier.isAbstract(m.getModifiers())) {
continue;
}
Class[] types = null;
if (m instanceof Constructor) {
types = ((Constructor) m).getParameterTypes();
} else {
types = ((Method) m).getParameterTypes();
}
boolean alreadyMatched = false;
for (Class paramType : types) {
if ( alreadyMatched )
continue;
boolean match = paramType.getName().equals(canaryType);
if ( match) {
alreadyMatched = true;
MethodWrapper wrapper = MethodWrapper.getWrapper((AccessibleObject) m);
MethodChanges change = new MethodChanges((AccessibleObject) m);
change.setNewStartSrc(CanaryUtil.getChirp(canaryType, clsName, wrapper.getName(), wrapper.getReturnType().getName()));
AgentLogger.debug("Applying canary to " + wrapper.getDescription());
sb.append(wrapper.getDescription());
sb.append(nl);
classChanges.add(change);
mtdCount++;
}
}
}
try {
manager.instrument(c, classChanges.toArray(new MethodChanges[]{}));
clsCount++;
} catch (InstrumentationException ex) {
AgentLogger.debug("Failed to apply canary to " + clsName + ": " + ex.getMessage());
}
}
prgCanary.setValue(prgCanary.getMaximum());
finalMsg = "Successfully canaried " + clsCount + " classes and " + mtdCount + " methods.";
AgentLogger.info(finalMsg);
return sb.toString();
}
};
canaryWorker.execute();
txtCanary.setEnabled(false);
lstDataTypes.setEnabled(false);
btnStartCanaryMode.setEnabled(false);
btnStopCanaryMode.setEnabled(true);
} catch (InstrumentationException ex) {
UIUtil.showErrorMessage(this, StringUtil.exception2string(ex));
AgentLogger.error(ex);
}
}
@Action
public void endCanaryMode() throws AgentCommunicationException {
if ( canaryWorker != null ) {
canaryWorker.cancel(true);
while(!canaryWorker.isDone()) {
try {
Thread.sleep(100);
} catch (InterruptedException ex) { }
}
}
txtCanary.setEnabled(true);
lstDataTypes.setEnabled(true);
btnStartCanaryMode.setEnabled(true);
btnStopCanaryMode.setEnabled(false);
try {
SnoopAgent.getAgentManager().resetAllClasses();
CanaryUtil.currentCanary = null;
} catch (InstrumentationException ex) {
UIUtil.showErrorMessage(this, StringUtil.exception2string(ex));
AgentLogger.error(ex);
}
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton btnStartCanaryMode;
private javax.swing.JButton btnStopCanaryMode;
private javax.swing.JCheckBox chkIncludeJava;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JComboBox lstDataTypes;
private javax.swing.JProgressBar prgCanary;
private javax.swing.JTable tblCanaries;
private javax.swing.JTextField txtCanary;
private javax.swing.JTextField txtPackage;
// End of variables declaration//GEN-END:variables
private List<Chirp> chirps = new ArrayList<Chirp>();
public void addChirp(Class c, AccessibleObject method) {
chirps.add(new Chirp(c, method));
model.addRow(new Object[]{method.toString(),"Add Hook"});
model.fireTableDataChanged();
}
}