一、实验内容描述
本实验是用java实现的网络抓包程序,在windows环境下安装winpcap4.0和jpcap6.0后,下载eclipse和jigloo插件(一种在eclipse底下作图形化开发的工具),将其安装好,然后就可以进行java的网络抓包图形化开发了。
二、原理与关键技术
2.1 网络抓包技术原理
网络层上有各种各样的数据包,它们以不同的帧格式在网络层上进行传输,但是在传输时它们都遵循相同的格式,即有相同的长度,如果一种协议的帧格式达不到这种长度,就让其补齐,以达到我们的要求。
2.2 网络抓包关键技术
无论是在windows操作系统下还是在linux操作系统下,要想捕获网络上的数据包,必须要对网卡进行控制,因为本机的数据报从网络上来到本机是通过网卡然后再保存到本地缓冲区上的,所以要抓获网包就必须调用网卡驱动中的对外函数,在linux系统中有net.h文件,可以调用net.h文件中的函数来操作网卡,可以直接编程实现,但为了更方便的使用,可以安装一个叫libpcap的软件,这样调用函数更好用,而在windows系统中,因为源代码不对外公开,所以要安装一个叫winpcap的软件,这样用C或VC++就可以实现了,但因为我用的是java语言来实现的,所以无论是在哪个系统都要安装一个叫jpcap的软件,它本身就把底层的函数又封装了一下,这样就可以让java来使用了。
三、设计与实现
3.1 基于java的设计方案
我的这个网络抓包程序是图形化操作界面,在菜单栏点击抓包按钮后选择网卡和过滤字还有最长字长,点击开始,然后就可以开始抓包了,在主界面中就会显示出一行又一行的数据,这些数据就是抓获到的数据包。
3.2 具体实现
1、安装winpcap4.0和jpcap6.0
2、下载eclipse3.3和jigloo,jigloo是eclipse底下的插件,是用来支持eclipse底下的java 图形化开发的。
3、编写java抓包程序:
建立三个文件,一个主程序,一个抓包程序,一个抓包选项程序对话框程序
第一个程序:主程序如下
package netcap; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JSeparator; import javax.swing.JMenuItem; import javax.swing.JMenu; import javax.swing.JMenuBar; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.table.*; import netcap.*; import jpcap.*; import jpcap.packet.*; import java.util.*; import java.sql.Timestamp; /** * This code was edited or generated using CloudGarden's Jigloo * SWT/Swing GUI Builder, which is free for non-commercial * use. If Jigloo is being used commercially (ie, by a corporation, * company or business for any purpose whatever) then you * should purchase a license for each developer using Jigloo. * Please visit www.cloudgarden.com for details. * Use of Jigloo implies acceptance of these licensing terms. * A COMMERCIAL LICENSE HAS NOT BEEN PURCHASED FOR * THIS MACHINE, SO JIGLOO OR THIS CODE CANNOT BE USED * LEGALLY FOR ANY CORPORATE OR COMMERCIAL PURPOSE. */ public class JFrameMain extends javax.swing.JFrame implements ActionListener{ private JMenuItem exitMenuItem; private JSeparator jSeparator2; private JMenuItem saveAsMenuItem; private JMenuItem saveMenuItem; private JMenuItem stopMenuItem; private JMenuItem startMenuItem; private JMenu Menu; private JMenuBar jMenuBar1; JTable tabledisplay = null; Vector rows,columns; DefaultTableModel tabModel; JScrollPane scrollPane; JLabel statusLabel; Netcaptor captor = new Netcaptor(); /** * Auto-generated main method to display this JFrame */ public static void main(String[] args) { JFrameMain inst = new JFrameMain(); inst.setVisible(true); } public JFrameMain() { super(); initGUI(); } private void initGUI() { try { setSize(400, 300); { jMenuBar1 = new JMenuBar(); setJMenuBar(jMenuBar1); { Menu = new JMenu(); jMenuBar1.add(Menu); Menu.setText("\u6293\u5305"); Menu.setPreferredSize(new java.awt.Dimension(35, 21)); { startMenuItem = new JMenuItem(); Menu.add(startMenuItem); startMenuItem.setText("开始"); startMenuItem.setActionCommand("start"); startMenuItem.addActionListener(this); } { stopMenuItem = new JMenuItem(); Menu.add(stopMenuItem); stopMenuItem.setText("停止"); stopMenuItem.setActionCommand("stop"); stopMenuItem.addActionListener(this); } { saveMenuItem = new JMenuItem(); Menu.add(saveMenuItem); saveMenuItem.setText("保存"); } { saveAsMenuItem = new JMenuItem(); Menu.add(saveAsMenuItem); saveAsMenuItem.setText("保存为 ..."); } { jSeparator2 = new JSeparator(); Menu.add(jSeparator2); } { exitMenuItem = new JMenuItem(); Menu.add(exitMenuItem); exitMenuItem.setText("Exit"); exitMenuItem.setActionCommand("exit"); exitMenuItem.addActionListener(this); } } } rows=new Vector(); columns= new Vector(); columns.addElement("数据报时间"); columns.addElement("源IP地址"); columns.addElement("目的IP地址"); columns.addElement("首部长度"); columns.addElement("数据长度"); columns.addElement("是否分段"); columns.addElement("分段偏移量"); columns.addElement("首部内容"); columns.addElement("数据内容"); tabModel=new DefaultTableModel(); tabModel.setDataVector(rows,columns); tabledisplay = new JTable( tabModel ); scrollPane= new JScrollPane(tabledisplay); this.getContentPane().add( new JScrollPane(tabledisplay),BorderLayout.CENTER); statusLabel=new JLabel("06610班 张琛雨 066100583"); this.getContentPane().add(statusLabel,BorderLayout.SOUTH); } catch (Exception e) { e.printStackTrace(); } } public void actionPerformed(ActionEvent event){ String cmd=event.getActionCommand(); if(cmd.equals("start")){ captor.capturePacketsFromDevice(); captor.setJFrame(this); } else if(cmd.equals("stop")){ captor.stopCapture(); } else if(cmd.equals("exit")){ System.exit(0); } } public void dealPacket( Packet packet ) { try { Vector r=new Vector(); String strtmp; Timestamp timestamp = new Timestamp((packet.sec * 1000) + (packet.usec / 1000)); r.addElement( timestamp.toString() ); //数据报时间 r.addElement(((IPPacket)packet).src_ip.toString()); //源IP地址 r.addElement(((IPPacket)packet).dst_ip.toString()); //目的IP地址 r.addElement( packet.header.length ); //首部长度 r.addElement( packet.data.length ); //数据长度 r.addElement( ((IPPacket)packet).dont_frag == true ? "分段" : "不分段" ); //是否不分段 r.addElement( ((IPPacket)packet).offset ); //数据长度 strtmp = ""; for(int i=0;i<packet.header.length;i++){ strtmp += Byte.toString(packet.header[i]); } r.addElement(strtmp); //首部内容 strtmp = ""; for(int i=0;i<packet.data.length;i++){ strtmp += Byte.toString(packet.data[i]); } r.addElement(strtmp); //数据内容 rows.addElement(r); tabledisplay.addNotify(); } catch( Exception e) { } } } 在这里定义了一个向量r,当有数据包产生时,便将数据包赋值给r,rows.AddElement(r)语句便将r添加到向量rows中,然后tabledisplay.addNotify();语句就会刷新界面将新的数据包显示出来。 第二个程序:抓包 package netcap; import java.io.File; import java.util.Vector; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import jpcap.JpcapCaptor; import jpcap.PacketReceiver; import jpcap.JpcapWriter; import jpcap.packet.Packet; public class Netcaptor { JpcapCaptor jpcap = null; JFrameMain frame; public void setJFrame(JFrameMain frame){ this.frame=frame; } public void capturePacketsFromDevice() { if(jpcap!=null) jpcap.close(); jpcap = Jcapturedialog.getJpcap(frame); if (jpcap != null) { startCaptureThread(); } } private Thread captureThread; private void startCaptureThread(){ if(captureThread != null) return; captureThread = new Thread(new Runnable(){ public void run(){ while(captureThread != null){ jpcap.processPacket(1, handler); } } }); captureThread.setPriority(Thread.MIN_PRIORITY); captureThread.start(); } void stopcaptureThread(){ captureThread = null; } public void stopCapture(){ System.out.println(2); stopcaptureThread(); } private PacketReceiver handler=new PacketReceiver(){ public void receivePacket(Packet packet) { //System.out.println(packet); frame.dealPacket(packet); } }; }
定义一个抓包对象JpcapCaptor jpcap = null;但点击开始时调用private void startCaptureThread()方法开始抓包,jpcap.processPacket(1, handler);语句能够反复调用handler所指向的方法,这个方法中定义的packet就是网络上抓到的数据包,经过frame.dealPacket(packet);就可以显示在主界面上。
程序三:抓包选项
package netcap; import javax.swing.JFrame; import jpcap.*; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * This code was edited or generated using CloudGarden's Jigloo * SWT/Swing GUI Builder, which is free for non-commercial * use. If Jigloo is being used commercially (ie, by a corporation, * company or business for any purpose whatever) then you * should purchase a license for each developer using Jigloo. * Please visit www.cloudgarden.com for details. * Use of Jigloo implies acceptance of these licensing terms. * A COMMERCIAL LICENSE HAS NOT BEEN PURCHASED FOR * THIS MACHINE, SO JIGLOO OR THIS CODE CANNOT BE USED * LEGALLY FOR ANY CORPORATE OR COMMERCIAL PURPOSE. */ public class Jcapturedialog extends javax.swing.JDialog implements ActionListener { /** * Auto-generated main method to display this JDialog */ static JpcapCaptor jpcap=null; private JRadioButton wholeRadioButton; private JPanel buttonPanel; private JButton cancelButton; private JButton okButton; private JRadioButton userRadioButton; private JRadioButton headRadioButton; private JPanel netPanel; private JTextField caplenTextField; private JPanel caplenPanel; private JTextField TextField; private JPanel filterPanel; private JCheckBox CheckBox; private JComboBox netJComboBox; private JPanel jPanel_east; private JPanel jPanel_west; NetworkInterface[] devices; public static void main(String[] args) { JFrame frame = new JFrame(); Jcapturedialog inst = new Jcapturedialog(frame); inst.setVisible(true); } public Jcapturedialog(JFrame frame) { super(frame,"选择要检测的网卡并设置参数",true); try { BoxLayout thisLayout = new BoxLayout( getContentPane(), javax.swing.BoxLayout.X_AXIS); getContentPane().setLayout(thisLayout); { jPanel_west = new JPanel(); jPanel_west.setLayout(new BoxLayout(jPanel_west,BoxLayout.Y_AXIS)); getContentPane().add(jPanel_west); { netPanel = new JPanel(); FlowLayout netPanelLayout = new FlowLayout(); netPanelLayout.setAlignOnBaseline(true); netPanel.setBorder(BorderFactory.createTitledBorder("选择网卡")); netPanel.setAlignmentX(Component.LEFT_ALIGNMENT); jPanel_west.add(netPanel); netPanel.setLayout(netPanelLayout); // netPanel.setPreferredSize(new java.awt.Dimension(239, 56)); { devices = JpcapCaptor.getDeviceList(); if(devices == null){ JOptionPane.showMessageDialog(frame, "没有找到网卡"); dispose(); return; } else{ String[] names = new String[devices.length]; for(int i=0;i < names.length;i++){ names[i] = (devices[i].description == null?devices[i].name:devices[i].description); } netJComboBox = new JComboBox(names); } netPanel.add(netJComboBox); } } { CheckBox = new JCheckBox(); jPanel_west.add(CheckBox); FlowLayout CheckBoxLayout = new FlowLayout(); CheckBoxLayout.setAlignOnBaseline(true); CheckBox.setText("\u662f\u5426\u8bbe\u7f6e\u4e3a\u6df7\u6742\u6a21\u5f0f"); CheckBox.setLayout(null); } { filterPanel = new JPanel(); filterPanel.setBorder(BorderFactory.createTitledBorder("捕获过滤器")); filterPanel.setAlignmentX(Component.LEFT_ALIGNMENT); FlowLayout filterPanelLayout = new FlowLayout(); filterPanelLayout.setAlignment(FlowLayout.LEFT); filterPanelLayout.setAlignOnBaseline(true); jPanel_west.add(filterPanel); filterPanel.setLayout(filterPanelLayout); { TextField = new JTextField(20); filterPanel.add(TextField); } } } { jPanel_east = new JPanel(); jPanel_east.setLayout(new BoxLayout(jPanel_east,BoxLayout.Y_AXIS)); getContentPane().add(jPanel_east); { caplenPanel = new JPanel(); caplenPanel.setBorder(BorderFactory.createTitledBorder("最长字长")); caplenPanel.setAlignmentX(Component.LEFT_ALIGNMENT); jPanel_east.add(caplenPanel); caplenPanel.setLayout(new BoxLayout(caplenPanel,BoxLayout.Y_AXIS)); { caplenTextField = new JTextField(20); caplenPanel.add(caplenTextField); caplenTextField.setText("1514"); caplenTextField.setEnabled(false); } { wholeRadioButton = new JRadioButton(); FlowLayout userRadioButtonLayout = new FlowLayout(); userRadioButtonLayout.setAlignOnBaseline(true); caplenPanel.add(wholeRadioButton); wholeRadioButton.setText("\u6574\u4e2a\u6570\u636e\u62a5"); wholeRadioButton.setSelected(true); wholeRadioButton.addActionListener(this); } { headRadioButton = new JRadioButton(); caplenPanel.add(headRadioButton); headRadioButton.setText("\u4ec5\u9996\u90e8"); headRadioButton.addActionListener(this); } { userRadioButton = new JRadioButton(); caplenPanel.add(userRadioButton); userRadioButton.setText("\u5176\u4ed6\u90e8\u5206"); userRadioButton.addActionListener(this); } ButtonGroup group=new ButtonGroup(); group.add(wholeRadioButton); wholeRadioButton.setActionCommand("Whole"); group.add(headRadioButton); headRadioButton.setActionCommand("Head"); group.add(userRadioButton); userRadioButton.setActionCommand("user"); } { buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT)); // buttonPanel.setLayout(new BoxLayout(buttonPanel,BoxLayout.X_AXIS)); jPanel_east.add(buttonPanel); { okButton = new JButton(); buttonPanel.add(okButton); FlowLayout cancelButtonLayout = new FlowLayout(); cancelButtonLayout.setAlignOnBaseline(true); okButton.setText("\u786e\u5b9a"); okButton.setActionCommand("ok"); okButton.addActionListener(this); } { cancelButton = new JButton(); buttonPanel.add(cancelButton); cancelButton.setText("\u53d6\u6d88"); cancelButton.setActionCommand("cancel"); cancelButton.addActionListener(this); } // buttonPanel.setAlignmentX(Component.RIGHT_ALIGNMENT); } } getContentPane().setLayout(new BoxLayout(getContentPane(),BoxLayout.X_AXIS)); getContentPane().add(jPanel_west); getContentPane().add(jPanel_east); pack(); } catch (Exception e) { e.printStackTrace(); } } public void actionPerformed(ActionEvent evt){ String cmd=evt.getActionCommand(); if(cmd.equals("Whole")){ caplenTextField.setText("1514"); caplenTextField.setEnabled(false); }else if(cmd.equals("Head")){ caplenTextField.setText("68"); caplenTextField.setEnabled(false); }else if(cmd.equals("user")){ caplenTextField.setText(""); caplenTextField.setEnabled(true); caplenTextField.requestFocus(); }else if(cmd.equals("ok")){ try{ int caplen=Integer.parseInt(caplenTextField.getText()); if(caplen<68 || caplen>1514){ JOptionPane.showMessageDialog(null,"捕获长度必须介于 68 和 1514之间"); return; } jpcap=JpcapCaptor.openDevice(devices[netJComboBox.getSelectedIndex()],caplen, CheckBox.isSelected(),50); if(TextField.getText()!=null && TextField.getText().length()>0){ jpcap.setFilter(TextField.getText(),true); } }catch(NumberFormatException e){ JOptionPane.showMessageDialog(null,"捕获长度必须是正整数"); }catch(java.io.IOException e){ JOptionPane.showMessageDialog(null,e.toString()); jpcap=null; }finally{ dispose(); } }else if(cmd.equals("cancel")){ dispose(); } } public static JpcapCaptor getJpcap(JFrame parent){ new Jcapturedialog(parent).setVisible(true); return jpcap; } }
这一部分主要是界面操作,根据jigloo插件对不同的按钮和文本框还有其他的组件设置监听操作,以激发不同的函数操作,主要是devices = JpcapCaptor.getDeviceList();语句和
jpcap=JpcapCaptor.openDevice(devices[netJComboBox.getSelectedIndex()],caplen,
CheckBox.isSelected(),50);语句要选择一下监听的网卡,比如说笔记本就有两个网卡,一个无线一个有线,选择一下就会监听相应的网卡。函数
public static JpcapCaptor getJpcap(JFrame parent){
new Jcapturedialog(parent).setVisible(true);
return jpcap;
}
返回jpcap,这个jpcap就是对应的选择上的网卡对象,接下来就从对应的网卡对象jpcap上不断得到数据包。