Skip to content

Commit

Permalink
Merge pull request #9 from jorgeaduran/master
Browse files Browse the repository at this point in the history
Major Enhancements in Feature Detection and JSON Output Management
  • Loading branch information
marirs authored Feb 17, 2024
2 parents 2ac0eb2 + dc46bb8 commit b6bb90c
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 157 deletions.
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "capa"
version = "0.3.12"
description = "File capability extractor."
authors = ["Marirs <[email protected]>", "Andrey Mnatsakanov <[email protected]>", "Jorge Alejandro Durán Royo"]
authors = ["Marirs <[email protected]>", "Andrey Mnatsakanov <[email protected]>", "Jorge Alejandro Durán Royo<[email protected]>"]
keywords = ["capa", "fce", "capability", "file"]
readme = "README.md"
license-file = "LICENSE"
Expand All @@ -19,7 +19,7 @@ petgraph = "0.6.2"
regex = "1.5"
fancy-regex = { git = "https://github.com/mnaza/fancy-regex.git" }
serde = { version = "1", features = ["derive"] }
smda = "0.2.6"
smda = "0.2.7"
thiserror = "1"
walkdir = "2.3.2"
yaml-rust = "0.4.5"
Expand Down
21 changes: 12 additions & 9 deletions examples/capa_cli.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use std::fs;
use capa::FileCapabilities;
use clap::Parser;
use prettytable::{color, format::Alignment, Attr, Cell, Row, Table};
use serde_json::{to_value, Map, Value};
use std::fs;
use std::time::Instant;

