From 33ff22b303396a76f96ed82f64e05858c59c4c9d Mon Sep 17 00:00:00 2001 From: Janus Varmarken Date: Sat, 19 May 2018 17:39:53 -0700 Subject: [PATCH] First (rushed) implementation of pattern seach at the MAC layer. Not pretty, but seems functional. --- .../main/java/edu/uci/iotproject/Main.java | 61 +++++++---- .../maclayer/MacLayerFlowPattern.java | 46 ++++++++ .../maclayer/MacLayerFlowPatternFinder.java | 102 ++++++++++++++++++ 3 files changed, 187 insertions(+), 22 deletions(-) create mode 100644 Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/maclayer/MacLayerFlowPattern.java create mode 100644 Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/maclayer/MacLayerFlowPatternFinder.java diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Main.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Main.java index 624136f..9696bd6 100644 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Main.java +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/Main.java @@ -1,5 +1,7 @@ package edu.uci.iotproject; +import edu.uci.iotproject.maclayer.MacLayerFlowPattern; +import edu.uci.iotproject.maclayer.MacLayerFlowPatternFinder; import org.pcap4j.core.*; import java.io.EOFException; @@ -21,34 +23,49 @@ public class Main { public static void main(String[] args) throws PcapNativeException, NotOpenException, EOFException, TimeoutException, UnknownHostException { - final String fileName = args.length > 0 ? args[0] : "/home/rtrimana/pcap_processing/smart_home_traffic/Code/Projects/SmartPlugDetector/pcap/wlan1.local.remote.dns.pcap"; - final String trainingFileName = "./pcap/TP_LINK_LOCAL_ON_SUBSET.pcap"; - //final String trainingFileName = "./pcap/TP_LINK_REMOTE_ON.pcap"; - - // ====== Debug code ====== + // ------------------------------------------------------------------------------------------------------------- + // Example/debug code for searching for a pattern at the MAC layer. + String fileName = "./pcap/mac-tplink.local.pcapng"; PcapHandle handle; - PcapHandle trainingPcap; try { handle = Pcaps.openOffline(fileName, PcapHandle.TimestampPrecision.NANO); - trainingPcap = Pcaps.openOffline(trainingFileName, PcapHandle.TimestampPrecision.NANO); } catch (PcapNativeException pne) { handle = Pcaps.openOffline(fileName); - trainingPcap = Pcaps.openOffline(trainingFileName); } +// Arrays.asList(1590, 1590, 1590, 1001, 337, 197, 636, 1311, 177) // Full pattern (all non-zero payload packets). + MacLayerFlowPattern pattern = new MacLayerFlowPattern("TP_LINK_LOCAL_OFF_MAC", "50:c7:bf:33:1f:09", Arrays.asList(637, 1312)); + MacLayerFlowPatternFinder finder = new MacLayerFlowPatternFinder(handle, pattern); + finder.findFlowPattern(); + // ------------------------------------------------------------------------------------------------------------- - // TODO: The followings are the way to extract multiple hostnames and their associated packet lengths lists - //List list = new ArrayList<>(); - //list.add("events.tplinkra.com"); - //FlowPattern fp = new FlowPattern("TP_LINK_LOCAL_ON", list, trainingPcap); - //List list2 = new ArrayList<>(); - //list2.add("devs.tplinkcloud.com"); - //list2.add("events.tplinkra.com"); - //FlowPattern fp3 = new FlowPattern("TP_LINK_REMOTE_ON", list2, trainingPcap); - - FlowPattern fp = new FlowPattern("TP_LINK_LOCAL_ON", "events.tplinkra.com", trainingPcap); - FlowPatternFinder fpf = new FlowPatternFinder(handle, fp); - fpf.start(); - - // ======================== +// final String fileName = args.length > 0 ? args[0] : "/home/rtrimana/pcap_processing/smart_home_traffic/Code/Projects/SmartPlugDetector/pcap/wlan1.local.remote.dns.pcap"; +// final String trainingFileName = "./pcap/TP_LINK_LOCAL_ON_SUBSET.pcap"; +// //final String trainingFileName = "./pcap/TP_LINK_REMOTE_ON.pcap"; +// +// // ====== Debug code ====== +// PcapHandle handle; +// PcapHandle trainingPcap; +// try { +// handle = Pcaps.openOffline(fileName, PcapHandle.TimestampPrecision.NANO); +// trainingPcap = Pcaps.openOffline(trainingFileName, PcapHandle.TimestampPrecision.NANO); +// } catch (PcapNativeException pne) { +// handle = Pcaps.openOffline(fileName); +// trainingPcap = Pcaps.openOffline(trainingFileName); +// } +// +// // TODO: The followings are the way to extract multiple hostnames and their associated packet lengths lists +// //List list = new ArrayList<>(); +// //list.add("events.tplinkra.com"); +// //FlowPattern fp = new FlowPattern("TP_LINK_LOCAL_ON", list, trainingPcap); +// //List list2 = new ArrayList<>(); +// //list2.add("devs.tplinkcloud.com"); +// //list2.add("events.tplinkra.com"); +// //FlowPattern fp3 = new FlowPattern("TP_LINK_REMOTE_ON", list2, trainingPcap); +// +// FlowPattern fp = new FlowPattern("TP_LINK_LOCAL_ON", "events.tplinkra.com", trainingPcap); +// FlowPatternFinder fpf = new FlowPatternFinder(handle, fp); +// fpf.start(); +// +// // ======================== } } diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/maclayer/MacLayerFlowPattern.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/maclayer/MacLayerFlowPattern.java new file mode 100644 index 0000000..7cff733 --- /dev/null +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/maclayer/MacLayerFlowPattern.java @@ -0,0 +1,46 @@ +package edu.uci.iotproject.maclayer; + +import java.util.Collections; +import java.util.List; + +/** + * TODO create base class for FlowPattern and derive MacLayer, TCP/IP layer versions from that. + * + * @author Janus Varmarken + */ +public class MacLayerFlowPattern { + + private final List mPacketLengthSequence; + private final String mMacPrefix; + private final String mPatternId; + private final byte[] mMacPreixBytes; + + public MacLayerFlowPattern(String patternId, String macPrefix, List packetLengthSequence) { + mMacPrefix = macPrefix; + mPatternId = patternId; + mPacketLengthSequence = packetLengthSequence; + // Conversion provided by https://stackoverflow.com/a/10839361/1214974 + String[] addressParts = macPrefix.split(":"); + mMacPreixBytes = new byte[addressParts.length]; + for(int i = 0; i < mMacPreixBytes.length; i++) { + Integer hex = Integer.parseInt(addressParts[i], 16); + mMacPreixBytes[i] = hex.byteValue(); + } + } + + public String getPatternId() { + return mPatternId; + } + + public byte[] getMacPrefixRawBytes() { + return mMacPreixBytes; + } + + public List getPacketLengthSequence() { + return Collections.unmodifiableList(mPacketLengthSequence); + } + + public int getLength() { + return mPacketLengthSequence.size(); + } +} diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/maclayer/MacLayerFlowPatternFinder.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/maclayer/MacLayerFlowPatternFinder.java new file mode 100644 index 0000000..6cca3ad --- /dev/null +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/maclayer/MacLayerFlowPatternFinder.java @@ -0,0 +1,102 @@ +package edu.uci.iotproject.maclayer; + +import edu.uci.iotproject.FlowPattern; +import org.pcap4j.core.NotOpenException; +import org.pcap4j.core.PcapHandle; +import org.pcap4j.core.PcapNativeException; +import org.pcap4j.core.PcapPacket; +import org.pcap4j.packet.RadiotapPacket; + +import java.io.EOFException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeoutException; + +/** + * Performs a search for {@link FlowPattern} + * TODO: May want to create an abstract FlowPatternFinder and then derive MacLayer, TcpipLayer FlowPatternFinders from that one. + * + * @author Janus Varmarken + */ +public class MacLayerFlowPatternFinder { + + private final MacLayerFlowPattern mPattern; + private final PcapHandle mPcap; + + public MacLayerFlowPatternFinder(PcapHandle pcap, MacLayerFlowPattern pattern) { + this.mPcap = Objects.requireNonNull(pcap, + String.format("Argument of type '%s' cannot be null", PcapHandle.class.getSimpleName())); + this.mPattern = Objects.requireNonNull(pattern, + String.format("Argument of type '%s' cannot be null", FlowPattern.class.getSimpleName())); + } + + public void findFlowPattern() { + PcapPacket packet; + try { + // Packets matched to flow pattern searched for. + List patternPackets = new ArrayList<>(); + while ((packet = mPcap.getNextPacketEx()) != null) { + RadiotapPacket radiotapPacket; + try { + // Some packets throw an IAE with message "msi must be between 0 and 6 but is actually: 7" + // when accessing the RadiotapPacket. + radiotapPacket = packet.get(RadiotapPacket.class); + } catch (IllegalArgumentException iae) { + System.out.println(iae.getMessage()); + continue; + } + if (radiotapPacket == null) { + continue; + } + // Restart search if pattern not found within reasonable time frame (hardcoded for now). + if (patternPackets.size() > 0 && packet.getTimestamp().getEpochSecond() - + patternPackets.get(patternPackets.size()-1).getTimestamp().getEpochSecond() > 2) { + patternPackets = new ArrayList<>(); + } + + byte[] rawData = radiotapPacket.getPayload().getRawData(); + // Search rawData for MAC of FlowPattern in sender/receiver section + // [TODO needs verification that this section is actually the sender/receiver section] + if (rawData.length < 16) { + continue; + } + int prefixLength = mPattern.getMacPrefixRawBytes().length; + byte[] mac1 = Arrays.copyOfRange(rawData, 4, prefixLength < 6 ? 4 + prefixLength : 10); + byte[] mac2 = Arrays.copyOfRange(rawData, 10, prefixLength < 6 ? 10 + prefixLength : 16); + if (!Arrays.equals(mac1, mPattern.getMacPrefixRawBytes()) && !Arrays.equals(mac2, mPattern.getMacPrefixRawBytes())) { + // MAC prefix not present in raw data. + continue; + } + // Packet related to device associated with the pattern we are looking for. + int expectedLength = mPattern.getPacketLengthSequence().get(patternPackets.size()); + if (packet.length() == expectedLength) { + patternPackets.add(packet); + if (patternPackets.size() == mPattern.getLength()) { + // Full pattern found, declare success if packets are within some reasonable amount of time of + // one another. + // For now, we use a hardcoded value. + if (patternPackets.get(patternPackets.size()-1).getTimestamp().getEpochSecond() - + patternPackets.get(0).getTimestamp().getEpochSecond() < 5) { + System.out.println(String.format("[ find ] Detected a COMPLETE MATCH of pattern '%s' at %s!", + mPattern.getPatternId(), patternPackets.get(0).getTimestamp().toString())); + } + // Reset search by resetting list. + patternPackets = new ArrayList<>(); + } + } else { + // Discard packet, not relevant to pattern. + continue; + } + } + } catch (EOFException e) { + // TODO wait for, and print, results. + } catch (PcapNativeException|TimeoutException|NotOpenException e) { + e.printStackTrace(); + } + } + +} + + -- 2.34.1