Mobile Malware Sevgi Com.txluwgiw
Introduction
Hello, I’m Dızmana. In the last deep dive we tore a banking trojan apart down to its native RC4 routine. This time the story ends differently, and that is exactly why it is worth telling.
The sample is a Turkish dating-scam app called Sevgi (“Love”). I pulled it straight from the URLhaus open feed, where it sits under the creduz campaign tag next to a swarm of sibling domains. I expected another easy win: decrypt the strings, find the C2, watch it call home in the emulator, done. The static side went exactly to plan. The dynamic side did not. This malware was built by someone who assumed an analyst would be watching, and it acts accordingly. By the end of this post you will see a piece of malware that we fully understood on paper but that flatly refused to reveal its live behavior inside a sandbox, and you will see every trick I used to try to force its hand.
This is a write-up about a fight that the malware partially won, and the lessons are more useful than another clean kill.
Malware Profile & IOCs
Malware Overview
| Attribute | Details |
|---|---|
| Campaign / Family | creduz (Turkish dating-scam, Android spyware/bot) |
| App Name | Sevgi |
| Payload Package | txluwgiw.com |
| Dropper Package | com.fepsfdmbeu.nlivobl |
| Dropper SHA-256 | 79287bab2f1c02cf2623efb3c997128d4eb704b4d509fea702c90ddec09b598e |
| Payload SHA-256 | ebb1510bce00dd783daa872f2d2bb1d1e3ac9e11d47915248c7f80f8ba6deeda |
| Fake Signing CN | com_android_nfc (impersonating O=Google Inc.) |
Command & Control (C2)
| Type | Value |
|---|---|
| C2 / Campaign Host | avtorizaciadating1.shop |
| Distribution Host | uzdating1.shop |
| Sibling Domains | datingtj22.shop, da-tinguz1.shop, razdev11tj.shop, kis2kis.shop |
Cryptography & Obfuscation
| Attribute | Details |
|---|---|
| String Cipher | Base64decode(s) then repeating-key XOR |
| XOR Key | JrvwjFPNf9yb3cwjaXNSLTTW7VN6 |
| Decryptor Method | lmoeqeyx.kjoi.lcmobcomyjbfxlly.ppfuynoermcebboy() |
| Native Module | okhttpintercept.so (encrypted ELF) + .dat config |
Targeting
| Attribute | Details |
|---|---|
| Primary Region | Turkey |
| Lure Theme | Online dating / romance |
| Cover Application | A trojanized WhatsApp code base used as camouflage |
Phase 1: Acquisition
There was no clever User-Agent gate this time. The creduz operators publish their droppers through short-lived .shop domains, and the abuse community catches them fast, so the sample was already sitting in the URLhaus feed with a clean download link. That is a useful lesson on its own: you do not need a victim’s phone to start hunting, you need to know where threat intelligence is published in the open.
I grabbed the APK and hashed it. A quick VirusTotal lookup confirmed I was holding a known threat and not a false alarm, with a healthy chunk of the engines already flagging it as an Android trojan.
Then I loaded it into Jadx. The first thing I always read is the manifest, because the permission set is a confession written before the trial.
The list was aggressive in a very specific way. RECEIVE_SMS and READ_SMS for intercepting one-time passwords. BIND_NOTIFICATION_LISTENER_SERVICE to read every notification the device shows. RECEIVE_BOOT_COMPLETED for persistence. QUERY_ALL_PACKAGES to inventory the installed apps. And REQUEST_INSTALL_PACKAGES, the signature of a dropper. The outer APK was not the real malware. It was a delivery vehicle.
Phase 2: Cracking the Strings
Inside the dropper resources I found three encrypted blobs, yokswa.spe (around 827 KB), pgixukmo.sps, and rlpemtdk.sps. The largest one was the real payload, packed and encrypted so that the file you upload to a scanner looks nothing like the code that eventually runs. After unpacking it I had the actual payload, a clean APK named yokswa_payload.apk, package name txluwgiw.com, and I loaded that into Jadx instead.
The payload was obfuscated the way modern Android malware always is. Class names were random noise, and every interesting string was wrapped in a call that looked like lcmobcomyjbfxlly.ppfuynoermcebboy("..."). There is no point reading code where the strings are sealed, so the decryptor is always the first target.
The routine was short and honest about itself once you read it: take the literal, Base64-decode it, then XOR the bytes against a fixed key, JrvwjFPNf9yb3cwjaXNSLTTW7VN6, cycling the key for inputs longer than its length. That is textbook repeating-key XOR over Base64, no native code, no key derivation, nothing exotic.
So I wrote a small Python script that walks every decompiled file, pulls out every ppfuynoermcebboy("...") literal, and decrypts it. That one script turned 2,636 dead strings into readable text and became the Rosetta Stone for everything that followed.
The very first prize fell out immediately. One decrypted literal read https://avtorizaciadating1.shop. There was the C2.
Phase 3: Reading the Confession
With the strings open, the personality of the malware came into focus, and it was a strange one. Thousands of the decrypted strings belonged to WhatsApp: message tables, calling sheets, bot prompt stores. This payload carries a trojanized WhatsApp code base as camouflage, so that a casual look at its classes sees a familiar messenger and moves on. Bolted onto that cover is the part that actually matters.
The spyware core is a NotificationListenerService (txluwgiw.com.Ulrjn6e58iecw). On Android, notification access is a quiet superpower. Every banking one-time password, every login alert, every two-factor prompt passes through the notification shade, and a listener reads all of it without ever touching SMS permissions. Combine that with the SMS interception this app also requests, and you have a complete pipeline for stealing the codes that protect a bank account. The decrypted strings even contained queries against WhatsApp’s own database for message_quarantine, chat_lock, and the block list.
Persistence was layered. A boot receiver, package-replaced and reboot handlers, and a WorkManager schedule to keep the malware breathing in the background.
Then I hit the part that defined the rest of this investigation. A cluster of decrypted strings was not about banks at all. It was about me.
1
2
emulatorDetected= fridaDetected= rootDetected=
adbDetected= bootTimeDetected= appsCountDetected=
These are the fields the malware reports about its own environment. Reading further, I found the data behind them: a list of emulator fingerprints (goldfish, ranchu, BlueStacks, Genymotion, Nox), a list of Frida artifacts (frida-server, frida-gadget.so, gum-js-loop, linjector), the default Frida port 27042 and its neighbors, a long list of su binary paths and root-manager packages, and a proxy-detection module that sniffs for mitmproxy, Burp, Charles, and Fiddler. This sample does not just run its payload. Before anything else, it asks one question: am I being watched?
I traced the C2 back to a single class, Gzwwswrgsnpblabpmkuxv.Wuunlygkbuvrxeayayefkfbktxgsvxh. There the base64 blob IgYCBxl8f2EHTw0NQQoNCwIxLzctID05UGdgRSIdBg== is decrypted at runtime into https://avtorizaciadating1.shop and passed straight into an Intent, the lure’s bridge back to the operator.
I also documented the native piece I would deal with later, an encrypted okhttpintercept.so paired with a .dat config. On paper the malware was fully exposed. Now I had to make it run.
Phase 4: The Sandbox Standoff
Here is where most write-ups quietly cut to a screenshot of captured C2 traffic. I am not going to do that, because it is not what happened, and the truth is the more instructive outcome.
I built a stock Android 13 emulator and routed every packet through tcpdump so nothing could leave the device without me seeing it. Then I let the malware run, on a clean boot, with no instrumentation attached at all. It started on its own through the boot receiver, it stayed alive, and over more than twenty minutes it sent exactly zero packets to anything that was not Google. No DNS lookup for avtorizaciadating1.shop. No connection to any .shop host. Nothing.
This is the anti-analysis working as designed. Remember the catch: the malware checks for an emulator, and it checks for Frida. Run it bare on the emulator and it sees the virtual hardware and stays dormant. Attach Frida to spoof the hardware and it sees Frida and kills itself. The two defenses cover each other.
So I worked the problem from both sides. First I made Frida invisible. I renamed frida-server to an innocent name and bound it to a non-standard port instead of 27042, which defeats the process-name and port scan the malware relies on. With that in place the app survived instrumentation. One small detail worth knowing: Frida reported the process under the app label Sevgi, not its package name, so the attach has to target it by PID.
Second, I went after the checks directly. I disassembled the payload with apktool and traced the anti-analysis logic to a single chokepoint, a method that runs every detector and returns a small object carrying the six boolean results. I patched that method in smali so it always reports a clean device, rebuilt the APK, re-signed it, and installed it. It ran without complaint, which told me something useful by itself: this malware does not verify its own signature, so repackaging is fair game.
And still, it stayed silent.
That silence is a finding, not a failure, so I chased down why. Reading the runtime artifacts the app wrote to its own data directory told the story. Its WorkManager database held two scheduled jobs:
The fifteen-minute job is a watchdog. Its only purpose is to check whether the malware’s alarm is still registered and to re-arm it if not. That is persistence, not communication, which is why a short capture never shows traffic from it. The real network task is the twenty-four hour job, a coroutine that drives a transfer module with a tell-tale method named setTransferCompleted.
A daily beacon explains a quiet capture, so I stopped waiting and forced it. Android lets you push a scheduled job to run immediately, so I triggered every one of the malware’s jobs by hand.
The system confirmed Running job [FORCED] for each. The malware still sent nothing. I posted fake banking notifications to feed its listener, then forced the daily worker again. Its database stayed empty. No traffic left the device.
At that point the conclusion was solid. This payload does not beacon on a timer alone. Its transfer task only has something to send when it has actually captured victim data from real, targeted banking apps, and its full activation almost certainly depends on the encrypted config and native library that the dropper provisions during a normal infection. Strip it out of that chain, drop it on a sandbox, and it correctly decides there is nothing worth saying and no one safe to say it to.
Phase 5: The Verdict (Conclusion)
Static analysis won. We unpacked the dropper, extracted and decrypted the payload, broke the Base64-plus-XOR string scheme with a twenty-line script, recovered the C2 at avtorizaciadating1.shop, and mapped the full capability set: notification interception, SMS theft for one-time passwords, layered persistence, and a WhatsApp code base worn as a disguise.
Dynamic analysis was fought to a draw. The malware carries emulator, Frida, root, and proxy detection that interlock so that neither a bare sandbox nor an instrumented one gets it to talk. I defeated the Frida checks by hiding the agent and patched the detection chokepoint out of the binary entirely, and it still refused to beacon, because the final trigger lives in a dropper-provisioned native module and in the presence of real stolen data.
That is the takeaway. A clean kill is satisfying, but a sample like this teaches more. It shows that for mature commodity malware, a sterile emulator is not enough, and that the honest result of an analysis is sometimes “here is everything it can do, and here is the precise reason it would not do it for me.” The next round belongs to a real, rooted device running the complete dropper to payload chain, and that is where I will pick this one up.
A note on the live infrastructure: the C2 and distribution domains in this post are active threats. I have reported them to USOM (the Turkish national CERT) for takedown. Do not open them in a browser.
See you in the next deep dive. Stay safe.
- Dızmana










