
Android reverse engineering for humans and AI agents: typed Java in milliseconds, xrefs, API and secret recon, APK diff, re-signing, a native viewer and an MCP server. One Rust executable, no JVM.
Open any Android app and see what it really does.
What it collects, how it makes money from you, what it exposes. In seconds, as readable Java.
Viewer, CLI and MCP server for AI agents. One executable. No Java needed.
Each one needed the manifest, the resources and the decompiled code read together. A grep for
http:// finds none of it. Click a finding for the proof: real output, trimmed to the lines that matter.
Block Blast! 10.9.3 (Play build), 75,938 classes, scanned in 3.1 s.
Who gets your data: AppLovin MAX, AdMob, Meta, Unity, ironSource, Pangle, Liftoff/Vungle, InMobi,
Moloco, Amazon APS, Digital Turbine/Fyber, PubMatic, Bigo. Trackers: AppsFlyer, Firebase, Facebook and
TikTok events, ThinkingData, the studio's own cn.hsdata. Permissions: advertising ID and all four
Privacy Sandbox ad APIs.
Two endpoints aren't from any SDK. infer is a model call: what are this player's ads worth?
$ apkscope search blockblast.xapk urls
q.mb("https://low-ecpm.afafb.com/infer/v1/low_ecpm_cut", appActivity, jSONObject, new c() {
return "https://ecpm.afafb.com/infer/v1/ecpm";
$ apkscope decompile blockblast.xapk me.f # the code calls it AlgorithmInterCutManager
m2.mg(jSONObject, "ActUserInfo", ClassE.ma());
m2.me(jSONObject, "AdHistInfo", jSONArray);
m2.mg(jSONObject, "UserContext", ClassE.mc(str2, appActivity, z, UUID.randomUUID().toString(), "INTER"));
m2.mg(jSONObject, "UserInfo", ClassE.md());
Those fill in deviceModel, ram, disk, cpu, carrier, country, ip_country, timeZone,
install_time, cold_start_num and your past ads with their revenue. The answer sets your floor
price, the lowest bid an advertiser must beat to show you an ad:
$ apkscope decompile blockblast.xapk v2.d --method Y # AlgorithmBy1004, AppLovin MAX
double d3 = d * d2; // floor = predicted eCPM x "floor_factor" (remote, default 2.0)
this.g_c.mi(d3);
String.format(Locale.locale_US, "算法接口成功更新底价 - FloorPrice: %.2f, ... // "algorithm API updated the floor price"
Verdict: legal, and how ad-funded games work. Now you can see it.
Block Blast hides its game classes behind a NetEase packer: org.cocos2dx.javascript.AppActivity isn't
in the APK. apkscope dump let the game unpack itself and pulled 15 DEX out of memory in 25 s, about
3,100 hidden classes. The scripts call Java through JsCallJava; its methods show what they ask for:
$ apkscope dump com.block.juggle out/ --attach
$ apkscope decompile out/dex/classes9.dex org.cocos2dx.javascript.pay.o --method w # SPayManager.serverControl
if (jSONObject.has("isStatus")) {
boolean z = k0.ma("1", jSONObject.getString("isStatus"));
SPStore.mk().y(z);
JsCallJava.setGameAuditMode(z, "auditMode"); // → window.IPAUtils.respHearingState(1)
auditMode on in the game's scripts,
and the game remembers it. Games use this to act differently while a store reviews them; what
Block Blast changes is in its encrypted scripts, so we don't claim more.callNativeGetLeftBattery (battery %), callNativeIsInstallOtherApp
(is app X installed?), getDeviceInfoByAdb (CPU speeds, RAM, storage, screen, model).trackGameEndRevenue stores revenue data at the end of each game.BleBumpKit pairs two phones nearby, after asking permission.Not shown: how it decides your next blocks. That's in the encrypted scripts (see limits).
The file-database provider is exported, and its <path-permission> tags name a permission literally
called "false". The only real guard is in code, and it skips file/, dir/ and the root:
$ apkscope decompile nextcloud.apk com.owncloud.android.providers.FileContentProvider
private boolean isCallerNotAllowed(Uri uri) {
switch (i) {
case 4: case 5: case 6: case 7: case 8: // shares, capabilities, uploads, ...
str = this.mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
}
return false; // file (1), dir (2), root (3): anyone
}
public Cursor query(Uri uri, ...) {
if (i != 1 && i != 2 && i != 3 && isCallerNotAllowed(uri)) {
(Case labels folded, comments added.) insert, update and delete use the same check. The table
holds path, path_decrypted, file_owner, sharees, share_by_link, metadata_gps...
Verdict: any installed app can read your file names, folders and shares, decrypted names of end-to-end-encrypted files included, and change or delete rows. GitHub Security Lab noted it in 2021; the SQL injection from that report was fixed, the open access is still there in 35.0.1. Read from code, not run on a device.
$ apkscope resource antennapod.apk res/xml/network_security_config.xml
<base-config cleartextTrafficPermitted="true">
<trust-anchors>
<certificates src="user" />
<certificates src="system" />
$ apkscope decompile antennapod.apk de.danoeh.antennapod.net.ssl.CompositeX509TrustManager --method checkServerTrusted
X509TrustManager x509TrustManager = (X509TrustManager) iterator.next();
try {
x509TrustManager.checkServerTrusted(x509CertificateArr, str);
Verdict: since Android 7 apps ignore user certificates; this one opts back in, so a managed device or a "VPN" app that installs one can read its HTTPS. The custom TrustManager tries each real one and throws if none accepts: a deliberate choice, not the trust-everything bug.
scan lists Jetpack Compose's PreviewActivity as exported. It runs any class named in the intent.
$ apkscope decompile thunderbird.apk androidx.compose.ui.tooling.PreviewActivity --method onCreate
int i = getApplicationInfo().flags & 2;
if (i == 0) {
Log.md(str, "Application is not debuggable. Compose Preview not allowed.");
finish();