Skip to content

Set DexTrace as the default core library - #967

Merged
haeter525 merged 28 commits into
ev-flow:masterfrom
haeter525:feat/dextrace-default-backend
Sep 2, 2026
Merged

haeter525 merged 28 commits into
ev-flow:masterfrom
haeter525:feat/dextrace-default-backend

Conversation

@haeter525

@haeter525 haeter525 commented Aug 23, 2026 •

Copy link
Copy Markdown
Member

Description

Changes Quark's default --core-library from androguard to dextrace, per the comparison results in #966.

Also makes rizin/radare2 optional extras instead of hard dependencies.

Adds a clear error message when a selected backend is unavailable.

Key Changes

  • Changed the default --core-library value (CLI and Quark.__init__) to dextrace.
  • Added CoreLibraryUnavailable exception in baseapkinfo.py.
    • cli.py now catches it and prints a clean error instead of a raw traceback.
  • Moved rzpipe/r2pipe from core install requirements into new extras:
    • pip install "quark-engine[rizin]"
    • pip install "quark-engine[radare2]"
  • Fixed a Level 4 sequence-check bug in quark/core/quark.py.
    • Previously: return on the first (first_api, second_api) pair with no mutual parent function aborted the whole rule check.
    • Now: continue, so remaining candidate pairs are still tried.
    • Side effect: fixed a PYTHONHASHSEED-dependent confidence-score flakiness.
  • Updated docs/source/install.rst for the new default and extras.
  • Minor backend-agnostic cleanup in r2apkinfo.py, rzapkinfo.py, shurikenapkinfo.py, dextraceapkinfo.py, axmlreader/__init__.py, script/__init__.py, report.py.

Motivation and Context

Closes part of #966.

A comparison between DexTrace and Androguard across 470 comparable samples from the malware-family corpus showed a 99.6% exact confidence-score match (468/470).

The 2 remaining mismatches trace to a known Androguard adapter bug, not to DexTrace:

  • Duplicate-class dedup miscount when an API is defined across multiple DEX files.

Blocked on: DexTrace#19.

  • Needs merging, then a new DexTrace release published to PyPI.
  • The dextrace version pin in setup.py needs bumping once that's out — until then CI here won't pass.

How Has This Been Tested?

  • Full accuracy run across all malware families: 468/470 comparable samples match exactly between DexTrace and Androguard.
  • Seed-bug fix verified separately on the 18 previously-diffing samples (35 sample-rule combos): 35/35 now match.

Checklist

  • DexTrace#19 merged and new DexTrace version released
  • Bump dextrace version pin in setup.py
  • CI passing on Linux/macOS/Windows
  • Existing unit tests pass locally

haeter525 and others added 10 commits August 16, 2026 03:36
Classes referenced but not defined in the app's DEX (e.g. NetworkInfo,
ConnectivityManager) had no entry in superclass_relationships, so
find_api_usage()'s subtype walk dead-ended before reaching
Ljava/lang/Object;. Rules pairing such a class with a generic
Object.equals call as the second API could never reach full
combination/sequence confidence.

Every Java class ultimately extends Object, so default any class with
no DEX-derived hierarchy entry to that instead of a lookup dead end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JB4p3nzcxeJ16U7WQMEh3Q
- quark/cli.py, quark/core/quark.py, quark/report.py: default
  core_library changed from androguard to dextrace. quark/forensic/
  forensic.py intentionally left on androguard -- it has no dextrace
  branch and DexTraceImp.get_strings() is a stub returning an empty
  set, so switching its default would silently break string
  extraction rather than just swap backends.

- setup.py: move rzpipe and r2pipe==1.8.0 out of install_requires
  into new 'rizin' / 'radare2' extras. androguard and dextrace stay
  required. Shuriken was already optional (never in install_requires).

- quark/core/axmlreader/__init__.py: the module-level 'import rzpipe'
  / 'import r2pipe' transitively broke every backend, since
  BaseApkinfo (which all backends inherit) imports
  axmlreader.python, which runs axmlreader/__init__.py as a package
  side effect. Verified empirically that BaseApkinfo failed to import
  with rzpipe/r2pipe unavailable before this fix. Moved both imports
  into the rizin/radare2 branches of AxmlReader.__init__, guarded
  with try/except raising AxmlException (the file's own local
  exception -- can't import the new shared exception here without a
  circular import back through baseapkinfo.py -> axmlreader.python ->
  axmlreader/__init__.py).

- quark/core/rzapkinfo.py, quark/core/r2apkinfo.py: same guard
  pattern already used by ShurikenImp (try/except ModuleNotFoundError
  around the top-level import, check-and-raise moved into __init__).
  Now raise the new shared CoreLibraryUnavailable exception.

- quark/core/interface/baseapkinfo.py: add CoreLibraryUnavailable, a
  shared exception for 'this --core-library backend's dependency
  isn't installed'. ShurikenImp now raises this too instead of a bare
  Exception.

