Skip to content

Android Static Analysis - Reading Apps Like Books

APK Analysis Workflow

Overview - Analyzing Code Without Running It

Static analysis is like reading someone's diary - except instead of embarrassing childhood memories, you're looking for security vulnerabilities. You're examining the app's code, structure, and configuration without ever installing or running it. It's the foundation of Android security testing.

Why Static Analysis Matters:

Before you even touch a device, static analysis tells you: - What permissions the app requests - What components are exported (and vulnerable) - What hardcoded secrets exist - What security configurations are present (or missing) - What the app architecture looks like - Where the attack surface is

The Process:

  1. Decompile: Turn APK back into (mostly) readable code
  2. Extract: Pull out manifest, resources, native libraries
  3. Analyze: Review code for vulnerabilities
  4. Map: Understand app structure and data flows
  5. Document: Note findings for dynamic testing later

What You'll Find:

  • Exported components anyone can call
  • Hardcoded API keys and secrets
  • Weak cryptography implementations
  • Missing security configurations
  • Insecure data storage patterns
  • Intent handling vulnerabilities

Prerequisites: Understanding of Android Basics, APK Structure, and Reverse Engineering concepts. If you don't know what an APK is or how decompilation works, you're going to have a bad time.

Table of Contents

  1. APK Decompilation
  2. Manifest Analysis
  3. Code Analysis
  4. Resource Analysis
  5. Automated Static Analysis Tools
  6. Finding Hardcoded Secrets

APK Decompilation

Tools Overview

Primary Tools: - APKTool - Decompile resources and rebuild APKs - JADX - Decompile DEX to Java source - dex2jar + JD-GUI - Alternative Java decompiler - Bytecode Viewer - Multi-format viewer

APKTool Usage

Basic Decompilation:

# Decompile APK
apktool d target.apk -o output/

# Rebuild APK
apktool b output/ -o rebuilt.apk

Advanced Options:

# Decompile with framework resources
apktool if framework-res.apk

# Force decode (ignore errors)
apktool d target.apk -f -o output/

# Keep original sources
apktool d target.apk --keep-broken-res -o output/

JADX Usage

Basic Decompilation:

# Decompile to Java source
jadx target.apk -d output/

# Decompile with resources
jadx -r target.apk -d output/

# Export to Gradle project
jadx target.apk --export-gradle -d output/

Advanced Options:

# Decompile specific DEX file
jadx --deobf target.apk -d output/

# Show debug info
jadx --show-bad-code target.apk -d output/

dex2jar + JD-GUI

# Convert DEX to JAR
d2j-dex2jar classes.dex -o output.jar

# Open in JD-GUI
jd-gui output.jar

Related reading: See Reverse Engineering for decompilation fundamentals.


Manifest Analysis

Extracting AndroidManifest.xml

Using APKTool:

apktool d target.apk -o output/
cat output/AndroidManifest.xml

Using AAPT:

aapt dump xmltree target.apk AndroidManifest.xml
aapt dump badging target.apk

Key Manifest Checks

Exported Components:

# Find exported activities
grep -A 10 "activity" output/AndroidManifest.xml | grep "exported=\"true\""

# Find exported services
grep -A 10 "service" output/AndroidManifest.xml | grep "exported=\"true\""

# Find exported receivers
grep -A 10 "receiver" output/AndroidManifest.xml | grep "exported=\"true\""

# Find exported providers
grep -A 15 "provider" output/AndroidManifest.xml | grep "exported=\"true\""

Permissions:

# List all permissions
grep "uses-permission" output/AndroidManifest.xml

# Dangerous permissions
grep "DANGEROUS\|SIGNATURE" output/AndroidManifest.xml

Debuggable Flag:

grep "android:debuggable" output/AndroidManifest.xml
# Should be "false" or absent in release builds

Network Security:

# Check for cleartext traffic
grep "usesCleartextTraffic" output/AndroidManifest.xml

# Network security config
grep "networkSecurityConfig" output/AndroidManifest.xml

Manifest Analysis Script

#!/usr/bin/env python3
import xml.etree.ElementTree as ET
import sys

def analyze_manifest(manifest_path):
    tree = ET.parse(manifest_path)
    root = tree.getroot()

    print("=== Manifest Analysis ===\n")

    # Package name
    package = root.get('package')
    print(f"Package: {package}\n")

    # Permissions
    print("Permissions:")
    for perm in root.findall('.//uses-permission'):
        name = perm.get('{http://schemas.android.com/apk/res/android}name')
        print(f"  - {name}")

    # Exported components
    print("\nExported Components:")
    for component_type in ['activity', 'service', 'receiver', 'provider']:
        for comp in root.findall(f'.//{component_type}'):
            exported = comp.get('{http://schemas.android.com/apk/res/android}exported')
            name = comp.get('{http://schemas.android.com/apk/res/android}name')
            if exported == 'true':
                print(f"  [{component_type.upper()}] {name} - EXPORTED")

    # Debuggable
    application = root.find('application')
    debuggable = application.get('{http://schemas.android.com/apk/res/android}debuggable')
    if debuggable == 'true':
        print("\n[!] App is debuggable - security issue")

