Skip to main content

windows_reactor/
netio.rs

1use std::net::IpAddr;
2
3use vmi::{
4    Registers as _, Va, VmiContext, VmiError, VmiEventResponse, VmiOs,
5    arch::GpRegisters as _,
6    driver::VmiRead,
7    os::windows::{ArchAdapter, WindowsOs},
8    utils::reactor::Action,
9};
10use zerocopy::{FromBytes, IntoBytes};
11
12/// Corresponds to `enum FWPS_BUILTIN_LAYERS`.
13#[derive(Copy, Clone, PartialEq, Eq)]
14struct FwpsLayer(u16);
15
16impl FwpsLayer {
17    /// Corresponds to `FWPS_LAYER_INBOUND_TRANSPORT_V4`.
18    const INBOUND_TRANSPORT_V4: Self = Self(12);
19
20    /// Corresponds to `FWPS_LAYER_INBOUND_TRANSPORT_V6`.
21    const INBOUND_TRANSPORT_V6: Self = Self(14);
22
23    /// Corresponds to `FWPS_LAYER_OUTBOUND_TRANSPORT_V4`.
24    const OUTBOUND_TRANSPORT_V4: Self = Self(16);
25
26    /// Corresponds to `FWPS_LAYER_OUTBOUND_TRANSPORT_V6`.
27    const OUTBOUND_TRANSPORT_V6: Self = Self(18);
28
29    /// Corresponds to `FWPS_LAYER_ALE_AUTH_CONNECT_V4`.
30    const ALE_AUTH_CONNECT_V4: Self = Self(48);
31
32    /// Corresponds to `FWPS_LAYER_ALE_AUTH_CONNECT_V6`.
33    const ALE_AUTH_CONNECT_V6: Self = Self(50);
34
35    /// Corresponds to `FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4`.
36    const ALE_FLOW_ESTABLISHED_V4: Self = Self(52);
37
38    /// Corresponds to `FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6`.
39    const ALE_FLOW_ESTABLISHED_V6: Self = Self(54);
40
41    fn network_5tuple_indexes(self) -> Option<(u64, u64, u64, u64, u64)> {
42        // enum FWPS_FIELDS_ALE_AUTH_CONNECT_V4 & _V6 (common fields) {
43        const FWPS_FIELD_IP_LOCAL_ADDRESS: u64 = 2;
44        const FWPS_FIELD_IP_LOCAL_PORT: u64 = 4;
45        const FWPS_FIELD_IP_PROTOCOL: u64 = 5;
46        const FWPS_FIELD_IP_REMOTE_ADDRESS: u64 = 6;
47        const FWPS_FIELD_IP_REMOTE_PORT: u64 = 7;
48        // }
49
50        match self {
51            FwpsLayer::ALE_AUTH_CONNECT_V4
52            | FwpsLayer::ALE_AUTH_CONNECT_V6
53            | FwpsLayer::ALE_FLOW_ESTABLISHED_V4
54            | FwpsLayer::ALE_FLOW_ESTABLISHED_V6 => Some((
55                FWPS_FIELD_IP_PROTOCOL,
56                FWPS_FIELD_IP_LOCAL_ADDRESS,
57                FWPS_FIELD_IP_LOCAL_PORT,
58                FWPS_FIELD_IP_REMOTE_ADDRESS,
59                FWPS_FIELD_IP_REMOTE_PORT,
60            )),
61            // Unknown layer.
62            _ => None,
63        }
64    }
65}
66
67impl std::fmt::Debug for FwpsLayer {
68    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
69        let name = match *self {
70            Self::INBOUND_TRANSPORT_V4 => "FWPS_LAYER_INBOUND_TRANSPORT_V4",
71            Self::INBOUND_TRANSPORT_V6 => "FWPS_LAYER_INBOUND_TRANSPORT_V6",
72            Self::OUTBOUND_TRANSPORT_V4 => "FWPS_LAYER_OUTBOUND_TRANSPORT_V4",
73            Self::OUTBOUND_TRANSPORT_V6 => "FWPS_LAYER_OUTBOUND_TRANSPORT_V6",
74            Self::ALE_AUTH_CONNECT_V4 => "FWPS_LAYER_ALE_AUTH_CONNECT_V4",
75            Self::ALE_AUTH_CONNECT_V6 => "FWPS_LAYER_ALE_AUTH_CONNECT_V6",
76            Self::ALE_FLOW_ESTABLISHED_V4 => "FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4",
77            Self::ALE_FLOW_ESTABLISHED_V6 => "FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6",
78            _ => return self.0.fmt(f),
79        };
80        f.write_str(name)
81    }
82}
83
84bitflags::bitflags! {
85    /// Flags that can specified which entries are present
86    /// in the FWPS_INCOMING_METADATA_VALUES0 structure.
87    ///
88    /// Corresponds to the `FWPS_METADATA_FIELD_*` constants.
89    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
90    struct FwpsMetadataFields: u32 {
91        const DISCARD_REASON = 0x00000001;
92        const FLOW_HANDLE = 0x00000002;
93        const IP_HEADER_SIZE = 0x00000004;
94        const PROCESS_PATH = 0x00000008;
95        const TOKEN = 0x00000010;
96        const PROCESS_ID = 0x00000020;
97        const SYSTEM_FLAGS = 0x00000040;
98    }
99}
100
101/// Corresponds to `enum FWP_DATA_TYPE`.
102#[repr(C)]
103#[derive(Copy, Clone, PartialEq, Eq, FromBytes, IntoBytes)]
104struct FwpDataType(u32);
105
106impl FwpDataType {
107    /// Corresponds to `FWP_EMPTY`.
108    const EMPTY: Self = Self(0);
109
110    /// Corresponds to `FWP_UINT8`.
111    const UINT8: Self = Self(1);
112
113    /// Corresponds to `FWP_UINT16`.
114    const UINT16: Self = Self(2);
115
116    /// Corresponds to `FWP_UINT32`.
117    const UINT32: Self = Self(3);
118
119    /// Corresponds to `FWP_UINT64`.
120    const UINT64: Self = Self(4);
121
122    /// Corresponds to `FWP_INT8`.
123    const INT8: Self = Self(5);
124
125    /// Corresponds to `FWP_INT16`.
126    const INT16: Self = Self(6);
127
128    /// Corresponds to `FWP_INT32`.
129    const INT32: Self = Self(7);
130
131    /// Corresponds to `FWP_INT64`.
132    const INT64: Self = Self(8);
133
134    // const FWP_FLOAT: u32 = 9;
135    // const FWP_DOUBLE: u32 = 10;
136    // const FWP_BYTE_ARRAY16_TYPE: u32 = 11;
137    // const FWP_BYTE_BLOB_TYPE: u32 = 12;
138    // const FWP_SID: u32 = 13;
139    // const FWP_SECURITY_DESCRIPTOR_TYPE: u32 = 14;
140    // const FWP_TOKEN_INFORMATION_TYPE: u32 = 15;
141    // const FWP_TOKEN_ACCESS_INFORMATION_TYPE: u32 = 16;
142    // const FWP_UNICODE_STRING_TYPE: u32 = 17;
143    // const FWP_BYTE_ARRAY6_TYPE: u32 = 18;
144    // const FWP_SINGLE_DATA_TYPE_MAX: u32 = 0xff;
145    // const FWP_V4_ADDR_MASK: u32 = 0x100;
146    // const FWP_V6_ADDR_MASK: u32 = 0x101;
147    // const FWP_RANGE_TYPE: u32 = 0x102;
148    // const FWP_DATA_TYPE_MAX: u32 = 0x103;
149}
150
151impl std::fmt::Debug for FwpDataType {
152    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
153        let name = match *self {
154            Self::EMPTY => "FWP_EMPTY",
155            Self::UINT8 => "FWP_UINT8",
156            Self::UINT16 => "FWP_UINT16",
157            Self::UINT32 => "FWP_UINT32",
158            Self::UINT64 => "FWP_UINT64",
159            Self::INT8 => "FWP_INT8",
160            Self::INT16 => "FWP_INT16",
161            Self::INT32 => "FWP_INT32",
162            Self::INT64 => "FWP_INT64",
163            _ => return self.0.fmt(f),
164        };
165        f.write_str(name)
166    }
167}
168
169#[repr(C)]
170#[derive(Debug, Copy, Clone, FromBytes, IntoBytes)]
171struct FWP_VALUE0 {
172    ty: FwpDataType, // enum FWP_DATA_TYPE
173    _pad: u32,
174    data: u64,
175}
176
177#[repr(C)]
178#[derive(Debug, Copy, Clone, FromBytes, IntoBytes)]
179struct FWPS_INCOMING_VALUE0 {
180    value: FWP_VALUE0, // FWP_VALUE0
181}
182
183#[repr(C)]
184#[derive(Debug, Copy, Clone, FromBytes, IntoBytes)]
185struct FWPS_INCOMING_VALUES0 {
186    layerId: u16, // UINT16
187    _pad: u16,
188    valueCount: u32,    // UINT32
189    incomingValue: u64, // FWPS_INCOMING_VALUE0*
190}
191
192#[repr(C)]
193#[derive(Debug, Copy, Clone, FromBytes, IntoBytes)]
194struct FWPS_DISCARD_METADATA0 {
195    discardModule: u32, // enum FWPS_DISCARD_MODULE0
196    discardReason: u32, // UINT32
197    filterId: u64,      // UINT64
198}
199
200#[repr(C)]
201#[derive(Debug, Copy, Clone, FromBytes, IntoBytes)]
202struct FWPS_INCOMING_METADATA_VALUES0 {
203    currentMetadataValues: u32,              // UINT32
204    flags: u32,                              // UINT32
205    reserved: u64,                           // UINT64
206    discardMetadata: FWPS_DISCARD_METADATA0, // FWPS_DISCARD_METADATA0
207    flowHandle: u64,                         // UINT64
208    ipHeaderSize: u32,                       // UINT32
209    transportHeaderSize: u32,                // UINT32
210    processPath: u64,                        // FWP_BYTE_BLOB*
211    token: u64,                              // UINT64
212    processId: u64,                          // UINT64
213
214                                             // ... we don't need the rest of the fields
215}
216
217/// Corresponds to `enum IPPROTO`.
218#[derive(Copy, Clone, PartialEq, Eq)]
219struct IpProtocol(u8);
220
221impl IpProtocol {
222    /// Corresponds to `IPPROTO_HOPOPTS`.
223    const HOPOPTS: Self = Self(0);
224
225    /// Corresponds to `IPPROTO_ICMP`.
226    const ICMP: Self = Self(1);
227
228    /// Corresponds to `IPPROTO_IGMP`.
229    const IGMP: Self = Self(2);
230
231    /// Corresponds to `IPPROTO_GGP`.
232    const GGP: Self = Self(3);
233
234    /// Corresponds to `IPPROTO_IPV4`.
235    const IPV4: Self = Self(4);
236
237    /// Corresponds to `IPPROTO_ST`.
238    const ST: Self = Self(5);
239
240    /// Corresponds to `IPPROTO_TCP`.
241    const TCP: Self = Self(6);
242
243    /// Corresponds to `IPPROTO_CBT`.
244    const CBT: Self = Self(7);
245
246    /// Corresponds to `IPPROTO_EGP`.
247    const EGP: Self = Self(8);
248
249    /// Corresponds to `IPPROTO_IGP`.
250    const IGP: Self = Self(9);
251
252    /// Corresponds to `IPPROTO_PUP`.
253    const PUP: Self = Self(12);
254
255    /// Corresponds to `IPPROTO_UDP`.
256    const UDP: Self = Self(17);
257
258    /// Corresponds to `IPPROTO_IDP`.
259    const IDP: Self = Self(22);
260
261    /// Corresponds to `IPPROTO_RDP`.
262    const RDP: Self = Self(27);
263
264    /// Corresponds to `IPPROTO_IPV6`.
265    const IPV6: Self = Self(41);
266
267    /// Corresponds to `IPPROTO_ROUTING`.
268    const ROUTING: Self = Self(43);
269
270    /// Corresponds to `IPPROTO_FRAGMENT`.
271    const FRAGMENT: Self = Self(44);
272
273    /// Corresponds to `IPPROTO_ESP`.
274    const ESP: Self = Self(50);
275
276    /// Corresponds to `IPPROTO_AH`.
277    const AH: Self = Self(51);
278
279    /// Corresponds to `IPPROTO_ICMPV6`.
280    const ICMPV6: Self = Self(58);
281
282    /// Corresponds to `IPPROTO_NONE`.
283    const NONE: Self = Self(59);
284
285    /// Corresponds to `IPPROTO_DSTOPTS`.
286    const DSTOPTS: Self = Self(60);
287
288    /// Corresponds to `IPPROTO_ND`.
289    const ND: Self = Self(77);
290
291    /// Corresponds to `IPPROTO_ICLFXBM`.
292    const ICLFXBM: Self = Self(78);
293
294    /// Corresponds to `IPPROTO_PIM`.
295    const PIM: Self = Self(103);
296
297    /// Corresponds to `IPPROTO_PGM`.
298    const PGM: Self = Self(113);
299
300    /// Corresponds to `IPPROTO_L2TP`.
301    const L2TP: Self = Self(115);
302
303    /// Corresponds to `IPPROTO_SCTP`.
304    const SCTP: Self = Self(132);
305
306    /// Corresponds to `IPPROTO_RAW`.
307    const RAW: Self = Self(255);
308}
309
310impl std::fmt::Debug for IpProtocol {
311    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
312        let name = match *self {
313            Self::HOPOPTS => "IPPROTO_HOPOPTS",
314            Self::ICMP => "IPPROTO_ICMP",
315            Self::IGMP => "IPPROTO_IGMP",
316            Self::GGP => "IPPROTO_GGP",
317            Self::IPV4 => "IPPROTO_IPV4",
318            Self::ST => "IPPROTO_ST",
319            Self::TCP => "IPPROTO_TCP",
320            Self::CBT => "IPPROTO_CBT",
321            Self::EGP => "IPPROTO_EGP",
322            Self::IGP => "IPPROTO_IGP",
323            Self::PUP => "IPPROTO_PUP",
324            Self::UDP => "IPPROTO_UDP",
325            Self::IDP => "IPPROTO_IDP",
326            Self::RDP => "IPPROTO_RDP",
327            Self::IPV6 => "IPPROTO_IPV6",
328            Self::ROUTING => "IPPROTO_ROUTING",
329            Self::FRAGMENT => "IPPROTO_FRAGMENT",
330            Self::ESP => "IPPROTO_ESP",
331            Self::AH => "IPPROTO_AH",
332            Self::ICMPV6 => "IPPROTO_ICMPV6",
333            Self::NONE => "IPPROTO_NONE",
334            Self::DSTOPTS => "IPPROTO_DSTOPTS",
335            Self::ND => "IPPROTO_ND",
336            Self::ICLFXBM => "IPPROTO_ICLFXBM",
337            Self::PIM => "IPPROTO_PIM",
338            Self::PGM => "IPPROTO_PGM",
339            Self::L2TP => "IPPROTO_L2TP",
340            Self::SCTP => "IPPROTO_SCTP",
341            Self::RAW => "IPPROTO_RAW",
342            _ => return self.0.fmt(f),
343        };
344        f.write_str(name)
345    }
346}
347
348/// Demonstrates how to log network connection attempts and flows.
349pub fn KfdClassify<Driver>(
350    vmi: &VmiContext<WindowsOs<Driver>>,
351) -> Result<Action<<WindowsOs<Driver> as VmiOs>::Architecture>, VmiError>
352where
353    Driver: VmiRead,
354    Driver::Architecture: ArchAdapter<Driver>,
355{
356    //
357    // PVOID
358    // NTAPI
359    // KfdClassify (
360    //     _In_ UINT16 layerId,
361    //     _In_ const FWPS_INCOMING_VALUES* inFixedValues,
362    //     _In_ const FWPS_INCOMING_METADATA_VALUES* inContext,
363    //     _In_ PVOID packet,
364    //     _In_ const FWPP_SHIM_PROVIDER_CONTEXT* shimProvContext,
365    //     _Inout_ FWPS_CLASSIFY_OUT* classifyOut
366    //     );
367    //
368
369    let layerId = FwpsLayer(vmi.os().function_argument(0)? as u16);
370    let inFixedValues = Va(vmi.os().function_argument(1)?);
371    let inContext = Va(vmi.os().function_argument(2)?);
372
373    let (
374        protocol_index,
375        local_address_index,
376        local_port_index,
377        remote_address_index,
378        remote_port_index,
379    ) = match layerId.network_5tuple_indexes() {
380        Some(indexes) => indexes,
381        None => return Ok(Action::default()),
382    };
383
384    let incoming_values = vmi.read_struct::<FWPS_INCOMING_VALUES0>(inFixedValues)?;
385    let incoming = Va(incoming_values.incomingValue);
386
387    const SIZEOF_VALUE: u64 = size_of::<FWPS_INCOMING_VALUE0>() as u64;
388
389    //
390    // Protocol.
391    //
392
393    let protocol =
394        vmi.read_struct::<FWPS_INCOMING_VALUE0>(incoming + protocol_index * SIZEOF_VALUE)?;
395
396    if protocol.value.ty != FwpDataType::UINT8 {
397        tracing::debug!(
398            protocol_type = ?protocol.value.ty,
399            expected = ?FwpDataType::UINT8,
400            "unexpected protocol type"
401        );
402        return Ok(Action::default());
403    }
404
405    //
406    // Local Address.
407    //
408
409    let local_address =
410        vmi.read_struct::<FWPS_INCOMING_VALUE0>(incoming + local_address_index * SIZEOF_VALUE)?;
411
412    if local_address.value.ty != FwpDataType::UINT32 {
413        tracing::debug!(
414            local_address_type = ?local_address.value.ty,
415            expected = ?FwpDataType::UINT32,
416            "unexpected local address type"
417        );
418        return Ok(Action::default());
419    }
420
421    //
422    // Local Port.
423    //
424
425    let local_port =
426        vmi.read_struct::<FWPS_INCOMING_VALUE0>(incoming + local_port_index * SIZEOF_VALUE)?;
427
428    if local_port.value.ty != FwpDataType::UINT16 {
429        tracing::debug!(
430            local_port_type = ?local_port.value.ty,
431            expected = ?FwpDataType::UINT16,
432            "unexpected local port type"
433        );
434        return Ok(Action::default());
435    }
436
437    //
438    // Remote Address.
439    //
440
441    let remote_address =
442        vmi.read_struct::<FWPS_INCOMING_VALUE0>(incoming + remote_address_index * SIZEOF_VALUE)?;
443
444    if remote_address.value.ty != FwpDataType::UINT32 {
445        tracing::debug!(
446            remote_address_type = ?remote_address.value.ty,
447            expected = ?FwpDataType::UINT32,
448            "unexpected remote address type"
449        );
450        return Ok(Action::default());
451    }
452
453    //
454    // Remote Port.
455    //
456
457    let remote_port =
458        vmi.read_struct::<FWPS_INCOMING_VALUE0>(incoming + remote_port_index * SIZEOF_VALUE)?;
459
460    if remote_port.value.ty != FwpDataType::UINT16 {
461        tracing::debug!(
462            remote_port_type = ?remote_port.value.ty,
463            expected = ?FwpDataType::UINT16,
464            "unexpected remote port type"
465        );
466        return Ok(Action::default());
467    }
468
469    let protocol = IpProtocol(protocol.value.data as u8);
470    let local_address = local_address.value.data as u32;
471    let local_port = local_port.value.data as u16;
472    let remote_address = remote_address.value.data as u32;
473    let remote_port = remote_port.value.data as u16;
474
475    let local_ip = IpAddr::from(local_address.to_be_bytes());
476    let remote_ip = IpAddr::from(remote_address.to_be_bytes());
477
478    // Fetch the most valuable information that can't be obtained
479    // from the pcap: the process ID that initiated the connection.
480    let context = vmi.read_struct::<FWPS_INCOMING_METADATA_VALUES0>(inContext)?;
481    let metadata_values = FwpsMetadataFields::from_bits_retain(context.currentMetadataValues);
482    let pid = if metadata_values.contains(FwpsMetadataFields::PROCESS_ID) {
483        Some(context.processId)
484    }
485    else {
486        None
487    };
488
489    tracing::info!(
490        ?protocol,
491        %local_ip,
492        local_port,
493        %remote_ip,
494        remote_port,
495        pid,
496    );
497
498    Ok(Action::default())
499}
500
501/// Demonstrates changing the behavior of a function.
502///
503/// In many places, the `tcpip.sys` driver does roughly the following:
504///
505/// ```ignore
506/// if (KfdIsLayerEmpty(layerId)) {
507///     // Early exit - no active filter for this layer.
508///     return STATUS_SUCCESS;
509/// }
510/// ...
511/// KfdClassify(layerId, ...);
512/// ```
513///
514/// Problem is, there's no guarantee that any filter will be active
515/// for the layers we're interested in.
516///
517/// By hooking `KfdIsLayerEmpty` and returning `FALSE` for the layers
518/// we're interested in, we can ensure that `KfdClassify` will get called.
519///
520/// Note that calling `KfdClassify` without an active filter is not a problem.
521/// The function will simply realize that there's no filter and return without
522/// doing anything. But most importantly, we intercept its execution with its
523/// valuable arguments.
524pub fn KfdIsLayerEmpty<Driver>(
525    vmi: &VmiContext<WindowsOs<Driver>>,
526) -> Result<Action<<WindowsOs<Driver> as VmiOs>::Architecture>, VmiError>
527where
528    Driver: VmiRead,
529    Driver::Architecture: ArchAdapter<Driver>,
530{
531    //
532    // BOOLEAN
533    // NTAPI
534    // KfdIsLayerEmpty (
535    //     _In_ UINT16 layerId
536    //     );
537    //
538
539    let layerId = FwpsLayer(vmi.os().function_argument(0)? as u16);
540
541    if !matches!(
542        layerId,
543        FwpsLayer::ALE_AUTH_CONNECT_V4
544            | FwpsLayer::ALE_AUTH_CONNECT_V6
545            | FwpsLayer::ALE_FLOW_ESTABLISHED_V4
546            | FwpsLayer::ALE_FLOW_ESTABLISHED_V6
547    ) {
548        tracing::trace!(?layerId, "passing through");
549        return Ok(Action::default());
550    }
551
552    tracing::trace!(?layerId, "overriding");
553
554    let return_address = vmi.return_address()?;
555    let stack_pointer = vmi.registers().stack_pointer();
556    let address_width = vmi.registers().address_width() as u64;
557
558    let mut registers = vmi.registers().gp_registers();
559    registers.set_result(0); // Return FALSE
560    registers.set_instruction_pointer(return_address.into());
561    registers.set_stack_pointer(stack_pointer + address_width);
562
563    Ok(Action::Response(
564        VmiEventResponse::default().with_registers(registers),
565    ))
566}