Compare commits

...

14 Commits

3 changed files with 153 additions and 16 deletions

View File

@ -9,11 +9,17 @@ const PacketType = {
Acknowledgment: 'Acknowledgment',
BlockAcknowledgment: 'BlockAcknowledgment',
NoData: 'NoData',
Authentication: 'Authentication',
AssociationRequest: 'AssociationRequest',
AssociationResponse: 'AssociationResponse',
Disassociation: 'Disassociation',
Handshake: 'Handshake',
Unknown: 'Unknown'
}
class Packet{
timestampMicros;
isRetry;
srcMac;
dstMac;
@ -23,9 +29,11 @@ class Packet{
frequency;
dataRate;
durationMicros;
payloadData;
get payloadSize(){
return payloadData.length/2;
return this.payloadData.length;
}
packetType;
@ -40,6 +48,34 @@ class BeaconPacket extends PacketWithSSID{}
class ProbeRequestPacket extends PacketWithSSID{}
class ProbeResponsePacket extends PacketWithSSID{}
const AuthenticationType = {
OpenSystem_1: 'OpenSystem_1',
OpenSystem_2: 'OpenSystem_2',
Unknown: 'Unknown',
}
class AuthenticationPacket extends Packet{
authenticationType;
}
class AssociationRequestPacket extends PacketWithSSID{}
class AssociationResponsePacket extends Packet{
associationIsSuccessful;
}
class DisassociationPacket extends Packet{
disassociationReason;
}
const HandshakeStage = {
1: '1',
2: '2',
3: '3',
4: '4'
}
class HandshakePacket extends Packet{
handshakeStage;
}
// Specify exports
module.exports = {
@ -48,5 +84,12 @@ module.exports = {
PacketWithSSID,
BeaconPacket,
ProbeRequestPacket,
ProbeResponsePacket
ProbeResponsePacket,
AuthenticationType,
AuthenticationPacket,
AssociationRequestPacket,
AssociationResponsePacket,
DisassociationPacket,
HandshakeStage,
HandshakePacket,
};

View File

@ -0,0 +1,24 @@
// From https://stackoverflow.com/a/34356351
// Convert a hex string to a byte array
function hexToBytes(hex) {
for (var bytes = [], c = 0; c < hex.length; c += 2)
bytes.push(parseInt(hex.substr(c, 2), 16));
return bytes;
}
// Convert a byte array to a hex string
function bytesToHex(bytes) {
for (var hex = [], i = 0; i < bytes.length; i++) {
var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
hex.push((current >>> 4).toString(16));
hex.push((current & 0xF).toString(16));
}
return hex.join("");
}
// Specify exports
module.exports = {
hexToBytes,
bytesToHex
}

View File

@ -1,7 +1,8 @@
const logger = require.main.require("./helper/logger.js")("PacketStreamFactory");
const { Transform } = require('stream');
const { DateTime } = require("luxon");
const { PacketType, Packet, PacketWithSSID, BeaconPacket, ProbeRequestPacket, ProbeResponsePacket } = require.main.require('./dto/Packet.js');
const { PacketType, Packet, PacketWithSSID, BeaconPacket, ProbeRequestPacket, ProbeResponsePacket, AuthenticationPacket, AuthenticationType, AssociationResponsePacket, DisassociationPacket, HandshakePacket, HandshakeStage } = require.main.require('./dto/Packet.js');
const hexConv = require.main.require("./helper/hexConverter.js");
const PACKET_TYPE_MAP = {
"Beacon": PacketType.Beacon,
@ -12,10 +13,20 @@ const PACKET_TYPE_MAP = {
"Request-To-Send": PacketType.RequestToSend,
"Clear-To-Send": PacketType.ClearToSend,
"Acknowledgment": PacketType.Acknowledgment,
"BA": PacketType.BlockAcknowledgment
"BA": PacketType.BlockAcknowledgment,
"Authentication": PacketType.Authentication,
"Assoc Request": PacketType.AssociationRequest,
"Assoc Response": PacketType.AssociationResponse,
"Disassociation:": PacketType.Disassociation,
"EAPOL": PacketType.Handshake,
};
const PACKET_TYPES_REGEX = Object.keys(PACKET_TYPE_MAP).join('|');
const AUTHENTICATION_TYPE_MAP = {
"(Open System)-1": AuthenticationType.OpenSystem_1,
"(Open System)-2": AuthenticationType.OpenSystem_2,
}
/**
* Read data from text-blocks and convert them to Packet
*/
@ -36,8 +47,8 @@ class PacketStreamFactory extends Transform{
const lines = chunk.split('\n');
const header = lines.splice(0, 1)[0]; // Grab first line, 'lines' is now the payload
this._handleHeader(packet, header);
this._handlePayload(packet, lines);
packet = this._handleHeader(packet, header);
packet = this._handlePayload(packet, lines);
logger.debug(packet);
@ -48,34 +59,93 @@ class PacketStreamFactory extends Transform{
// Convert time to epoch-micros Unfortunately luxon doesnt use micros, but millis as smallest time-unit requiring some "hacks"
packet.timestampMicros = DateTime.fromISO(data.slice(0, 12)).toSeconds() + data.slice(12, 15)/1000000;
packet.dataRate = Number(data.match(/(^| )([0-9]+(\.[0-9]+)?) Mb\/s($| )/i)?.[2]) || null;
packet.frequency = Number(data.match(/(^| )([0-9]{4}) MHz($| )/i)?.[2]) || null;
packet.isRetry = data.match(/(?<=^|\s)Retry(?=$|\s)/i)? true: false;
packet.signal = Number(data.match(/(^| )(-[0-9]{2})dBm Signal($| )/i)?.[2]) || null;
packet.dataRate = Number(data.match(/(?<=^|\s)[0-9]+(\.[0-9]+)?(?=\sMb\/?s($|\s))/i)?.[0]) || null;
packet.frequency = Number(data.match(/(?<=^|\s)[0-9]{4}(?=\sMHz($|\s))/i)?.[0]) || null;
let packetTypeStr = data.match(new RegExp(`(^|.{80} )(${PACKET_TYPES_REGEX})($| )`, 'i'))?.[2];
packet.durationMicros = Number(data.match(/(?<=^|\s)[0-9]{1,4}(?=us($|\s))/i)?.[0]) || null;
packet.signal = Number(data.match(/(?<=^|\s)-[0-9]{2,3}(?=dBm\sSignal($|\s))/i)?.[0]) || null;
let packetTypeStr = data.match(new RegExp('(?<=^|\\s)('+ PACKET_TYPES_REGEX +')(?=$|\\s)', 'i'))?.[0];
packet.packetType = packetTypeStr? PACKET_TYPE_MAP[packetTypeStr]: PacketType.Unknown;
packet.srcMac = data.match(/(^| )(SA|TA):(.{17})($| )/i)?.[3] ?? null;
packet.srcMac = data.match(/(?<=(^|\s)(SA|TA):).{17}(?=$|\s)/i)?.[0] ?? null;
packet.dstMac = data.match(/(^| )(DA|RA):(.{17})($| )/i)?.[3] ?? null;
packet.dstMac = data.match(/(?<=(^|\s)(DA|RA):).{17}(?=$|\s)/i)?.[0] ?? null;
packet.bssid = data.match(/(^| )BSSID:(.{17})($| )/i)?.[2] ?? null;
packet.bssid = data.match(/(?<=(^|\s)BSSID:).{17}(?=$|\s)/i)?.[0] ?? null;
// Cover special cases with more data
let newPacket;
switch(packet.packetType){
case PacketType.Beacon:
case PacketType.ProbeRequest:
case PacketType.ProbeResponse:
packet = Object.assign(new PacketWithSSID(), packet); // Create new, more specific, packet and copy old data over
packet.ssid = data.match(new RegExp(`(^| )${packetTypeStr} `+'\\'+`((.{0,32})`+'\\'+`)($| )`, 'i'))?.[2] ?? null;
case PacketType.AssociationRequest:
newPacket = new PacketWithSSID();
newPacket.ssid = data.match(new RegExp('(?<=(^|\\s)'+ packetTypeStr +'\\s\\().{0,32}(?=\\)($|\\s))', 'i'))?.[0] ?? null;
break;
case PacketType.Authentication:
newPacket = new AuthenticationPacket();
newPacket.authenticationType = AUTHENTICATION_TYPE_MAP[data.match(/(?<=(^|\s)Authentication\s).{3,}(?=\:(\s|$))/i)[0]] ?? AuthenticationType.Unknown;
break;
case PacketType.AssociationResponse:
newPacket = new AssociationResponsePacket();
newPacket.associationIsSuccessful = data.match(/(?<=(^|\s)Assoc\sResponse\s.{0,30})Successful(?=\s|$)/i) ? true : false;
break;
case PacketType.Disassociation:
newPacket = new DisassociationPacket();
newPacket.disassociationReason = data.match(/(?<=(^|\s)Disassociation:\s).*$/i)?.[0] ?? null;
break;
}
if(newPacket) packet = Object.assign(newPacket, packet); // Use new, more specific, packet and copy old data over
return packet;
}
_handlePayload(packet, data){
data = data.join('');
// Get payload-Hex-Data. If there is no data: empty
packet.payloadData = data.join('').match(/(?<=\s)([A-F0-9]{1,4}(?!(\.|x)))/igm)?.join('') ?? '';
packet.payloadData = hexConv.hexToBytes(data.match(/(?<=\s)([A-F0-9]{1,4}(?!(\.|x)))/igm)?.join('') ?? '');
// Cover special cases with more data
let newPacket;
switch(packet.packetType){
case PacketType.Handshake:
newPacket = new HandshakePacket();
// Read key-information
let keyInfoRaw = (packet.payloadData[0x5]<<0x8) + packet.payloadData[0x6];
let keyInfo = {
"KeyDescriptorVersion": keyInfoRaw>>0 & 0b111,
"KeyType": keyInfoRaw>>3 & 0b1,
"KeyIndex": keyInfoRaw>>4 & 0b11,
"Install": keyInfoRaw>>6 & 0b1,
"KeyACK": keyInfoRaw>>7 & 0b1,
"KeyMIC": keyInfoRaw>>8 & 0b1,
"Secure": keyInfoRaw>>9 & 0b1,
"Error": keyInfoRaw>>10 & 0b1,
"Request": keyInfoRaw>>11 & 0b1,
"EncryptedKeyData": keyInfoRaw>>12 & 0b1,
"SMKMessage": keyInfoRaw>>13 & 0b1,
};
newPacket.handshakeStage = (!keyInfo.Install && keyInfo.KeyACK && !keyInfo.KeyMIC && !keyInfo.Secure)? HandshakeStage[1] :
(!keyInfo.Install && !keyInfo.KeyACK && keyInfo.KeyMIC && !keyInfo.Secure)? HandshakeStage[2] :
( keyInfo.Install && keyInfo.KeyACK && keyInfo.KeyMIC && keyInfo.Secure)? HandshakeStage[3] :
(!keyInfo.Install && !keyInfo.KeyACK && keyInfo.KeyMIC && keyInfo.Secure)? HandshakeStage[4] :
null;
break;
}
if(newPacket) packet = Object.assign(newPacket, packet);
return packet;
}
}