Add nDPI protocol analyzer and integrate with handshake analysis
This commit is contained in:
18
Dockerfile
18
Dockerfile
@@ -6,8 +6,22 @@ RUN apt-get update && apt-get install -y \
|
||||
iproute2 \
|
||||
iptables \
|
||||
tcpdump \
|
||||
build-essential \
|
||||
git \
|
||||
libpcap-dev \
|
||||
libjson-c-dev \
|
||||
pkg-config \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Build and install nDPI
|
||||
RUN git clone https://github.com/ntop/nDPI.git /tmp/nDPI && \
|
||||
cd /tmp/nDPI && \
|
||||
./autogen.sh && \
|
||||
make && \
|
||||
make install && \
|
||||
ldconfig && \
|
||||
rm -rf /tmp/nDPI
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy pubspec files first (for dependency caching)
|
||||
@@ -18,6 +32,10 @@ RUN dart pub get
|
||||
|
||||
# Copy source code (invalidates cache from here)
|
||||
COPY lib/ ./lib/
|
||||
COPY protocol_analyzer.c ./
|
||||
|
||||
# Compile the C protocol analyzer
|
||||
RUN gcc -o protocol_analyzer protocol_analyzer.c -lndpi -lpcap
|
||||
|
||||
# Compile the application
|
||||
RUN dart compile exe lib/main.dart -o waylume_server
|
||||
|
||||
@@ -358,53 +358,82 @@ class ProtocolBlockingService {
|
||||
}
|
||||
}
|
||||
|
||||
static void _analyzeHandshake(String handshakeData, Connection conn) {
|
||||
print('════════════════ HANDSHAKE SIGNATURE ANALYSIS ════════════════');
|
||||
static Future<void> _analyzeHandshake(String handshakeData, Connection conn) async {
|
||||
print('════════════════ nDPI PROTOCOL ANALYSIS ════════════════');
|
||||
print('📍 Connection: $conn');
|
||||
|
||||
// Extract raw bytes from tcpdump hex output
|
||||
// Extract hex bytes from tcpdump output
|
||||
final hexBytes = _extractHexBytes(handshakeData);
|
||||
final asciiData = _extractAsciiFromHex(hexBytes);
|
||||
|
||||
print('📊 Raw Data Length: ${handshakeData.length} chars');
|
||||
print('🔢 Hex Bytes (first 64): ${hexBytes.take(64).join(' ')}');
|
||||
print('📝 ASCII Representation: ${asciiData.replaceAll('\n', '\\n').replaceAll('\r', '\\r')}');
|
||||
print('🔍 First 32 bytes as string: ${String.fromCharCodes(hexBytes.take(32).map((h) => int.tryParse(h, radix: 16) ?? 0).where((b) => b >= 32 && b <= 126))}');
|
||||
|
||||
// Protocol detection with signature details
|
||||
final data = handshakeData.toLowerCase();
|
||||
String? detectedProtocol;
|
||||
String signature = '';
|
||||
|
||||
if (data.contains('bittorrent protocol') || hexBytes.join('').contains('13426974546f7272656e742070726f746f636f6c')) {
|
||||
detectedProtocol = 'BitTorrent';
|
||||
signature = 'BitTorrent handshake signature detected';
|
||||
} else if (data.contains('ssh-2.0') || data.contains('ssh-1.')) {
|
||||
detectedProtocol = 'SSH';
|
||||
signature = 'SSH protocol version string';
|
||||
} else if (data.contains('get ') || data.contains('post ') || data.contains('http/')) {
|
||||
detectedProtocol = 'HTTP';
|
||||
signature = 'HTTP request headers';
|
||||
} else if (data.contains('220 ') && conn.remotePort == 25) {
|
||||
detectedProtocol = 'SMTP';
|
||||
signature = 'SMTP welcome message';
|
||||
} else if (data.contains('220 ') && conn.remotePort == 21) {
|
||||
detectedProtocol = 'FTP';
|
||||
signature = 'FTP welcome message';
|
||||
} else if (hexBytes.isNotEmpty && hexBytes.first == '16' && hexBytes.length > 5) {
|
||||
// TLS detection
|
||||
detectedProtocol = 'TLS/SSL';
|
||||
signature = 'TLS ClientHello/ServerHello (0x16 record type)';
|
||||
}
|
||||
|
||||
if (detectedProtocol != null) {
|
||||
print('🎯 PROTOCOL IDENTIFIED: $detectedProtocol');
|
||||
print('📋 Signature: $signature');
|
||||
} else {
|
||||
print('❓ UNKNOWN PROTOCOL');
|
||||
print('💡 Pattern not recognized - logging for analysis');
|
||||
}
|
||||
if (hexBytes.isEmpty) {
|
||||
print('❌ No hex data found in tcpdump output');
|
||||
print('══════════════════════════════════════════════════════════════');
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert hex bytes to single hex string for C analyzer
|
||||
final hexString = hexBytes.join('');
|
||||
print('🔢 Analyzing ${hexBytes.length} bytes of packet data');
|
||||
|
||||
try {
|
||||
// Call our C nDPI analyzer
|
||||
final result = await Process.run('./protocol_analyzer', [hexString]);
|
||||
|
||||
if (result.exitCode == 0) {
|
||||
print('✅ nDPI Analysis Results:');
|
||||
print(result.stdout.toString().trim());
|
||||
|
||||
// Parse JSON output to extract protocol info
|
||||
try {
|
||||
final jsonStr = result.stdout.toString().trim();
|
||||
// Simple protocol extraction - look for protocol field
|
||||
final protocolMatch = RegExp(r'"protocol":\s*"([^"]+)"').firstMatch(jsonStr);
|
||||
final categoryMatch = RegExp(r'"category":\s*"([^"]+)"').firstMatch(jsonStr);
|
||||
|
||||
if (protocolMatch != null) {
|
||||
final protocol = protocolMatch.group(1) ?? 'Unknown';
|
||||
final category = categoryMatch?.group(1) ?? 'Unknown';
|
||||
|
||||
print('🎯 DETECTED: $protocol (Category: $category)');
|
||||
|
||||
// Check if this is a protocol we want to block
|
||||
if (_shouldBlockProtocol(protocol, category)) {
|
||||
print('🚫 BLOCKING PROTOCOL: $protocol');
|
||||
// TODO: Implement blocking logic here
|
||||
} else {
|
||||
print('✅ ALLOWING PROTOCOL: $protocol');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('⚠️ Error parsing nDPI results: $e');
|
||||
}
|
||||
|
||||
} else {
|
||||
print('❌ nDPI analyzer failed:');
|
||||
print(' Exit code: ${result.exitCode}');
|
||||
print(' Error: ${result.stderr}');
|
||||
}
|
||||
} catch (e) {
|
||||
print('❌ Error running nDPI analyzer: $e');
|
||||
}
|
||||
|
||||
print('══════════════════════════════════════════════════════════════');
|
||||
}
|
||||
|
||||
static bool _shouldBlockProtocol(String protocol, String category) {
|
||||
// Define protocols/categories to block
|
||||
final blockedProtocols = {
|
||||
'BitTorrent', 'uTorrent', 'Transmission', 'qBittorrent',
|
||||
'eMule', 'KaZaA', 'Gnutella', 'DirectConnect',
|
||||
'Skype_Call', // Block Skype calls but allow chat
|
||||
};
|
||||
|
||||
final blockedCategories = {
|
||||
'Download', 'P2P', 'FileSharing'
|
||||
};
|
||||
|
||||
return blockedProtocols.contains(protocol) ||
|
||||
blockedCategories.contains(category);
|
||||
}
|
||||
|
||||
static List<String> _extractHexBytes(String tcpdumpOutput) {
|
||||
|
||||
121
protocol_analyzer.c
Normal file
121
protocol_analyzer.c
Normal file
@@ -0,0 +1,121 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pcap.h>
|
||||
#include <ndpi_api.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <netinet/udp.h>
|
||||
|
||||
struct ndpi_detection_module_struct *ndpi_struct = NULL;
|
||||
struct ndpi_flow_struct *ndpi_flow = NULL;
|
||||
struct ndpi_id_struct *src_id = NULL, *dst_id = NULL;
|
||||
|
||||
void init_ndpi() {
|
||||
NDPI_PROTOCOL_BITMASK all;
|
||||
|
||||
ndpi_struct = ndpi_init_detection_module();
|
||||
if (ndpi_struct == NULL) {
|
||||
printf("ERROR: ndpi_init_detection_module failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Enable all protocols
|
||||
NDPI_BITMASK_SET_ALL(all);
|
||||
ndpi_set_protocol_detection_bitmask2(ndpi_struct, &all);
|
||||
|
||||
ndpi_finalize_initialization(ndpi_struct);
|
||||
|
||||
// Allocate flow and ID structures
|
||||
ndpi_flow = calloc(1, NDPI_DETECTION_ONLY_IPV4_FLOW_SIZE);
|
||||
src_id = calloc(1, NDPI_ID_SIZE);
|
||||
dst_id = calloc(1, NDPI_ID_SIZE);
|
||||
}
|
||||
|
||||
void cleanup_ndpi() {
|
||||
if (ndpi_flow) free(ndpi_flow);
|
||||
if (src_id) free(src_id);
|
||||
if (dst_id) free(dst_id);
|
||||
if (ndpi_struct) ndpi_exit_detection_module(ndpi_struct);
|
||||
}
|
||||
|
||||
void analyze_packet_from_hex(const char* hex_data) {
|
||||
// Convert hex string to binary data
|
||||
size_t hex_len = strlen(hex_data);
|
||||
if (hex_len % 2 != 0) {
|
||||
printf("ERROR: Invalid hex data length\n");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t bin_len = hex_len / 2;
|
||||
unsigned char *packet_data = malloc(bin_len);
|
||||
|
||||
for (size_t i = 0; i < bin_len; i++) {
|
||||
sscanf(hex_data + 2*i, "%2hhx", &packet_data[i]);
|
||||
}
|
||||
|
||||
// Basic IP header parsing
|
||||
struct iphdr *ip_header = (struct iphdr*)packet_data;
|
||||
if (bin_len < sizeof(struct iphdr) || ip_header->version != 4) {
|
||||
printf("ERROR: Not a valid IPv4 packet\n");
|
||||
free(packet_data);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t src_ip = ntohl(ip_header->saddr);
|
||||
uint32_t dst_ip = ntohl(ip_header->daddr);
|
||||
uint16_t src_port = 0, dst_port = 0;
|
||||
|
||||
// Extract ports for TCP/UDP
|
||||
if (ip_header->protocol == IPPROTO_TCP) {
|
||||
struct tcphdr *tcp_header = (struct tcphdr*)(packet_data + (ip_header->ihl * 4));
|
||||
src_port = ntohs(tcp_header->source);
|
||||
dst_port = ntohs(tcp_header->dest);
|
||||
} else if (ip_header->protocol == IPPROTO_UDP) {
|
||||
struct udphdr *udp_header = (struct udphdr*)(packet_data + (ip_header->ihl * 4));
|
||||
src_port = ntohs(udp_header->source);
|
||||
dst_port = ntohs(udp_header->dest);
|
||||
}
|
||||
|
||||
// Reset flow for new analysis
|
||||
memset(ndpi_flow, 0, NDPI_DETECTION_ONLY_IPV4_FLOW_SIZE);
|
||||
|
||||
// Perform nDPI detection
|
||||
ndpi_protocol protocol = ndpi_detection_process_packet(
|
||||
ndpi_struct, ndpi_flow, packet_data, bin_len,
|
||||
0, /* timestamp */
|
||||
src_id, dst_id
|
||||
);
|
||||
|
||||
// Output results in JSON format for easy parsing
|
||||
printf("{\n");
|
||||
printf(" \"src_ip\": \"%u.%u.%u.%u\",\n",
|
||||
(src_ip >> 24) & 0xFF, (src_ip >> 16) & 0xFF,
|
||||
(src_ip >> 8) & 0xFF, src_ip & 0xFF);
|
||||
printf(" \"dst_ip\": \"%u.%u.%u.%u\",\n",
|
||||
(dst_ip >> 24) & 0xFF, (dst_ip >> 16) & 0xFF,
|
||||
(dst_ip >> 8) & 0xFF, dst_ip & 0xFF);
|
||||
printf(" \"src_port\": %u,\n", src_port);
|
||||
printf(" \"dst_port\": %u,\n", dst_port);
|
||||
printf(" \"protocol\": \"%s\",\n", ndpi_protocol2name(ndpi_struct, protocol, NULL, 0));
|
||||
printf(" \"category\": \"%s\",\n", ndpi_category_get_name(ndpi_struct, protocol.category));
|
||||
printf(" \"confidence\": %u\n", protocol.confidence);
|
||||
printf("}\n");
|
||||
|
||||
free(packet_data);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s <hex_packet_data>\n", argv[0]);
|
||||
printf("Example: %s \"4500003c...\"\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
init_ndpi();
|
||||
analyze_packet_from_hex(argv[1]);
|
||||
cleanup_ndpi();
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user