#[derive(Parser)]
#[clap(
author,
version,
about,
long_about = "Find Capabilities of a given file!"
author,
version,
about,
long_about = "Find Capabilities of a given file!"
)]
struct CliOpts {
/// File to analyse
Expand All @@ -20,14 +20,17 @@ struct CliOpts {
#[clap(short = 'r', long, value_name = "CAPA_RULES")]
rules_path: String,
/// verbose output
#[clap(long)]
#[clap(short = 'v', long, default_value = "false")]
verbose: bool,
/// file path to save the result in json format
#[clap(short = 'o', long, value_name = "JSON_PATH")]
output: Option<String>,
/// map_features
#[clap(short = 'm', long, default_value = "false")]
#[clap(short = 'm', long, value_name = "MAP_FEATURES", default_value = "false")]
map_features: bool,
/// filter map_features
#[clap(short = 'f', long, value_name = "FILTER_MAP_FEATURES")]
filter_map_features: Option<String>,
}

fn main() {
Expand All @@ -41,7 +44,7 @@ fn main() {
let start = Instant::now();
match FileCapabilities::from_file(&filename, &rules_path, true, true, &|_s| {}, map_features) {
Err(e) => println!("{:?}", e),
Ok(s) => {
Ok(mut s) => {
match to_value(&s) {
Err(e) => println!("serde_json_error: {}", e),
Ok(data) => {
Expand Down Expand Up @@ -115,7 +118,7 @@ fn main() {
}
}
if let Some(json_path) = json_path {
let json = s.serialize_file_capabilities().unwrap();
let json = s.serialize_file_capabilities(cli.filter_map_features).unwrap();
fs::write(json_path.clone(), json).expect("Unable to write file");
println!("Analysis result saved in JSON format at: {}", json_path);
}
Expand Down
2 changes: 2 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,6 @@ pub enum Error {
InvalidToken(String),
#[error("not implemented")]
NoiImplementedError,
#[error("Buffer overflow error")]
BufferOverflowError,
}
118 changes: 67 additions & 51 deletions src/extractor/dnfile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ impl super::Extractor for Extractor {
OpCodeValue::Jmp,
OpCodeValue::Newobj,
]
.contains(&insn.opcode.value)
.contains(&insn.opcode.value)
{
continue;
}
Expand Down Expand Up @@ -210,15 +210,15 @@ impl super::Extractor for Extractor {
) -> Result<Vec<(crate::rules::features::Feature, u64)>> {
let f: &Function = f.as_any().downcast_ref::<Function>().unwrap();
Ok([
self.extract_function_call_to_features(f)?,
self.extract_function_call_from_features(f)?,
self.extract_recurcive_call_features(f)?,
self.extract_function_call_to_features(&f)?,
self.extract_function_call_from_features(&f)?,
self.extract_recurcive_call_features(&f)?,
]
.into_iter()
.fold(Vec::new(), |mut acc, f| {
acc.extend(f);
acc
}))
.into_iter()
.fold(Vec::new(), |mut acc, f| {
acc.extend(f);
acc
}))
}

fn get_basic_blocks(
Expand Down Expand Up @@ -315,12 +315,15 @@ impl Extractor {
));
//split :: get last part and add stringFeature
let ss = imp.split("::").collect::<Vec<&str>>();
res.push((
crate::rules::features::Feature::String(
crate::rules::features::StringFeature::new(ss[1], "")?,
),
*token,
));
let trimmed = ss[1].trim();
if !trimmed.is_empty() {
res.push((
crate::rules::features::Feature::String(
crate::rules::features::StringFeature::new(trimmed, "")?,
),
*token,
));
}
} else {
let ss = imp.split('.').collect::<Vec<&str>>();
for symbol_variant in crate::extractor::smda::generate_symbols(
Expand Down Expand Up @@ -373,12 +376,14 @@ impl Extractor {
))
}
for ns in namespaces {
res.push((
crate::rules::features::Feature::Namespace(
crate::rules::features::NamespaceFeature::new(&ns, "")?,
),
0,
))
if !ns.is_empty() {
res.push((
crate::rules::features::Feature::Namespace(
crate::rules::features::NamespaceFeature::new(&ns, "")?,
),
0,
))
}
}
Ok(res)
}
Expand Down Expand Up @@ -922,12 +927,15 @@ impl Extractor {
match self.pe.net()?.get_us(t.rid()) {
Err(_) => Ok(res),
Ok(s) => {
res.push((
crate::rules::features::Feature::String(
crate::rules::features::StringFeature::new(&s, "")?,
),
insn.offset as u64,
));
let trimmed = s.trim();
if !trimmed.is_empty() {
res.push((
crate::rules::features::Feature::String(
crate::rules::features::StringFeature::new(&trimmed, "")?,
),
insn.offset as u64,
));
}
Ok(res)
}
}
Expand Down Expand Up @@ -966,40 +974,48 @@ impl Extractor {
)?;
if let Some(s) = operand.downcast_ref::<MemberRef>() {
if let Ok(ss) = &self.pe.net()?.resolve_coded_index::<TypeDef>(&s.class) {
res.push((
crate::rules::features::Feature::Namespace(
crate::rules::features::NamespaceFeature::new(&ss.type_namespace, "")?,
),
insn.offset as u64,
))
if !ss.type_namespace.is_empty() {
res.push((
crate::rules::features::Feature::Namespace(
crate::rules::features::NamespaceFeature::new(&ss.type_namespace, "")?,
),
insn.offset as u64,
))
}
} else if let Ok(ss) = &self.pe.net()?.resolve_coded_index::<TypeRef>(&s.class) {
res.push((
crate::rules::features::Feature::Namespace(
crate::rules::features::NamespaceFeature::new(&ss.type_namespace, "")?,
),
insn.offset as u64,
));
if !ss.type_namespace.is_empty() {
res.push((
crate::rules::features::Feature::Namespace(
crate::rules::features::NamespaceFeature::new(&ss.type_namespace, "")?,
),
insn.offset as u64,
));
}
}
} else if operand.downcast_ref::<MethodDef>().is_some() {
if let Some(Callee::Method(dm)) = self.get_callee(insn.operand.value()? as u64)? {
res.push((
crate::rules::features::Feature::Namespace(
crate::rules::features::NamespaceFeature::new(&dm.namespace, "")?,
),
insn.offset as u64,
));
if !dm.namespace.is_empty() {
res.push((
crate::rules::features::Feature::Namespace(
crate::rules::features::NamespaceFeature::new(&dm.namespace, "")?,
),
insn.offset as u64,
));
}
}
} else if operand.downcast_ref::<Field>().is_some() {
let fields_lock = self.get_fields()?.read();

if let Some(fields) = &*fields_lock {
if let Some(field) = fields.clone().get(&(insn.operand.value()? as u64)) {
res.push((
crate::rules::features::Feature::Namespace(
crate::rules::features::NamespaceFeature::new(&field.namespace, "")?,
),
insn.offset as u64,
));
if !field.namespace.is_empty() {
res.push((
crate::rules::features::Feature::Namespace(
crate::rules::features::NamespaceFeature::new(&field.namespace, "")?,
),
insn.offset as u64,
));
}
}
}
}
Expand Down
Loading

0 comments on commit b6bb90c

Please sign in to comment.