The Code
Because this bot is going to allow
remote access to a shell, it will be called ShellBot. The access
password will be stored in the field
password, and it will respond to any private
message that contains only this password.
When a user sends the correct password, the bot will start up a new
ProcessThread in which to handle the shell
process.
Save the following as ShellBot.java:
import org.jibble.pircbot.*;
public class ShellBot extends PircBot {
private String password = "password";
public ShellBot(String name) {
setName(name);
}
public void onPrivateMessage(String sender, String login,
String hostname, String message) {
if (message.trim( ).equals(password)) {
// Password was correct, so create a new thread to handle the shell.
Thread thread = new ProcessThread(this, sender);
thread.start( );
}
}
}
The ProcessThread class
launches the shell process in its own thread so that it
doesn't block the normal operation of the bot. The
thread starts by sending a DCC
CHAT request to the user and waits
for up to 120,000 milliseconds for the recipient to establish the
connection to the bot. If the connection is not made in time, the bot
gives up and the user will have to resend the correct password and
try again.
If the user's IRC client accepts the
DCC CHAT request in time, it will
establish a direct TCP connection to the bot. This connection will be
used to provide the remote shell access, as it is not subject to
flood prevention restrictions like normal IRC messages.
This particular bot is set up to run on Windows, so it executes
cmd.exe to provide shell access. If you are
using an older version of Windows, you may have to change this to
command.com. If you wish to use ShellBot on
Unix/Linux or Mac, you will
need to change it to /bin/sh or suchlike.
Save the following as
ProcessThread.java:
import org.jibble.pircbot.*;
import java.io.*;
public class ProcessThread extends Thread {
private PircBot bot;
private String nick;
public ProcessThread(PircBot bot, String nick) {
this.bot = bot;
this.nick = nick;
}
public void run( ) {
DccChat chat = null;
try {
// Send a DCC CHAT request.
chat = bot.dccSendChatRequest(nick, 120000);
if (chat != null) {
Runtime runtime = Runtime.getRuntime( );
// Use "/bin/sh" on Linux.
Process p = runtime.exec("cmd.exe");
// Start a new thread to handle input from the client.
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(p.getOutputStream( )));
ProcessInputThread inputThread =
new ProcessInputThread(chat, writer);
inputThread.start( );
// Return all lines from the shell.
BufferedReader reader = new BufferedReader(
new InputStreamReader(p.getInputStream( )));
String line;
while ((line = reader.readLine( )) != null) {
chat.sendLine(line);
}
}
}
catch (IOException e) {
// Chat session ended unexpectedly.
}
if (chat != null) {
try {
chat.close( );
}
catch (IOException e) {
// Do nothing.
}
}
}
}
The ProcessThread runs the shell process and reads all lines that it
outputs. Each line is then sent down the DCC connection to the
connected client. The shell process is running all the time, so you
must create another thread to handle input from the connected client.
Save the following as
ProcessInputThread.java:
import org.jibble.pircbot.*;
import java.io.*;
public class ProcessInputThread extends Thread {
private DccChat chat;
private BufferedWriter writer;
public ProcessInputThread(DccChat chat, BufferedWriter writer) {
this.chat = chat;
this.writer = writer;
}
public void run( ) {
try {
// Kludge... write a blank line to shell so path shows up.
writer.write("\r\n");
writer.flush( );
// Pass each line from the client to the process.
String line;
while ((line = chat.readLine( )) != null) {
writer.write(line + "\r\n\r\n");
writer.flush( );
}
}
catch (Exception e) {
// Just let the thread end...
}
}
}
Because DCC CHAT is designed to send and receive
only complete lines, you will not see the
command-line prompt, as it does not end with
a newline character. The kludge fix for this is to enter a blank line
after every input from the user. This is implemented in the
ProcessInputThread class and causes the command-line prompt to appear
each time the shell is ready to receive the next command; it also
causes it to be echoed a second time when each command is processed.
The ShellBot doesn't need to join any channels, as
it responds only to private messages, but it is sometimes useful to
place such a bot in a channel so that you know it's
there.
Save the following as ShellBotMain.java:
public class ShellBotMain {
public static void main(String[] args) throws Exception {
ShellBot bot = new ShellBot("ShellBot");
bot.setVerbose(true);
bot.connect("irc.freenode.net");
bot.joinChannel("#irchacks");
}
}