Add nDPI protocol analyzer and integrate with handshake analysis

This commit is contained in:
ImBenji
2025-08-29 01:29:06 +01:00
parent 9e8432293e
commit 7f28184857
3 changed files with 207 additions and 39 deletions

View File

@@ -6,8 +6,22 @@ RUN apt-get update && apt-get install -y \
iproute2 \ iproute2 \
iptables \ iptables \
tcpdump \ tcpdump \
build-essential \
git \
libpcap-dev \
libjson-c-dev \
pkg-config \
&& rm -rf /var/lib/apt/lists/* && 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 WORKDIR /app
# Copy pubspec files first (for dependency caching) # Copy pubspec files first (for dependency caching)
@@ -18,6 +32,10 @@ RUN dart pub get
# Copy source code (invalidates cache from here) # Copy source code (invalidates cache from here)
COPY lib/ ./lib/ 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 # Compile the application
RUN dart compile exe lib/main.dart -o waylume_server RUN dart compile exe lib/main.dart -o waylume_server

View File

@@ -358,55 +358,84 @@ class ProtocolBlockingService {
} }
} }
static void _analyzeHandshake(String handshakeData, Connection conn) { static Future<void> _analyzeHandshake(String handshakeData, Connection conn) async {
print('════════════════ HANDSHAKE SIGNATURE ANALYSIS ════════════════'); print('════════════════ nDPI PROTOCOL ANALYSIS ════════════════');
print('📍 Connection: $conn'); print('📍 Connection: $conn');
// Extract raw bytes from tcpdump hex output // Extract hex bytes from tcpdump output
final hexBytes = _extractHexBytes(handshakeData); final hexBytes = _extractHexBytes(handshakeData);
final asciiData = _extractAsciiFromHex(hexBytes);
print('📊 Raw Data Length: ${handshakeData.length} chars'); if (hexBytes.isEmpty) {
print('🔢 Hex Bytes (first 64): ${hexBytes.take(64).join(' ')}'); print('❌ No hex data found in tcpdump output');
print('📝 ASCII Representation: ${asciiData.replaceAll('\n', '\\n').replaceAll('\r', '\\r')}'); print('══════════════════════════════════════════════════════════════');
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))}'); return;
// 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) { // Convert hex bytes to single hex string for C analyzer
print('🎯 PROTOCOL IDENTIFIED: $detectedProtocol'); final hexString = hexBytes.join('');
print('📋 Signature: $signature'); print('🔢 Analyzing ${hexBytes.length} bytes of packet data');
} else {
print('❓ UNKNOWN PROTOCOL'); try {
print('💡 Pattern not recognized - logging for analysis'); // 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('══════════════════════════════════════════════════════════════'); 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) { static List<String> _extractHexBytes(String tcpdumpOutput) {
final hexPattern = RegExp(r'0x[0-9a-f]+:\s*([0-9a-f\s]+)', caseSensitive: false); final hexPattern = RegExp(r'0x[0-9a-f]+:\s*([0-9a-f\s]+)', caseSensitive: false);
final matches = hexPattern.allMatches(tcpdumpOutput); final matches = hexPattern.allMatches(tcpdumpOutput);

121
protocol_analyzer.c Normal file
View 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;
}