https://github.com/block/stoic
Run code within any debuggable Android process, without modifying its APK
https://github.com/block/stoic
android debug jvmti
Last synced: 10 months ago
JSON representation
Run code within any debuggable Android process, without modifying its APK
- Host: GitHub
- URL: https://github.com/block/stoic
- Owner: block
- License: apache-2.0
- Created: 2024-05-31T18:06:42.000Z (about 2 years ago)
- Default Branch: main
- Last Pushed: 2025-07-28T09:27:55.000Z (10 months ago)
- Last Synced: 2025-07-28T11:31:17.059Z (10 months ago)
- Topics: android, debug, jvmti
- Language: Kotlin
- Homepage:
- Size: 1.32 MB
- Stars: 113
- Watchers: 1
- Forks: 10
- Open Issues: 11
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Stoic 
> *"Σκόπει εἰς σεαυτόν. Ἐντὸς σοῦ πηγὴ ἀγαθοῦ ἐστιν, ἡ ἀεὶ ἐκβρύειν ἑτοίμη, ἐὰν ἀεὶ σκάπτῃς."*
>
> "Look within. Within is the fountain of good, and it will ever bubble up, if you will ever dig."
*- Marcus Aurelius (121-180 AD), Roman Emperor and Stoic philosopher*
> *"Ignis aurum probat, miseria fortes."*
>
> "Fire tests gold and adversity tests the brave."
*-Seneca the Younger (c. 4 BC - AD 65), Roman statesman and Stoic philosopher*
## Introduction
Stoic lets you look within your Android processes, giving you the courage to
take on difficult bugs.
Stoic is a tool for
1. running code inside another process - without any modifications to its APK,
2. exposing extra capabilities to code, normally only available to a debugger, and
3. blurring the lines between code and debugger
You can write plugins that
1. provide command-line access to APIs normally only available inside the process
2. leverage debugger functionality (e.g. use breakpoints to hook arbitrary methods)
3. examine the internal state of a process without restarting the process
Stoic is fast. The first time you run a Stoic plugin in a process it will take 2-3
seconds to attach. Thereafter, Stoic plugins typically run in less than a second.
## Getting started
1. Install with [Homebrew](https://brew.sh/): `brew install block/tap/stoic`
2. Run your first Stoic plugin: `stoic helloworld`
3. When you don't specify a package, Stoic injects itself into `com.squareup.stoic.demoapp.withoutsdk`
by default - a simple app bundled with Stoic. Run `stoic --pkg helloworld` to inject into your
own app instead.
4. Create a new plugin: `stoic plugin --new scratch`
5. Run your plugin with: `stoic scratch`
6. Open up `~/.config/stoic/plugin/scratch` with Android Studio to modify this plugin and explore what Stoic can do.
Stoic works on any API 26+ Android device / emulator, with any debuggable app (that I've tested so far).
## Bundled Plugins
Stoic bundles a few plugins:
1. [appexitinfo](https://github.com/square/stoic/blob/main/docs/APPEXITINFO.md) - command-line access to the ApplicationExitInfo API
2. breakpoint - print when methods get called, optionally with arguments/return-value/stack-trace
3. crasher - see how your app handles various types of crashes
## Authoring Plugins
Each plugin is a normal Java `main` function. You access debugger functionality via the `com.squareup.stoic.jvmti` package. e.g.
```
// get callbacks whenever any method of interest is called
val method = jvmti.virtualMachine.methodBySig("android/view/InputEventReceiver.dispatchInputEvent(ILandroid/view/InputEvent;)V")
jvmti.breakpoint(method.startLocation) { frame ->
println("dispatchInputEvent called")
}
// iterate over each bitmap in the heap
for (bitmap in jvmti.instances(Bitmap::class.java)) {
println("$bitmap: size=${bitmap.allocationByteCount}")
}
```
## Architecture
The primary technologies powering Stoic are
[JVMTI](https://en.wikipedia.org/wiki/Java_Virtual_Machine_Tools_Interface),
[Unix Domain Sockets](https://en.wikipedia.org/wiki/Unix_domain_socket), and
[run-as](https://cs.android.com/android/platform/superproject/main/+/main:system/core/run-as/run-as.cpp).
Stoic is written in Kotlin and uses
[Clikt](https://ajalt.github.io/clikt/) for command-line parsing and
[GraalVM](https://www.graalvm.org/) for snappy start-ups.
The first time you run Stoic on a process it will attach a jvmti agent which
will start a server inside the process. We connect to this server through a
unix domain socket, and multiplex stdin/stdout/stderr over this connection. See
https://github.com/square/stoic/blob/main/docs/ARCHITECTURE.md for more details.