add color to armitage's presentation of the Metasploit console

unstable
Raphael Mudge 2012-05-16 04:23:21 -04:00
parent c7b9b711f1
commit 74e4812946
16 changed files with 344 additions and 71 deletions

Binary file not shown.

View File

@ -1,6 +1,18 @@
Armitage Changelog Armitage Changelog
================== ==================
17 May 12
---------
- Fixed bug with loot/download viewer breaking with a font resize.
- Default console font color is now grey. I never noticed that I had
white text on a black background before. That's a lot of contrast.
This is adjustable too through Armitage -> Preferences.
- And... the Armitage console now displays pretty colors. If you don't
like colors, set the console.show_colors.boolean preference to false
through Armitage -> Preferences.
- Fixed a bug preventing input field from getting focus when popping a
console tab using Ctrl+W.
14 May 12 14 May 12
--------- ---------
- Oopserific--dynamic workspace shortcuts were not bound until you - Oopserific--dynamic workspace shortcuts were not bound until you

View File

@ -3,7 +3,7 @@
<center><h1>Armitage 1.44-dev</h1></center> <center><h1>Armitage 1.44-dev</h1></center>
<p>An attack management tool for Metasploit&reg; <p>An attack management tool for Metasploit&reg;
<br />Release: 14 May 12</p> <br />Release: 17 May 12</p>
<br /> <br />
<p>Developed by:</p> <p>Developed by:</p>

View File

@ -16,7 +16,7 @@ console.page_up.shortcut=pressed PAGE_UP
console.highlight.color=\#0000cc console.highlight.color=\#0000cc
console.font_size_plus.shortcut=ctrl pressed EQUALS console.font_size_plus.shortcut=ctrl pressed EQUALS
console.font_size_minus.shortcut=ctrl pressed MINUS console.font_size_minus.shortcut=ctrl pressed MINUS
console.foreground.color=\#ffffff console.foreground.color=\#cccccc
console.background.color=\#000000 console.background.color=\#000000
console.font.font=Monospaced-BOLD-14 console.font.font=Monospaced-BOLD-14
graph.arrange_icons_hierarchical.shortcut=ctrl pressed H graph.arrange_icons_hierarchical.shortcut=ctrl pressed H
@ -39,10 +39,20 @@ armitage.no_msf_banner.boolean=false
tab.highlight.color=#0000ff tab.highlight.color=#0000ff
armitage.show_all_commands.boolean=true armitage.show_all_commands.boolean=true
armitage.application_title.string=Armitage armitage.application_title.string=Armitage
console.color_0.color=#000000 console.color_0.color=\#ffffff
console.color_1.color=#ffffff console.color_1.color=\#000000
console.color_2.color=#0000ff console.color_2.color=\#000080
console.color_3.color=#00ff00 console.color_3.color=\#009000
console.color_4.color=#ff0000 console.color_4.color=\#ff0000
console.color_5.color=#ffff00 console.color_5.color=\#800000
console.color_6.color=#ff00ff console.color_6.color=\#a000a0
console.color_7.color=\#ff8000
console.color_8.color=\#ffff00
console.color_9.color=\#00ff00
console.color_10.color=\#009090
console.color_11.color=\#00ffff
console.color_12.color=\#0000ff
console.color_13.color=\#ff00ff
console.color_14.color=\#808080
console.color_15.color=\#c0c0c0
console.show_colors.boolean=true

View File

@ -0,0 +1,4 @@
^(..:..:..) \[\*\] (.*) $1 \cA[*]\o $2
^\[\*\] (.*) \cA[*]\o $1
^(..:..:..) \* (.*) $1 \c7*\o $2
^(\w+)> \u$1\o>

View File

