Repository: williballenthin/lancelot (language: python)

Issue:
use features to sandbox dependencies
e.g. for the PE loader, don't need a disassembler.


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 851321e0..f5d28a8d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -58,3 +58,4 @@ resources/evaluation/SoK/SoK-windows-testsuite
 resources/evaluation/SoK/.ipynb_checkpoints
 resources/evaluation/SoK/binaries.txt
 .vscode
+*/target/
diff --git a/bin/Cargo.toml b/bin/Cargo.toml
index 850166d1..24599fb6 100644
--- a/bin/Cargo.toml
+++ b/bin/Cargo.toml
@@ -5,6 +5,8 @@ description = "binary analysis tools for x32/x64 PE files"
 authors = ["William Ballenthin <william.ballenthin@fireeye.com>"]
 license = "Apache-2.0"
 edition = "2018"
+homepage = "https://github.com/williballenthin/lancelot"
+repository = "https://github.com/williballenthin/lancelot"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
diff --git a/core/Cargo.toml b/core/Cargo.toml
index 644b5daa..1c219df8 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -5,34 +5,42 @@ license = "Apache-2.0"
 version = "0.6.5"
 authors = ["Willi Ballenthin <wilbal1087@gmail.com>"]
 edition = "2018"
+homepage = "https://github.com/williballenthin/lancelot"
+repository = "https://github.com/williballenthin/lancelot"
 
 [badges]
 travis-ci = { repository = "https://github.com/williballenthin/lancelot", branch = "master" }
 
 [dependencies]
 log = "0.4"
-goblin = "0.4"
-zydis = "3"
+goblin = { version = "0.4", features = ["std", "pe32"], default-features = false }
+zydis = { version = "3", optional = true }
 byteorder = "1"
 bitflags = "1"
 lazy_static = "1"
-fern = "0.6"
-chrono = "0.4"
 anyhow = "1"
 thiserror = "1"
 regex = "1"
 smallvec = "1"
 widestring = "0.4"
 smol_str = "0.1"
-bitvec = "0.20"
+
+# chrono, bitvec, and fern are only needed by tests, but because of the need for a feature named
+# test, they also have to be optional dependencies as well.
+fern = { version = "0.6", optional = true }
+chrono = { version = "0.4", features = ["clock"], default-features = false, optional = true}
+bitvec = { version = "0.20", optional = true }
 
 # https://github.com/myrrlyn/funty/issues/3#issuecomment-778629965
 funty="=1.1.0"
 
-lancelot-flirt = { path = "../flirt", version = "0.6.5" }
+lancelot-flirt = { path = "../flirt", version = "0.6.5", optional = true}
 
 [dev-dependencies]
 criterion = "0.3"
+chrono = { version = "0.4", features = ["clock"], default-features = false }
+bitvec = "0.20"
+fern = "0.6"
 # while there is a newer unicorn-rs release (0.9.1)
 # it doesn't build the underlying `unicorn` library transparently.
 # so we use 0.8 that will do it for us.
@@ -46,8 +54,13 @@ dynasmrt = "1.0.1"
 [[bench]]
 name = "emu"
 harness = false
+required-features = ["emulator"]
 
 [features]
+default = ["emulator", "flirt", "disassembler"]
 # The reason we do this is because doctests don't get cfg(test)
 # See: https://github.com/rust-lang/cargo/issues/4669
-test = []
\ No newline at end of file
+test = ["chrono", "fern", "emulator"]
+flirt = ["lancelot-flirt", "disassembler"]
+emulator = ["bitvec", "zydis"]
+disassembler = ["zydis"]
diff --git a/core/src/analysis/mod.rs b/core/src/analysis/mod.rs
index b5add3bf..91bde806 100644
--- a/core/src/analysis/mod.rs
+++ b/core/src/analysis/mod.rs
@@ -1,5 +1,9 @@
+#[cfg(feature = "disassembler")]
 pub mod call_graph;
+#[cfg(feature = "disassembler")]
 pub mod cfg;
+#[cfg(feature = "disassembler")]
 pub mod dis;
+#[cfg(feature = "flirt")]
 pub mod flirt;
 pub mod pe;
