Skip to main content

Engine

Struct Engine 

Source
pub struct Engine { /* private fields */ }
Expand description

Engine over one APK.

Implementations§

Source§

impl Engine

Source

pub fn call_graph(&self, q: &CallQuery) -> Result<CallTree>

Callers or callees of a method, depth levels deep, as a tree.

The roots are every overload of method declared by class (or the exact signature) that some DEX references; an unknown method is an error. Each method is expanded once, at its shallowest occurrence; other occurrences are marked repeated.

§Errors

EngineError::Query for an inconsistent query (from on a callees walk), a root method no DEX references or a condition no method matches; EngineError::Apk when the APK cannot be opened.

Source

pub fn call_graph_cancellable( &self, q: &CallQuery, cancel: &AtomicBool, ) -> Result<CallTree>

Engine::call_graph that stops, with ApkError::Cancelled, once cancel is raised.

§Errors

As Engine::call_graph, plus the cancellation.

Source§

impl Engine

Source

pub fn scan_classes( &self, prefix: Option<&str>, progress: impl Fn(usize, usize) + Sync, ) -> Result<Vec<EntryClasses>>

Enumerate the classes of every root DEX entry whose descriptor starts with prefix (a Dalvik descriptor prefix, used as is).

Entries are read in parallel, largest first; the result follows the central-directory order, and each entry’s logical DEXes and classes keep their file order. progress(done, total) runs after each entry. Problems are reported per entry / DEX, never as a failure of the call; only an unreadable APK fails it.

§Errors

EngineError::Apk when the APK cannot be opened.

Source

pub fn list_classes(&self, prefix: Option<&str>) -> Result<Vec<ClassListing>>

Every class of the APK, optionally restricted to a package / class prefix (com.poc, com/poc or Lcom/poc), in DEX order. Strict: the first unreadable entry, unparsable DEX or bad class_def fails the call.

§Errors

EngineError::Query for an empty prefix, the error of the first entry that cannot be read, and EngineError::BadDex for the first bad DEX or class_def (DexClasses::check).

Source§

impl Engine

Source

pub fn matching_methods( &self, queries: &[Query], cancel: &AtomicBool, ) -> Result<MatchingMethods>

The signatures (Lcls;->name(params)ret) of the methods whose code matches each query, all queries in one pass.

§Errors

EngineError::Query for a class relation query (it matches classes, not code) or when some DEX entry could not be searched (a partial set would silently drop methods), plus the errors of Engine::find_refs.

Source§

impl Engine

Source

pub fn hierarchy(&self, q: &HierarchyQuery) -> Result<HierarchyTree>

Transitive subclasses / implementers (or supertypes) of a class, as a tree, with the classes defining method marked.

§Errors

EngineError::Query when no DEX defines or references the class, EngineError::Smali for a malformed method signature, and EngineError::Apk when the APK cannot be opened.

Source

pub fn hierarchy_cancellable( &self, q: &HierarchyQuery, cancel: &AtomicBool, ) -> Result<HierarchyTree>

Engine::hierarchy that stops, with ApkError::Cancelled, once cancel is raised.

§Errors

As Engine::hierarchy, plus the cancellation.

Source§

impl Engine

Source

pub fn no_results_hints(&self, query: &Query, entries: usize) -> Vec<String>

One line each on why a search over entries DEX entries found nothing and what to change: case-sensitive patterns, a class no DEX references, members that do exist, direct relations only.

Source§

impl Engine

Source

pub fn manifest_summary(&self) -> Result<ManifestSummary>

Parse AndroidManifest.xml (and the XML resources it points at) into a ManifestSummary.

§Errors

EngineError::Apk when the APK or manifest cannot be read, EngineError::Arsc when resources.arsc is malformed, and EngineError::Axml when the manifest is not valid binary XML. Referenced XML resources that fail are ManifestSummary::warnings instead.

Source§

impl Engine

Source

pub fn with_name_mappings( self, mappings: impl Into<Arc<NameMappings>>, ) -> Engine