@ -0,0 +1,11 @@
^msf> \umsf\u>
^meterpreter > \umeterpreter\u >
^msf > \umsf\u >
^msf (.*?)\((.*?)\) > \umsf\u $1(\c4$2\o) >
^\[\*\] (.*) \cA[*]\o $1
^\[\+\] (.*) \c9[+]\o $1
^\[\-\] (.*) \c4[-]\o $1
^ =\[ (.*) =[\c7 $1
^(=[=\s]+) \cE$1
^(\s*-[-\s]+) \cE$1
^(.*?): (.*) $1\cE:\o $2

View File

@ -13,6 +13,7 @@ sub createEventLogTab {
if ($client is $null && $console is $null) { if ($client is $null && $console is $null) {
$client = [new ConsoleClient: $null, $mclient, "armitage.poll", "armitage.push", $null, "", $null]; $client = [new ConsoleClient: $null, $mclient, "armitage.poll", "armitage.push", $null, "", $null];
$console = [new ActivityConsole: $preferences]; $console = [new ActivityConsole: $preferences];
setupEventStyle($console);
logCheck($console, "all", "events"); logCheck($console, "all", "events");
[$client setWindow: $console]; [$client setWindow: $console];
[$client setEcho: $null]; [$client setEcho: $null];

View File

@ -76,17 +76,16 @@ sub _postLoot {
local('$host $location $name $type $when'); local('$host $location $name $type $when');
($host, $location, $name, $type, $when) = $1; ($host, $location, $name, $type, $when) = $1;
[$2 append: " [$2 append: "\c9
# #
# $host $+ : $name # $host $+ : $name
# #\n"];
", "3", "#00ff00"];
if ("*binary*" iswm $type) { if ("*binary*" iswm $type) {
[$2 append: "This is a binary file\n", "4", "#ff0000"]; [$2 append: "\c4This is a binary file\n"];
} }
else { else {
[$2 append: getFileContent($location), $null, $null]; [$2 append: getFileContent($location)];
} }
} }

View File

@ -116,6 +116,7 @@ sub createMeterpreterTab {
# set up a meterpreter console window # set up a meterpreter console window
$console = [new Console: $preferences]; $console = [new Console: $preferences];
setupConsoleStyle($console);
logCheck($console, sessionToHost($1), "meterpreter_ $+ $1"); logCheck($console, sessionToHost($1), "meterpreter_ $+ $1");
[$console setPopupMenu: lambda(&meterpreterPopup, $session => sessionData($1), $sid => $1)]; [$console setPopupMenu: lambda(&meterpreterPopup, $session => sessionData($1), $sid => $1)];

View File

@ -69,10 +69,33 @@ sub cleanText {
return tr($1, "\x01\x02", ""); return tr($1, "\x01\x02", "");
} }
sub setupConsoleStyle {
this('$style');
if ($style is $null) {
local('$handle');
$handle = [SleepUtils getIOHandle: resource("resources/msfconsole.style"), $null];
$style = join("\n", readAll($handle));
closef($handle);
}
[$1 setStyle: $style];
}
sub setupEventStyle {
this('$style');
if ($style is $null) {
local('$handle');
$handle = [SleepUtils getIOHandle: resource("resources/eventlog.style"), $null];
$style = join("\n", readAll($handle));
closef($handle);
}
[$1 setStyle: $style];
}
sub createDisplayTab { sub createDisplayTab {
local('$console $host $queue $file'); local('$console $host $queue $file');
$queue = [new ConsoleQueue: $client]; $queue = [new ConsoleQueue: $client];
$console = [new Console: $preferences]; $console = [new Console: $preferences];
setupConsoleStyle($console);
[$queue setDisplay: $console]; [$queue setDisplay: $console];
[new QueueTabCompletion: $console, $queue]; [new QueueTabCompletion: $console, $queue];
logCheck($console, iff($host, $host, "all"), iff($file, $file, strrep($1, " ", "_"))); logCheck($console, iff($host, $host, "all"), iff($file, $file, strrep($1, " ", "_")));
@ -84,6 +107,7 @@ sub createDisplayTab {
sub createConsolePanel { sub createConsolePanel {
local('$console $result $thread $1'); local('$console $result $thread $1');
$console = [new Console: $preferences]; $console = [new Console: $preferences];
setupConsoleStyle($console);
$result = call($client, "console.create"); $result = call($client, "console.create");
$thread = [new ConsoleClient: $console, $client, "console.read", "console.write", "console.destroy", $result['id'], $1]; $thread = [new ConsoleClient: $console, $client, "console.read", "console.write", "console.destroy", $result['id'], $1];

View File

@ -165,19 +165,30 @@ public class ArmitageApplication extends JFrame {
i.remove(); i.remove();
/* pop goes the tab! */ /* pop goes the tab! */
JFrame r = new JFrame(t.title); final JFrame r = new JFrame(t.title);
r.setIconImages(getIconImages()); r.setIconImages(getIconImages());
r.setLayout(new BorderLayout()); r.setLayout(new BorderLayout());
r.add(t.component, BorderLayout.CENTER); r.add(t.component, BorderLayout.CENTER);
r.pack(); r.pack();
r.setVisible(true);
r.addWindowListener(new WindowAdapter() { r.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent ev) { public void windowClosing(WindowEvent ev) {
if (t.removeListener != null) if (t.removeListener != null)
t.removeListener.actionPerformed(new ActionEvent(ev.getSource(), 0, "close")); t.removeListener.actionPerformed(new ActionEvent(ev.getSource(), 0, "close"));
} }
public void windowOpened(WindowEvent ev) {
r.setState(JFrame.NORMAL);
t.component.requestFocusInWindow();
}
public void windowActivated(WindowEvent ev) {
t.component.requestFocusInWindow();
}
}); });
r.setState(JFrame.ICONIFIED);
r.setVisible(true);
} }
} }
} }

View File

@ -6,6 +6,7 @@ import sleep.console.*;
import sleep.bridges.*; import sleep.bridges.*;
import sleep.error.*; import sleep.error.*;
import sleep.engine.*; import sleep.engine.*;
import sleep.parser.ParserConfig;
import java.util.*; import java.util.*;
@ -79,6 +80,11 @@ public class ArmitageMain implements RuntimeWarningWatcher, Loadable, Function {
} }
public ArmitageMain(String[] args) { public ArmitageMain(String[] args) {
/* tweak the parser to recognize a few useful escapes */
ParserConfig.installEscapeConstant('c', console.Colors.color + "");
ParserConfig.installEscapeConstant('o', console.Colors.cancel + "");
/* setup a function or two */
Hashtable environment = new Hashtable(); Hashtable environment = new Hashtable();
environment.put("&resource", this); environment.put("&resource", this);

View File

@ -0,0 +1,138 @@
package console;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
/* a class for managing and parsing colors */
public class Colors {
public static final char bold = (char)2;
public static final char underline = (char)31;
public static final char color = (char)3;
public static final char cancel = (char)15;
public static final char reverse = (char)22;
private static final class Fragment {
protected SimpleAttributeSet attr = new SimpleAttributeSet();
protected StringBuffer text = new StringBuffer(32);
protected Fragment next = null;
public void advance() {
next = new Fragment();
next.attr = (SimpleAttributeSet)attr.clone();
}
}
protected boolean showcolors = true;
public Colors(java.util.Properties prefs) {
colorTable = new Color[16];
colorTable[0] = Color.white;
colorTable[1] = new Color(0, 0, 0);
colorTable[2] = new Color(0, 0, 128);
colorTable[3] = new Color(0, 144, 0);
colorTable[4] = new Color(255, 0, 0);
colorTable[5] = new Color(128, 0, 0);
colorTable[6] = new Color(160, 0, 160);
colorTable[7] = new Color(255, 128, 0);
colorTable[8] = new Color(255, 255, 0);
colorTable[9] = new Color(0, 255, 0);
colorTable[10] = new Color(0, 144, 144);
colorTable[11] = new Color(0, 255, 255);
colorTable[12] = new Color(0, 0, 255);
colorTable[13] = new Color(255, 0, 255);
colorTable[14] = new Color(128, 128, 128);
colorTable[15] = Color.lightGray;
for (int x = 0; x < 16; x++) {
String temps = prefs.getProperty("console.color_" + x + ".color", null);
//System.err.println("console.color_" + x + ".color=\\#" + Integer.toHexString(colorTable[x].getRGB()).substring(2));
if (temps != null) {
colorTable[x] = Color.decode(temps);
}
}
/* do we want to show colors or automatically strip all of them? */
showcolors = "true".equals(prefs.getProperty("console.show_colors.boolean", "true"));
}
protected Color colorTable[];
/* strip format codes from the text */
public String strip(String text) {
StringBuffer buffer = new StringBuffer(text.length());
Fragment f = parse(text);
while (f != null) {
buffer.append(f.text);
f = f.next;
}
return buffer.toString();
}
public void append(JTextPane console, String text) {
StyledDocument doc = console.getStyledDocument();
Fragment f = parse(text);
while (f != null) {
try {
if (f.text.length() > 0)
doc.insertString(doc.getLength(), f.text.toString(), showcolors ? f.attr : null);
}
catch (Exception ex) {
ex.printStackTrace();
}
f = f.next;
}
}
public void set(JTextPane console, String text) {
console.setText("");
append(console, text);
}
private Fragment parse(String text) {
Fragment current = new Fragment();
Fragment first = current;
char[] data = text.toCharArray();
int fore, back;
for (int x = 0; x < data.length; x++) {
switch (data[x]) {
case bold:
current.advance();
StyleConstants.setBold(current.next.attr, !StyleConstants.isBold(current.attr));
current = current.next;
break;
case underline:
current.advance();
StyleConstants.setUnderline(current.next.attr, !StyleConstants.isUnderline(current.attr));
current = current.next;
break;
case color: /* look for 0-9a-f = 16 colors */
current.advance();
if ((x + 1) < data.length && ((data[x + 1] >= '0' && data[x + 1] <= '9') || (data[x + 1] >= 'A' && data[x + 1] <= 'F'))) {
int index = Integer.parseInt(data[x + 1] + "", 16);
StyleConstants.setForeground(current.next.attr, colorTable[index]);
x += 1;
}
current = current.next;
break;
case '\n':
current.advance();
current = current.next;
current.attr = new SimpleAttributeSet();
current.text.append(data[x]);
break;
case cancel:
current.advance();
current = current.next;
current.attr = new SimpleAttributeSet();
break;
default:
current.text.append(data[x]);
}
}
return first;
}
}

View File

@ -11,19 +11,21 @@ import java.awt.event.*;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.*; import java.util.*;
import java.util.regex.*;
import armitage.Activity; import armitage.Activity;
/** A generic multi-feature console for use in the Armitage network attack tool */ /** A generic multi-feature console for use in the Armitage network attack tool */
public class Console extends JPanel implements FocusListener { public class Console extends JPanel implements FocusListener {
protected JTextArea console; protected JTextPane console;
protected JTextField input; protected JTextField input;
protected JLabel prompt; protected JTextPane prompt;
protected PrintStream log = null; protected PrintStream log = null;
protected Properties display; protected Properties display;
protected Font consoleFont; protected Font consoleFont;
protected Colors colors;
protected ClickListener clickl; protected ClickListener clickl;
@ -139,6 +141,8 @@ public class Console extends JPanel implements FocusListener {
} }
private void updateComponentLooks() { private void updateComponentLooks() {
colors = new Colors(display);
Color foreground = Color.decode(display.getProperty("console.foreground.color", "#ffffff")); Color foreground = Color.decode(display.getProperty("console.foreground.color", "#ffffff"));
Color background = Color.decode(display.getProperty("console.background.color", "#000000")); Color background = Color.decode(display.getProperty("console.background.color", "#000000"));
@ -146,7 +150,10 @@ public class Console extends JPanel implements FocusListener {
while (i.hasNext()) { while (i.hasNext()) {
JComponent component = (JComponent)i.next(); JComponent component = (JComponent)i.next();
component.setForeground(foreground); component.setForeground(foreground);
component.setBackground(background); if (component == console || component == prompt)
component.setOpaque(false);
else
component.setBackground(background);
component.setFont(consoleFont); component.setFont(consoleFont);
if (component == console || component == prompt) { if (component == console || component == prompt) {
@ -173,11 +180,11 @@ public class Console extends JPanel implements FocusListener {
public void setPrompt(String text) { public void setPrompt(String text) {
String bad = "\ufffd\ufffd"; String bad = "\ufffd\ufffd";
if (text.equals(bad) || text.equals("null")) { if (text.equals(bad) || text.equals("null")) {
prompt.setText(defaultPrompt); colors.set(prompt, fixText(defaultPrompt));
} }
else { else {
defaultPrompt = text; defaultPrompt = text;
prompt.setText(text); colors.set(prompt, fixText(text));
} }
} }
@ -196,15 +203,64 @@ public class Console extends JPanel implements FocusListener {
} }
} }
protected void appendToConsole(String _text) { private static class Replacements {
if (_text.endsWith("\n") || _text.endsWith("\r")) { public Pattern original;
if (!promptLock) { public String replacer;
console.append(_text);
if (log != null) public Replacements(String o, String r) {
log.print(_text); original = Pattern.compile(o);
replacer = r;
}
}
public void setStyle(String text) {
String lines[] = text.trim().split("\n");
colorme = new Replacements[lines.length];
for (int x = 0; x < lines.length; x++) {
String ab[] = lines[x].split("\\t+");
if (ab.length == 2) {
ab[1] = ab[1].replace("\\c", Colors.color + "");
ab[1] = ab[1].replace("\\o", Colors.cancel + "");
ab[1] = ab[1].replace("\\u", Colors.underline + "");
colorme[x] = new Replacements(ab[0], ab[1]);
} }
else { else {
console.append(prompt.getText()); System.err.println(lines[x] + "<-- didn't split right:" + ab.length);
}
}
}
protected Replacements colorme[] = null;
protected String fixText(String text) {
if (colorme == null)
return text;
StringBuffer result = new StringBuffer();
String[] lines = text.split("(?<=\\n)");
for (int x = 0; x < lines.length; x++) {
String temp = lines[x];
for (int y = 0; y < colorme.length; y++) {
if (colorme[y] != null)
temp = colorme[y].original.matcher(temp).replaceFirst(colorme[y].replacer);
}
result.append(temp);
}
return result.toString();
}
protected void appendToConsole(String _text) {
_text = fixText(_text);
if (_text.endsWith("\n") || _text.endsWith("\r")) {
if (!promptLock) {
colors.append(console, _text);
if (log != null)
log.print(colors.strip(_text));
}
else {
colors.append(console, prompt.getText());
} }
if (!_text.startsWith(prompt.getText())) if (!_text.startsWith(prompt.getText()))
@ -214,18 +270,17 @@ public class Console extends JPanel implements FocusListener {
int breakp = _text.lastIndexOf("\n"); int breakp = _text.lastIndexOf("\n");
if (breakp != -1) { if (breakp != -1) {
console.append(_text.substring(0, breakp + 1)); colors.append(console, _text.substring(0, breakp + 1));
prompt.setText(_text.substring(breakp + 1) + " "); colors.set(prompt, _text.substring(breakp + 1) + " ");
if (log != null) if (log != null)
log.print(_text.substring(0, breakp + 1)); log.print(colors.strip(_text.substring(0, breakp + 1)));
} }
else { else {
prompt.setText(_text); colors.set(prompt, _text);
} }
promptLock = true; promptLock = true;
} }
if (console.getDocument().getLength() >= 1) { if (console.getDocument().getLength() >= 1) {
console.setCaretPosition(console.getDocument().getLength() - 1); console.setCaretPosition(console.getDocument().getLength() - 1);
} }
@ -276,9 +331,9 @@ public class Console extends JPanel implements FocusListener {
/* init the console */ /* init the console */
console = new JTextArea(); console = new JTextPane();
console.setEditable(false); console.setEditable(false);
console.setLineWrap(true); //console.setLineWrap(true);
console.addFocusListener(this); console.addFocusListener(this);
JScrollPane scroll = new JScrollPane( JScrollPane scroll = new JScrollPane(
@ -290,7 +345,8 @@ public class Console extends JPanel implements FocusListener {
/* init the prompt */ /* init the prompt */
prompt = new JLabel(); prompt = new JTextPane();
prompt.setEditable(false);
/* init the input */ /* init the input */
@ -373,6 +429,13 @@ public class Console extends JPanel implements FocusListener {
/* setup our word click listener */ /* setup our word click listener */
clickl = new ClickListener(this); clickl = new ClickListener(this);
console.addMouseListener(clickl); console.addMouseListener(clickl);
/* work-around for Nimbus L&F */
Color background = Color.decode(display.getProperty("console.background.color", "#000000"));
console.setBackground(new Color(0,0,0,0));
prompt.setBackground(new Color(0,0,0,0));
scroll.getViewport().setBackground(background);
console.setOpaque(false);
} }
public JPopupMenu getPopupMenu(final JTextComponent _component) { public JPopupMenu getPopupMenu(final JTextComponent _component) {

View File

@ -17,18 +17,24 @@ public class Display extends JPanel {
protected JTextPane console; protected JTextPane console;
protected Properties display; protected Properties display;
protected Font consoleFont; protected Font consoleFont;
protected Colors colors;
protected LinkedList components = new LinkedList(); protected LinkedList components = new LinkedList();
private void updateComponentLooks() { private void updateComponentLooks() {
colors = new Colors(display);
Color foreground = Color.decode(display.getProperty("console.foreground.color", "#ffffff")); Color foreground = Color.decode(display.getProperty("console.foreground.color", "#ffffff"));
Color background = Color.decode(display.getProperty("console.background.color", "#000000")); Color background = Color.decode(display.getProperty("console.background.color", "#000000"));
Iterator i = components.iterator(); Iterator i = components.iterator();
while (i.hasNext()) { while (i.hasNext()) {
JComponent component = (JComponent)i.next(); JComponent component = (JComponent)i.next();
if (component == console)
component.setOpaque(false);
else
component.setBackground(background);
component.setForeground(foreground); component.setForeground(foreground);
component.setBackground(background);
component.setFont(consoleFont); component.setFont(consoleFont);
if (component == console) { if (component == console) {
@ -45,48 +51,23 @@ public class Display extends JPanel {
} }
} }
private static Map colors = new HashMap(); public void append(final String text) {
public static AttributeSet getColor(String index, Properties preferences, String def) {
synchronized (colors) {
if (colors.get(index) == null) {
SimpleAttributeSet attrs = new SimpleAttributeSet();
Color temp = Color.decode(preferences.getProperty("console.color_" + index + ".color", def));
StyleConstants.setForeground(attrs, temp);
colors.put(index, attrs);
}
return (SimpleAttributeSet)colors.get(index);
}
}
public void append(final String text, final String index, final String fg) {
if (SwingUtilities.isEventDispatchThread()) { if (SwingUtilities.isEventDispatchThread()) {
_append(text, index, fg); _append(text);
} }
else { else {
SwingUtilities.invokeLater(new Runnable() { SwingUtilities.invokeLater(new Runnable() {
public void run() { public void run() {
_append(text, index, fg); _append(text);
} }
}); });
} }
} }
public void _append(String text, String index, String foreground) { public void _append(String text) {
try { Rectangle r = console.getVisibleRect();
Rectangle r = console.getVisibleRect(); colors.append(console, text);
StyledDocument doc = console.getStyledDocument(); console.scrollRectToVisible(r);
if (foreground == null) {
doc.insertString(doc.getLength(), text, null);
}
else {
doc.insertString(doc.getLength(), text, getColor(index, display, foreground));
}
console.scrollRectToVisible(r);
}
catch(Exception e) {
System.out.println(e);
}
} }
public void setText(final String _text) { public void setText(final String _text) {

View File

@ -1,6 +1,18 @@
Armitage Changelog Armitage Changelog
================== ==================
17 May 12
---------
- Fixed bug with loot/download viewer breaking with a font resize.
- Default console font color is now grey. I never noticed that I had
white text on a black background before. That's a lot of contrast.
This is adjustable too through Armitage -> Preferences.
- And... the Armitage console now displays pretty colors. If you don't
like colors, set the console.show_colors.boolean preference to false
through Armitage -> Preferences.
- Fixed a bug preventing input field from getting focus when popping a
console tab using Ctrl+W.
14 May 12 14 May 12
--------- ---------
- Oopserific--dynamic workspace shortcuts were not bound until you - Oopserific--dynamic workspace shortcuts were not bound until you