Skip to content

Commit

Permalink
feat: implement import_rva function in PE module
Browse files Browse the repository at this point in the history
  • Loading branch information
plusvic committed Nov 21, 2023
1 parent 90443e9 commit 207f8cc
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 2 deletions.
83 changes: 81 additions & 2 deletions yara-x/src/modules/pe/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ fn standard_imports_ordinal(
/// Returns the number of imported functions where the function's name matches
/// `func_name` and the DLL name matches `dll_name`.
///
/// Both `dll_name` and `func_name` are case sensitive unless you use the "/i"
/// Both `dll_name` and `func_name` are case-sensitive unless you use the "/i"
/// modifier in the regexp, as shown in the example below.
#[module_export(name = "imports")]
fn standard_imports_regexp(
Expand Down Expand Up @@ -354,7 +354,7 @@ fn imports_ordinal(
/// Returns the number of imported functions where the function's name matches
/// `func_name` and the DLL name matches `dll_name`.
///
/// Both `dll_name` and `func_name` are case sensitive unless you use the "/i"
/// Both `dll_name` and `func_name` are case-sensitive unless you use the "/i"
/// modifier in the regexp, as shown in the example below. See [`imports_dll`]
/// for details about the `import_flags` argument.
#[module_export(name = "imports")]
Expand All @@ -372,6 +372,39 @@ fn imports_regexp(
)
}

/// Returns the RVA of a function imported with `ordinal` from `dll_name`.
///
/// `dll_name` is case-insensitive.
#[module_export(name = "import_rva")]
fn import_rva_func(
ctx: &ScanContext,
dll_name: RuntimeString,
func_name: RuntimeString,
) -> Option<i64> {
import_rva_impl(
ctx,
MatchCriteria::Name(dll_name.as_bstr(ctx)),
MatchCriteria::Name(func_name.as_bstr(ctx)),
)
}

/// Returns the RVA of an imported functions where the DLL name matches
/// `dll_name` and the
///
/// Both `dll_name` and `func_name` are case-insensitive.
#[module_export(name = "import_rva")]
fn import_rva_ordinal(
ctx: &ScanContext,
dll_name: RuntimeString,
ordinal: i64,
) -> Option<i64> {
import_rva_impl(
ctx,
MatchCriteria::Name(dll_name.as_bstr(ctx)),
MatchCriteria::Ordinal(ordinal),
)
}

/// Returns true if the PE file exports a function with the given name.
#[module_export(name = "exports")]
fn exports_func(ctx: &ScanContext, func_name: RuntimeString) -> Option<bool> {
Expand Down Expand Up @@ -501,6 +534,52 @@ fn imports_impl(
total.try_into().ok()
}

fn import_rva_impl(
ctx: &ScanContext,
expected_dll_name: MatchCriteria,
expected_func_name: MatchCriteria,
) -> Option<i64> {
let pe = ctx.module_output::<PE>()?;

for import in pe.import_details.iter() {
let matches = match expected_dll_name {
MatchCriteria::Any => true,
MatchCriteria::Name(expected_name) => {
import.library_name.as_ref().is_some_and(|name| {
expected_name.eq_ignore_ascii_case(name.as_bytes())
})
}
MatchCriteria::Regexp(_) => unreachable!(),
MatchCriteria::Ordinal(_) => unreachable!(),
};

if matches {
for func in import.functions.iter() {
match expected_func_name {
MatchCriteria::Any => return func.rva.map(|r| r as i64),
MatchCriteria::Name(expected_name) => {
if func.name.as_ref().is_some_and(|name| {
expected_name.eq_ignore_ascii_case(name.as_bytes())
}) {
return func.rva.map(|r| r as i64);
}
}
MatchCriteria::Ordinal(expected_ordinal) => {
if func.ordinal.is_some_and(|ordinal| {
ordinal as i64 == expected_ordinal
}) {
return func.rva.map(|r| r as i64);
}
}
MatchCriteria::Regexp(_) => unreachable!(),
}
}
}
}

None
}

fn exports_impl(
ctx: &ScanContext,
expected_func_name: MatchCriteria,
Expand Down
29 changes: 29 additions & 0 deletions yara-x/src/modules/pe/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,35 @@ fn imports() {
);
}

#[test]
fn import_rva() {
let pe = create_binary_from_zipped_ihex(
"src/modules/pe/tests/testdata/0ba6042247d90a187919dd88dc2d55cd882c80e5afc511c4f7b2e0e193968f7f.in.zip",
);

rule_true!(
r#"
import "pe"
rule test {
condition:
pe.import_rva("ws2_32.dll", 20) == 38116
}
"#,
&pe
);

rule_true!(
r#"
import "pe"
rule test {
condition:
pe.import_rva("kernel32.dll", "VirtualProtect") == 38072
}
"#,
&pe
);
}

#[test]
fn exports() {
let pe = create_binary_from_zipped_ihex(
Expand Down

0 comments on commit 207f8cc

Please sign in to comment.