analyze_manifest('output/AndroidManifest.xml')

Related reading: See Android Basics - AndroidManifest.xml.


Code Analysis

Finding Security Issues

Insecure Storage:

jadx target.apk -d output/
grep -r "SharedPreferences\|getSharedPrefs\|openFileOutput" output/sources/

Cryptography Issues:

# Weak algorithms
grep -r "DES\|MD5\|SHA1\|ECB" output/sources/

# Hardcoded keys
grep -r "SecretKeySpec\|new byte\[\]" output/sources/ | grep -v "0x00"

Network Security:

# HTTP URLs
grep -r "http://" output/sources/

# Certificate pinning
grep -r "CertificatePinner\|X509TrustManager" output/sources/

Intent Handling:

# Intent extras parsing
grep -r "getStringExtra\|getIntExtra\|getSerializableExtra" output/sources/

Code Review Checklist

  • Hardcoded secrets (API keys, passwords, tokens)
  • Insecure random number generation
  • Weak cryptography implementations
  • SQL injection vulnerabilities
  • Intent handling without validation
  • Insecure file operations
  • Logging sensitive information
  • Missing input validation

Related reading: See Java and Kotlin.


Resource Analysis

Analyzing Resources

Strings.xml:

find output/res/ -name "strings.xml" -exec cat {} \; | grep -iE "password|token|key|secret"

Network Security Config:

find output/ -name "network_security_config.xml" -exec cat {} \;

Layout Files:

# Check for hardcoded credentials in layouts
find output/res/layout/ -name "*.xml" -exec grep -l "password\|admin" {} \;


Automated Static Analysis Tools

MobSF (Mobile Security Framework)

Installation:

git clone https://github.com/MobSF/Mobile-Security-Framework-MobSF.git
cd Mobile-Security-Framework-MobSF
pip install -r requirements.txt

Usage:

# Start MobSF
python manage.py runserver

# Upload APK via web interface (http://localhost:8000)
# Or use API
curl -X POST -F "file=@target.apk" http://localhost:8000/api/v1/upload

QARK (Quick Android Review Kit)

# Install
pip install qark

# Run analysis
qark --apk target.apk

AndroBugs Framework

# Install
git clone https://github.com/AndroBugs/AndroBugs_Framework.git
cd AndroBugs_Framework
python androbugs.py -f target.apk -o report.json

Finding Hardcoded Secrets

Common Secret Locations

In Code:

jadx target.apk -d output/
grep -r "password.*=" output/sources/ | grep -v "getText()"
grep -r "api.*key\|apikey\|secret" output/sources/ -i

In Resources:

find output/res/ -type f -exec grep -l "AKIA\|AIza\|sk_live" {} \;

In Native Libraries:

strings lib/*.so | grep -iE "password|key|secret|token"

Secret Extraction Script

#!/usr/bin/env python3
import re
import os

def find_secrets(directory):
    patterns = {
        'API Key': r'[Aa][Pp][Ii][_\-]?[Kk][Ee][Yy][\s:=]+["\']?([A-Za-z0-9_\-]{20,})',
        'AWS Key': r'AKIA[0-9A-Z]{16}',
        'Private Key': r'-----BEGIN (RSA |EC )?PRIVATE KEY-----',
        'JWT': r'eyJ[A-Za-z0-9\-_]+\.eyJ[A-Za-z0-9\-_]+\.[A-Za-z0-9\-_]+',
        'Password': r'[Pp]assword[\s:=]+["\']?([^"\'\s]{8,})',
    }

    for root, dirs, files in os.walk(directory):
        for file in files:
            if file.endswith(('.java', '.kt', '.xml', '.properties')):
                filepath = os.path.join(root, file)
                try:
                    with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
                        content = f.read()
                        for secret_type, pattern in patterns.items():
                            matches = re.findall(pattern, content)
                            if matches:
                                print(f"[{secret_type}] {filepath}")
                                for match in matches[:3]:  # Limit output
                                    print(f"  {match}")
                except:
                    pass

find_secrets('output/')

Static Analysis Checklist

  • APK successfully decompiled
  • Manifest analyzed for exported components
  • Permissions reviewed and validated
  • Debuggable flag checked
  • Network security config analyzed
  • Code reviewed for common vulnerabilities
  • Resources searched for hardcoded secrets
  • Automated tools executed and reviewed
  • Native libraries analyzed if present


Static analysis provides the foundation for understanding app structure before dynamic testing. Combine findings with Dynamic Analysis for comprehensive assessments.