Documentation

Go to Portal Website

Call Indirection

PlanPlatformsMASVS
TeamAndroidMASVS-RESILIENCE-3

Overview

Call Indirection replaces direct method calls within the protected application with indirect dispatch through generated trampoline classes. By routing calls through a central dispatcher that uses numeric IDs to select the target method via conditional branches, this control collapses the call graph into a star topology, making it extremely difficult for static analysis tools to trace control flow and understand inter-method relationships.

This control is implemented as a build-time bytecode rewrite. During the protected app's compilation, MobileDefender's build pipeline rewrites eligible invoke-static call sites in the app's smali code to redirect through generated dispatcher methods. No runtime overhead beyond the indirect call itself is added.

How It Works

Build-Time Smali Rewrite

The control operates during the AppTego Android protected build process, after the tenant app is decompiled to smali (bytecode) and before final reassembly. The CallIndirection pass:

  1. Scans all user smali files for invoke-static calls to methods within the app (not framework classes).
  2. Assigns each unique eligible call target a numeric dispatch ID (up to 200 targets per DEX file).
  3. Generates a dispatcher smali class with randomized package and class names (e.g., com/r8a3f1b2e/Rc94f2a817b), containing one trampoline method per dispatch target.
  4. Replaces original call sites with calls to the corresponding dispatcher trampoline method.

Each dispatcher method is a simple forwarding stub:

.method public static m7af2e1(II)V
    .locals 2
    const/16 v1, 0x3a7c
    xor-int/lit8 v1, v1, 0x4f
    invoke-static {p0, p1}, Lcom/example/app/RealClass;->realMethod(II)V
    return-void
.end method

The filler instructions (const/16, xor-int, etc.) use register v1 and introduce structural variation to resist pattern-based automated un-trampolining.

Eligibility and Safety Constraints

Eligible call sites:

Skipped call sites:

Probabilistic filtering: Only ~50% of eligible call sites are redirected. This mixing of direct and indirect calls increases analysis difficulty without exhausting method-ID headroom.

Per-DEX Method-ID Cap: The rewriter respects the 65,536 method-ID limit per DEX file, leaving 500 method-IDs of headroom. If a DEX file is near capacity, the pass is skipped for that DEX to prevent build failures.

How to Enable the Control

Navigate to Code Obfuscation from the AppTego portal, and expand the Control Flow Obfuscation section. Under this section you will find the Call Indirection control. Click Enable to apply it to the next protected build.

API Configuration Example

{
  "CallIndirection": {
    "protection": true
  }
}
FieldPurpose
protectionEnables call indirection for protected builds.

Threats Mitigated

Caveats

Runtime Overhead

Each indirected call introduces a small overhead:

For most applications, this overhead is negligible (sub-microsecond per call). However, apps with extremely tight performance constraints (game loops, real-time audio processing, sensor data pipelines) may experience measurable impact if hot-path methods are redirected.

Mitigation: The probabilistic filter ensures not all calls are redirected. If profiling reveals a performance issue, consider disabling Call Indirection or identifying specific hot methods and excluding them via allowlist (contact support for custom allowlist configuration).

Stack Traces

Stack traces will include the dispatcher class in the call chain:

at com.example.app.RealClass.realMethod(RealClass.java:42)
at com.r8a3f1b2e.Rc94f2a817b.m7af2e1(Unknown Source)
at com.example.app.Caller.doWork(Caller.java:15)

The (Unknown Source) entry is the generated dispatcher, which has no source file mapping. This does not prevent debugging or crash reporting, but analysts will see extra frames in traces.

Reflection

Call Indirection only affects static invoke-static bytecode. If the tenant app uses reflection to invoke methods (Method.invoke(...)), those calls are not redirected and remain visible in the call graph.

Compatibility with Other Obfuscation

Call Indirection runs after smali decompilation but before other obfuscation passes (Control Flow Obfuscation, Dead Code Injection, etc.). The dispatcher class itself is subject to subsequent obfuscation, which further obscures its structure.

R8/ProGuard code shrinking and optimization may inline some dispatcher methods or remove them if deemed unused (rare, as R8 runs before MobileDefender's build pipeline). If this occurs, the call is effectively restored to direct dispatch.

Support Matrix

PlatformMinimum VersionMaximum VersionNotes
AndroidAPI 21 (Android 5.0)LatestAll smali bytecode versions supported
iOSNot applicable

Plan Requirement

TEAM or higher. Not available on FREE or BASIC plans.