How to properly use NmAddFilter and NmAddField functions when writing an app using NmApi?

heavenly uwu 1 Reputation point
2021-09-12T14:53:47.78+00:00

I'm trying to make an executable which can use the functionality provided by Network Monitor 3.4 API. So far I've managed to successfully record traffic, but now I'm attempting to preform analysis on the live data before it will be recorded. I try to use examples provided in the in-app documentation but its not always possible due to language difference (I'm writing mine in rust).

Although the binding seem to work fine, and I guess It's more a question of me misinterpreting the function call or something...

below is the stripped down version of my code:

#![allow(non_camel_case_types, non_snake_case)]

use cpp_xport::core_funcs::NMAPI::{NmCreateCaptureFile, NmOpenCaptureEngine, NmConfigAdapter, NmStopCapture, NmCloseHandle, NmAddFrame, NmStartCapture, NmGetAdapterCount, NmLoadNplParser, NmAddFilter, NmCreateFrameParserConfiguration, NmAddField, NmCreateFrameParser, NmParseFrame, NmEvaluateFilter, NmGetFieldValueString};
use cpp_xport::shared::types::*;
use std::mem::zeroed;
use std::thread::sleep;
use std::time::{Duration, SystemTime};
use cpp_xport::util::WideString::{ToWide, FromWide};
use std::ptr::null_mut;
use cpp_xport::shared::types::NmNplParserLoadingOption::NmAppendRegisteredNplSets;
use cpp_xport::shared::types::NmFrameParserOptimizeOption::NmParserOptimizeNone;
use std::ops::AddAssign;
use cpp_xport::shared::types::BOOL::{FALSE, TRUE};

/// Wi-Fi network interface
pub const adapter_idx: ULONG = 4;


/// struct for passing handles to callback
pub struct HandleBundle {
    pub frames_elapsed: ULONG,
    pub integrity_check: &'static str,  //'// to cancel the highlight, rust lifetime character is misinterpreted for string start or something
    pub cap_file: HANDLE,
    pub frame_parser: HANDLE,
    pub filter_id: ULONG,
    pub field_id: ULONG,
}


/// on frame callback
pub unsafe extern "system" fn _MyFrameIndication(hCaptureEngine: HANDLE, adapterIndex: ULONG, pContext:  LPVOID, hRawFrame: HANDLE) {


    /// getting the HandleBundle
    let handle_bundle: &mut HandleBundle = (pContext as *mut HandleBundle).as_mut().unwrap();
    println!("integrity check, handle bundle says: {}", handle_bundle.integrity_check);


    /// frame preprocessing
    let mut parsed_frame: HANDLE = unsafe {zeroed()};
    let ret = unsafe { NmParseFrame(handle_bundle.frame_parser, hRawFrame, handle_bundle.frames_elapsed,
                                    0, &mut parsed_frame, null_mut())
    };
    handle_bundle.frames_elapsed.add_assign(1); /// incrementing frame count by 1
    println!("NmParseFrame: {}",ret);
    assert_eq!(ret, 0);


    /// using the filter added to evaluate the frame data
    let mut passed: BOOL = FALSE;
    let ret = unsafe {
        NmEvaluateFilter(parsed_frame,
                         handle_bundle.filter_id,
                         &mut passed)
    };
    println!("NmEvaluateFilter: {}",ret);
    assert_eq!(ret, 0);


    /// if the frame data triggers our filter
    if passed == TRUE {


        /// extracting the value from the added field into string buffer
        let mut string_buffer: Vec<u16> = vec![0_u16;256]; /// making a wide string buffer of known length
        let ret = unsafe {
            NmGetFieldValueString(parsed_frame,
                                  handle_bundle.field_id,
                                  256,
                                  string_buffer.as_mut_ptr())
        };
        println!("NmGetFieldValueString: {}", ret);
        assert_eq!(ret, 0);


        /// outputting the value
        println!("field value: {}", FromWide(string_buffer.as_mut_ptr(), 256));

    }


    /// releasing the parsed frame handle
    unsafe {
        NmCloseHandle(parsed_frame)
    };


    /// storing the raw frame in the capture file
    let ret = unsafe {
        NmAddFrame(handle_bundle.cap_file, hRawFrame)
    };
    println!("NmAddFrame: {}",ret);
    assert_eq!(ret, 0);


    /// releasing the raw frame handle
    unsafe {
        NmCloseHandle(hRawFrame)
    };


}


/// build (on init) callback
pub unsafe extern "system" fn _onBuild(_: PVOID, _: ULONG, _: LPCWSTR, _: ULONG) {
    println!("on init called");
}



