ポートスキャンツールをjavaで作ってみる
ポートスキャンツールなんて、ネット上にあふれているし、nmap使えばいいんですが・・・
それはさておき、自分で作ってみたかったのと、久しぶりにJavaでGUIを書いてみたかったので作ってみました。
非常に簡単なプログラムです。
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番が開いてます。
あと、ポートスキャンはマナー上あまりよくありません。管理者によっては、ポートスキャンに応答しないようにしていたり、ポートスキャンが来たら、banするところもあります。
あくまで、自分が管理しているホストのセキュリティチェック程度に使いましょう。
マインクラフトサーバー1.7.10への更新
マイクラのアップデートで、1.7.10が来ていたので、マイクラサーバーのアップデート時のまとめ。
1.7.9からのアップデートです。
環境は、Cent OS 6.5
もし、マイクらサーバーを起動している場合は、最初にstopしておきます。
まずは、ディレクトリごとバックアップをとっておきます。
cp -r ~ ~
〜のところには、ディレクトリ名とコピー先を指定してください。
その後、マイクラのjarファイルおいてるディレクトリに飛び、
wget https://s3.amazonaws.com/Minecraft.Download/versions/1.7.10/minecraft_server.1.7.10.jar
起動用スクリプトを、書きなおしてもいいのですが、まずは普通に起動テストします。
java -Xmx1024M -Xms1024M -jar minecraft_server.1.7.10.jar nogui
すると、
You need to agree to the EULA in order to run the server. Go to eula.txt for more info.
こんなメッセージが、
どうやら、EULA(End User License Agreement)が変更されたので同意しないといけないとのこと。
eula.txtをみると
#By changing the setting below to TRUE you are indicating your agreement to our EULA (https://account.mojang.com/documents/minecraft_eula).
EULAはちゃんと読みましょう。
よんだら、
最後の行を
eula=true
にします。
再び、
java -Xmx1024M -Xms1024M -jar minecraft_server.1.7.10.jar nogui
今度はちゃんと起動しました。
一旦stopと打ち込み、起動用スクリプトを変更します。
#!/bin/sh screen -AmdS minecraft java -Xmx1024M -Xms1024M -jar minecraft_server.1.7.10.jar nogui
jarのところの数字が変わっただけです。
起動用スクリプトは、使う人によって違った形にしてるかもしれないので、参考までに。
アップデートで、今まで使えていたものが使えなくなったりするかもしれないので、バックアップをとって、テスト環境で実行してみるといいかもしれません。
dummynetをWinodwsで使うときのまとめ
Windows 7 32bitで使った時のまとめ
まずdummynetのソースをダウンロードしてくる。
つぎに、ControlPanel->Network and Internet -> Network and Sharing Center -> Change adapter Settingsで今使ってるネットワークコネクションを右クリック
Properties->Install->Service->Add->Have Disk
でダウンロードしたdummynetのbinary/netipfwを選択しおk
あと、windows vista以降の場合デジタル署名制限を一時的に無効にしないといけません。
管理者権限で、コマンドプロンプトを開いて
bcdedit /set TESTSIGNING ON
これでテストモードが有効になるので、エラーが消えます。
リブートしても、この設定は変わらないので、使い終わったらオフにしておきます。
bcdedit /set TESTSIGNING OFF
あとは、ipfw.exeのあるディレクトリでなんなりと。。
そのうち、dummynetの使い方は書くと思います。
だいぶ、使い方限定してるけど
重複しない乱数の生成
友人から、配列の中にあるものを重複なしでランダムで取り出す方法ってどうやる?って聞かれまして、
愚直にやるんなら、2重ループ書いて、重複したら、また生成すればいいんじゃね。
いや、それだと賢くないなーって言ったら、
友人は、もうその方法で実装していたらしく、最悪ループの回数が恐ろしくなるので、賢い方法で
実装することにしました。
まずは、愚直に
int[] a={10,11,12,13,14}; boolean[] b = new boolean[5]; int[] c = new int[5]; for(int i=0;i<a.length;i++){ Random rand =new Random(); while(true){ int n = rand.nextInt(a.length); if(b[n] == false) { b[n] = true; c[i] = a[n]; break; } } }
これだと、賢くないので、配列をシャッフルする方法にします。
int[] a={10,11,12,13,14}; //boolean[] b = new boolean[5]; int[] c = a; for(int i=0;i<a.length;i++){ Random rand =new Random(); int n = rand.nextInt(a.length); int temp = c[i]; c[i] = c[n]; c[n] = temp; }
うんこれでシンプルになった。
使用する言語によりますが、Javaの場合は、ライブラリにshuffleがあるのでそちらを使ったほうが楽ですよ。
Java Threadの復習
最近、他人が書いたソースコードを読んでるんですが、変数名がわかりにくいんで、ストレスが溜まってます。あと、コメントも書いてて欲しかったなと思うところです。
それは、おいておくとして、Java Threadを使っていたので、なんとなく覚えていたのですが、曖昧なところがあったので復習です。
Threadの説明はいらないとして、
ユーザースレッドと、デーモンスレッドの違いから、
ユーザースレッドは、アプリケーションが作るスレッドで、すべてのユーザースレッドが終了すると、アプリケーションが終了します。
デーモンスレッドは、ユーザースレッドの終了と同時に、消えます。
ThreadのsetDaemonメソッドをつかってrunするまえに設定しておくと、メインスレッドが終了すると同時に消えます。
ちなみに、joinメソッドはそのスレッドの終了を待ちます。
あとは、synchronizedについてですが、これは、複数のスレッドが同時に処理をして、同じ変数を書き換え、そのときに割り込みがあった場合、想定の結果にならないことがあるので、その時に使います。
synchronizedメソッドにするか、オブジェクトを引数にいれ、ロックする方法があります。
だいぶ、忘れているなと実感