它在攔截了一些封包之後,可以透過另存新檔的方式,將攔截下來的內容儲存成一個檔案(*.pcap)
接著還想用程式化的方式自動分析封包的話,在 Java 上可以使用 jNetPcap 函式庫 [1] 來分析攔截下來的封包內容了。
從 jNetPcap 官方網站 [1] 上下載了二進位檔之後,解開會看到裡面除了 jar 以外還有其他檔案
其中在 Windows 平台上必須將 jnetpcap.dll 複製到系統路徑裡,例如複製到 C:\Windows\System32 資料夾內
此外,電腦上還必須有安裝 Wireshark(不過...或許實際需要安裝的是 WinPcap 而不是整個 Wireshark 吧?)
否則執行時可能會出現類似以下的錯誤訊息:
1 2 3 4 5 6 7 8 9 10 |
java.lang.UnsatisfiedLinkError: C:\Windows\System32\jnetpcap.dll: Can't find dependent libraries at java.lang.ClassLoader$NativeLibrary.load(Native Method) at java.lang.ClassLoader.loadLibrary1(ClassLoader.java:1965) at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1890) at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1880) at java.lang.Runtime.loadLibrary0(Runtime.java:849) at java.lang.System.loadLibrary(System.java:1088) at org.jnetpcap.nio.JMemory.<clinit>(Unknown Source) at PcapTest.sniffer(PcapTest.java:97) at Sniffer.main(Sniffer.java:10) |
接著,先手動產生一個 *.pcap 的檔案,內容需要攔截到想透過程式處理的封包
在本篇的範例中,目標是攔截 SMTP 協定的封包,並且把從本機透過任意郵件軟體寄出去的信的附件全部另存新檔。
以下是程式碼。程式碼主要的概念是在掃 pcap 檔的過程中,如果遇到 TCP 協定的封包、且封包的目的地是 25 port,就會對該封包進行內容複製
把內容暫存到一個 Map 上,Map 的 key 是封包的 sequence number。
同時因為封包的 sequence number 會變,因此每次處理完一個封包,都會將該內容對應的 sequence number 更新為下一個封包的 sequence number
以確保內容在眾多封包夾雜的狀態下,仍然可以正確地把一連串的封包內容都串連起來,組回完整且正確的內容。(封包的串接原理可以參考 [2])
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import javax.mail.MessagingException; import javax.mail.Multipart; import javax.mail.internet.MimeBodyPart; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeUtility; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.LogManager; import org.apache.log4j.Logger; import org.jnetpcap.Pcap; import org.jnetpcap.packet.PcapPacket; import org.jnetpcap.packet.PcapPacketHandler; import org.jnetpcap.protocol.tcpip.Tcp; public class PcapTest { private static Logger log = LogManager.getLogger( "PcapTest" ); public static void main(String[] args) { try { Path pcapFilePath = Paths.get( "D:" , "test" , "manyFile.pcap" ); log.debug( "Source file: " + pcapFilePath.toString()); Path outputPath = Paths.get( "D:" , "test" , "output" ); log.debug( "Output path: " + outputPath.toString()); PcapTest test = new PcapTest(); test.capture(pcapFilePath, outputPath); } catch (Exception | Error e) { e.printStackTrace(); } } /** * Capture the bytes from a specified pcap file, and stores the attachments to the specified output folder. * @param pcapPath Path of the pcap file. * @param outputFolder Folder for storing the attachments. */ public void capture (Path pcapPath, Path outputFolder) { /*************************************************************************** * First we setup error buffer and name for our file **************************************************************************/ // For any error final StringBuilder errbuf = new StringBuilder(); // Path of the off line sniffered file (*.pcap). final String filePath = pcapPath.toString(); // Parser for parsing packet using TCP protocol. final Tcp tcp = new Tcp(); // Map for temporary storing the un-complete bytes. final Map<Long, ByteArrayOutputStream> packetMap = new HashMap<Long, ByteArrayOutputStream>(); /*************************************************************************** * Second we open up the selected file using openOffline call **************************************************************************/ Pcap pcap = Pcap.openOffline(filePath, errbuf); // Error occurred while accessing the specified *.pcap file. if (pcap == null ) { log.error( "Error while opening device for capture: " + errbuf.toString()); return ; } /*************************************************************************** * Third we create a packet handler which will receive packets from * the libpcap loop. **************************************************************************/ PcapPacketHandler<Path> jpacketHandler = new PcapPacketHandler<Path>() { /** * Get the next packet from the captured file. * @param packet Captured packet. * @param outputPath Path for outputting the captured result. */ public void nextPacket(PcapPacket packet, Path outputPath) { // Filter packets which is using TCP protocol and the destination port is 25 (which is SMTP protocol). if (packet.hasHeader(tcp) && tcp.destination() == 25 ) { // Prepare the byte array for caching the captured bytes. ByteArrayOutputStream outputStream = (packetMap.containsKey(tcp.seq())) ? packetMap.remove(tcp.seq()) : new ByteArrayOutputStream(); try { // Write the captured payload and append it to the end of the cached byte array. outputStream.write(tcp.getPayload()); // Predict the sequence number of the next packet. long ack = tcp.seq() + tcp.getPayloadLength(); // If this packet is the end of a TCP connection, try to output the content through outputPacket(). // Otherwise, put the stream back to the map with updating its sequence number. if (tcp.getPayloadLength() > 0 ) packetMap.put(ack, outputStream); else outputPacket(outputStream, outputPath); } catch (IOException | MessagingException e) { e.printStackTrace(); } } } }; try { pcap.loop(- 1 , jpacketHandler, outputFolder); } catch (Exception | Error e) { e.printStackTrace(); } finally { pcap.close(); } } /** * Output the captured packets of SMTP protocol to the storage. * @param outputStream Cached stream which stores the content of captured bytes. * @param outputFolderPath Path of the folder for outputting the content. * @throws MessagingException * @throws IOException */ public void outputPacket (ByteArrayOutputStream outputStream, Path outputFolderPath) throws MessagingException, IOException { MimeMessage msg = new MimeMessage( null , new ByteArrayInputStream(outputStream.toByteArray())); if (msg.getContentType().contains( "multipart" )) { // Get the body part from the multipart content. Multipart multipart = (Multipart) msg.getContent(); for ( int i = 0 ; i < multipart.getCount(); i++) { try { MimeBodyPart bodyPart = (MimeBodyPart) multipart.getBodyPart(i); // Stores only the attachments from the multipart content to files. if (StringUtils.isNotEmpty(bodyPart.getDisposition()) && bodyPart.getDisposition().equalsIgnoreCase(MimeBodyPart.ATTACHMENT)) { String fileName = MimeUtility.decodeText(bodyPart.getFileName()); log.debug( "Store attachment '" + fileName + "'." ); // Save the attachment to a file. bodyPart.saveFile(Paths.get(outputFolderPath.toString(), fileName).toString()); } } catch (Exception | Error e) { e.printStackTrace(); } } } } } |
上述程式的執行過程中,最後會將找到的附件檔名印出,並且將那些附件輸出到指定的輸出資料夾。
以下是在我的本機讀取某個 *.pcap 檔案後,印出的內容:
1 2 3 4 5 6 7 8 9 10 11 12 |
2014 - 10 - 17 13 : 53 : 29.425 | DEBUG | PcapTest > Source file: D:\test\manyFile.pcap 2014 - 10 - 17 13 : 53 : 29.426 | DEBUG | PcapTest > Output path: D:\test\output 2014 - 10 - 17 13 : 53 : 30.146 | DEBUG | PcapTest > Store attachment 'abc.jpg' . 2014 - 10 - 17 13 : 53 : 30.167 | DEBUG | PcapTest > Store attachment 'Chrysanthemum.jpg' . 2014 - 10 - 17 13 : 53 : 30.176 | DEBUG | PcapTest > Store attachment 'Desert.jpg' . 2014 - 10 - 17 13 : 53 : 30.184 | DEBUG | PcapTest > Store attachment 'Hydrangeas.jpg' . 2014 - 10 - 17 13 : 53 : 30.190 | DEBUG | PcapTest > Store attachment 'Jellyfish.jpg' . 2014 - 10 - 17 13 : 53 : 30.198 | DEBUG | PcapTest > Store attachment 'Koala.jpg' . 2014 - 10 - 17 13 : 53 : 30.205 | DEBUG | PcapTest > Store attachment 'Lighthouse.jpg' . 2014 - 10 - 17 13 : 53 : 30.211 | DEBUG | PcapTest > Store attachment 'Penguins.jpg' . 2014 - 10 - 17 13 : 53 : 30.219 | DEBUG | PcapTest > Store attachment 'Tulips.jpg' . 2014 - 10 - 17 13 : 53 : 30.229 | DEBUG | PcapTest > Store attachment '未命名.png' . |
參考資料:
1、jNetPcap
2、TCP 與 UDP
沒有留言:
張貼留言