androguard-js
v0.1.1
Published
Android file analysis library for JavaScript — DEX, ODEX, APK, binary XML, resources, Dalvik disassembler, decompiler, Frida integration
Maintainers
Readme
androguard-js
A JavaScript library for analyzing Android files, inspired by the Python Androguard project.
Features
- APK parsing — list files, extract components, read signatures
- DEX / ODEX parsing — full binary format support (strings, types, protos, fields, methods, class definitions, bytecode)
- Android binary XML (AXML) — parse
AndroidManifest.xmland any compiled XML resource - Android resources (ARSC) — parse
resources.arsc, look up resource values by ID - Dalvik disassembler — complete 224-opcode instruction set with symbol resolution
- Basic decompiler — convert Dalvik bytecode to readable pseudo-Java
- Frida integration — attach, hook methods, bypass SSL pinning, trace network traffic
- SQLite session — persist analysis state across runs with
sql.js(no native compilation)
Installation
npm installOptional — for live dynamic analysis with Frida:
npm install frida
# frida-server must also be running on the target Android device
sql.jsis used for the session database (pure WebAssembly, no build tools required).
Quick Start
const { openAPK, openDEX, openSession, FridaHelper } = require('./src');
// ── APK ──────────────────────────────────────────────────────────────────────
const apk = openAPK('app.apk');
const info = apk.getManifestInfo();
console.log(info.packageName); // "com.example.app"
console.log(info.permissions); // ["android.permission.INTERNET", ...]
console.log(info.activities); // ["com.example.MainActivity", ...]
// ── DEX ──────────────────────────────────────────────────────────────────────
const dex = apk.getDex(); // or openDEX('classes.dex')
console.log(dex.summary()); // { version, classes, methods, strings, ... }
// Find a class
const cls = dex.getClass('Lcom/example/CryptoHelper;');
// ── Disassembly ───────────────────────────────────────────────────────────────
for (const { cls, method, code } of dex.allMethods()) {
if (!code) continue;
console.log(`${cls.type} -> ${method.name}`);
console.log(dex.disassembleToString(code));
break;
}
// ── Decompiler ────────────────────────────────────────────────────────────────
const { decompileClass } = require('./src/decompiler');
console.log(decompileClass(cls, dex._data));
// ── Session ───────────────────────────────────────────────────────────────────
const session = await openSession('analysis.db');
session.setApk({ packageName: info.packageName, path: 'app.apk' });
session.importDex(dex);
session.addNote('Hardcoded AES key in CryptoHelper.encrypt()', { tag: 'crypto' });
session.save(); // flush to disk
session.close();API Reference
openAPK(source) → APK
source is a file path or Buffer.
| Method | Returns | Description |
|---|---|---|
| listFiles() | Array | All entries in the ZIP with name and size |
| getFile(entryName) | Buffer\|null | Raw bytes of any file inside the APK |
| getManifest() | { root, strings, namespaces } | Full parsed binary XML tree |
| getManifestInfo() | object | Package name, version, SDK versions, permissions, activities, services, receivers, providers |
| getDexFiles() | DEX[] | All classes*.dex as DEX instances |
| getDex() | DEX | Primary classes.dex |
| getResources() | object | Parsed resources.arsc |
| lookupResource(resId) | object\|null | Look up a resource by ID (e.g. 0x7f040001) |
| getSignatureFiles() | object | Raw bytes of all META-INF/*.RSA / *.DSA / *.EC files |
| summary() | object | High-level summary |
openDEX(source) → DEX
source is a file path or Buffer. Accepts both DEX (dex\n) and ODEX (dey\n) magic.
| Method | Returns | Description |
|---|---|---|
| summary() | object | Version, SHA-1, file size, counts |
| getClass(descriptor) | object\|null | Find a class by descriptor e.g. "Ljava/lang/String;" |
| findClasses(fragment) | Array | Case-insensitive search across all class descriptors |
| allMethods() | Generator | Iterate over { cls, method, accessFlags, code } for every method |
| disassemble(codeItem) | Instruction[] | Structured array: { offset, mnemonic, operands, text, raw } |
| disassembleToString(codeItem) | string | Human-readable disassembly |
| dumpDisassembly() | string | All classes and methods disassembled |
Direct tables (arrays on the instance): dex.strings, dex.types, dex.protos, dex.fields, dex.methods, dex.classes
parseAXML(buf) → { root, strings, namespaces }
Parse any Android binary XML buffer. Each node:
{
name: 'activity',
ns: 'http://schemas.android.com/apk/res/android',
attrs: { 'android:name': 'com.example.MainActivity', ... },
children: [ ... ]
}parseARSC(buf) → { globalStrings, packages }
Parse resources.arsc. Use ARSC.lookupResource(arsc, resId) to resolve a resource ID to its value and type.
Disassembler
const { disassemble, disassembleToString } = require('./src/disassembler');
const instructions = disassemble(codeItem, dex._data);
// [
// { offset: 0, mnemonic: 'const-string', operands: 'v0, "Hello"', text: 'const-string v0, "Hello"' },
// { offset: 4, mnemonic: 'sget-object', operands: 'v1, java.lang.System->out:java.io.PrintStream', ... },
// { offset: 8, mnemonic: 'invoke-virtual', ... },
// { offset: 12, mnemonic: 'return-void', operands: '', text: 'return-void' }
// ]Symbols (strings, types, fields, methods) are resolved automatically when dex is provided.
Decompiler
const { decompileMethod, decompileClass } = require('./src/decompiler');
// Single method
const code = decompileMethod(codeItem, methodInfo, dex._data);
// Whole class
const code = decompileClass(classInfo, dex._data);Output is pseudo-Java. Each statement is annotated with its byte offset for cross-referencing with the disassembly.
Frida Integration
Script generation (no device required)
const { FridaHelper } = require('./src/frida');
// Hook a method and log all calls
const script = FridaHelper.generateHookScript(
'Lcom/example/CryptoHelper;',
'encrypt',
{ logArgs: true, logReturn: true }
);
// Bypass SSL certificate pinning
const bypass = FridaHelper.generateSslPinBypassScript();
// Trace all network calls
const netTrace = FridaHelper.generateNetworkTraceScript();
// Dump all loaded classes matching a pattern
const dump = FridaHelper.generateClassDumpScript('com.example');Live dynamic analysis (requires frida package + frida-server on device)
const helper = new FridaHelper.FridaHelper();
// Attach to a running process
await helper.attach('com.example.app');
// Or spawn it
await helper.spawn('com.example.app');
// Hook a method
await helper.hookMethod('Lcom/example/CryptoHelper;', 'encrypt');
// Trace all methods in a class
await helper.traceClass('Lcom/example/NetworkManager;');
// Install SSL pinning bypass
await helper.bypassSslPinning();
// Run arbitrary Frida script
await helper.loadScript(myScript, msg => console.log(msg));
await helper.detach();Session
Analysis sessions are backed by SQLite via sql.js (pure WebAssembly).
// Open (creates file if it doesn't exist)
const session = await openSession('analysis.db');
// Register the APK being analysed
session.setApk({
path: 'app.apk',
packageName: 'com.example.app',
versionName: '3.1.2',
sha1: 'abc123...',
minSdk: 21,
targetSdk: 33,
});
// Import all classes and methods from a DEX
session.importDex(dex, {
saveDisasm: false, // store disassembly text (can be large)
saveDecompiled: false, // store decompiled text
});
// Permissions
session.addPermissions(info.permissions);
// Strings of interest
session.addString('AIzaSy...', 'hardcoded API key', classId);
// Analyst notes
session.addNote('Uses custom AES mode without IV', { tag: 'crypto' });
session.addNote('Sends IMEI to third-party server', { tag: 'privacy' });
// Query
const cryptoNotes = session.listNotes({ tag: 'crypto' });
const cryptoClass = session.getClass('Lcom/example/CryptoHelper;');
const allMethods = session.getMethodsForClass(cryptoClass.id);
const keyStrings = session.searchStrings('AIza');
// Persist to disk
session.save(); // saves to the path given at open()
session.save('copy.db'); // or to an explicit path
console.log(session.stats());
// { apk: {...}, classes: 312, methods: 2847, strings: 14, permissions: 6, notes: 2 }
session.close();Project Structure
src/
├── index.js Main entry point
├── utils.js ULEB128/SLEB128, descriptor helpers
├── apk/index.js APK (ZIP) parser
├── axml/index.js Android binary XML parser
├── arsc/index.js resources.arsc parser
├── dex/
│ ├── parser.js DEX/ODEX binary parser
│ └── index.js DEX high-level wrapper
├── disassembler/
│ ├── opcodes.js Full Dalvik opcode table (224 opcodes)
│ └── index.js Instruction decoder and formatter
├── decompiler/index.js Pseudo-code generator
├── frida/index.js Frida helpers and script generators
└── session/index.js SQLite session (sql.js)
examples/
└── basic.js Usage demoRunning the Example
node examples/basic.js app.apk # full APK analysis
node examples/basic.js classes.dex # DEX / ODEX only
node examples/basic.js AndroidManifest.xml # binary XML
node examples/basic.js # Frida scripts + session demo onlySupported Formats
| Format | Support |
|---|---|
| DEX 035 / 036 / 037 / 038 / 039 | Full |
| ODEX (Dalvik, dey\n) | Full (unwraps embedded DEX) |
| APK | Full (ZIP + sub-parsers) |
| Android Binary XML | Full |
| resources.arsc | Full (simple + complex entries) |
| ART/OAT ODEX | Not yet supported |
Dependencies
| Package | Purpose |
|---|---|
| adm-zip | APK (ZIP) extraction |
| sql.js | SQLite session (pure WASM) |
| frida (optional) | Live dynamic analysis |
License
MIT
