ev-flow / ev-flow/DexTrace

Cross-DEX method calls not executed by VM

Open
#10 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
4
Forks
3
Avg merge
3d 4h
Merged PRs (30d)
2

Description

## Summary
The Dalvik VM is bound to a single DEX, so a method whose bytecode lives in a different DEX of a multi-dex APK is never executed. When a method in `classes.dex` invokes one defined in `classes2.dex`, the direct/static dispatch lookup misses the single-DEX method map and the call is silently downgraded to an external API miss (stub or no-op). `dextrace run ` therefore only produces correct results when every method actually executed happens to live in the one DEX that was loaded.

## Root cause
```python
# src/dextrace/vm/engine.py (_do_invoke, direct/static path)
callee_code_off = self._sig_to_codeoff.get(callee_sig)
if callee_code_off is None:
self._handle_external_miss(callee_sig, insn, state)
return None
```
`self._sig_to_codeoff`, `self._parser`, `self._disasm`, `self._resolver`, and `self._hierarchy` are all built from a single `dex_bytes`. A `code_off` is a byte offset valid only within its own DEX, so the engine has no way to reach a callee defined in another DEX — the lookup misses and real bytecode is skipped.

## Fix
Make the VM module-aware: build one `DexModule` (parser, resolver, disasm, local method map) per DEX, resolve direct/static calls through a global map, and carry the current module through the execution loop and `CallFrame`.

```python
# global dispatch map: sig -> (module_id, code_off), stable order (classes.dex first)
location = self._sig_to_location.get(callee_sig)
if location is None:
self._handle_external_miss(callee_sig, insn, state)
return None
callee_module_id, callee_code_off = location
# switch active module on invoke; restore caller's module on return
```
Cross-DEX virtual dispatch and inheritance additionally require merging `ClassHierarchy` across all DEXes (vtable values become `(module_id, code_off)`) — a separable follow-up.

## Affected locations
| File | Line(s) | Context |
|------|---------|---------|
| `src/dextrace/vm/engine.py` | 137-162 | Per-DEX state built from single `dex_bytes` |
| `src/dextrace/vm/engine.py` | 708-711 | Direct/static dispatch misses cross-DEX callee |
| `src/dextrace/vm/engine.py` | 692-694 | Virtual dispatch via single-DEX `ClassHierarchy` |
| `src/dextrace/vm/class_hierarchy.py` | 71-90 | Vtables built from one DEX; values are bare `code_off` |
| `src/dextrace/cli/cmd_run.py` | 137-139 | `dextrace run` loads only the entry DEX |

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.