Show the names of mappings in decompiled source (typically read from the user’s mapping.txt with NameMappings::from_proguard_file). Shared, since one mapping file serves many engines.

Source

pub fn name_mappings(&self) -> &NameMappings

The names this program’s classes and members go by in decompiled output: the set given to Engine::with_name_mappings, or the identity (empty) when there is none, in which case every class and member keeps its own name (backends still escape names their language cannot spell). Narrow it to a request with NameMappings::restricted_to.

Source§

impl Engine

Source

pub fn prebuild(&self) -> Result<PrebuildReport>

Build (or map from L2) the locator tables of every DEX entry the caches hold, so the first query of each kind skips its table builds. Blocks until done; meant for a background thread after a warm open. Does nothing without a memory cache (the tables would be dropped).

§Errors

EngineError::Apk when the APK cannot be opened. A DEX that fails to parse or build tables is skipped (its queries report the error later).

§Panics

When a worker thread panics (a bug); the panic is propagated.

Source

pub fn cancel_prebuild(&self)

Stop a running Engine::prebuild (and any later one) and wait for its workers to finish the table they are on.

§Panics

When the prebuild’s state mutex is poisoned (a prebuild thread panicked while holding it).

Source§

impl Engine

Source

pub fn resources(&self) -> Result<Option<Arc<ResTable>>>

The APK’s resource table, parsed once per APK version and kept for later calls. None when the APK has no resources.arsc.

§Errors

EngineError::Apk when the APK or the table cannot be read, EngineError::Arsc when the table is malformed.

Source

pub fn release_resources(&self)

Forget the parsed resource table (and its name index); the next resource query parses it again (about a millisecond even for a large app).

Source

pub fn xml(&self, path: &str) -> Result<String>

Decode a binary XML file of the APK (res/xml/..., a layout, the manifest), naming references and app attributes from the resource table.

§Errors

EngineError::Query when the APK has no entry path, the errors of Engine::resources, and EngineError::Axml when the entry is not valid binary XML.

Source

pub fn manifest_xml_resolved(&self) -> Result<String>

Engine::manifest_xml with @7F... references shown by name.

§Errors

As Engine::xml.

Source

pub fn dump_resources(&self, f: impl FnMut(ResourceRow)) -> Result<()>

Call f with every value of every resource, in id order (one row per resource and configuration).

§Errors

EngineError::Query when the APK has no resources.arsc, and the errors of Engine::resources.

Source

pub fn resource(&self, query: &str) -> Result<Vec<ResourceRow>>

Every configuration’s value of the resource query: an id (0x7f0d0001) or a name (string/app_name, @pkg:type/name).

§Errors

EngineError::Query when the APK has no resources.arsc or no resource query, and the errors of Engine::resources.

Source

pub fn resolve_resource( &self, query: &str, config: Option<&Config>, ) -> Result<(Vec<String>, ResourceRow)>

Resolve the reference chain of query for a device described by config (default: Config::any_device). Returns the rendered chain (@string/a -> @string/b) and the final value.

§Errors

EngineError::Query when the APK has no resources.arsc, no resource query, or a chain that cannot be resolved (no value for the configuration, a dangling or cyclic reference), and the errors of Engine::resources.

§Panics

Never: a resolved chain always starts with the queried id.

Source

pub fn search_resources( &self, pattern: &str, literal: bool, field: ResourceField, limit: usize, ) -> Result<(Vec<ResourceRow>, usize)>

Resources whose name and/or string value matches the regex pattern (a plain substring when literal), one row per matching configuration value (names: one row per configuration). At most limit rows; the second value is the total number of matches.

§Errors

EngineError::Query for an invalid regex or an APK without resources.arsc, and the errors of Engine::resources.

Source§

impl Engine

Source

pub fn annotate_resource_ids(&self, smali: &str) -> String

Name the resource ids of smali: a const (or .array-data element) whose literal is a resource of this APK gets # @type/name appended, e.g. const v0, 0x7f0b001c # @layout/activity_main. Unchanged when the APK has no (readable) resource table: names are a convenience, never a reason for the class itself to fail.