- quark/cli.py: wrap both Quark(...)/ParallelQuark(...) construction
  sites in try/except CoreLibraryUnavailable, printing a clean
  one-line message via print_error() and exiting 1, instead of
  letting a raw traceback reach the user (previously true even for
  Shuriken's existing 'not installed' message).

- tests/core/test_quark.py, tests/agent/test_agentTools.py: pin the
  three Quark(...) call sites that relied on the implicit default to
  core_library="androguard" explicitly, since these fixtures test
  Quark's evaluation logic against Androguard-specific behavior, not
  'whatever the current default backend is'.

Verified: BaseApkinfo, quark.core.quark, RizinImp, and R2Imp all
import/construct correctly with rzpipe/r2pipe simulated missing
(RizinImp/R2Imp raise CoreLibraryUnavailable with install
instructions instead of ModuleNotFoundError at import time). CLI with
no --core-library flag now runs dextrace by default end-to-end;
--core-library androguard still works explicitly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JB4p3nzcxeJ16U7WQMEh3Q
install.rst's --core-library list was already stale (missing dextrace
before this change). Add it, mark it as the default, and document
pip install quark-engine[rizin]/[radare2] for the two backends that
moved out of install_requires.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JB4p3nzcxeJ16U7WQMEh3Q
- add _register_no_caller_callee_methods: walk all declared methods
  up front so a concrete method with no outgoing calls and no visible
  caller in-DEX (e.g. interface-callback overrides like
  HostnameVerifier.verify()) still lands in all_methods/android_apis/
  custom_methods, instead of only being discoverable via
  extract_api_calls (invoke targets) or _register_abstract_methods
  (code_off==0 declarations)
- wire extract_declared_methods and extract_strings into imports
- implement get_strings() via extract_strings instead of returning
  an empty set
- add debug logger gated on config.DEBUG
- sort per-caller calls by real invoke offset when all present;
  fall back to DexTrace's original call order (with a warning)
  when any offset is missing, instead of a stable-sort tiebreak
  that guessed order
- propagate the real offset into _calls_by_caller and
  _calls_by_caller_sig instead of always using enumerate() index
Drop "_from_cache" suffix — implementation detail, not part of
the name's meaning; caller doesn't care about caching.
- strip quotes and unescape smali string literals (const-string
  etc keep quotes; androguard's operand is the raw unquoted string)
- parse const/const-wide literal operands (decimal, 0x-hex, optional
  'L' wide suffix) into int to match androguard's int operand type
Method.get_upper_methods walked apkinfo.analysis.get_class_analysis,
an androguard-only API; DexTrace backend has no analysis object.
Derive class hierarchy from apkinfo.superclass_relationships instead,
which both backends provide.

superclass_relationships mixes extends+implements edges; first
parent is treated as the superclass edge since rules/backends
don't distinguish the two either.
- swap Dict/List/Set/Tuple/Optional[X] for dict/list/set/tuple/X | None
  builtin generics (PEP 585, PEP 604) throughout
- move Generator/Iterable imports to collections.abc
- drop empty parens on functools.lru_cache decorators
- drop unused second Generator type param (yields only)
@codecov

codecov Bot commented Aug 23, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.09302% with 48 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.48%. Comparing base (528c937) to head (4c86848).
⚠️ Report is 5 commits behind head on master.

Files with missing lines Patch % Lines
quark/core/dextraceapkinfo.py 58.10% 31 Missing ⚠️
quark/cli.py 57.14% 3 Missing ⚠️
quark/core/r2apkinfo.py 66.66% 3 Missing ⚠️
quark/core/rzapkinfo.py 66.66% 3 Missing ⚠️
quark/core/axmlreader/__init__.py 80.00% 2 Missing ⚠️
tests/core/test_apkinfo.py 77.77% 2 Missing ⚠️
quark/core/quark.py 75.00% 1 Missing ⚠️
quark/core/shurikenapkinfo.py 50.00% 1 Missing ⚠️
quark/forensic/forensic.py 85.71% 1 Missing ⚠️
setup.py 0.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##           master     #967   +/-   ##
=======================================
  Coverage   78.48%   78.48%           
=======================================
  Files          84       84           
  Lines        7692     7692           
=======================================
  Hits         6037     6037           
  Misses       1655     1655           
Flag Coverage Δ
unittests 78.48% <72.09%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

haeter525 and others added 18 commits August 27, 2026 14:22
The evidence-line callee sig, invoke-parameter normalization, and
call-graph builder each reimplemented the same class/name/proto split
and reformat. Extract one _method_sig_to_androguard_format helper and
call it from all three places.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017QWzj8burTJiu1wGRBi4ik
…s walk

Cover three diff lines flagged by codecov as untested: the missing-
offset warning fallback in DexTraceImp._build_graph, the numeric-
literal parse failure branch in _parse_smali_to_bytecodeobject, and
the Object-terminated branch of Method.findSuperclassHierarchy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WEytPsM5pWn5gGJsQrH5yd
Cover the CLI's new CoreLibraryUnavailable handling: entry_point must
print the error and exit 1 instead of letting the exception propagate,
when the chosen --core-library backend's dependency is missing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WEytPsM5pWn5gGJsQrH5yd
pull_request_target checks out the base branch by default, so CI ran
tests and uploaded coverage against master instead of the PR's actual
commit — codecov's diff percentage on this PR measured master's code,
not this branch's changes.

Switching to pull_request checks out the PR head automatically and
keeps CODECOV_TOKEN out of untrusted PR code: GitHub withholds repo
secrets from pull_request runs triggered by forks, so token stays
available for same-repo PRs and push, and codecov-action falls back
to its own rate-limited tokenless upload for fork PRs instead of
exposing the secret to PR-controlled code.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WEytPsM5pWn5gGJsQrH5yd

@pulorsok pulorsok left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@haeter525
haeter525 merged commit 63aa424 into ev-flow:master Sep 2, 2026
17 of 20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants