Compare commits
14 Commits
3c3ab18410
...
d0ee1a7044
Author | SHA1 | Date | |
---|---|---|---|
d0ee1a7044 | |||
57394882cc | |||
b41d728ba9 | |||
d6135a02e9 | |||
ffe14e3f53 | |||
09bea81058 | |||
66c0fdfe12 | |||
b32b5cbed7 | |||
e86b5fca9a | |||
d2178f3b73 | |||
fe5dec7860 | |||
0d472e8cb3 | |||
bba5da2599 | |||
c2210dfb59 |
@ -9,11 +9,17 @@ const PacketType = {
|
|||||||
Acknowledgment: 'Acknowledgment',
|
Acknowledgment: 'Acknowledgment',
|
||||||
BlockAcknowledgment: 'BlockAcknowledgment',
|
BlockAcknowledgment: 'BlockAcknowledgment',
|
||||||
NoData: 'NoData',
|
NoData: 'NoData',
|
||||||
|
Authentication: 'Authentication',
|
||||||
|
AssociationRequest: 'AssociationRequest',
|
||||||
|
AssociationResponse: 'AssociationResponse',
|
||||||
|
Disassociation: 'Disassociation',
|
||||||
|
Handshake: 'Handshake',
|
||||||
Unknown: 'Unknown'
|
Unknown: 'Unknown'
|
||||||
}
|
}
|
||||||
|
|
||||||
class Packet{
|
class Packet{
|
||||||
timestampMicros;
|
timestampMicros;
|
||||||
|
isRetry;
|
||||||
|
|
||||||
srcMac;
|
srcMac;
|
||||||
dstMac;
|
dstMac;
|
||||||
@ -23,9 +29,11 @@ class Packet{
|
|||||||
frequency;
|
frequency;
|
||||||
dataRate;
|
dataRate;
|
||||||
|
|
||||||
|
durationMicros;
|
||||||
|
|
||||||
payloadData;
|
payloadData;
|
||||||
get payloadSize(){
|
get payloadSize(){
|
||||||
return payloadData.length/2;
|
return this.payloadData.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
packetType;
|
packetType;
|
||||||
@ -40,6 +48,34 @@ class BeaconPacket extends PacketWithSSID{}
|
|||||||
class ProbeRequestPacket extends PacketWithSSID{}
|
class ProbeRequestPacket extends PacketWithSSID{}
|
||||||
class ProbeResponsePacket 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
|
// Specify exports
|
||||||
module.exports = {
|
module.exports = {
|
||||||
@ -48,5 +84,12 @@ module.exports = {
|
|||||||
PacketWithSSID,
|
PacketWithSSID,
|
||||||
BeaconPacket,
|
BeaconPacket,
|
||||||
ProbeRequestPacket,
|
ProbeRequestPacket,
|
||||||
ProbeResponsePacket
|
ProbeResponsePacket,
|
||||||
|
AuthenticationType,
|
||||||
|
AuthenticationPacket,
|
||||||
|
AssociationRequestPacket,
|
||||||
|
AssociationResponsePacket,
|
||||||
|
DisassociationPacket,
|
||||||
|
HandshakeStage,
|
||||||
|
HandshakePacket,
|
||||||
};
|
};
|
||||||
|
24
src/helper/hexConverter.js
Normal file
24
src/helper/hexConverter.js
Normal 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
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
const logger = require.main.require("./helper/logger.js")("PacketStreamFactory");
|
const logger = require.main.require("./helper/logger.js")("PacketStreamFactory");
|
||||||
const { Transform } = require('stream');
|
const { Transform } = require('stream');
|
||||||
const { DateTime } = require("luxon");
|
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 = {
|
const PACKET_TYPE_MAP = {
|
||||||
"Beacon": PacketType.Beacon,
|
"Beacon": PacketType.Beacon,
|
||||||
@ -12,10 +13,20 @@ const PACKET_TYPE_MAP = {
|
|||||||
"Request-To-Send": PacketType.RequestToSend,
|
"Request-To-Send": PacketType.RequestToSend,
|
||||||
"Clear-To-Send": PacketType.ClearToSend,
|
"Clear-To-Send": PacketType.ClearToSend,
|
||||||
"Acknowledgment": PacketType.Acknowledgment,
|
"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 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
|
* Read data from text-blocks and convert them to Packet
|
||||||
*/
|
*/
|
||||||
@ -36,8 +47,8 @@ class PacketStreamFactory extends Transform{
|
|||||||
|
|
||||||
const lines = chunk.split('\n');
|
const lines = chunk.split('\n');
|
||||||
const header = lines.splice(0, 1)[0]; // Grab first line, 'lines' is now the payload
|
const header = lines.splice(0, 1)[0]; // Grab first line, 'lines' is now the payload
|
||||||
this._handleHeader(packet, header);
|
packet = this._handleHeader(packet, header);
|
||||||
this._handlePayload(packet, lines);
|
packet = this._handlePayload(packet, lines);
|
||||||
|
|
||||||
logger.debug(packet);
|
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"
|
// 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.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.isRetry = data.match(/(?<=^|\s)Retry(?=$|\s)/i)? true: false;
|
||||||
packet.frequency = Number(data.match(/(^| )([0-9]{4}) MHz($| )/i)?.[2]) || null;
|
|
||||||
|
|
||||||
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.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
|
// Cover special cases with more data
|
||||||
|
let newPacket;
|
||||||
switch(packet.packetType){
|
switch(packet.packetType){
|
||||||
case PacketType.Beacon:
|
case PacketType.Beacon:
|
||||||
case PacketType.ProbeRequest:
|
case PacketType.ProbeRequest:
|
||||||
case PacketType.ProbeResponse:
|
case PacketType.ProbeResponse:
|
||||||
packet = Object.assign(new PacketWithSSID(), packet); // Create new, more specific, packet and copy old data over
|
case PacketType.AssociationRequest:
|
||||||
packet.ssid = data.match(new RegExp(`(^| )${packetTypeStr} `+'\\'+`((.{0,32})`+'\\'+`)($| )`, 'i'))?.[2] ?? null;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
if(newPacket) packet = Object.assign(newPacket, packet); // Use new, more specific, packet and copy old data over
|
||||||
|
|
||||||
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
_handlePayload(packet, data){
|
_handlePayload(packet, data){
|
||||||
|
data = data.join('');
|
||||||
|
|
||||||
// Get payload-Hex-Data. If there is no data: empty
|
// 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user