Hugal31 / yara-rust

Rust bindings for VirusTotal/Yara
Apache License 2.0
76 stars 29 forks source link

Exporting yr_object_create() to enable custom structures? #127

Closed msuiche closed 1 year ago

msuiche commented 1 year ago

Currently, if you call define_variable() and it adds variable to the "default" namespace. This means you can't have variables under a custom namespace such as "namespace.parent.children" as an identifier.

Currently, if you want to define a structure begin_struct_array you need to recompile yaralib itself for new module. This would require yrobject.* to be added to build.rs

One of the advantages would be to be able to define structures without having to recompile and relink yaralib itself, which is probably one of the most ridiculous design decision of yaralib so far.

There may be another way to achieve the same goal, I may be missing something. This ticket is kind of an open discussion for that.

msuiche commented 1 year ago

Okay, so just an update everyone. I've tried, I feel like I'm close to it but it may be more complicated than I thought because I'm running into double free malloc errors after adding objects via yr_hash_table_add() and when I don't I run into an assert inside OP_OBJ_LOAD.

yr_hash_table_add() is a bit messy but is usable with (*compiler).objects_table

This was my attempt of creating a vt.metadata.file_name variable and set it to csrss.exe - my understanding is that the root object (that are usually modules), is basically a OBJECT_TYPE_STRUCTURE which you need to create manually. You can't rely on yr_object_from_external_variable() for those as it's not a int/boolean/string/float.

That's what I got so far but didn't have it working. After spending few days on it I realized it makes more sense to wait for @plusvic to come up with something similar inside rust-x directly - and in the meantime to just use add_rules_str() to preload YARA rule files and do some string replacement and stick to single level variables and pass them to the scanner.

Closing this for now since I don't think there is a clean way of doing this, but sharing my notes in case it's useful for anyone.

pub fn compiler_create_struct(
    compiler: *mut yara_sys::YR_COMPILER,
    identifier: &str
) -> Result<(), YaraError> {
    let mut array: *mut YR_OBJECT = ptr::null_mut();
    let mut structure: *mut YR_OBJECT = ptr::null_mut();
    let mut field: *mut YR_OBJECT = ptr::null_mut();

    let mut module_object: *mut YR_OBJECT = ptr::null_mut();

    let identifier = CString::new(identifier).unwrap();
    let module_name = CString::new("vt").unwrap();

    // vt
    println!("Starting value for vt object is {:?}", module_object);
    let result = unsafe {
        yara_sys::yr_object_create(OBJECT_TYPE_STRUCTURE as i8, module_name.as_ptr() as *const i8, ptr::null_mut(), &mut module_object)
    };
    println!("Result for vt: {:?} - and object is {:?}", result, module_object);
    //yara_sys::Error::from_code(result).map(|()| module_object).map_err(Into::into);

    unsafe {
        yara_sys::yr_object_set_canary(module_object, 0x0eadbeef as i32);
    };

    let mut obj: *mut c_void = module_object as *mut c_void;
    unsafe {
        yara_sys::yr_hash_table_add((*compiler).objects_table, module_name.as_ptr(), ptr::null_mut(), obj);
    };

    println!("obj {:?} should be equal to {:?}", obj, module_object);

    // vt.metadata
    /*
    let result = unsafe {
        yara_sys::yr_object_create(OBJECT_TYPE_ARRAY as i8, identifier.as_ptr(), module_object, &mut array)
    };
    println!("Result for {:?} (OBJECT_TYPE_ARRAY): {:?} - and object is {:?}", identifier, result, array);
    // yara_sys::Error::from_code(result).map(|()| array).map_err(Into::into);
    */
    let result = unsafe {
        let c_var = CString::new("metadata").unwrap();
        yara_sys::yr_object_create(OBJECT_TYPE_STRUCTURE as i8, c_var.as_ptr(), module_object, &mut structure)
    };
    println!("Result for {:?} (OBJECT_TYPE_STRUCTURE): {:?} - and object is {:?}", "vt.metadata", result, structure);

    /*
    unsafe {
        let c_var = CString::new("metadata").unwrap();
        yara_sys::yr_hash_table_add((*compiler).objects_table, identifier.as_ptr(), ptr::null_mut(), structure as *mut c_void);
    };
    // yara_sys::Error::from_code(result).map(|()| structure).map_err(Into::into)
    */

    // mf.metadata.file_name

    let c_var_name = CString::new("file_name").unwrap();
    let result = unsafe {
        yara_sys::yr_object_create(OBJECT_TYPE_STRING as i8, c_var_name.as_ptr(), structure, &mut field)
    };
    println!("Result for {:?} (OBJECT_TYPE_STRING): {:?} - and object is {:?}", "vt.metadata.file_name", result, field);

    /*
    unsafe {
        let c_var = CString::new("metadata.file_name").unwrap();
        yara_sys::yr_hash_table_add((*compiler).objects_table, c_var.as_ptr(), ptr::null_mut(), field as *mut c_void);
    };*/

    unsafe {
        let c_var_name = CString::new("metadata.file_name").unwrap();
        let c_var_value = CString::new("csrss.exe").unwrap();
        yara_sys::yr_object_set_string(c_var_value.as_ptr(), 9, module_object, c_var_name.as_ptr());
    };
    // compiler_define_str_variable(compiler, "metadata.file_name", "csrss.exe")?;

    Ok(())
}
msuiche commented 1 year ago

Although compiler.define_variable() does not allow you to create a declare_string_array like and only a declare_string. I may be back to square one haha