Adds opt-in dynamic_resolve=False parameter to Quark.__init__ that uses
DexTrace to execute encrypted string methods at analysis time, recovering
plaintext for Phase 5 keyword matching.
Core changes:
- _resolveStringCalls: two-phase resolver — (1) prior calls of methodCall,
(2) PyEval-scan of parentMethod's callers for String-returning calls when
the encryption function lives in a higher frame
- _dextrace_cached: LRU-cached per (apk_path, sig) to avoid re-executing
the same method across multiple keyword checks
- getMatchedKeywords: fixed Primitive('') guard, accepts parentMethod,
delegates to DexTrace only when no meaningful static primitives found
- checkParameterOnSingleMethod / check_parameter: thread parentMethod down
- CLI: --dynamic-resolve flag; setup.py: extras_require["dynamic"]
Verified: bianlian.apk auto_click_comfirm_button_on_dialog.json
static → 80% (Phase 5 FN: dilemmaexact() string not resolved)
dynamic → 100% (Phase 5 passes after DexTrace executes dilemmaexact())
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Add DexTrace-based dynamic string resolution to resolve encrypted strings for Phase 5 keyword matching. The false negative on bianlian APK (80% → 100% confidence) is the motivating case.
How to Test
1. Install DexTrace (development install)
2. Install quark-engine with this branch
3. Download the sample APK
Download bianlian.zip (Password: infected) and extract
bianlian.apk.4. Save the detection rule
Save the following as
auto_click_comfirm_button_on_dialog.json:{ "crime": "Auto click button on system dialog", "permission": [], "api": [ { "class": "Landroid/view/accessibility/AccessibilityNodeInfo;", "method": "findAccessibilityNodeInfosByText", "descriptor": "(Ljava/lang/String;)Ljava/util/List;", "match_keywords": [ "android:id/button1" ] }, { "class": "Landroid/view/accessibility/AccessibilityNodeInfo;", "method": "performAction", "descriptor": "(I)Z" } ], "score": 10, "label": ["sms", "calllog", "collection"] }5. Run — compare static vs dynamic
The sample first calls the method
dilemmaexact()to compute a string, then takes it as the 2nd argument to call thefindButtonAndClickmethod:The method then calls the first API
findAccessibilityNodeInfosByTextwith this string to locate a button on the system dialog:Quark Analysis Without
--dynamic-resolve(static only)Since the string is dynamically computed, the Quark rule above cannot fully detect this behavior, resulting in an 80% confidence score.
Quark Analysis With
--dynamic-resolveWith the dynamic string resolution feature, Quark successfully resolves the string from the
dilemmaexact()method and identifies the target behavior with 100% confidence.