MOFU MOFU

技術的なことから、趣味の分野までいろいろメモ程度に書いていきます

ポートスキャンツールをjavaで作ってみる

ポートスキャンツールなんて、ネット上にあふれているし、nmap使えばいいんですが・・・
それはさておき、自分で作ってみたかったのと、久しぶりにJavaGUIを書いてみたかったので作ってみました。

非常に簡単なプログラムです。
TCPの0番から、65535番ポートまでスキャンします。

まずは、スキャン部分のコード

import java.net.InetSocketAddress;
import java.net.Socket;


public class TCPScan {

	private String hostName;
	private boolean status;
	private Socket socket;

	public TCPScan(String hostName) {

		this.hostName = hostName;
		this.status = false;

	}

	public void Scan(int portNumber) {
		try{
			System.out.println(portNumber);
			InetSocketAddress address = new InetSocketAddress(this.hostName, portNumber);
			//this.socket = new Socket(this.hostName, portNumber);
			this.socket = new Socket();
			this.socket.connect(address, 100);
			System.out.println(portNumber);

			this.status = true;
			this.socket.close();
		}catch(Exception e) {
			this.status = false;
			return;
		}
	}


	public boolean getStatus() {
		return this.status;
	}

}

非常にシンプルにかけました。

コンストラクタを呼び出す際に、ホスト名を指定します。スキャンメソッドで、ソケットを作るわけですが、タイムアウトを指定しないと、ポートが閉じていた場合応答しないホストもあるので、処理が終わらなくなってしまいます。そこで、InetSocketAddressでアドレスと、ポート番号を指定して、それを元に、通信します。タイムアウトしたり、コネクションが確立できない場合は、エクセプションを吐くので、その場合statusをfalseにします。

これだけです。
UDPもつくろうと思ったのですが、そもそも、UDPは3 way hand shakingをしないので、ソケットを作っただけじゃダメです。パケットを送って、応答が帰ってきたら開いてることにしようかなと思いましたが、それでうまくいくんですかね?
確証がなかったのと、UDPの場合はパケットロスした場合、再送しないので、信用性が低くなるので作りませんでした。
追加する気になったらまた書きますw

次にGUI部、

import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.JTextArea;
import javax.swing.JScrollPane;

import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

import javax.swing.JTable;
import javax.swing.JProgressBar;


public class PortScan extends JFrame {

	private JPanel contentPane;
	private JProgressBar progressBar;
	private JScrollPane scrollPane;
	private JTextField txtAddress;
	private JButton btnRun;
	private JTable table;
	private String hostName;
	private TCPScan tcpScan;
	private UDPScan udpScan;
	private int NumberOfPort = 65535;

	/**
	 * Launch the application.
	 */
	public static void main(String[] args) {
		EventQueue.invokeLater(new Runnable() {
			public void run() {
				try {
					PortScan frame = new PortScan();
					frame.setVisible(true);
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
	}

	/**
	 * Create the frame.
	 */
	public PortScan() {
		this.setTitle("Port Scan Tool 1.0");
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		this.setBounds(100, 100, 450, 350);
		this.contentPane = new JPanel();
		this.contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
		this.setContentPane(contentPane);
		this.contentPane.setLayout(null);

		this.txtAddress = new JTextField();
		this.txtAddress.setText("Address");
		this.txtAddress.setBounds(75, 26, 191, 28);
		this.contentPane.add(txtAddress);
		this.txtAddress.setColumns(10);

		this.btnRun = new JButton("Run");
		this.btnRun.addActionListener(new ActionListener() {

			public void actionPerformed(ActionEvent e) {
				PortScan.this.btnRun.setEnabled(false);
				DefaultTableModel model = (DefaultTableModel) PortScan.this.table.getModel();
				//String[][] str = {{"12","23","34"}};
				//model.addRow(str[0]);
				model.setRowCount(0);
				PortScan.this.hostName = txtAddress.getText();


				PortScan.this.tcpScan = new TCPScan(PortScan.this.hostName);

				//System.out.println(hostName);
				for(int i=0;i<NumberOfPort;i++) {
					PortScan.this.tcpScan.Scan(i);
					boolean portStatus = PortScan.this.tcpScan.getStatus();

					String[][] str ={{hostName,"TCP("+i+")","Open"}};
					if(portStatus == false) str[0][2] = "Close";
					//System.out.println(str[0][0] +str[0][1] + str[0][2]);
					model.addRow(str[0]);
					PortScan.this.progressBar.setValue(i+1);
					

					
				}
				/*
				PortScan.this.udpScan = new UDPScan(PortScan.this.hostName);
				
				for(int i=0;i<NumberOfPort;i++) {
					PortScan.this.udpScan.Scan(i);
					boolean portStatus = PortScan.this.udpScan.getStatus();

					String[][] str ={{hostName,"TCP("+i+")","Open"}};
					if(portStatus == false) str[0][2] = "Close";
					//System.out.println(str[0][0] +str[0][1] + str[0][2]);
					model.addRow(str[0]);
					PortScan.this.progressBar.setValue(NumberOfPort + i + 1);
					
					
				}
				
				*/

				//progressBar.setValue(5050);
				PortScan.this.btnRun.setEnabled(true);
				


			}
		});
		
		this.btnRun.setBounds(308, 27, 117, 29);
		this.contentPane.add(this.btnRun);

		this.scrollPane = new JScrollPane();
		this.scrollPane.setBounds(48, 92, 350, 162);
		this.contentPane.add(scrollPane);

		String title[] = {"Address","Port Number","Port Status"};
		DefaultTableModel tableModel = new DefaultTableModel(title, 0);

		this.table = new JTable(tableModel);



		this.table.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
			@Override
			public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
				super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
				if ("Open".equals(table.getValueAt(row, 2))) {
					this.setForeground(Color.RED);
				} else {
					this.setForeground(table.getForeground());
				}
				return this;
			}
		});
		this.table.setDefaultEditor(Object.class, null);

		this.scrollPane.setViewportView(this.table);

		this.progressBar = new JProgressBar();
		this.progressBar.setStringPainted(true);
		this.progressBar.setBounds(48, 284, 350, 20);
		this.progressBar.setMaximum(this.NumberOfPort);
		this.progressBar.setMinimum(0);
		this.contentPane.add(this.progressBar);

	}
}

説明はめんどくさいですwww 
JFrameに、ペタペタ貼ってるだけです。あとJTableで、ポートが開いてたら赤字にするようにしてます。その部分はぐぐりました。
ちなみに、eclipseのSwingDesignerとか使うと楽?かもしれません。ほとんどコードを自動生成してくれるので、楽といえば楽です。ちょっと、変数とかで問題が起きたりするので、コードをかえなきゃいけなくなったりしますが。
GUI書いたことない人は、勉強になるので、使わないで書いてみた方がいいかもしれないです。

ボタンが押された時の操作は、ActionListnerのところに書いてます。まあ、ループしてTCPScanのクラスのメソッド実行してるだけです。


プログレスバーが、最後まで更新されないんだけどなんでなんでしょう?

localhostをスキャンした結果
apacheを走らせてたので、80番が開いてます。
f:id:regry358:20140725175140p:plain

あと、ポートスキャンはマナー上あまりよくありません。管理者によっては、ポートスキャンに応答しないようにしていたり、ポートスキャンが来たら、banするところもあります。
あくまで、自分が管理しているホストのセキュリティチェック程度に使いましょう。