1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
use netscan::pcap::PacketFrame;
use xenet::packet::ethernet::EthernetHeader;

use crate::define;
use crate::ip;
use crate::model;
use std::collections::HashMap;

pub fn get_oui_detail_map() -> HashMap<String, String> {
    let mut oui_map: HashMap<String, String> = HashMap::new();
    let ds_oui: Vec<model::Oui> = bincode::deserialize(define::OUI_BIN).unwrap_or(vec![]);
    for oui in ds_oui {
        oui_map.insert(oui.mac_prefix, oui.vendor_name_detail);
    }
    oui_map
}

pub fn get_vm_oui_map() -> HashMap<String, String> {
    let mut oui_map: HashMap<String, String> = HashMap::new();
    let ds_oui: Vec<model::Oui> = bincode::deserialize(define::OUI_VM_BIN).unwrap_or(vec![]);
    for oui in ds_oui {
        oui_map.insert(oui.mac_prefix, oui.vendor_name_detail);
    }
    oui_map
}

pub fn get_tcp_map() -> HashMap<u16, String> {
    let mut tcp_map: HashMap<u16, String> = HashMap::new();
    let ds_tcp_service: Vec<model::TcpService> = bincode::deserialize(define::TCP_SERVICE_BIN).unwrap_or(vec![]);
    for port in ds_tcp_service {
        tcp_map.insert(port.port, port.service_name);
    }
    tcp_map
}

pub fn get_default_ports() -> Vec<u16> {
    let default_ports: Vec<u16> = bincode::deserialize(define::DEFAULT_PORTS_BIN).unwrap_or(vec![]);
    default_ports
}

pub fn get_wellknown_ports() -> Vec<u16> {
    let wellknown_ports: Vec<u16> = bincode::deserialize(define::WELLKNOWN_PORTS_BIN).unwrap_or(vec![]);
    wellknown_ports
}

pub fn get_http_ports() -> Vec<u16> {
    let http_ports: Vec<u16> = bincode::deserialize(define::HTTP_PORTS_BIN).unwrap_or(vec![]);
    http_ports
}

pub fn get_https_ports() -> Vec<u16> {
    let https_ports: Vec<u16> = bincode::deserialize(define::HTTPS_PORTS_BIN).unwrap_or(vec![]);
    https_ports
}

pub fn get_os_ttl_map() -> HashMap<u8, String> {
    let mut os_ttl_map: HashMap<u8, String> = HashMap::new();
    let ds_os_ttl: Vec<model::OsTtl> = bincode::deserialize(define::OS_TTL_BIN).unwrap_or(vec![]);
    for os_ttl in ds_os_ttl {
        os_ttl_map.insert(os_ttl.initial_ttl, os_ttl.os_description);
    }
    os_ttl_map
}

pub fn get_os_ttl_list() -> Vec<model::OsTtl> {
    let ds_os_ttl: Vec<model::OsTtl> = bincode::deserialize(define::OS_TTL_BIN).unwrap_or(vec![]);
    ds_os_ttl
}

pub fn get_subdomain() -> Vec<String> {
    let subdomain: Vec<String> = bincode::deserialize(define::SUBDOMAIN_BIN).unwrap_or(vec![]);
    subdomain
}

pub fn get_os_fingerprints() -> Vec<model::OsFingerprint> {
    let ds_os_fingerprints: Vec<model::OsFingerprint> = bincode::deserialize(define::OS_FINGERPRINT_BIN).unwrap_or(vec![]);
    ds_os_fingerprints
}

pub fn get_os_family_fingerprints() -> Vec<model::OsFamilyFingerprint> {
    let ds_os_fingerprints: Vec<model::OsFamilyFingerprint> = bincode::deserialize(define::OS_FAMILY_FINGERPRINT_BIN).unwrap_or(vec![]);
    ds_os_fingerprints
}

pub fn get_os_family_list() -> Vec<String> {
    let os_families: Vec<String> = bincode::deserialize(define::OS_FAMILY_BIN).unwrap_or(vec![]);
    os_families
}

pub fn is_vm_fingerprint(fingerprint: &model::OsFingerprint) -> bool {
    if fingerprint.os_family == "Player".to_string() && fingerprint.device_type == "specialized".to_string() {
        return true;
    }
    false
}

pub fn in_vm_network(ether_header: EthernetHeader) -> bool {
    let vm_oui_map: HashMap<String, String> = get_vm_oui_map();
    let mac = ether_header.source.address();
    if mac.len() > 16 {
        let prefix8 = mac[0..8].to_uppercase();
        vm_oui_map.contains_key(&prefix8)
    } else {
        vm_oui_map.contains_key(&mac)
    }
}

