Skip to main content

ctree_dump/
ctree_dump.rs

1//! Auto-analyzes a binary and dumps the ctree of matching functions, IDA's own pseudocode
2//! beside our owned-tree render.
3//!
4//! A development lens for seeing the real node shapes the decompiler produces (e.g. how a
5//! constructor installs a vtable).
6//!
7//!   `cargo run -p idakit --example ctree_dump -- <binary> [name-substring]`
8
9use idakit::prelude::*;
10
11/// Recursively prints each node's kind, indented by depth.
12///
13/// The structural ground truth behind the render.
14fn dump(tree: &Ctree, node: idakit::decompiler::ctree::NodeRef, depth: usize) {
15    use idakit::decompiler::ctree::NodeRef;
16    let pad = "  ".repeat(depth);
17    let label = match node {
18        NodeRef::Expression(id) => format!("{:?}", tree.kind(id)),
19        NodeRef::Statement(id) => format!("{:?}", tree.statement_kind(id)),
20    };
21    let line: String = label
22        .lines()
23        .next()
24        .unwrap_or("")
25        .chars()
26        .take(90)
27        .collect();
28    println!("{pad}{line}");
29    for c in tree.children(node) {
30        dump(tree, c, depth + 1);
31    }
32}
33
34fn main() -> Result<(), Box<dyn std::error::Error>> {
35    let mut args = std::env::args().skip(1);
36    let bin = args
37        .next()
38        .expect("usage: ctree_dump <binary> [name-substring]");
39    let filter = args.next().unwrap_or_default();
40
41    Ida::run(move |ida| -> Result<(), Error> {
42        ida.call(move |idb| -> Result<(), Error> {
43            idb.open(&bin).run_auto(true).call()?;
44
45            let mut matched = 0;
46            let eas: Vec<_> = idb.functions().map(|f| (f.address(), f.name())).collect();
47            for (address, name) in eas {
48                let name = String::from(name);
49                if !filter.is_empty() && !name.contains(&filter) {
50                    continue;
51                }
52                let Ok(cf) = idb.decompile(address) else {
53                    continue;
54                };
55                let Ok(tree) = cf.ctree() else { continue };
56                matched += 1;
57                println!("\n========== {name}  @ {address:#x} ==========");
58                if let Some(pc) = cf.pseudocode() {
59                    println!("--- IDA ---\n{pc}");
60                }
61                println!("--- idakit ---\n{}", tree.to_pseudocode());
62                println!("--- structure ---");
63                dump(
64                    &tree,
65                    idakit::decompiler::ctree::NodeRef::Statement(tree.root()),
66                    0,
67                );
68            }
69            println!("\n[ctree_dump] {matched} function(s) matched filter {filter:?}");
70
71            idb.close(false);
72            Ok(())
73        })?
74    })??;
75
76    Ok(())
77}