/// <summary> /// Attempts to parse raw data into a structured packet /// </summary> /// <param name="buffer">Raw data to parse</param> /// <param name="packet">Parsed packet</param> /// <param name="count">The length of the packet in bytes</param> /// <param name="index">The index into the buffer at which the packet begins</param> /// <returns>True if parsing was successful, false if it is not.</returns> internal static bool TryParse(byte[] buffer, int index, int count, out MSMon802_11 packet) { try { if (count < MinimumParseableBytes) { packet = null; return(false); } using (var ms = new MemoryStream(buffer, index, count, false)) { using (var br = new BinaryReader(ms)) { var version = br.ReadByte(); var length = br.ReadUInt16(); var opMode = br.ReadUInt32(); var receiveFlags = br.ReadUInt32(); var phyID = br.ReadUInt32(); var chCenterFrequency = br.ReadUInt32(); var RSSI = br.ReadInt32(); var dataRate = br.ReadByte(); var timestamp = br.ReadUInt64(); packet = null; // TODO: Consider parsed length IEEE802_11 payload80211; if (IEEE802_11.TryParse( buffer, index + (int)br.BaseStream.Position, (int)(count - br.BaseStream.Position), out payload80211)) { packet = new MSMon802_11 <IEEE802_11> { Payload = payload80211 }; } if (packet == null) { Generic payload; Generic.TryParse( buffer, index + (int)br.BaseStream.Position, (int)(count - br.BaseStream.Position), out payload); // This can never fail, so I'm not checking the output packet = new MSMon802_11 <Generic> { Payload = payload }; } packet.Version = version; packet.LengthMS = length; packet.OpMode = opMode; packet.ReceiveFlags = receiveFlags; packet.PhyID = phyID; packet.CenterFrequency = chCenterFrequency; packet.RSSI = RSSI; packet.DataRate = dataRate; packet.Timestamp = timestamp; return(true); } } } catch (Exception) { packet = null; return(false); } }
/// <summary> /// Attempts to parse raw data into a structured packet /// </summary> /// <param name="buffer">Raw data to parse</param> /// <param name="packet">Parsed packet</param> /// <param name="count">The length of the packet in bytes</param> /// <param name="index">The index into the buffer at which the packet begins</param> /// <returns>True if parsing was successful, false if it is not.</returns> internal static bool TryParse(byte[] buffer, int index, int count, out IEEE802_11 packet) { try { if (count < MinimumParseableBytes) { packet = null; return(false); } using (var ms = new MemoryStream(buffer, index, count, false)) { using (var br = new BinaryReader(ms)) { var frameControlVersionTypeAndSubtype = new BitVector32(br.ReadByte()); var frameControlFlags = new BitVector32(br.ReadByte()); var durationID = br.ReadUInt16(); var toDS = frameControlFlags[1]; var fromDS = frameControlFlags[2]; byte[] destination; byte[] source; byte[] bssid; byte[] receiver = null; byte[] transmitter = null; UInt16 sequenceControl; if (!toDS) { destination = br.ReadBytes(6); if (!fromDS) { source = br.ReadBytes(6); bssid = br.ReadBytes(6); } else { bssid = br.ReadBytes(6); source = br.ReadBytes(6); } sequenceControl = br.ReadUInt16(); //Was NTHO, wireshark disagrees } else { if (!fromDS) { bssid = br.ReadBytes(6); source = br.ReadBytes(6); destination = br.ReadBytes(6); sequenceControl = br.ReadUInt16(); //Was NTHO, wireshark disagrees } else { receiver = br.ReadBytes(6); transmitter = br.ReadBytes(6); bssid = transmitter; //Per airdecap-ng destination = br.ReadBytes(6); sequenceControl = br.ReadUInt16(); //Was NTHO, wireshark disagrees source = br.ReadBytes(6); } } var frameType = frameControlVersionTypeAndSubtype[BitVector32.CreateSection(3, BitVector32.CreateSection(3)) ]; var subType = frameControlVersionTypeAndSubtype[ BitVector32.CreateSection(15, BitVector32.CreateSection(15))]; byte[] qosControl = null; if (frameType == (int)FrameTypes.Data && subType == (int)DataSubTypes.QoS) { qosControl = br.ReadBytes(2); } var isProtected = frameControlFlags[64]; byte[] ccmp_WEP_Data = null; if (isProtected) { if (count - br.BaseStream.Position < 4) { packet = null; return(false); } var firstFour = br.ReadBytes(4); if (firstFour[3] == 0) { ccmp_WEP_Data = firstFour; } else { if (count - br.BaseStream.Position < 4) { packet = null; return(false); } ccmp_WEP_Data = new byte[8]; firstFour.CopyTo(ccmp_WEP_Data, 0); br.ReadBytes(4).CopyTo(ccmp_WEP_Data, 4); } } var isWep = isProtected && ccmp_WEP_Data?.Length == 4; if (count - br.BaseStream.Position - (isWep ? 4 : 0) < 0) { packet = null; return(false); } var unsafePayloadLen = count - (int)br.BaseStream.Position - (isWep ? 4 : 0); var safePayloadLen = Math.Max(0, unsafePayloadLen); packet = null; if (frameType == (int)FrameTypes.Data && ((subType & 4) != 1) && !isProtected) { LLC payload; if (LLC.TryParse(buffer, index + (int)br.BaseStream.Position, safePayloadLen, out payload)) { packet = new IEEE802_11 <LLC> { Payload = payload }; } } else if (frameType == (int)FrameTypes.Management && subType == (int)ManagementSubTypes.Beacon) { Beacon802_11 payload; if (Beacon802_11.TryParse( buffer, index + (int)br.BaseStream.Position, safePayloadLen, out payload)) { packet = new IEEE802_11 <Beacon802_11> { Payload = payload }; } } if (packet == null) { Generic payload; Generic.TryParse(buffer, index + (int)br.BaseStream.Position, safePayloadLen, out payload); // This can never fail, so I'm not checking the output packet = new IEEE802_11 <Generic> { Payload = payload }; } br.BaseStream.Seek(packet.Payload.Length(), SeekOrigin.Current); UInt32 wep_ICV = 0; if (isWep) { wep_ICV = ByteOrder.NetworkToHostOrder(br.ReadUInt32()); } packet.FrameControlVersionTypeAndSubtype = frameControlVersionTypeAndSubtype; packet.FrameControlFlags = frameControlFlags; packet.DurationID = durationID; packet.Destination = destination; packet.Source = source; packet.BSSID = bssid; packet.SequenceControl = sequenceControl; packet.Receiver = receiver; packet.Transmitter = transmitter; packet.QosControl = qosControl; packet.CCMP_WEP_Data = ccmp_WEP_Data; packet.WEP_ICV = wep_ICV; if (br.BaseStream.Position == count - 4) { packet.FrameCheckSequence = br.ReadUInt32(); } return(true); } } } catch (Exception) { packet = null; return(false); } }
/// <summary> /// Decrypt a WEP-encrypted packet /// </summary> /// <param name="encryptedPacket">WEP encrypted packet</param> /// <param name="key">Decryption key</param> /// <param name="decrypted">Decrypted packet</param> /// <returns>True if successful, false if not</returns> public static bool TryDecryptWEP(IEEE802_11 encryptedPacket, byte[] key, out IPacket decrypted) { if (key.Length != 5 && key.Length != 13 && key.Length != 16 && key.Length != 29 && key.Length != 61) { //throw new ArgumentException("Invalid WEP key length. [5,13,16,29,61]"); decrypted = null; return false; } if (encryptedPacket.FrameType != (int)FrameTypes.Data || !encryptedPacket.IsWep) { decrypted = null; return false; } var keyWithIV = new byte[key.Length + 3]; Array.Copy(encryptedPacket.CCMP_WEP_Data, keyWithIV, 3); Array.Copy(key, 0, keyWithIV, 3, key.Length); var rc4 = new RC4(keyWithIV); var encryptedPayload = encryptedPacket.Payload.ToArray() .Concat(BitConverter.GetBytes(ByteOrder.NetworkToHostOrder(encryptedPacket.WEP_ICV))) .ToArray(); var dec = new byte[encryptedPayload.Length]; rc4.decrypt(encryptedPayload, encryptedPayload.Length, dec); var expectedCRC = BitConverter.ToUInt32(dec, dec.Length - 4); dec = dec.Take(dec.Length - 4).ToArray(); if (Crc32Algorithm.Compute(dec) == expectedCRC) { var decryptedBytes = encryptedPacket.ToArray().Take(24 + (encryptedPacket.QosControl?.Length ?? 0)).Concat(dec).ToArray(); decryptedBytes[1] &= 191; //Everything except protected return IEEE802_11Factory.Instance.TryParse(decryptedBytes, out decrypted); } decrypted = null; return false; }
/// <summary> /// Decrypt a packet encrypted with CCMP /// </summary> /// <param name="encryptedPacket">The encrypted packet</param> /// <param name="temporalKey">The temporal key</param> /// <param name="headerMode">Mode in which headers should be handled</param> /// <param name="decrypted">Decrypted packet</param> /// <returns>True if successful, false if not</returns> /// <remarks>Ported from airdecap-ng</remarks> public static bool TryDecryptCCMP( IEEE802_11 encryptedPacket, byte[] temporalKey, DecryptionHeaderMode headerMode, out IPacket decrypted) { if (temporalKey.Length != 16) { // throw new ArgumentException("temporalKey must be 16 bytes"); decrypted = null; return false; } var decryptedBytes = encryptedPacket.ToArray(); int z, data_len, blocks, last, offset; bool is_a4, is_qos; var B0 = new byte[16]; var B = new byte[16]; var MIC = new byte[16]; var PacketNumber = new byte[6]; var AdditionalAuthData = new byte[32]; is_a4 = (decryptedBytes[1] & 3) == 3; is_qos = (decryptedBytes[0] & 0x8C) == 0x88; z = 24 + 6 * (is_a4 ? 1 : 0); z += 2 * (is_qos ? 1 : 0); PacketNumber[0] = decryptedBytes[z + 7]; PacketNumber[1] = decryptedBytes[z + 6]; PacketNumber[2] = decryptedBytes[z + 5]; PacketNumber[3] = decryptedBytes[z + 4]; PacketNumber[4] = decryptedBytes[z + 1]; PacketNumber[5] = decryptedBytes[z + 0]; data_len = decryptedBytes.Length - z - 8 - 8; B0[0] = 0x59; B0[1] = 0; Array.Copy(decryptedBytes, 10, B0, 2, 6); Array.Copy(PacketNumber, 0, B0, 8, 6); B0[14] = (byte)((data_len >> 8) & 0xFF); B0[15] = (byte)(data_len & 0xFF); AdditionalAuthData[2] = (byte)(decryptedBytes[0] & 0x8F); AdditionalAuthData[3] = (byte)(decryptedBytes[1] & 0xC7); Array.Copy(decryptedBytes, 4, AdditionalAuthData, 4, 3 * 6); AdditionalAuthData[22] = (byte)(decryptedBytes[22] & 0x0F); if (is_a4) { Array.Copy(decryptedBytes, 24, AdditionalAuthData, 24, 6); if (is_qos) { AdditionalAuthData[30] = (byte)(decryptedBytes[z - 2] & 0x0F); AdditionalAuthData[31] = 0; B0[1] = AdditionalAuthData[30]; AdditionalAuthData[1] = 22 + 2 + 6; } else { AdditionalAuthData[30] = 0; AdditionalAuthData[31] = 0; B0[1] = 0; AdditionalAuthData[1] = 22 + 6; } } else { if (is_qos) { AdditionalAuthData[24] = (byte)(decryptedBytes[z - 2] & 0x0F); AdditionalAuthData[25] = 0; B0[1] = AdditionalAuthData[24]; AdditionalAuthData[1] = 22 + 2; } else { AdditionalAuthData[24] = 0; AdditionalAuthData[25] = 0; B0[1] = 0; AdditionalAuthData[1] = 22; } } using (var aesFactory = Aes.Create()) { aesFactory.Mode = CipherMode.ECB; aesFactory.Key = temporalKey; var aes = aesFactory.CreateEncryptor(); aes.TransformBlock(B0, 0, B0.Length, MIC, 0); CFunctions.XOR(MIC, 0, AdditionalAuthData, 16); aes.TransformBlock(MIC, 0, MIC.Length, MIC, 0); CFunctions.XOR(MIC, 0, AdditionalAuthData.Skip(16).ToArray(), 16); aes.TransformBlock(MIC, 0, MIC.Length, MIC, 0); B0[0] &= 0x07; B0[14] = 0; B0[15] = 0; aes.TransformBlock(B0, 0, B0.Length, B, 0); CFunctions.XOR(decryptedBytes, decryptedBytes.Length - 8, B, 8); blocks = (data_len + 16 - 1) / 16; last = data_len % 16; offset = z + 8; for (var i = 1; i <= blocks; i++) { var n = last > 0 && i == blocks ? last : 16; B0[14] = (byte)((i >> 8) & 0xFF); B0[15] = (byte)(i & 0xFF); aes.TransformBlock(B0, 0, B0.Length, B, 0); CFunctions.XOR(decryptedBytes, offset, B, n); CFunctions.XOR(MIC, 0, decryptedBytes.Skip(offset).ToArray(), n); aes.TransformBlock(MIC, 0, MIC.Length, MIC, 0); offset += n; } } switch (headerMode) { case DecryptionHeaderMode.Removed: { decryptedBytes[1] &= 191; // Remove the protected bit var decryptedBytesList = decryptedBytes.ToList(); decryptedBytesList.RemoveRange(34 - 8, 8); // Remove CCMP Parameters (otherwise, without the protected bit it will break the parsing) IEEE802_11 decryptedWithHeader; if (IEEE802_11Factory.Instance.TryParse(decryptedBytesList.ToArray(), out decryptedWithHeader)) { decrypted = decryptedWithHeader.Payload; // Remove the altered 802.11 header } else { decrypted = null; return false; } break; } case DecryptionHeaderMode.Modified: { decryptedBytes[1] &= 191; // Remove the protected bit var decryptedBytesList = decryptedBytes.ToList(); decryptedBytesList.RemoveRange(34 - 8, 8); // Remove CCMP Parameters (otherwise, without the protected bit it will break the parsing) if (!IEEE802_11Factory.Instance.TryParse(decryptedBytesList.ToArray(), out decrypted)) { return false; } break; } case DecryptionHeaderMode.Untouched: if (!IEEE802_11Factory.Instance.TryParse(decryptedBytes.ToArray(), out decrypted)) { return false; } break; default: decrypted = null; return false; } return CFunctions.memcmp(decryptedBytes.Skip(offset).Take(8).ToArray(), MIC.Take(8).ToArray()) == 0; }
/// <summary> /// Attempts to parse raw data into a structured packet /// </summary> /// <param name="buffer">Raw data to parse</param> /// <param name="packet">Parsed packet</param> /// <param name="count">The length of the packet in bytes</param> /// <param name="index">The index into the buffer at which the packet begins</param> /// <returns>True if parsing was successful, false if it is not.</returns> internal static bool TryParse(byte[] buffer, int index, int count, out IEEE802_11 packet) { try { if (count < MinimumParseableBytes) { packet = null; return false; } using (var ms = new MemoryStream(buffer, index, count, false)) { using (var br = new BinaryReader(ms)) { var frameControlVersionTypeAndSubtype = new BitVector32(br.ReadByte()); var frameControlFlags = new BitVector32(br.ReadByte()); var durationID = br.ReadUInt16(); var toDS = frameControlFlags[1]; var fromDS = frameControlFlags[2]; byte[] destination; byte[] source; byte[] bssid; byte[] receiver = null; byte[] transmitter = null; UInt16 sequenceControl; if (!toDS) { destination = br.ReadBytes(6); if (!fromDS) { source = br.ReadBytes(6); bssid = br.ReadBytes(6); } else { bssid = br.ReadBytes(6); source = br.ReadBytes(6); } sequenceControl = br.ReadUInt16(); //Was NTHO, wireshark disagrees } else { if (!fromDS) { bssid = br.ReadBytes(6); source = br.ReadBytes(6); destination = br.ReadBytes(6); sequenceControl = br.ReadUInt16(); //Was NTHO, wireshark disagrees } else { receiver = br.ReadBytes(6); transmitter = br.ReadBytes(6); bssid = transmitter; //Per airdecap-ng destination = br.ReadBytes(6); sequenceControl = br.ReadUInt16(); //Was NTHO, wireshark disagrees source = br.ReadBytes(6); } } var frameType = frameControlVersionTypeAndSubtype[BitVector32.CreateSection(3, BitVector32.CreateSection(3)) ]; var subType = frameControlVersionTypeAndSubtype[ BitVector32.CreateSection(15, BitVector32.CreateSection(15))]; byte[] qosControl = null; if (frameType == (int)FrameTypes.Data && subType == (int)DataSubTypes.QoS) { qosControl = br.ReadBytes(2); } var isProtected = frameControlFlags[64]; byte[] ccmp_WEP_Data = null; if (isProtected) { if (count - br.BaseStream.Position < 4) { packet = null; return false; } var firstFour = br.ReadBytes(4); if (firstFour[3] == 0) { ccmp_WEP_Data = firstFour; } else { if (count - br.BaseStream.Position < 4) { packet = null; return false; } ccmp_WEP_Data = new byte[8]; firstFour.CopyTo(ccmp_WEP_Data, 0); br.ReadBytes(4).CopyTo(ccmp_WEP_Data, 4); } } var isWep = isProtected && ccmp_WEP_Data?.Length == 4; if (count - br.BaseStream.Position - (isWep ? 4 : 0) < 0) { packet = null; return false; } var unsafePayloadLen = count - (int)br.BaseStream.Position - (isWep ? 4 : 0); var safePayloadLen = Math.Max(0, unsafePayloadLen); packet = null; if (frameType == (int)FrameTypes.Data && ((subType & 4) != 1) && !isProtected) { LLC payload; if (LLC.TryParse(buffer, index + (int)br.BaseStream.Position, safePayloadLen, out payload)) { packet = new IEEE802_11<LLC> { Payload = payload }; } } else if (frameType == (int)FrameTypes.Management && subType == (int)ManagementSubTypes.Beacon) { Beacon802_11 payload; if (Beacon802_11.TryParse( buffer, index + (int)br.BaseStream.Position, safePayloadLen, out payload)) { packet = new IEEE802_11<Beacon802_11> { Payload = payload }; } } if (packet == null) { Generic payload; Generic.TryParse(buffer, index + (int)br.BaseStream.Position, safePayloadLen, out payload); // This can never fail, so I'm not checking the output packet = new IEEE802_11<Generic> { Payload = payload }; } br.BaseStream.Seek(packet.Payload.Length(), SeekOrigin.Current); UInt32 wep_ICV = 0; if (isWep) { wep_ICV = ByteOrder.NetworkToHostOrder(br.ReadUInt32()); } packet.FrameControlVersionTypeAndSubtype = frameControlVersionTypeAndSubtype; packet.FrameControlFlags = frameControlFlags; packet.DurationID = durationID; packet.Destination = destination; packet.Source = source; packet.BSSID = bssid; packet.SequenceControl = sequenceControl; packet.Receiver = receiver; packet.Transmitter = transmitter; packet.QosControl = qosControl; packet.CCMP_WEP_Data = ccmp_WEP_Data; packet.WEP_ICV = wep_ICV; if (br.BaseStream.Position == count - 4) { packet.FrameCheckSequence = br.ReadUInt32(); } return true; } } } catch (Exception) { packet = null; return false; } }
/// <summary> /// Attempts to parse raw data into a structured packet /// </summary> /// <param name="buffer">Raw data to parse</param> /// <param name="packet">Parsed packet</param> /// <param name="count">The length of the packet in bytes</param> /// <param name="index">The index into the buffer at which the packet begins</param> /// <returns>True if parsing was successful, false if it is not.</returns> internal static bool TryParse(byte[] buffer, int index, int count, out Radiotap packet) { try { using (var ms = new MemoryStream(buffer, index, count, false)) { using (var br = new BinaryReader(ms)) { var version = br.ReadByte(); var pad = br.ReadByte(); var length = br.ReadUInt16(); var present = br.ReadUInt32(); if (count - br.BaseStream.Position < length - 8) { packet = null; return(false); } var fieldData = br.ReadBytes(length - 8); packet = null; IEEE802_11 payload80211; if (IEEE802_11.TryParse( buffer, index + (int)br.BaseStream.Position, (int)(count - br.BaseStream.Position), out payload80211)) { packet = new Radiotap <IEEE802_11> { Payload = payload80211 }; } if (packet == null) { Generic payload; Generic.TryParse( buffer, index + (int)br.BaseStream.Position, (int)(count - br.BaseStream.Position), out payload); // This can never fail, so I'm not checking the output packet = new Radiotap <Generic> { Payload = payload }; } packet.Version = version; packet.Pad = pad; packet.LengthRadiotap = length; packet.Present = present; packet.FieldData = fieldData; return(true); } } } catch (Exception) { packet = null; return(false); } }