Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
c4fb537
Function attribute data types fom i64 to u64
valbucci Jun 11, 2025
6e5ae8d
Added option for custom function file naming
valbucci Jun 11, 2025
887c7df
Centralised function name sanitisation (extract+generate)
valbucci Jun 12, 2025
3e75913
Changed log level for extraction skipping from warn to debug
valbucci Jul 9, 2025
2bc7327
Fix tmp path bug in 6e5ae8d; Use output path over job suffix in extra…
valbucci Jul 9, 2025
9d36bf7
Added error logging and option to skip extraction if previously aborted
valbucci Jul 10, 2025
c91b051
Added --log-level option to set verbosity
valbucci Jul 10, 2025
03e4ac7
Fixed mix up between log lines and progress bar
valbucci Jul 10, 2025
63369f6
Bumped indicatif to 0.17.11
valbucci Jul 10, 2025
ebba989
Added 'addr' alias for AFIJFunctionInfo 'offset'
valbucci Sep 11, 2025
21eb2df
Bumped indicatif to 0.18.0
valbucci Sep 11, 2025
9dfd5e5
Added 'minaddr' and 'maxaddr' aliases
valbucci Sep 11, 2025
1e087cf
Changed offset data types to u64 and added addr alias
valbucci Oct 9, 2025
3f9e509
Implemented JSON stream function for @@f commands
valbucci Oct 9, 2025
b64edf7
Cargo fmt
valbucci Oct 9, 2025
7d27786
Enhanced bytes extraction with mask (p8fmj)
valbucci Oct 9, 2025
3148d3f
Added bytes-masked extraction and restored function-separated bin files
valbucci Oct 11, 2025
097faa9
Added func-cfg mode: same as CFG, but split into multiple files
valbucci Oct 12, 2025
d985ecc
Function-wise error handling in bytes extraction
valbucci Oct 13, 2025
45ef518
Merge branch 'function-bytes' into dev
valbucci Oct 13, 2025
1b2f811
Fixed directory naming bug for bytes-masked mode
valbucci Oct 13, 2025
e3026d0
Merge branch 'function-bytes' into split-cfg-extraction
valbucci Oct 13, 2025
4d3c421
Merge branch 'split-cfg-extraction' into dev
valbucci Oct 13, 2025
05296c1
Skip extract log level changed to info
valbucci Oct 13, 2025
7a848ea
Added more control to radare2 spawn options
valbucci Oct 13, 2025
b34a16d
Improved error handling of bytes mask extraction
valbucci Oct 13, 2025
8f43837
Merge branch 'function-bytes' into dev
valbucci Oct 13, 2025
eba79ee
Refactored bytes and bytes-masked extraction
valbucci Oct 14, 2025
ced68cf
Added count of successful function bytes extraction
valbucci Oct 14, 2025
3fdf3ed
Merge branch 'function-bytes' into dev
valbucci Oct 14, 2025
70fd2e5
Cargo fmt
valbucci Oct 14, 2025
f3f494d
Better handling for broken R2Pipe
valbucci Oct 16, 2025
b750aa8
Removed go_to_address for improved efficiency
valbucci Oct 16, 2025
d48bfeb
Lazy initialisation of functions list
valbucci Oct 16, 2025
cd7fb7e
Function index for func-cfg and bytes extract modes
valbucci Oct 17, 2025
d90a25e
Filter functions based on minimum number of blocks
valbucci Oct 17, 2025
f995d31
Tidier bytes-masked extraction and keep_raw_bytes option
valbucci Oct 17, 2025
3ab5104
Fixup mismatched function bytes mask
valbucci Oct 17, 2025
dc96614
Changed minbound from i64 to u64
valbucci Oct 18, 2025
924c046
Removed wasteful aflj parsing
valbucci Oct 18, 2025
4ca6aa7
Warning instead of error when function list is empty
valbucci Oct 18, 2025
6cc92eb
Fixed bug related to error log file names
valbucci Oct 18, 2025
de76fb8
Resume function-level data extraction
valbucci Oct 20, 2025
6589761
Process FuncInfo extraction first
valbucci Oct 20, 2025
231ca6c
Reuse data from finfo to populate function list cache
valbucci Oct 20, 2025
7fcd627
Only cache FuncInfo list if other modes need it
valbucci Oct 20, 2025
39d547c
Populate function list cache from existing finfo JSON file
valbucci Oct 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ edition = "2021"
[dependencies]
r2pipe = "0.6.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.60"
serde_json = { version = "1.0.60", features = ["raw_value"] }
clap = { version = "4.0.29", features = ["derive"] }
goblin = { version = "0.6.0", optional = true }
walkdir = "2"
Expand All @@ -19,7 +19,8 @@ tokenizers = "0.13.2"
rust_search = "2.1.0"
tch = {version = "0.10.3", optional = true}
mimalloc = { version = "*", default-features = false }
indicatif = { version = "0.17.3", features = ["rayon"]}
indicatif = { version = "0.18.0", features = ["rayon"]}
indicatif-log-bridge = "0.2.3"
itertools = "0.10.5"
prettytable = "0.10.0"
serde-aux = "4.2.0"
Expand All @@ -32,6 +33,8 @@ hex = { version = "0.4", features = ["alloc"] }
md5 = "0.7.0"
sha1 = "0.10.6"
sha2 = "0.10.8"
csv = "1.3"
once_cell = "1.19"