pub fn verify_os_fingerprint(fingerprint: &PacketFrame) -> model::OsFingerprint {
    let os_family_list: Vec<String> = get_os_family_list();
    let os_fingerprints: Vec<model::OsFingerprint> = get_os_fingerprints();
    let in_vm: bool = if let Some(ether_header) = &fingerprint.ethernet_header {
        in_vm_network(ether_header.clone())
    } else {
        false
    };

    // 0. Check TTL
    let os_ttl_list: Vec<model::OsTtl> = get_os_ttl_list();
    let initial_ttl = if let Some(ipv4_header) = &fingerprint.ipv4_header {
        ip::guess_initial_ttl(ipv4_header.ttl)
    } else {
        if let Some(ipv6_header) = &fingerprint.ipv6_header {
            ip::guess_initial_ttl(ipv6_header.hop_limit)
        } else {
            0
        }
    };
    let mut tcp_window_size = 0;
    let mut tcp_options: Vec<String> = vec![];
    if let Some(ref tcp_header) = fingerprint.tcp_header {
        tcp_window_size = tcp_header.window;
        for option in &tcp_header.options {
            tcp_options.push(option.kind.name());
        }
    }
    let tco_option_pattern = tcp_options.join("-");
    let mut os_ttl_info: model::OsTtl = model::OsTtl {
        initial_ttl: initial_ttl,
        os_description: String::new(),
        os_family: String::new(),
    };
    for os_ttl in os_ttl_list {
        if os_ttl.initial_ttl == initial_ttl {
            os_ttl_info.initial_ttl = os_ttl.initial_ttl;
            os_ttl_info.os_description = os_ttl.os_description;
            os_ttl_info.os_family = os_ttl.os_family;
        }
    }
    // 1. Select OS Fingerprint that match tcp_window_size and tcp_option_pattern
    let mut matched_fingerprints: Vec<model::OsFingerprint> = vec![];
    for f in &os_fingerprints {
        let mut window_size_match: bool = false;
        let mut option_pattern_match: bool = false;
        if f.tcp_window_sizes.contains(&tcp_window_size) {
            window_size_match = true;
        }
        for option_pattern in &f.tcp_option_patterns {
            if option_pattern == &tco_option_pattern {
                option_pattern_match = true;
            }
        }
        if window_size_match && option_pattern_match {
            matched_fingerprints.push(f.clone());
        }
    }
    if matched_fingerprints.len() == 1 {
        return matched_fingerprints[0].clone();
    } else if matched_fingerprints.len() > 1 {
        // Check VM Fingerprint
        if in_vm {
            for f in &matched_fingerprints {
                if is_vm_fingerprint(f) {
                    let mut vmf = f.clone();
                    vmf.cpe = String::from("(Failed to OS Fingerprinting)");
                    vmf.os_name = format!("{} (Probably in VM Network)", vmf.os_name);
                    return vmf;
                }
            }
        }
        // Search fingerprint that match general OS Family
        matched_fingerprints.reverse();
        for f in &matched_fingerprints {
            if os_ttl_info.os_family == f.os_family.to_lowercase() {
                return f.clone();
            }
        }
        for f in matched_fingerprints {
            if os_family_list.contains(&f.os_family) {
                return f;
            }
        }
    }
    // 2. Select OS Fingerprint that match tcp_option_pattern and have most closely tcp_window_size
    let mut matched_fingerprints: Vec<model::OsFingerprint> = vec![];
    for f in os_fingerprints {
        let mut window_size_match: bool = false;
        let mut option_pattern_match: bool = false;
        for window_size in &f.tcp_window_sizes {
            if tcp_window_size - 100 < *window_size && *window_size < tcp_window_size + 100 {
                window_size_match = true;
            }
        }
        for option_pattern in &f.tcp_option_patterns {
            if option_pattern == &tco_option_pattern {
                option_pattern_match = true;
            }
        }
        if window_size_match && option_pattern_match {
            matched_fingerprints.push(f.clone());
        }
    }
    if matched_fingerprints.len() == 1 {
        return matched_fingerprints[0].clone();
    } else if matched_fingerprints.len() > 1 {
        // Check VM Fingerprint
        if in_vm {
            for f in &matched_fingerprints {
                if is_vm_fingerprint(f) {
                    let mut vmf = f.clone();
                    vmf.cpe = String::from("(Failed to OS Fingerprinting)");
                    vmf.os_name = format!("{} (Probably using VM network interface)", vmf.os_name);
                    return vmf;
                }
            }
        }
        // Search fingerprint that match general OS Family
        matched_fingerprints.reverse();
        for f in &matched_fingerprints {
            if os_ttl_info.os_family == f.os_family.to_lowercase() {
                return f.clone();
            }
        }
        for f in matched_fingerprints {
            if os_family_list.contains(&f.os_family) {
                return f;
            }
        }
    }
    // 3. from TTL
    return model::OsFingerprint {
        cpe: String::from("(Failed to OS Fingerprinting)"),
        os_name: os_ttl_info.os_description,
        os_vendor: String::new(),
        os_family: os_ttl_info.os_family,
        os_generation: String::new(),
        device_type: String::new(),
        tcp_window_sizes: vec![tcp_window_size],
        tcp_option_patterns: vec![tco_option_pattern],
    };
}