fn main () {

    /// opening a file to store the capture
    let mut myCapFile: HANDLE = unsafe { zeroed() };
    let mut CapSize: ULONG = unsafe { zeroed() };
    let mut pname: Vec<u16> = ToWide("C:\\Users\\grass\\Desktop\\codes\\Rust\\cpp_xport\\res\\20sec.cap");
    let ret = unsafe { NmCreateCaptureFile(pname.as_mut_ptr(), 2000,
                                           0, &mut myCapFile as PHANDLE, &mut CapSize)
    };
    println!("NmCreateCaptureFile: {}", ret);
    assert_eq!(ret, 0);


    /// initializing the capture engine
    let mut myCaptureEngine: HANDLE = unsafe { zeroed() };
    let ret = unsafe { NmOpenCaptureEngine(&mut myCaptureEngine) };
    println!("NmOpenCaptureEngine: {}", ret);
    assert_eq!(ret, 0);



    // =============================================================================================

    /// loads configuration language parser
    let mut parser: HANDLE = unsafe {zeroed()};
    let ret = unsafe { NmLoadNplParser(null_mut(),
                                       NmAppendRegisteredNplSets,
                                       Some(_onBuild),
                                       null_mut(), &mut parser) };
    println!("NmLoadNplParser: {}",ret);
    assert_eq!(ret, 0);


    /// creates a frame parser config
    let mut parser_config: HANDLE = unsafe {zeroed()};
    let ret = unsafe { NmCreateFrameParserConfiguration(parser,
                                                        Some(_onBuild),
                                                        null_mut(),
                                                        &mut parser_config) };
    println!("NmCreateFrameParserConfiguration: {}",ret);
    assert_eq!(ret, 0);


    /// adds a filtering rule to the frame parser config
    let mut rule = ToWide("http.request.command == \"GET\"");
    let mut filter_id: ULONG = 0;
    let ret = unsafe { NmAddFilter(parser_config,
                                   rule.as_mut_ptr(),
                                   &mut filter_id)
    };
    println!("NmAddFilter: {}",ret);
    assert_eq!(ret, 0);


    /// enables optimized access to the selected fields (http.request.uri in this case)
    let mut field_id: ULONG = 0;
    let ret = unsafe {
        let mut eq_string = ToWide("http.request.uri");
        NmAddField(parser_config,
                   eq_string.as_mut_ptr(),
                   &mut field_id)

    };
    println!("NmAddField: {}", ret);
    assert_eq!(ret, 0);


    /// creates the frame parser
    let mut frame_parser: HANDLE = unsafe {zeroed()};
    let ret = unsafe {
        NmCreateFrameParser(parser_config,
                            &mut frame_parser,
                            NmParserOptimizeNone)
    };
    println!("NmCreateFrameParser: {}", ret);
    assert_eq!(ret, 0);

    // =============================================================================================


    /// getting the number of available network interfaces
    let mut total_ifaces: ULONG = unsafe { zeroed() };
    let ret = unsafe { NmGetAdapterCount(myCaptureEngine, &mut total_ifaces as PULONG) };
    println!("NmGetAdapterCount: {}", ret);
    assert_eq!(ret, 0);
    println!("{} network interfaces are available", total_ifaces);


    /// configuring network monitor to use certain adapter, and assign per frame callback
    /// using pCallerContext to pass a bundle with handles
    let mut handle_bundle: HandleBundle = HandleBundle {
        frames_elapsed: 0,
        integrity_check: "hewwo, warudo",
        cap_file: myCapFile,
        frame_parser: frame_parser,
        filter_id: filter_id,
        field_id: field_id,
    };
    let ret = unsafe { NmConfigAdapter(myCaptureEngine,
                                       adapter_idx,
                                       Some(_MyFrameIndication),
                                       &mut handle_bundle as *mut _ as _,
                                       NmCaptureCallbackExitMode::DiscardRemainFrames)
    };
    println!("configuring adapter: code is {}", ret);
    assert_eq!(ret, 0);


    /// starting capture
    let ret = unsafe {
        NmStartCapture(myCaptureEngine,
                       adapter_idx,
                       NmAdapterCaptureMode::NmLocalOnly)
    };
    println!("starting capture: code is {}",ret);
    assert_eq!(ret, 0);

    // =============================================================================================


    /// wasting time
    let before = SystemTime::now();
    while (SystemTime::now().duration_since(before).unwrap().as_secs() < 25) {

    }

    // =============================================================================================


    println!("stopping capture");

    /// closing the capture
    unsafe {
        NmStopCapture(myCaptureEngine, adapter_idx)
    };

    println!("releasing handles");

    /// releasing the capture engine and file
    unsafe {
        NmCloseHandle(myCaptureEngine);
        NmCloseHandle(myCapFile);
    }

    // =============================================================================================
}

to ensure i have the traffic needed for testing - I've created and ran in parallel the following python script:

import requests
from kivy.app import App
from kivy.uix.label import Label
from kivy.clock import Clock


class MyApp(App):
    def build(self):
        self.dot_counter = 0
        self.lbl = Label(text="hello")
        Clock.schedule_interval(self.update_status_code, 1)
        return self.lbl

    def update_status_code(self, *args):
        self.lbl.text = str(requests.get("https://google.com").status_code)+("."*self.dot_counter)
        self.dot_counter = self.dot_counter + 1 if self.dot_counter < 4 else 0


if __name__ == '__main__':
    MyApp().run()

Still I cant get the NmEvaluateFilter function to output true.

Not Monitored
Not Monitored
Tag not monitored by Microsoft.
40,143 questions
0 comments No comments
{count} votes

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.