[dependencies.petgraph]
version = "0.6.2"
Expand Down
45 changes: 24 additions & 21 deletions src/afij.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ use serde_json::Value;
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AFIJFunctionInfo {
#[serde(alias = "addr")]
pub offset: u64,
pub name: String,
pub size: i128,
pub size: u64,
#[serde(rename = "is-pure")]
pub is_pure: String,
pub realsz: u64,
Expand All @@ -23,21 +24,23 @@ pub struct AFIJFunctionInfo {
pub nbbs: u64,
#[serde(rename = "is-lineal")]
pub is_lineal: bool,
pub ninstrs: i64,
pub edges: i64,
pub ninstrs: u64,
pub edges: u64,
pub ebbs: u64,
pub signature: String,
#[serde(alias = "minaddr")]
pub minbound: u64,
pub maxbound: i128,
#[serde(alias = "maxaddr")]
pub maxbound: u64,
pub callrefs: Option<Vec<Callref>>,
// TODO: Need to fix this and change to string instead of i64 to get round large random numbers
pub datarefs: Option<Vec<Dataref>>,
pub codexrefs: Option<Vec<Codexref>>,
pub dataxrefs: Option<Vec<i64>>,
pub indegree: Option<i64>,
pub outdegree: Option<i64>,
pub nlocals: Option<i64>,
pub nargs: Option<i64>,
pub dataxrefs: Option<Vec<i64>>, // TODO: Check this data type
pub indegree: Option<u64>,
pub outdegree: Option<u64>,
pub nlocals: Option<u64>,
pub nargs: Option<u64>,
pub bpvars: Option<Vec<Bpvar>>,
// Cannot find a good example of an spvars yet
pub spvars: Option<Vec<Value>>,
Expand Down Expand Up @@ -103,12 +106,12 @@ pub struct Regvar {
#[serde(rename_all = "camelCase")]
pub struct AFIJFeatureSubset {
pub name: String,
pub ninstrs: i64,
pub edges: i64,
pub indegree: i64,
pub outdegree: i64,
pub nlocals: i64,
pub nargs: i64,
pub ninstrs: u64,
pub edges: u64,
pub indegree: u64,
pub outdegree: u64,
pub nlocals: u64,
pub nargs: u64,
pub signature: String,
}

Expand All @@ -130,12 +133,12 @@ impl From<&AFIJFunctionInfo> for AFIJFeatureSubset {
#[derive(Default, Debug, Clone, PartialEq, Hash, Serialize, Deserialize)]
pub struct AFIJFeatureSubsetExtended {
pub name: String,
pub ninstrs: i64,
pub edges: i64,
pub indegree: i64,
pub outdegree: i64,
pub nlocals: i64,
pub nargs: i64,
pub ninstrs: u64,
pub edges: u64,
pub indegree: u64,
pub outdegree: u64,
pub nlocals: u64,
pub nargs: u64,
pub nbbs: u64,
pub avg_ins_bb: OrderedFloat<f32>,
}
Expand Down
29 changes: 7 additions & 22 deletions src/agcj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::networkx::{
CallGraphFuncNameNode, CallGraphFuncWithMetadata, CallGraphTikNibFeatures,
CallGraphTikNibFinfoFeatures, NetworkxDiGraph,
};
use crate::utils::{check_or_create_dir, get_save_file_path};
use crate::utils::{check_or_create_dir, get_save_file_path, sanitize_filename};
use itertools::Itertools;
use petgraph::prelude::Graph;
use serde::{Deserialize, Serialize};
Expand All @@ -14,14 +14,14 @@ use std::path::{Path, PathBuf};
#[serde(rename_all = "camelCase")]
pub struct AGCJFunctionCallGraph {
pub name: String,
pub size: i64,
pub size: u64,
pub imports: Option<Vec<String>>,
}

#[derive(Serialize, Deserialize, Debug)]
pub struct AGCJParsedObjects {
pub edge_property: String,
pub edges: Vec<Vec<i32>>,
pub edges: Vec<Vec<u32>>,
pub node_holes: Vec<String>,
pub nodes: Vec<String>,
}
Expand All @@ -45,10 +45,7 @@ impl AGCJFunctionCallGraph {

let mut function_name = self.name.clone();

// This is a pretty dirty fix and may break things
if function_name.chars().count() > 100 {
function_name = self.name[..75].to_string();
}
function_name = sanitize_filename(&function_name);

let filename = format!("{}-{}.json", function_name, type_suffix);

Expand Down Expand Up @@ -82,11 +79,7 @@ impl AGCJFunctionCallGraph {
check_or_create_dir(&full_output_path);

let mut function_name = self.name.clone();

// This is a pretty dirty fix and may break things
if function_name.chars().count() > 100 {
function_name = self.name[..75].to_string();
}
function_name = sanitize_filename(&function_name);

let filename = format!(
"{}/{}-{}.json",
Expand Down Expand Up @@ -121,11 +114,7 @@ impl AGCJFunctionCallGraph {
check_or_create_dir(&full_output_path);

let mut function_name = self.name.clone();

// This is a pretty dirty fix and may break things
if function_name.chars().count() > 100 {
function_name = self.name[..75].to_string();
}
function_name = sanitize_filename(&function_name);

let filename = format!(
"{}/{}-{}.json",
Expand Down Expand Up @@ -159,11 +148,7 @@ impl AGCJFunctionCallGraph {
check_or_create_dir(&full_output_path);
debug!("Built Path: {:?}", full_output_path);
let mut function_name = self.name.clone();

// This is a pretty dirty fix and may break things
if function_name.chars().count() > 100 {
function_name = self.name[..75].to_string();
}
function_name = sanitize_filename(&function_name);

let filename = format!("{}-{}.json", function_name, type_suffix);
// Normalise string for windows
Expand Down
25 changes: 15 additions & 10 deletions src/agfj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,19 @@ struct EdgePair {
wt: u16,
}

#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct AGFJFunc {
pub name: String,
#[serde(alias = "addr")]
pub offset: u64,
pub size: u64,
pub ninstr: u64,
nargs: u64,
ninstr: u64,
nlocals: u64,
offset: u64,
size: Option<u64>,
stack: u64,
r#type: String,
pub blocks: Vec<ACFJBlock>,
addr_idx: Option<Vec<i64>>,
addr_idx: Option<Vec<u64>>,
pub edge_list: Option<Vec<(u32, u32, u32)>>,
graph: Option<Graph<String, u32>>,
}
Expand Down Expand Up @@ -143,7 +144,7 @@ impl AGFJFunc {
pub fn create_bb_edge_list(&mut self, min_blocks: &u16) {
if self.blocks.len() > <u16 as Into<usize>>::into(*min_blocks) && self.blocks[0].offset != 1
{
let bb_start_addrs: Vec<i64> = self.blocks.iter().map(|x| x.offset).collect::<Vec<_>>();
let bb_start_addrs: Vec<u64> = self.blocks.iter().map(|x| x.offset).collect::<Vec<_>>();
let mut edge_list = Vec::<(u32, u32, u32)>::new();

for bb in &self.blocks {
Expand Down Expand Up @@ -423,7 +424,7 @@ impl AGFJFunc {
}
};

let bb_start_addrs: Vec<i64> =
let bb_start_addrs: Vec<u64> =
self.blocks.iter().map(|x| x.offset).collect::<Vec<_>>();

match feature_type {
Expand Down Expand Up @@ -606,7 +607,7 @@ impl AGFJFunc {
}

// Convert string memory address to hex / string
fn str_to_hex_node_idxs(graph: &mut Graph<String, u32>, addr_idxs: &[i64]) {
fn str_to_hex_node_idxs(graph: &mut Graph<String, u32>, addr_idxs: &[u64]) {
for idx in graph.node_indices() {
let i_idx = idx.index();
let hex = addr_idxs[i_idx];
Expand Down Expand Up @@ -732,6 +733,7 @@ impl From<(&String, Vec<TikNibFeaturesBB>)> for TikNibFunc {
#[cfg(test)]
mod tests {
use crate::bb::FeatureType;
use crate::utils::get_default_addr;
use std::path::PathBuf;

use crate::AGFJFile;
Expand Down Expand Up @@ -792,7 +794,7 @@ mod tests {
assert_eq!(file.functions.as_ref().unwrap()[0][0].ninstr, 78);
assert_eq!(file.functions.as_ref().unwrap()[0][0].nargs, 2);
assert_eq!(file.functions.as_ref().unwrap()[0][0].nlocals, 0);
assert_eq!(file.functions.as_ref().unwrap()[0][0].size, Some(334));
assert_eq!(file.functions.as_ref().unwrap()[0][0].size, 334);
assert_eq!(file.functions.as_ref().unwrap()[0][0].stack, 56);
assert!(!file.functions.as_ref().unwrap()[0][0].blocks.is_empty());

Expand All @@ -811,7 +813,10 @@ mod tests {
assert!(!file.functions.as_ref().unwrap()[0][0].blocks[0]
.ops
.is_empty());
assert_eq!(file.functions.as_ref().unwrap()[0][0].blocks[0].fail, -1);
assert_eq!(
file.functions.as_ref().unwrap()[0][0].blocks[0].fail,
get_default_addr()
);

assert!(file.functions.as_ref().unwrap()[0][0].blocks[0]
.switchop
Expand Down
35 changes: 17 additions & 18 deletions src/bb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::consts::*;
#[cfg(feature = "inference")]
use crate::inference::InferenceJob;
use crate::normalisation::{normalise_disasm_simple, normalise_esil_simple};
use crate::utils::get_default_addr;
use serde::{Deserialize, Serialize};
use serde_aux::prelude::*;
use serde_json::Value;
Expand Down Expand Up @@ -58,8 +59,9 @@ pub enum InstructionMode {

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct SwitchOpCase {
pub jump: i64,
pub offset: i64,
pub jump: u64,
#[serde(alias = "addr")]
pub offset: u64,
#[serde(deserialize_with = "deserialize_string_from_number")]
// This will make it challenging to use downstream but has been
// added because sometimes it is a VERY large int (bigger than i64).
Expand All @@ -74,6 +76,7 @@ pub struct SwitchOp {
pub defval: u16,
pub maxval: u16,
pub minval: u16,
#[serde(alias = "addr")]
pub offset: u64,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
Expand All @@ -86,6 +89,7 @@ pub struct Op {
pub fcn_addr: Option<u64>,
pub fcn_last: Option<u64>,
pub flags: Option<Vec<String>>,
#[serde(alias = "addr")]
pub offset: u64,
pub opcode: Option<String>,
pub ptr: Option<u128>,
Expand All @@ -100,26 +104,21 @@ pub struct Op {
pub val: Option<u64>,
}

// Function to set offset, jump and fail to default values
fn return_minus_one() -> i64 {
-1
}

#[serde_as]
#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ACFJBlock {
#[serde(default = "return_minus_one")]
pub offset: i64,
#[serde(default = "return_minus_one")]
#[serde(default = "get_default_addr", alias = "addr")]
pub offset: u64,
#[serde(default = "get_default_addr")]
#[serde_as(deserialize_as = "DefaultOnError")]
// This has been added to eliminate an error where
// the jump address from x86-64 binaries is larger than
// an i64.
pub jump: i64,
#[serde(default = "return_minus_one")]
pub fail: i64,
pub jump: u64,
#[serde(default = "get_default_addr")]
pub fail: u64,
pub ops: Vec<Op>,
pub size: Option<i64>,
pub size: Option<u64>,
pub switchop: Option<SwitchOp>,
}

Expand Down Expand Up @@ -457,18 +456,18 @@ impl ACFJBlock {
}
num_offspring
}
pub fn get_block_edges(&self, bb_start_addrs: &[i64], edge_list: &mut Vec<(u32, u32, u32)>) {
pub fn get_block_edges(&self, bb_start_addrs: &[u64], edge_list: &mut Vec<(u32, u32, u32)>) {
let offset_idx = bb_start_addrs.iter().position(|&p| p == self.offset);

if let Some(offset_idx) = offset_idx {
if self.jump != -1 {
if self.jump != get_default_addr() {
let jump_idx = bb_start_addrs.iter().position(|&p| p == self.jump);
if let Some(jump_idx) = jump_idx {
edge_list.push((offset_idx as u32, jump_idx as u32, 1));
}
}

if self.fail != -1 {
if self.fail != get_default_addr() {
let fail_idx = bb_start_addrs.iter().position(|&p| p == self.fail);
if let Some(fail_idx) = fail_idx {
edge_list.push((offset_idx as u32, fail_idx as u32, 1));
Expand Down
10 changes: 5 additions & 5 deletions src/combos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,11 @@ impl ComboJob {
#[derive(Default, Hash, PartialEq, Clone, Debug, Deserialize, Serialize)]
pub struct FinfoTiknib {
pub name: String,
pub edges: i64,
pub indegree: i64,
pub outdegree: i64,
pub nlocals: i64,
pub nargs: i64,
pub edges: u64,
pub indegree: u64,
pub outdegree: u64,
pub nlocals: u64,
pub nargs: u64,
pub avg_arithshift: OrderedFloat<f32>,
pub avg_compare: OrderedFloat<f32>,
pub avg_ctransfer: OrderedFloat<f32>,
Expand Down
Loading
Loading