Files
FridaBox/ARCHITECTURE.md

134 lines
5.7 KiB
Markdown

# FridaBox architecture
FridaBox has two deliberate layers:
1. an independent host product under `com.qm4rs.fridabox`, responsible for APK
import, integrity, launch policy, agent storage, runtime presentation, and
the user experience;
2. a retained virtual Android runtime responsible for package, process, Binder,
filesystem, identity, signature, component, and lifecycle virtualization.
The runtime descends from the NewBlackbox/BlackBox lineage. Its internal package
names are compatibility API and are not the FridaBox product namespace. They are
kept stable because manifests, AIDL, reflection, native code, persisted paths,
and virtual process dispatch all depend on that contract.
## End-to-end lifecycle
```text
FridaBoxActivity
├── SAF document stream
├── private immutable APK copy
├── SHA-256 + ZIP ABI inspection
└── virtual installation for user 0
virtual package manager
allocate :pN process and dispatch ProxyActivity
BActivityThread.handleBindApplication()
├── establish virtual identity and runtime
├── initialize native IO redirection
├── create guest Context / LoadedApk / PathClassLoader
├── populate GuestRuntimeRegistry
└── apply per-package InstrumentationPreferences
├── disabled: continue
├── listen: load loopback Gadget and wait for attach
└── script: load private Gadget + agent.js
guest makeApplication()
guest Application.onCreate()
guest Activity
```
## Import boundary
The host copies a document-provider stream once into `files/imported-apks` and
computes SHA-256 while copying. It inspects native ZIP entries, parses package
metadata, verifies the stored hash, marks the private file read-only, and passes
that path only to the virtual package manager.
There is no package-installer Intent, PackageInstaller session, APK mutation,
repack, or resign. The original guest package is therefore absent from Android's
real PackageManager.
## Instrumentation boundary
`GuestRuntimeRegistry` is authoritative, volatile, process-local state. It is
populated only after `VirtualRuntime.setupRuntime`, `NativeCore.init`, IO
redirection, and guest `LoadedApk` creation have succeeded. This gives Frida a
real guest ClassLoader while still preceding `makeApplication()`.
`FridaGadgetLoader` makes one synchronized Gadget-load attempt per Linux process.
The loader reads per-package policy from private host storage:
- **disabled** emits an explicit clean-launch state and never loads Gadget;
- **listen** loads the pinned loopback configuration with `on_load=wait`;
- **script** creates a private per-guest Gadget copy and adjacent Script
interaction config referencing a read-only `agent.js`.
The build task `verifyInstrumentationOrdering` statically verifies that the
loader remains before guest `makeApplication()`.
## Controller boundary
Gadget always listens on device loopback. ADB forwarding is a controller concern,
not a guest concern. `tools/attach_guest.py` forwards a bounded port range,
probes candidates using a compiled registry agent, rejects unrelated endpoints,
and maps each endpoint to package/process metadata from `GuestRuntimeRegistry`.
Before loading a user agent, `guest-bootstrap.js` assigns the registry's guest
ClassLoader to `Java.classFactory.loader` and reports native module enumeration.
This prevents host-first class resolution from producing false negatives.
## Cross-process state
The registry itself is never treated as cross-process storage. A minimal private
SharedPreferences snapshot lets the host UI display the latest package, process,
virtual user, process slot, source APK, ClassLoader, state, and error. Reads use
multi-process reload semantics because host and guest processes maintain
independent in-memory preference caches.
## Process recycling
Instrumentation policy is process-scoped in effect even though it is persisted
per package. Changing mode stops the guest before relaunch. This is essential:
a native library cannot be unloaded safely from a living Android process, so a
Clean launch must receive a fresh virtual process rather than attempting to undo
Gadget initialization.
## Trust and security boundaries
- The imported APK and selected JavaScript agent are untrusted inputs.
- Imported files are size-bounded, hashed, privately stored, and made read-only.
- On-device agents require an explicit trust confirmation and execute with the
guest process's access.
- Gadget binds only to `127.0.0.1`; remote reachability requires an explicit ADB
forwarding action.
- Release builds never silently use the debug signing key.
- FridaBox does not claim to defeat kernel, SELinux, Frida, virtualization, or
hardware-attestation detection.
## Source layout
| Module | Responsibility |
| --- | --- |
| `app` | FridaBox application, UI, import pipeline, modes and management. |
| `Bcore` | Virtual Android runtime, Gadget load point, registry and native integration. |
| `black-reflection` | Generated/reflected access to hidden Android framework APIs. |
| `compiler` | Annotation processing used by the reflection layer. |
| `sample-guest` | Deterministic early-start and hook validation target. |
| `scripts` | Source and reproducibly bundled Frida 17 agents. |
| `tools` | Fetch, build, forward, discover and attach utilities. |