From b6ee2e350812b3a89d7f260860835c05ca9c460c Mon Sep 17 00:00:00 2001 From: Janus Varmarken Date: Fri, 21 Sep 2018 17:29:59 -0700 Subject: [PATCH] SignatureDetector.java: closer towards final implementation (e.g., also check packet direction when detecting signatures) --- .../.idea/modules/SmartPlugDetector_main.iml | 1 + .../.idea/modules/SmartPlugDetector_test.iml | 1 + .../detection/SignatureDetector.java | 104 ++++++++++++++++-- 3 files changed, 98 insertions(+), 8 deletions(-) diff --git a/Code/Projects/SmartPlugDetector/.idea/modules/SmartPlugDetector_main.iml b/Code/Projects/SmartPlugDetector/.idea/modules/SmartPlugDetector_main.iml index 6569157..740525e 100644 --- a/Code/Projects/SmartPlugDetector/.idea/modules/SmartPlugDetector_main.iml +++ b/Code/Projects/SmartPlugDetector/.idea/modules/SmartPlugDetector_main.iml @@ -5,6 +5,7 @@ + diff --git a/Code/Projects/SmartPlugDetector/.idea/modules/SmartPlugDetector_test.iml b/Code/Projects/SmartPlugDetector/.idea/modules/SmartPlugDetector_test.iml index 616bd52..8d4ae84 100644 --- a/Code/Projects/SmartPlugDetector/.idea/modules/SmartPlugDetector_test.iml +++ b/Code/Projects/SmartPlugDetector/.idea/modules/SmartPlugDetector_test.iml @@ -5,6 +5,7 @@ + diff --git a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/SignatureDetector.java b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/SignatureDetector.java index 54feeba..6cf0ba3 100644 --- a/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/SignatureDetector.java +++ b/Code/Projects/SmartPlugDetector/src/main/java/edu/uci/iotproject/detection/SignatureDetector.java @@ -3,11 +3,14 @@ package edu.uci.iotproject.detection; import edu.uci.iotproject.Conversation; import edu.uci.iotproject.TcpReassembler; import edu.uci.iotproject.analysis.TcpConversationUtils; -import edu.uci.iotproject.util.PcapPacketUtils; import org.pcap4j.core.PacketListener; import org.pcap4j.core.PcapPacket; +import java.util.Arrays; import java.util.List; +import java.util.Objects; + +import static edu.uci.iotproject.util.PcapPacketUtils.*; /** * TODO add class documentation. @@ -22,16 +25,46 @@ public class SignatureDetector implements PacketListener { */ private final List> mSignature; + /** + * The directions of packets in the sequence that make up {@link #mSignature}. + */ + private final Conversation.Direction[] mSignatureDirections; + /** * For reassembling the observed traffic into TCP connections. */ private final TcpReassembler mTcpReassembler = new TcpReassembler(); - public SignatureDetector(List> signature) { + /** + * IP of the router's WAN port (if analyzed traffic is captured at the ISP's point of view). + */ + private final String mRouterWanIp; + + public SignatureDetector(List> signature, String routerWanIp) { + Objects.requireNonNull(signature, "signature cannot be null"); + if (signature.isEmpty() || signature.stream().anyMatch(inner -> inner.isEmpty())) { + throw new IllegalArgumentException("signature is empty (or contains empty inner List)"); + } mSignature = signature; + mRouterWanIp = routerWanIp; + // Build the direction sequence. + // Note: assumes that the provided signature was captured within the local network (routerWanIp is set to null). + mSignatureDirections = getPacketDirections(mSignature.get(0), null); + /* + * Enforce restriction on cluster/signature members: all representatives must exhibit the same direction pattern + * and contain the same number of packets. Note that this is a somewhat heavy operation, so it may be disabled + * later on in favor of performance. However, it is only run once (at instantiation), so the overhead may be + * warranted in order to ensure correctness, especially during the development/debugging phase. + */ + if (mSignature.stream(). + anyMatch(inner -> !Arrays.equals(mSignatureDirections, getPacketDirections(inner, null)))) { + throw new IllegalArgumentException( + "signature members must contain the same number of packets and exhibit the same packet direction " + + "pattern" + ); + } } - @Override public void gotPacket(PcapPacket packet) { // Present packet to TCP reassembler so that it can be mapped to a connection (if it is a TCP packet). @@ -45,11 +78,30 @@ public class SignatureDetector implements PacketListener { for (Conversation c : mTcpReassembler.getTcpConversations()) { for (List sequence : mSignature) { boolean matchFound = isSequenceInConversation(sequence, c); - + if (matchFound) { + onSequenceDetected(sequence, c); + // Found signature in current conversation, so break inner loop and continue with next conversation. + break; + } } + /* + * TODO: + * if no item in cluster matches, also perform a distance-based matching to cover those cases where we did + * not manage to capture every single mutation of the sequence during training. + * + * Need to compute average/centroid of cluster to do so...? Compute within-cluster variance, then check if + * distance between input conversation and cluster average/centroid is smaller than or equal to the computed + * variance? + */ } } + private void onSequenceDetected(List sequence, Conversation c) { + // TODO implement whatever output we want, e.g., print to std.out or notify observer objects + + + } + /** * Examine if a {@link Conversation} contains a given sequence of packets. Note: the current implementation actually * searches for a substring as it does not allow for interleaved packets in {@code c} that are not in @@ -75,13 +127,19 @@ public class SignatureDetector implements PacketListener { if (packets.size() < sequence.size()) { return false; } + /* + * Generate packet direction array for c. We have already generated the packet direction array for sequence as + * part of the constructor (mSignatureDirections). + */ + Conversation.Direction[] cDirections = getPacketDirections(packets, mRouterWanIp); int seqIdx = 0; int convIdx = 0; while (convIdx < packets.size()) { PcapPacket seqPkt = sequence.get(seqIdx); PcapPacket convPkt = packets.get(convIdx); - if (convPkt.getOriginalLength() == seqPkt.getOriginalLength()) { - // TODO should also check direction of packets -- how to? + // We only have a match if packet lengths and directions match. + if (convPkt.getOriginalLength() == seqPkt.getOriginalLength() && + mSignatureDirections[seqIdx] == cDirections[convIdx]) { // A match, advance both indices to consider next packet in sequence vs. next packet in conversation seqIdx++; convIdx++; @@ -112,9 +170,39 @@ public class SignatureDetector implements PacketListener { private boolean isTlsSequence(List sequence) { // NOTE: Assumes ALL packets in sequence pertain to the same TCP connection! PcapPacket firstPkt = sequence.get(0); - int srcPort = PcapPacketUtils.getSourcePort(firstPkt); - int dstPort = PcapPacketUtils.getDestinationPort(firstPkt); + int srcPort = getSourcePort(firstPkt); + int dstPort = getDestinationPort(firstPkt); return TcpConversationUtils.isTlsPort(srcPort) || TcpConversationUtils.isTlsPort(dstPort); } + /** + * Given a {@code List}, generate a {@code Conversation.Direction[]} such that each entry in the + * resulting {@code Conversation.Direction[]} specifies the direction of the {@link PcapPacket} at the corresponding + * index in the input list. + * @param packets The list of packets for which to construct a corresponding array of packet directions. + * @param routerWanIp The IP of the router's WAN port. This is used for determining the direction of packets when + * the traffic is captured just outside the local network (at the ISP side of the router). Set to + * {@code null} if {@code packets} stem from traffic captured within the local network. + * @return A {@code Conversation.Direction[]} specifying the direction of the {@link PcapPacket} at the + * corresponding index in {@code packets}. + */ + private static Conversation.Direction[] getPacketDirections(List packets, String routerWanIp) { + Conversation.Direction[] directions = new Conversation.Direction[packets.size()]; + for (int i = 0; i < packets.size(); i++) { + PcapPacket pkt = packets.get(i); + if (getSourceIp(pkt).equals(getDestinationIp(pkt))) { + // Sanity check: we shouldn't be processing loopback traffic + throw new AssertionError("loopback traffic detected"); + } + if (isSrcIpLocal(pkt) || getSourceIp(pkt).equals(routerWanIp)) { + directions[i] = Conversation.Direction.CLIENT_TO_SERVER; + } else if (isDstIpLocal(pkt) || getDestinationIp(pkt).equals(routerWanIp)) { + directions[i] = Conversation.Direction.SERVER_TO_CLIENT; + } else { + throw new IllegalArgumentException("no local IP or router WAN port IP found, can't detect direction"); + } + } + return directions; + } + } -- 2.34.1