ApkFile is a library which creates a representation of an APK composed of Java objects which can be easily inspected for analysis or serialized into JSON. The goal of the library is to provide a robust way to inspect hostile malware samples, but it's general purpose enough to be used for other stuff too.
The main object is ApkFile and it extends java.io.File:
ApkFile apkFile = new ApkFile('ApiDemos.apk');
AndroidManifest androidManifest = apkFile.getAndroidManifest();
String packageName = androidManifest.getPackageName(); // com.example.android.apis
ApkFile provides objects for:
Below is an example of how to stream an ApkFile to JSON:
ApkFile apkFile = new ApkFile("ignore/DroidSwarm-1.0.1.apk");
GsonBuilder gsonBuilder = new GsonBuilder();
// Use these settings to handle odd strings and floating point values like "NaN":
Gson gson = new GsonBuilder().disableHtmlEscaping().serializeSpecialFloatingPointValues().setPrettyPrinting().create();
// Don't just create a JSON string since it's huge. Instead, stream it out.
Writer writer = new OutputStreamWriter(System.out);
gson.toJson(apkFile, writer);
writer.close();
apkFile.close();
Below is the highly abbreviated output from the code above. A full version can be found here.
```json { "androidManifest": { "application": { "activities": [ { "allowEmbedded": false, "allowTaskReparenting": false, "alwaysRetainTaskState": false, "autoRemoveFromRecents": false, "banner": "", "clearTaskOnLaunch": false, "configChanges": 0, "documentLaunchMode": 0, "excludeFromRecents": false, "finishOnTaskLaunch": false, "hardwareAccelerated": false, "launchMode": 0, "maxRecents": 16, "multiprocess": false, "noHistory": false, "parentActivityName": "", "persistableMode": 0, "relinquishTaskIdentity": false, "resizeableActivity": false, "screenOrientation": 0, "showForAllUsers": false, "stateNotNeeded": false, "supportsPictureInPicture": false, "taskAffinity": "", "theme": "", "uiOptions": 0, "windowSoftInputMode": 0, "intentFilters": [ { "actions": [ "android.intent.action.MAIN" ], "categories": [ "android.intent.category.LAUNCHER" ], "data": [], "icon": "", "label": "", "priority": 0 } ], "metaData": [], "directBootAware": false, "enabled": true, "exported": false, "icon": "", "label": "DroidSwarm", "name": "com.soong.droidswarm.Main", "permission": "", "process": "" }, // ** SNIP ** ], "activityAliases": [], "allowTaskReparenting": false, "allowBackup": true, "backupAgent": "", "backupInForeground": false, "banner": "", "debuggable": true, "description": "", "directBootAware": false, "enabled": true, "extractNativeLibs": true, "fullBackupContent": "", "fullBackupOnly": false, "hardwareAccelerated": true, "hasCode": true, "icon": "res/drawable-mdpi/ic_launcher.png", "isGame": false, "killAfterRestore": true, "largeHeap": false, "label": "DroidSwarm", "logo": "", "manageSpaceActivity": "", "name": "", "networkSecurityConfig": "", "permission": "", "persistent": false, "process": "", "providers": [], "receivers": [], "restoreAnyVersion": false, "requiredAccountType": "", "resizableActivity": false, "supportsRtl": false, "services": [ { "isolatedProcess": false, "intentFilters": [], "metaData": [], "directBootAware": false, "enabled": true, "exported": false, "icon": "", "label": "", "name": "com.soong.droidswarm.SwarmService", "permission": "", "process": "" } ], "taskAffinity": "", "theme": "", "uiOptions": 0, "usesCleartextTraffic": true, "vmSafeMode": false, "usesLibraries": [] }, "compatibleScreens": [], "hasResources": true, "installLocation": 0, "instrumentations": [], "maxSdkVersion": 0, "minSdkVersion": 7, "packageName": "com.soong.droidswarm", "permissionGroups": [], "permissionTrees": [], "permissions": [], "platformBuildVersionCode": -1, "platformBuildVersionName": "", "sharedUserId": "", "sharedUserLabel": "", "supportsGlTextures": [], "targetSdkVersion": 0, "usesConfigurations": [], "usesFeatures": [], "usesPermissions": [ "android.permission.INTERNET", "android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.ACCESS_NETWORK_STATE", "android.permission.CHANGE_WIFI_STATE" ], "versionCode": 5, "versionName": "0.9.2" }, "certificate": { "allRdns": [ { "issuerRdns": { "C": "US", "CN": "Android Debug", "O": "Android" }, "subjectRdns": { "C": "US", "CN": "Android Debug", "O": "Android" } } ] }, "entryNameToDex": { "classes.dex": { "apiCounts": { "Landroid/text/Editable;->toString()Ljava/lang/String;": 8, "Ljava/util/concurrent/ThreadPoolExecutor;->(IIJLjava/util/concurrent/TimeUnit;Ljava/util/concurrent/BlockingQueue;Ljava/util/concurrent/RejectedExecutionHandler;)V": 1, "Landroid/content/pm/PackageManager;->getPackageInfo(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;": 2, "Ljava/lang/Object;->toString()Ljava/lang/String;": 1, "Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;": 2, "Landroid/widget/CheckBox;->isChecked()Z": 1, "Landroid/widget/ListView;->setAdapter(Landroid/widget/ListAdapter;)V": 2, "Ljava/lang/CharSequence;->toString()Ljava/lang/String;": 1, "Ljava/util/Iterator;->hasNext()Z": 9, "Ljava/lang/String;->toLowerCase()Ljava/lang/String;": 1, "Landroid/widget/SeekBar;->getMax()I": 1, "Landroid/app/AlertDialog;->setMessage(Ljava/lang/CharSequence;)V": 1, // ** SNIP ** }, "classAccessorCounts": { "interface": 2, "final": 7, "protected": 0, "private": 0, "synchronized": 0, "abstract": 2, "native": 0, "volatile": 0, "transient": 0, "public": 22, "static": 0, "strict": 0 }, "classPathToClass": { // * SNIP ** "Lcom/soong/droidswarm/SwarmMonitor;": { "apiCounts": { "Ljava/lang/Thread;->sleep(J)V": 1, "Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;": 2, "Ljava/io/PrintStream;->println(Ljava/lang/String;)V": 1, "Ljava/util/ArrayList;->get(I)Ljava/lang/Object;": 1, "Ljava/util/concurrent/ThreadPoolExecutor;->getMaximumPoolSize()I": 1, "Ljava/util/concurrent/ThreadPoolExecutor;->execute(Ljava/lang/Runnable;)V": 1, "Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;": 5, "Ljava/util/concurrent/ThreadPoolExecutor;->getActiveCount()I": 1, "Ljava/util/concurrent/ThreadPoolExecutor;->getTaskCount()J": 1, "Ljava/lang/Object;->()V": 1, "Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;": 1, "Ljava/util/concurrent/ThreadPoolExecutor;->getPoolSize()I": 2, "Ljava/util/concurrent/ThreadPoolExecutor;->isShutdown()Z": 3, "Ljava/util/concurrent/ThreadPoolExecutor;->getCorePoolSize()I": 1, "Ljava/util/concurrent/ThreadPoolExecutor;->getCompletedTaskCount()J": 1, "Ljava/util/concurrent/ThreadPoolExecutor;->isTerminated()Z": 1 }, "classAccessors": { "interface": 0, "final": 0, "protected": 0, "private": 0, "synchronized": 0, "abstract": 0, "native": 0, "volatile": 0, "transient": 0, "public": 1, "static": 0, "strict": 0 }, "fieldReferenceCounts": { "Ljava/lang/System;->out:Ljava/io/PrintStream;": 1 }, "methodAccessorCounts": { "interface": 0, "final": 0, "protected": 0, "private": 0, "synchronized": 0, "abstract": 0, "native": 0, "volatile": 0, "transient": 0, "public": 2, "static": 0, "strict": 0 }, "methodSignatureToMethod": { "run()V": { "apiCounts": { "Ljava/util/concurrent/ThreadPoolExecutor;->getActiveCount()I": 1, "Ljava/lang/Thread;->sleep(J)V": 1, "Ljava/util/concurrent/ThreadPoolExecutor;->execute(Ljava/lang/Runnable;)V": 1, "Ljava/lang/Long;->valueOf(J)Ljava/lang/Long;": 2, "Ljava/util/concurrent/ThreadPoolExecutor;->getCompletedTaskCount()J": 1, "Ljava/util/ArrayList;->get(I)Ljava/lang/Object;": 1, "Ljava/util/concurrent/ThreadPoolExecutor;->getMaximumPoolSize()I": 1, "Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;": 5, "Ljava/util/concurrent/ThreadPoolExecutor;->getTaskCount()J": 1, "Ljava/util/concurrent/ThreadPoolExecutor;->isTerminated()Z": 1, "Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;": 1, "Ljava/util/concurrent/ThreadPoolExecutor;->getPoolSize()I": 2, "Ljava/util/concurrent/ThreadPoolExecutor;->getCorePoolSize()I": 1, "Ljava/io/PrintStream;->println(Ljava/lang/String;)V": 1, "Ljava/util/concurrent/ThreadPoolExecutor;->isShutdown()Z": 3 }, "fieldReferenceCounts": { "Ljava/lang/System;->out:Ljava/io/PrintStream;": 1 }, "methodAccessors": { "interface": 0, "final": 0, "protected": 0, "private": 0, "synchronized": 0, "abstract": 0, "native": 0, "volatile": 0, "transient": 0, "public": 1, "static": 0, "strict": 0 }, "opCounts": { "MOVE_RESULT": 12, "IF_NEZ": 1, "ADD_INT_LIT8": 1, "IF_GE": 1, "IF_LEZ": 1, "MOVE_EXCEPTION": 1, "SGET_OBJECT": 1, "MOVE_RESULT_WIDE": 2, "REM_INT": 1, "GOTO": 4, "IF_EQZ": 5, "CHECK_CAST": 1, "CONST_STRING": 1, "NEW_ARRAY": 1, "INVOKE_DIRECT": 1, "IF_LE": 1, "MOVE_RESULT_OBJECT": 9, "INVOKE_STATIC": 9, "IGET_OBJECT": 17, "IGET_BOOLEAN": 2, "RETURN_VOID": 1, "APUT_OBJECT": 7, "INVOKE_VIRTUAL": 17, "NEW_INSTANCE": 1, "MOVE": 4, "CONST_4": 12, "CONST_WIDE_16": 1 }, "stringReferenceCounts": { "[swarm-mon] [%d/%d] active:%d, completed:%d task:%d isShutdown:%s isTerminated:%s": 1 }, "annotationCount": 0, "cyclomaticComplexity": 19, "debugItemCount": 31, "instructionCount": 115, "registerCount": 11, "tryCatchCount": 1 }, "(Ljava/util/concurrent/ThreadPoolExecutor;Lcom/soong/droidswarm/TargetList;)V": { "apiCounts": { "Ljava/lang/Object;->()V": 1 }, "fieldReferenceCounts": {}, "methodAccessors": { "interface": 0, "final": 0, "protected": 0, "private": 0, "synchronized": 0, "abstract": 0, "native": 0, "volatile": 0, "transient": 0, "public": 1, "static": 0, "strict": 0 }, "opCounts": { "RETURN_VOID": 1, "INVOKE_DIRECT": 1, "CONST_4": 1, "IPUT_BOOLEAN": 1, "IPUT_OBJECT": 2 }, "stringReferenceCounts": {}, "annotationCount": 0, "cyclomaticComplexity": 1, "debugItemCount": 6, "instructionCount": 6, "registerCount": 4, "tryCatchCount": 0 } }, "opCounts": { "MOVE_RESULT": 12, "IF_NEZ": 1, "ADD_INT_LIT8": 1, "IF_GE": 1, "IF_LEZ": 1, "MOVE_EXCEPTION": 1, "SGET_OBJECT": 1, "MOVE_RESULT_WIDE": 2, "REM_INT": 1,
$ claude mcp add apkfile \
-- python -m otcore.mcp_server <graph>