Source§

impl Engine

Source

pub fn find_refs_rules( &self, rules: &[Query], opts: FindOptions, cancel: &AtomicBool, sink: &mut dyn FnMut(usize, RefBatch), ) -> Result<RefSummary>

Several queries in one pass: each DEX is loaded once and scanned once per index kind for all of them. sink gets (rule index, batch), one batch per rule and entry, entries in completion order.

Each rule shares L3 with Engine::find_refs_opts: rules an earlier run left there are replayed, only the others are searched, and each of those is kept under its own key.

§Errors

As Engine::find_refs, for the first invalid rule.

§Panics

Never: results are only recorded for L3 when a disk cache is in use.

Source

pub fn search_target_entry_rules( &self, target: &SearchTarget, entry: &Entry, rules: &[&CompiledQuery], aggregate: bool, cancel: &AtomicBool, ) -> Vec<RefBatch>

Engine::search_target_entry for a rule set: one batch per rule.

Source§

impl Engine

Source

pub fn cached_refs( &self, target: &SearchTarget, query: &Query, aggregate: bool, ) -> Option<Vec<RefBatch>>

The rows an earlier search of query on this APK left in L3: what Engine::find_refs_with replays, so the one-APK and the sweep paths share results.

Source

pub fn store_refs(&self, query: &Query, aggregate: bool, batches: &[RefBatch])

Keep the batches of a complete search of query on this APK in L3 (not when one failed, see Engine::cached_refs).

§Panics

Never: it returns early unless a disk cache with L3 is in use.

Source

pub fn search_target_entry_dedup( &self, target: &SearchTarget, entry: &Entry, query: &CompiledQuery, aggregate: bool, cancel: &AtomicBool, claim: &Claim, ) -> RefBatch

Engine::search_target_entry for an entry claimed in a DexDedup: a leading entry is searched and its rows offered to the entries that follow it; a following one reuses them when its bytes hash the same.

§Panics

Never: a search of one query yields exactly one batch.

Source

pub fn search_target_entry_rules_dedup( &self, target: &SearchTarget, entry: &Entry, rules: &[&CompiledQuery], aggregate: bool, cancel: &AtomicBool, claim: &Claim, ) -> Vec<RefBatch>

Engine::search_target_entry_rules for an entry claimed with DexDedup::claim_rules for these rules.

Source§

impl Engine

Source

pub fn new(apk_path: impl AsRef<Path>) -> Engine

An engine over apk_path: an APK, a jar, a bare DEX file, or a program of several read as one (a directory, or paths joined with ,; see Apk::open).

Source

pub fn with_limits(self, limits: Limits) -> Engine

Refuse entries that inflate past limits (zip bombs).

Source

pub fn with_threads(self, threads: impl Into<Threads>) -> Engine

Use threads worker threads per search (or pick them automatically).

Source

pub fn with_debug(self, debug: bool) -> Engine

Log timings and cache decisions of each query through the logger.

Source

