This report provides a detailed analysis of an Android application file identified as a sophisticated Remote Access Trojan (RAT). Static and dynamic analysis techniques revealed the application to be a variant of the well-known SpyNote malware family. The malware disguises as a benign utility but, upon installation, deploys extensive spyware capabilities, including keylogging, video/audio surveillance, and data exfiltration. It establishes a persistent, low-level TCP connection to a Command and Control (C2) server for communication, using a custom binary protocol with GZIP compression. The sample exhibits strong anti-analysis features, including emulator detection, to hinder investigation. This analysis outlines the malware's architecture, communication protocol, and key malicious functionalities.
First thing I need in the task is to find out what file types or architecture with which I am working. File utility is a great choice for such cases.
As I know one of the files is an APK so this must be the target one but for additional confirmation stage I can check manually the headers of that file.
Figure 1: File hex view
As seen a PK magic bytes with AndroidManifest.xml confirms that I am dealing with APK.
On dealing with APK, the first thing that comes to mind is the manifest file which can contain many interesting things and give clues for analysis. As this file is not a regular xml format it must be parsed with some tool, I prefer using “jadx-gui” all you need in one place. I can also use apktools as a second option sometimes.
Figure 2: Permissions from xml 1
Figure 3: Permissions from xml 2
From xml we can find the following:
Based on the extensive and alarming permissions requested, the application can be classified as Spyware with RAT (Remote Access Trojan) capabilities.
A quick strings analysis may give additional pieces of information.
Interesting strings are found at the like some Azure messages and list of IP addresses
1[.]0[.]0[.]0/8 100[.]0[.]0[.]0/10 100[.]128[.]0[.]0/9 101[.]0[.]0[.]0/8 102[.]0[.]0[.]0/7 104[.]0[.]0[.]0/5 11[.]0[.]0[.]0/8 11000011 112[.]0[.]0[.]0/5 12[.]0[.]0[.]0/6 120[.]0[.]0[.]0/6 124[.]0[.]0[.]0/7 126[.]0[.]0[.]0/8 128[.]0[.]0[.]0/3 16[.]0[.]0[.]0/4 160[.]0[.]0[.]0/5 168[.]0[.]0[.]0/8 169[.]0[.]0[.]0/9 169[.]128[.]0[.]0/10 169[.]192[.]0[.]0/11 169[.]224[.]0[.]0/12 169[.]240[.]0[.]0/13 169[.]248[.]0[.]0/14 169[.]252[.]0[.]0/15 169[.]255[.]0[.]0/16 170[.]0[.]0[.]0/7 172[.]0[.]0[.]0/12 172[.]128[.]0[.]0/9 172[.]32[.]0[.]0/11 172[.]64[.]0[.]0/10 173[.]0[.]0[.]0/8 174[.]0[.]0[.]0/7 176[.]0[.]0[.]0/4 192[.]0[.]1[.]0/24 192[.]0[.]128[.]0/17 192[.]0[.]16[.]0/20 192[.]0[.]3[.]0/24 192[.]0[.]32[.]0/19 192[.]0[.]4[.]0/22 192[.]0[.]64[.]0/18 192[.]0[.]8[.]0/21 192[.]1[.]0[.]0/16 192[.]128[.]0[.]0/11 192[.]16[.]0[.]0/12 192[.]160[.]0[.]0/13 192[.]169[.]0[.]0/16 192[.]170[.]0[.]0/15 192[.]172[.]0[.]0/14 192[.]176[.]0[.]0/12 192[.]192[.]0[.]0/10 192[.]2[.]0[.]0/15 192[.]32[.]0[.]0/11 192[.]4[.]0[.]0/14 192[.]64[.]0[.]0/10 192[.]8[.]0[.]0/13 193[.]0[.]0[.]0/8 194[.]0[.]0[.]0/7 196[.]0[.]0[.]0/7 198[.]0[.]0[.]0/12 198[.]128[.]0[.]0/9 198[.]16[.]0[.]0/15 198[.]20[.]0[.]0/14 198[.]24[.]0[.]0/13 198[.]32[.]0[.]0/12 198[.]48[.]0[.]0/15 198[.]50[.]0[.]0/16 198[.]51[.]0[.]0/18 198[.]51[.]101[.]0/24 198[.]51[.]102[.]0/23 198[.]51[.]104[.]0/21 198[.]51[.]112[.]0/20 198[.]51[.]128[.]0/17 198[.]51[.]64[.]0/19 198[.]51[.]96[.]0/22 198[.]52[.]0[.]0/14 198[.]56[.]0[.]0/13 198[.]64[.]0[.]0/10 199[.]0[.]0[.]0/8 2[.]0[.]0[.]0/7 200[.]0[.]0[.]0/7 202[.]0[.]0[.]0/8 203[.]0[.]0[.]0/18 203[.]0[.]112[.]0/24 203[.]0[.]114[.]0/23 203[.]0[.]116[.]0/22 203[.]0[.]120[.]0/21 203[.]0[.]128[.]0/17 203[.]0[.]64[.]0/19 203[.]0[.]96[.]0/20 203[.]1[.]0[.]0/16 203[.]128[.]0[.]0/9 203[.]16[.]0[.]0/12 203[.]2[.]0[.]0/15 203[.]32[.]0[.]0/11 203[.]4[.]0[.]0/14 203[.]64[.]0[.]0/10 203[.]8[.]0[.]0/13 204[.]0[.]0[.]0/6 208[.]0[.]0[.]0/4 224[.]0[.]0[.]0/4 32[.]0[.]0[.]0/3 4[.]0[.]0[.]0/6 64[.]0[.]0[.]0/3 8[.]0[.]0[.]0/7 96[.]0[.]0[.]0/6
This list can simply contain some c2 server IPs which are worth analyzing in future stages or may be just Version Updates.
Now jadx-gui will come to the playground one more time.
First, I need to determine a goal to follow to just save time and effort and not to get lost in the whole program.
A good place to start is MainActivity. It is like the entry point of the app but while locating it another interesting class needs my attention MyApplication.
Figure 4: MyApplication Activity
Primary Function: Crash Handling and Persistence The main purpose of this class is to set up a custom UncaughtExceptionHandler. This is a mechanism that catches any crash or unhandled error that occurs anywhere in the application.
Remote Error Logging When the app crashes, the uncaughtException method is triggered. It gathers a lot of information:
It then checks a boolean flag: ljkavjgzxyxekzboggghzdnmgnwrikzcaafugbjxudfxiqqfko38.echo. the class extends Service, and the Boolean default is false.
If true (meaning a C2 connection is likely active), it immediately sends this crash report to the C2 server using the obfuscated method qmyydfbllhmhlfxhghgkveoykvkmngrzshtgbnclxyzfisljag52.
If false, it saves the crash report locally using MySettings.Write. It will likely try to upload this saved report the next time it connects to the C2.
Automatic Restart (Persistence) After logging the crash, it uses AlarmManager to schedule a restart of the MainActivity one second later. This is a powerful persistence technique. No matter why the app crashes, it will try to bring itself back online automatically.
Figure 5: qmyydfbllhmhlfxhghgkveoykvkmngrzshtgbnclxyzfisljag52 method
This is the core data transmission function. A step by step analysis shows that:
Figure 6: uednoekajnvlmpjmkltaderreztowxjocwrpjejymruiisqojg42 method
To fully understand the target “possible_crypto”, I need first to reverse the atbhwqtktfcdakyackyazdyvurjfhoenadhczohivybdmogdla47 method.
Figure 7: atbhwqtktfcdakyackyazdyvurjfhoenadhczohivybdmogdla47 method
This method is surprisingly simple. It does one thing: it compresses the data using the GZIP algorithm.
This technique is widely used to reduce the message size.
Figure 9: Send_Data_to_C2 Deobfuscated
This function serves as the malware's C2 transport engine, responsible for sending all prepared data to the command and control server. It is a critical component that highlights the malware's sophisticated design.
Figure 10: Send_Data_to_C2 Xref calls
The function has been called 71 different times so it must be info stealer as assumed previously.
Now I need to find the socket information (IP/Port) so from this understanding of communication routine I think searching for “new Socket()” will give me a place to search.
Figure 11: Socket creation search
As search results show, there are three socket creations among the malware sample. To retrieve the C2 socket information I will simply search the three places.
The CameraHandler class is a specialized module with a single, powerful purpose: to secretly capture and stream live video from the device's camera.
The Service_Extended class has another socket creation routine.
Figure 13: Service_Extended socket creation routine
From the figure you find that IP/Port are passed through two string arrays defined previously.
Figure 14: Socket data variable initiation
From Figure 15 it is obvious that those two variables are initialized with arguments passed to the function.
Tracing function calls I simply found the hardcoded B64 IP/Port
Figure 15: IP/Port hardcoded
Decoding them using CyberChef I got simply “47.82.2.89 / 7772” also there is a variable called connection key containing another b64 value equal to “1234”.
Now it is time to see what the program does as a quick view of its capabilities.
The primary purpose of MainActivity is not to be the malware itself, but to act as a loader and setup agent. It has three main jobs:
Work method
Figure 16: Work method definition
From Figure 16 I can say that this method does only three things:
This means that as soon as MainActivity is created, it calls this method. This ensures that the two most important background services are launched. Service_Extended is the class we suspect contains the C2 IP/Port and manages the persistent connection. This Work() method is the trigger that kicks off the entire malicious operation in the background, completely hidden from the user who is looking at the WebView.
Next interesting part is Anti-Analysis and Evasion.
Figure 17: Anti-Emulation
The malware desperately wants to avoid being analyzed. It does this with the isEmu_DIV_ID_lator() method.
return (Build.BRAND.startsWith(utilities.whrjmjiqnkguefdnveonzicbymsvunvznsfnnrkuhgqvfvduww53("genfxubzhcydkvtnlvsauqnsncixsitzjdmjzuenjvcrhaihiwxvoetlriukkclmiggbhnyesvednetygsrqhpsgjkfxprifvggwgeqstoetlxezczrnllsirpjuecdjyimwurzxwxgnnhgbbdokqwvgrnjtvekshgkavzrlpzgjfvwnfxwpvfuvfejqybjkcrpsjtztkoioyhzkgaaxzinejfhsrccfdhfatlolzhatgmiusitgtbuvhjfcmozbyvyleqfgwaafmsnlykqgmjkuolfnggssbgkzsitwforftdnfafjoobumgkadynqboiiddgjurizkfvxyelyzmeeqjaycmxtnojtyvrwghqgavirqyvpyakmhyitxlvowcflhlywkizvjusbongn55eric", "fxubzhcydkvtnlvsauqnsncixsitzjdmjzuenjvcrhaihiwxvoetlriukkclmiggbhnyesvednetygsrqhpsgjkfxprifvggwgeqstoetlxezczrnllsirpjuecdjyimwurzxwxgnnhgbbdokqwvgrnjtvekshgkavzrlpzgjfvwnfxwpvfuvfejqybjkcrpsjtztkoioyhzkgaaxzinejfhsrccfdhfatlolzhatgmiusitgtbuvhjfcmozbyvyleqfgwaafmsnlykqgmjkuolfnggssbgkzsitwforftdnfafjoobumgkadynqboiiddgjurizkfvxyelyzmeeqjaycmxtnojtyvrwghqgavirqyvpyakmhyitxlvowcflhlywkizvjusbongn55")) && Build.DEVICE.startsWith("generic")) || Build.FINGERPRINT.startsWith(utilities.whrjmjiqnkguefdnveonzicbymsvunvznsfnnrkuhgqvfvduww53("genfxubzhcydkvtnlvsauqnsncixsitzjdmjzuenjvcrhaihiwxvoetlriukkclmiggbhnyesvednetygsrqhpsgjkfxprifvggwgeqstoetlxezczrnllsirpjuecdjyimwurzxwxgnnhgbbdokqwvgrnjtvekshgkavzrlpzgjfvwnfxwpvfuvfejqybjkcrpsjtztkoioyhzkgaaxzinejfhsrccfdhfatlolzhatgmiusitgtbuvhjfcmozbyvyleqfgwaafmsnlykqgmjkuolfnggssbgkzsitwforftdnfafjoobumgkadynqboiiddgjurizkfvxyelyzmeeqjaycmxtnojtyvrwghqgavirqyvpyakmhyitxlvowcflhlywkizvjusbongn55eric", "fxubzhcydkvtnlvsauqnsncixsitzjdmjzuenjvcrhaihiwxvoetlriukkclmiggbhnyesvednetygsrqhpsgjkfxprifvggwgeqstoetlxezczrnllsirpjuecdjyimwurzxwxgnnhgbbdokqwvgrnjtvekshgkavzrlpzgjfvwnfxwpvfuvfejqybjkcrpsjtztkoioyhzkgaaxzinejfhsrccfdhfatlolzhatgmiusitgtbuvhjfcmozbyvyleqfgwaafmsnlykqgmjkuolfnggssbgkzsitwforftdnfafjoobumgkadynqboiiddgjurizkfvxyelyzmeeqjaycmxtnojtyvrwghqgavirqyvpyakmhyitxlvowcflhlywkizvjusbongn55")) || Build.FINGERPRINT.startsWith(EnvironmentCompat.MEDIA_UNKNOWN) || Build.HARDWARE.contains("goldfish") || Build.HARDWARE.contains("ranchu") || Build.MODEL.contains("google_sdk") || Build.MODEL.contains("Emulator") || Build.MODEL.contains(utilities.whrjmjiqnkguefdnveonzicbymsvunvznsfnnrkuhgqvfvduww53("Android SDK bfxubzhcydkvtnlvsauqnsncixsitzjdmjzuenjvcrhaihiwxvoetlriukkclmiggbhnyesvednetygsrqhpsgjkfxprifvggwgeqstoetlxezczrnllsirpjuecdjyimwurzxwxgnnhgbbdokqwvgrnjtvekshgkavzrlpzgjfvwnfxwpvfuvfejqybjkcrpsjtztkoioyhzkgaaxzinejfhsrccfdhfatlolzhatgmiusitgtbuvhjfcmozbyvyleqfgwaafmsnlykqgmjkuolfnggssbgkzsitwforftdnfafjoobumgkadynqboiiddgjurizkfvxyelyzmeeqjaycmxtnojtyvrwghqgavirqyvpyakmhyitxlvowcflhlywkizvjusbongn55uilt for x86", "fxubzhcydkvtnlvsauqnsncixsitzjdmjzuenjvcrhaihiwxvoetlriukkclmiggbhnyesvednetygsrqhpsgjkfxprifvggwgeqstoetlxezczrnllsirpjuecdjyimwurzxwxgnnhgbbdokqwvgrnjtvekshgkavzrlpzgjfvwnfxwpvfuvfejqybjkcrpsjtztkoioyhzkgaaxzinejfhsrccfdhfatlolzhatgmiusitgtbuvhjfcmozbyvyleqfgwaafmsnlykqgmjkuolfnggssbgkzsitwforftdnfafjoobumgkadynqboiiddgjurizkfvxyelyzmeeqjaycmxtnojtyvrwghqgavirqyvpyakmhyitxlvowcflhlywkizvjusbongn55")) || Build.MANUFACTURER.contains("Genymotion") || Build.PRODUCT.contains("sdk_google") || Build.PRODUCT.contains("google_sdk") || Build.PRODUCT.contains("sdk") || Build.PRODUCT.contains("sdk_x86") || Build.PRODUCT.contains(utilities.whrjmjiqnkguefdnveonzicbymsvunvznsfnnrkuhgqvfvduww53("sdk_gphone6fxubzhcydkvtnlvsauqnsncixsitzjdmjzuenjvcrhaihiwxvoetlriukkclmiggbhnyesvednetygsrqhpsgjkfxprifvggwgeqstoetlxezczrnllsirpjuecdjyimwurzxwxgnnhgbbdokqwvgrnjtvekshgkavzrlpzgjfvwnfxwpvfuvfejqybjkcrpsjtztkoioyhzkgaaxzinejfhsrccfdhfatlolzhatgmiusitgtbuvhjfcmozbyvyleqfgwaafmsnlykqgmjkuolfnggssbgkzsitwforftdnfafjoobumgkadynqboiiddgjurizkfvxyelyzmeeqjaycmxtnojtyvrwghqgavirqyvpyakmhyitxlvowcflhlywkizvjusbongn554_arm64", "fxubzhcydkvtnlvsauqnsncixsitzjdmjzuenjvcrhaihiwxvoetlriukkclmiggbhnyesvednetygsrqhpsgjkfxprifvggwgeqstoetlxezczrnllsirpjuecdjyimwurzxwxgnnhgbbdokqwvgrnjtvekshgkavzrlpzgjfvwnfxwpvfuvfejqybjkcrpsjtztkoioyhzkgaaxzinejfhsrccfdhfatlolzhatgmiusitgtbuvhjfcmozbyvyleqfgwaafmsnlykqgmjkuolfnggssbgkzsitwforftdnfafjoobumgkadynqboiiddgjurizkfvxyelyzmeeqjaycmxtnojtyvrwghqgavirqyvpyakmhyitxlvowcflhlywkizvjusbongn55")) || Build.PRODUCT.contains("vbox86p") || Build.PRODUCT.contains("emulator") || Build.PRODUCT.contains("simulator");
This is a deliberate and strong anti-analysis technique. It is designed to prevent dynamic analysis in a safe environment.
Another amazing part is AskDraw() method.
It is designed to get the "Draw Over Apps" permission (MANAGE_OVERLAY_PERMISSION). This is one of the most dangerous permissions on Android. It allows an app to draw on top of any other app.
The Trick here is to convince the user, the app spoofs its identity. It tries to set its pop-up dialog's icon and title to that of the Google Play Store.
Figure 18: Identity Spoofing
With this permission, the malware can create fake login screens on top of legitimate banking, social media, or email apps to steal user credentials (a phishing attack known as tapjacking).
The tracking mechanism is implemented in the onDestroy() and finish() methods of MainActivity. These methods are part of the Android Activity Lifecycle and are called automatically by the system whenever the main user interface of the app is about to be closed or destroyed.
Figure 19: Tracking User Activity
A user action, such as pressing the back button to exit the app or swiping it away from the recent apps list, triggers the onDestroy() or finish() method.
Before sending anything, the code checks this boolean flag. Service_Extended.echo is almost certainly a status variable that is set to true only after the malware has successfully established a connection and performed a handshake with the C2 server. This check prevents the app from trying to send data when it is offline, which would cause errors.
The code calls the data sending function with utilities.ActiveNow as the command string. This is not a random name. It is a clear, predefined command that the C2 server is programmed to understand. It essentially means "Activity Status Update."
The actual data being sent is just a single space character. In this case, the content of the payload is irrelevant. The entire message is conveyed by the command itself. The C2 server simply needs to see the ActiveNow command arrive to know what happened.
The command and the empty payload are then passed through the entire communication pipeline we have analyzed: they are compressed with GZIP, formatted into the custom binary packet, and sent over the raw TCP socket to the C2 server.
Why This is Done
File Hash (SHA-256) | b35d06e5bd966766f3f6955af7d0e1ce061e103a6b2862ca969b7bc6aff8634b |
---|---|
File Hash (md5) | fa54bdc24c3e37b7f322e02da4d388fc |
Package Name | nervous.missing.somerset |
C2 Server IP | 47.82.2.89 |
C2 Server Port | 7772 |