diff --git a/core/src/analysis/pe/mod.rs b/core/src/analysis/pe/mod.rs
index c5cb1096..93149155 100644
--- a/core/src/analysis/pe/mod.rs
+++ b/core/src/analysis/pe/mod.rs
@@ -1,10 +1,9 @@
-use std::collections::{BTreeMap, HashSet};
+use std::collections::BTreeMap;
 
 use anyhow::Result;
 use log::debug;
 
 use crate::{
-    analysis::{cfg, dis},
     aspace::AddressSpace,
     loader::pe::{
         imports,
@@ -13,7 +12,12 @@ use crate::{
     },
     RVA, VA,
 };
+#[cfg(feature = "disassembler")]
+use crate::analysis::{cfg, dis};
+#[cfg(feature = "disassembler")]
+use std::collections::HashSet;
 
+#[cfg(feature = "disassembler")]
 pub mod call_targets;
 pub mod control_flow_guard;
 pub mod entrypoints;
@@ -109,6 +113,7 @@ pub fn get_imports(pe: &PE) -> Result<BTreeMap<VA, Import>> {
     Ok(imports)
 }
 
+#[cfg(feature = "disassembler")]
 pub fn find_thunks(pe: &PE, imports: &BTreeMap<VA, Import>, functions: &HashSet<VA>) -> Result<BTreeMap<VA, Thunk>> {
     let mut thunks: BTreeMap<VA, Thunk> = Default::default();
     let decoder = dis::get_disassembler(&pe.module)?;
@@ -179,6 +184,7 @@ pub fn find_thunks(pe: &PE, imports: &BTreeMap<VA, Import>, functions: &HashSet<
     Ok(thunks)
 }
 
+#[cfg(feature = "disassembler")]
 pub fn find_functions(pe: &PE) -> Result<Vec<Function>> {
     let imports = get_imports(pe)?;
     debug!("imports: found {} imports", imports.len());
@@ -216,6 +222,7 @@ pub fn find_functions(pe: &PE) -> Result<Vec<Function>> {
     Ok(functions)
 }
 
+#[cfg(feature = "disassembler")]
 pub fn find_function_starts(pe: &PE) -> Result<Vec<VA>> {
     Ok(find_functions(pe)?
         .into_iter()
diff --git a/core/src/lib.rs b/core/src/lib.rs
index a03ade11..0b9c78ff 100644
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -9,16 +9,16 @@ pub mod analysis;
 pub mod arch;
 pub mod aspace;
 pub mod config;
+#[cfg(feature = "emulator")]
 pub mod emu;
 pub mod loader;
 pub mod module;
 pub mod pagemap;
 pub mod util;
 
-// helpers that are useful during doctests, tests.
-// TODO: restrict this to tests only
+#[cfg(any(test, doctest, feature = "test"))]
 pub mod rsrc;
-
+#[cfg(all(any(test, doctest, feature = "test"), feature = "emulator"))]
 pub mod test;
 
 pub type VA = u64;
diff --git a/core/src/loader/pe/mod.rs b/core/src/loader/pe/mod.rs
index e2c793e7..f3e2f9d3 100644
--- a/core/src/loader/pe/mod.rs
+++ b/core/src/loader/pe/mod.rs
@@ -90,20 +90,13 @@ impl PE {
 }
 
 fn get_pe(buf: &[u8]) -> Result<goblin::pe::PE> {
-    match goblin::Object::parse(buf)? {
-        goblin::Object::PE(pe) => {
-            if let Some(opt) = pe.header.optional_header {
-                if opt.data_directories.get_clr_runtime_header().is_some() {
-                    return Err(PEError::FormatNotSupported(".NET assembly".to_string()).into());
-                }
-            }
-            Ok(pe)
+    let pe = goblin::pe::PE::parse(buf)?;
+    if let Some(opt) = pe.header.optional_header {
+        if opt.data_directories.get_clr_runtime_header().is_some() {
+            return Err(PEError::FormatNotSupported(".NET assembly".to_string()).into());
         }
-        goblin::Object::Elf(_) => Err(PEError::FormatNotSupported("elf".to_string()).into()),
-        goblin::Object::Archive(_) => Err(PEError::FormatNotSupported("archive".to_string()).into()),
-        goblin::Object::Mach(_) => Err(PEError::FormatNotSupported("macho".to_string()).into()),
-        goblin::Object::Unknown(_) => Err(PEError::FormatNotSupported("unknown".to_string()).into()),
     }
+    Ok(pe)
 }
 
 #[allow(clippy::unnecessary_wraps)]
diff --git a/flirt/Cargo.toml b/flirt/Cargo.toml
index 4f1c0acd..961e0a66 100644
--- a/flirt/Cargo.toml
+++ b/flirt/Cargo.toml
@@ -5,6 +5,8 @@ license = "Apache-2.0"
 version = "0.6.5"
 authors = ["Willi Ballenthin <wilbal1087@gmail.com>"]
 edition = "2018"
+homepage = "https://github.com/williballenthin/lancelot"
+repository = "https://github.com/williballenthin/lancelot"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
@@ -15,14 +17,13 @@ regex = "1.3"
 bitvec = "0.20"
 clap = "2.33"
 fern = "0.5"
-chrono = "0.4"
+chrono = { version = "0.4", features = ["std"], default-features = false }
 better-panic = "0.2"
 inflate = "0.4"
 anyhow = "1"
 thiserror = "1"
 bitflags = "1"
 smallvec = "1"
-libc = "0.2"
 
 # https://github.com/myrrlyn/funty/issues/3#issuecomment-778629965
 funty="=1.1.0"
diff --git a/pylancelot/Cargo.toml b/pylancelot/Cargo.toml
index 66657458..1d7025d4 100644
--- a/pylancelot/Cargo.toml
+++ b/pylancelot/Cargo.toml
@@ -3,6 +3,8 @@ name = "pylancelot"
 version = "0.6.5"
 authors = ["Willi Ballenthin <wilbal1087@gmail.com>"]
 edition = "2018"
+homepage = "https://github.com/williballenthin/lancelot"
+repository = "https://github.com/williballenthin/lancelot"
 
 [lib]
 name = "lancelot"


Test patch:
diff --git a/core/src/test.rs b/core/src/test.rs
index c202bb2d..57480ebc 100644
--- a/core/src/test.rs
+++ b/core/src/test.rs
@@ -1,4 +1,4 @@
-//< Helpers that are useful for tests and doctests.
+//! Helpers that are useful for tests and doctests.
 use crate::{
     arch::Arch,
     aspace::{AddressSpace, RelativeAddressSpace},
@@ -64,6 +64,7 @@ pub fn load_shellcode64(buf: &[u8]) -> Module {
 }
 
 /// this is for testing, so will panic on error.
+#[cfg(feature = "disassembler")]
 pub fn read_insn(module: &Module, va: VA) -> zydis::DecodedInstruction {
     use crate::analysis::dis;