pub fn with_logger( self, logger: impl Fn(&str) + Send + Sync + 'static, ) -> Engine

Route debug output somewhere other than stdout.

Source

pub fn with_cache(self, config: CacheConfig) -> Engine

Enable caching of inflated entries. A disk cache whose directory holds foreign files is dropped with a warning through the logger (set it first), instead of every write failing silently in the background.

Source

pub fn with_shared_cache(self, cache: &SharedCache) -> Engine

Use cache (shared with other engines) instead of a private one.

Source

pub fn apk_path(&self) -> &Path

The APK (or program) this engine queries.

Source

pub fn threads(&self) -> usize

The thread count for a search starting now.

Source

pub fn cache_stats(&self) -> CacheStats

Cache activity of this engine so far.

Source

pub fn resident_bytes(&self) -> usize

Heap this engine holds outside the memory cache, roughly: the class graph and the parsed resource table it keeps for later calls. A host keeping many engines counts it against its memory budget (SharedCache::set_reserved).

Source

pub fn memory_cache_bytes(&self) -> usize

Bytes currently charged to the in-memory cache: entry bytes plus the locator tables built for them.

Source

pub fn release_memory_cache(&self) -> usize

Drop this APK’s entries from the in-memory cache (shared or not), and its parsed resource table, and return the DEX bytes released; queries still holding them keep them alive.

Source

pub fn memory_cache_mapped_bytes(&self) -> usize

Entry bytes of the in-memory cache that are mapped from the disk cache (file-backed pages the OS can reclaim) rather than held on the heap.

Source

pub fn memory_cache_mapped_table_bytes(&self) -> usize

Bytes of the in-memory cache’s locator tables that are viewed in L2 files (file-backed pages shared with other processes) rather than held on the heap.

Source

pub fn flush_cache_writes(&self)

Block until every queued disk-cache write has been renamed into place.

Source

pub fn close_cache_writes(&self)

Engine::flush_cache_writes for process exit: also stops queueing, so a worker still holding the engine cannot start a writer thread that exiting would kill mid-write. Run on drop.

Source

pub fn open(&self) -> Result<Apk>

Open the APK, or reuse the one opened last while Apk::version_of finds the same files, sizes and times.

§Errors

EngineError::Apk when the APK cannot be opened or its central directory read.

Source

pub fn resolve_path(apk_path: &Path, base: &Path) -> PathBuf

apk_path (what Engine::new takes) with relative paths resolved against base, each path of a program on its own.

Source

pub fn can_open(apk_path: &Path) -> bool

Whether apk_path opens: a readable file, or a program with code.

Source

pub fn program_notes(&self) -> Vec<String>

One line per archive of a program left out of its code (no root DEX, e.g. a jar deodexed into oat/, or not a zip); none for one file.

Source

pub fn search_target(&self) -> Result<SearchTarget>

Open the APK and list its DEX entries for Engine::search_target_entry.

§Errors

EngineError::Apk when the APK cannot be opened or its central directory read.

Source

pub fn search_target_entry( &self, target: &SearchTarget, entry: &Entry, query: &CompiledQuery, aggregate: bool, cancel: &AtomicBool, ) -> RefBatch

Search one entry of target (what Engine::find_refs_with runs per entry), through this engine’s caches.

Source

pub fn dex_entries(&self) -> Result<Vec<Entry>>

Root-level DEX entries, smallest compressed first.

§Errors

EngineError::Apk when the APK cannot be opened or its central directory read.

Source

pub fn apk_key(&self, apk: &Apk) -> Option<ApkKey>

Cache key for apk when caching is enabled. Hashing the central directory is done once per APK version; later calls only stat.

Source

pub fn entry_bytes( &self, apk: &Apk, key: Option<&ApkKey>, entry: &Entry, ) -> Result<DexBytes>

Inflated bytes of entry, through the caches when enabled. Stored entries are copied only when an in-memory cache wants to keep them.

§Errors

EngineError::Apk when the entry cannot be read or inflated, or inflates past the InflateLimits.

Source

pub fn get_class_dex(&self, descriptor: &str) -> Result<Option<ClassHit>>

Find the DEX that defines descriptor (Lcom/poc/Main;).

DEX entries are inflated in parallel, smallest first. Entries that are not cached are first streamed only as far as their id tables and string data to answer the probe cheaply; only a hit is inflated fully.

A class several DEX files define resolves as a class loader would: the first archive of a program, then classes.dex, classes2.dex, and so on. Once a DEX defines it, DEX files after it are no longer read; workers still inflating one at that moment finish on their own in the background. A program reads every archive, to name the others that define the class too (ClassHit::also_defined_in).

A class no DEX defines is Ok(None).

§Errors

EngineError::Apk when the APK cannot be opened, and the error of a DEX that failed to read or parse when no other DEX defines the class.

Source

pub fn get_class_dex_cancellable( &self, descriptor: &str, cancel: &AtomicBool, ) -> Result<Option<ClassHit>>

Engine::get_class_dex that stops, with ApkError::Cancelled, once cancel is raised.

§Errors

As Engine::get_class_dex, plus the cancellation.

Source

pub fn get_class(&self, descriptor: &str) -> Result<ClassSource>

Locate, rebuild and disassemble a class.

§Errors

EngineError::ClassNotFound when no DEX defines it, the errors of Engine::get_class_dex, and EngineError::Rebuild / EngineError::Smali when the class cannot be rendered at all (a class the rebuild cannot represent is disassembled from the original DEX instead, with a ClassSource::warning).

Source

pub fn get_class_with( &self, descriptor: &str, opts: SmaliOptions, ) -> Result<ClassSource>

Engine::get_class with smali rendering options.

§Errors

As Engine::get_class.

Source

pub fn get_class_method( &self, descriptor: &str, opts: SmaliOptions, method: Option<&str>, ) -> Result<ClassSource>

Engine::get_class_with that renders only the method whose name(params)ret is method (no fields) when one is given. A malformed signature is rejected before the APK is read; a class without such a method is an error. The rebuilt DEX still holds the whole class.

§Errors

As Engine::get_class, plus EngineError::Smali for a malformed method or one the class does not have.

Source

pub fn get_class_method_cancellable( &self, descriptor: &str, opts: SmaliOptions, method: Option<&str>, cancel: &AtomicBool, ) -> Result<ClassSource>

Engine::get_class_method that stops looking for the class, with ApkError::Cancelled, once cancel is raised.

§Errors

As Engine::get_class_method, plus the cancellation.

Source

pub fn find_refs( &self, query: &Query, sink: impl FnMut(RefBatch), ) -> Result<RefSummary>

Search every DEX entry for references. sink receives one batch per entry in completion order. Returns a summary once all entries finish.

A query that finds no row as written is searched again with its names read as decompiled Java / Kotlin source spells DEX names (names::denormalize_query); RefSummary::notes says so when that finds rows.

§Errors

EngineError::Query / EngineError::Find for an invalid query, and EngineError::Apk when the APK cannot be opened. A DEX entry that fails to load or search does not fail the run: its batch carries the error in RefBatch::error.

Source

pub fn find_refs_with( &self, query: &Query, aggregate: bool, cancel: &AtomicBool, sink: &mut dyn FnMut(RefBatch), ) -> Result<RefSummary>

find_refs with control over aggregation and cancellation. An invalid pattern is reported once, up front, instead of once per DEX.

§Errors

As Engine::find_refs.

Source

pub fn find_refs_opts( &self, query: &Query, opts: FindOptions, cancel: &AtomicBool, sink: &mut dyn FnMut(RefBatch), ) -> Result<RefSummary>

find_refs with every option: aggregation, context, cancellation.

§Errors

As Engine::find_refs.

Source

pub fn find_refs_text( &self, query: &Query, cancel: &AtomicBool, sink: &mut dyn FnMut(RefBatch), ) -> Result<RefSummary>

The rows of Engine::find_refs (one per method, no context) as text: each batch holds them in RefBatch::text, exactly the RefResult::lines find_refs would give, with no structured rows. A broad query renders and replays much faster this way.

§Errors

As Engine::find_refs.

Source

pub fn fill_context( &self, rows: &mut [RefResult], ctx: RefContext, ) -> Result<Vec<String>>

Fill the context of rows an earlier Engine::find_refs returned (typically one page of them): only their DEX files are loaded, from the cache when it holds them. Returns the entries that failed to load.

§Errors

EngineError::Apk when the APK cannot be opened or its central directory read.

Source

pub fn manifest_xml(&self) -> Result<String>

Decode AndroidManifest.xml.

§Errors

EngineError::Apk when the APK or the manifest entry cannot be read, EngineError::Axml when the manifest is not valid binary XML.

Trait Implementations§

Source§

impl Drop for Engine

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

§

impl !Freeze for Engine

§

impl !RefUnwindSafe for Engine

§

impl !UnwindSafe for Engine

§

impl Forget for Engine

§

impl Move for Engine

§

impl Send for Engine

§

impl Sync for Engine

§

impl Unpin for Engine

§

impl UnsafeUnpin for Engine

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.