Operation NoVoice: Rootkit Tells No Tales

Authored By: Ahmad Zubair Zahid 
McAfee’s mobile research team identified and investigated an Android rootkit campaign tracked as Operation Novoice.

Authored By: Ahmad Zubair Zahid 

McAfee’s mobile research team identified and investigated an Android rootkit campaign tracked as Operation Novoice. The malware described in this blog relies on vulnerabilities Android made patches available for in 2016 – 2021. All Android devices with a security patch level of 2021-05-01 or higher are not susceptible to the exploits that we were able to obtain from the command-and-control server. However patched devices that downloaded these apps could have been exposed to unknown potential payloads outside of what we discovered. The attack begins with apps that were previously available on Google Play that appear to be simple tools such as cleaners, games, or gallery utilities. When a user downloaded and opened one of these apps, it appeared to behave as advertised, giving no obvious signs of malicious activity.  

In the background, however, the app contacts a remote server, profiles the device, and downloads root exploits tailored to that device’s specific hardware and software. If the exploits succeed, the malware gains full control of the device. From that moment onward, every app that the user opens are injected with attacker‑controlled code.  

This allows the operators to access any app data and exfiltrate it to their servers. One of the targeted apps is WhatsApp. We recovered a payload designed to execute when WhatsApp launches, gather all necessary data to clone the session, and send it to the attacker’s infrastructure.   

On older, unsupported devices (Android 7 and lower) that no longer receive Android security updates as of September 2021, this rootkit is highly persistent; a standard factory reset will not remove it, and only reflashing the device with a clean firmware will fully restore the device.   

In total, we identified more than 50 of these malicious apps on Google Play, with at least 2.3 million downloads.  

McAfee identified the malicious apps, conducted the technical analysis, and reported its findings to Google through responsible disclosure channels. Following McAfee’s report, Google removed the identified apps from Google Play and banned the associated developer accounts. McAfee is a member of the App Defense Alliance, which supports collaboration across the mobile ecosystem to improve user protection. McAfee Mobile Security detects this malware as a High-Risk Threat. For more information, and to get fully protected, visit McAfee Mobile Security 

Background And Key Findings

Android malware has been moving toward modular frameworks that update themselves remotely and adapt to each device. Campaigns like Triada and Keenadu have shown that replacing system libraries gives attackers persistence to survive factory resets. BADBOX has shown that backdoors pre-installed through the supply chain can reach millions of devices. Recent research has confirmed links between several of these families, suggesting shared tooling rather than isolated efforts. 

NoVoice fits both trends but does not rely on supply chain access. It reaches devices through Google Play and achieves the same level of persistence through exploitation. McAfee’s investigation revealed the following key findings: 

  • All carrier apps were distributed through Google Play. No sideloading required, no user interaction beyond opening the app. 
  • C2 infrastructure remains active at the time of publication. 
  • The C2 server profiles each device and delivers root exploits matched to its hardware and software version. 
  • The rootkit overwrites a core system library, causing every app on the device to run attacker code at launch. 
  • The infection survives factory reset and can only be removed by reflashing the firmware. 
  • The chain is fully plugin-based. Operators can push any payload to any app on the device at runtime. 
  • The only task we recovered clones WhatsApp sessions, but the framework is designed to accept any objective.

Naming  

The name comes from R.raw.novioce, a silent audio resource embedded in one of the later-stage payloads. It plays at zero volume to keep a foreground service alive, abusing Android’s media playback exemption. We believe it is a deliberate misspelling of “no voice.” 

Distribution Method 

All carrier apps were distributed through Google Play and request no unusual permissions. Their manifests include the same SDKs any legitimate app would (Firebase, Google Analytics, Facebook SDK, AndroidX). The malicious components are registered under tampered com.facebook.utils, blending in with the real Facebook SDK classes the apps already include.  

An example of one of the apps with hidden malware.
Figure 1One of the carrier apps on Google Play 

The initial payload is embedded in the app’s asset directory as a polyglot image. This means the file displays and renders a normal image, but a deeper inspection reveals that the encrypted malicious payload is appended after the PNG IEND marker. Since that marker signals to image viewers that the image data ends there, the appended payload remains hidden during normal viewing.

Geographical Prevalence 

The geographical prevalence map shows the highest infection rates in Nigeria, Ethiopia, Algeria, India, and Kenya, regions where budget devices and older Android versions that no longer receive security updates are common. 

Figure 2: Affected Users Around the World
Figure 2: Affected users around the world

Malware Analysis

The following breakdown walks through each stage of the chain in order, from the moment a user opens the app to the moment stolen data leaves the device. No single file contains the full chain. Each stage decrypts and loads the next, most are delivered from the server at runtime. 

Figure 3. The NoVoice Rootkit Payloads
Figure 3. The NoVoice rootkit payloads

Stage 1: The Delivery

The moment the app opens, code injected into the legitimate Facebook SDK initialization path runs automatically. No user interaction is needed. It first checks whether the device has already been processed and, in most samples, whether it is running Android 12L or below. A subset of the carrier apps skips the version check entirely. If either check fails, it stops and logs a message disguised as a Facebook SDK error: “FacebookSdk: Failed in initStore.” 

If the device was already processed, the code cleans up files assumed to be left behind by previous runs, including paths that do not belong to any standard Android component. None of these are visible to the user. 

If the checks pass, the app reads a polyglot image from its own assets’ directory, extracts the encrypted payload (enc.apk) hidden after the image data, decrypts it to produce h.apk, and loads it into memory. It then deletes all intermediate files, temporary directories. 

Figure 4: Normal Looking Image with Malicious Payload
Figure 4: Normal looking image with malicious payload
Figure 5: The malicious payload begins after the IEND marker, starting with the magic value CAFEBABE.
Figure 5: The malicious payload begins after the IEND marker, starting with the magic value CAFEBABE

Stage 2: The Gatekeeper 

The decrypted payload (h.apk) loads a native library (libkwc.so) that controls the rest of this stage. It first verifies it is running inside the intended carrier app by checking the package name and signing certificate against hardcoded values. It also checks whether the app is running in a debug environment. 

libkwc.so contains two encrypted embedded payloads. The first (sec.jar) is a gate designed to detect analysis environments. It runs 15 checks, including emulator detection, root indicators, debuggers, VPN and proxy connections, Xposed hooks, and GPS geofencing. If any check fails, the chain stops silently. The geofence compares the device’s location against bounding boxes for Beijing and Shenzhen hardcoded in the native library and excludes devices confirmed to be inside them. If the app does not have location permission, it cannot determine the device’s position and defaults to letting the chain continue. Two brands get special treatment: on Gionee devices, all checks except the geofence are skipped; on Meizu devices, the chain follows a separate code path entirely. Gionee devices have a documented history of shipping with pre-installed malware through supply chain compromise. 

Only if all checks in sec.jar pass does libkwc.so decrypt and load the second payload (hex.jar), which begins contacting the C2 server. If the gate fails, it deletes the working directory and stops. 

Figure 6: 15 validation checks before proceeding to the next stage
Figure 6: 15 validation checks before proceeding to the next stage

Stage 3: The Plugin 

Once the gate passes, hex.jar sets up a plugin framework built on an internal codebase the authors refer to as “kuwo” in their package names. It checks in with a C2 server every 60 seconds. Updates are delivered the same way as the initial payload: as image files with encrypted data hidden after the image content. The server returns download URLs in a response field named warningIcon, disguising plugin downloads as icon fetches. A log-deletion routine runs alongside the framework to remove forensic traces from the device. 

The first plugin delivered (rt) acts as an orchestrator. It manages sub-plugins and handles C2 communication. It checks in with the server, sending over 30 device identifiers including hardware model, kernel version, installed packages, and whether the device has already been rooted. The campaign’s name comes from this plugin: it embeds a silent audio resource named R.raw. novioce. 

The checkin tells the server two things: who this device is and whether it has already been rooted. If it has not, rt_plugin downloads security.jar, moving the chain into root exploitation. 

Figure 7: MediaPlayer initialized to load the embedded no voice audio
Figure 7: MediaPlayer initialized to load the embedded NoVoice audio

Stage 4: The Exploit 

security.jar first checks whether the device is already rooted. If it has been, it stops. For unrooted devices, it sends the device’s chipset, kernel version, security patch date, and other identifiers to the C2. The server responds with a list of exploit binaries matched to that specific device. 

Before running any exploit, the rootkit installer (CsKaitno.d) is decrypted from an embedded resource and written to disk. The rootkit is already in place before any exploit runs. 

The exploits are downloaded one at a time from the C2’s CDN, each encrypted and verified before execution. We recovered 22 exploits in total. Our deep analysis of one revealed a three-stage kernel attack: an IPv6 use-after-free for kernel read, a Mali GPU driver vulnerability for kernel read/write, and finally credential patching and SELinux disablement. 

The expected end result is the same across all exploits: a root shell with SELinux disabled. From that shell, the exploit loads CsKaitno.d. This is where exploitation ends and persistence begins. 

Figure 8: SELinux enforcement disabled as part of the exploit chain.
Figure 8: SELinux enforcement disabled as part of the exploit chain

Stage 5: The Rootkit 

CsKaitno.d carries four encrypted payloads: library hooks for ARM32 and ARM64 (asbymol and bdlomsd), a bytecode patcher (jkpatch), and a persistence daemon (watch_dog). It first removes files associated with possible competing rootkits, then decrypts and writes its own payloads to disk. 

The installer backs up the original libandroid_runtime.so and replaces it with a hook binary matched to the device’s architecture. It also replaces libmedia_jni.so. The replacements are not copies of the original libraries. They are wrappers that intercept the system’s own functions. When any hooked function runs, it redirects to attacker code. 

Figure 9: Rootkit copying and preparing modified system libraries before remounting the filesystem as writable.
Figure 9: Rootkit copying and preparing modified system libraries before remounting the filesystem as writable

After replacing the libraries, jkpatch modifies pre-compiled framework bytecode on disk. This is a second layer of persistence: even if someone restores the original library, the framework’s own compiled code still contains the injected redirections

Stage 6: The Watchdog 

To survive reboots, the installer replaces the system crash handler with a rootkit launcher, installs recovery scripts, and stores a fallback copy of the exploitation stage on the system partition. If any component is removed, the rootkit can reinstall itself. 

It then deploys a watchdog daemon (watch_dog) that checks the installation every 60 seconds. If anything is missing, it reinstalls it. If that fails repeatedly, it forces a reboot, bringing the device back up with the rootkit intact. 

After cleaning up all staging files, the installer marks the device as compromised. On the next boot, the system’s process launcher (zygote) loads the replaced library, and every app it starts inherits the attacker’s code. 

Figure 10: Watchdog payload decrypted, written to disk, permissioned, and launched with a 60‑second restart interval.
Figure 10: Watchdog payload decrypted, written to disk, permissioned, and launched with a 60‑second restart interval

Stage 7: The Injection 

On the next boot, every app on the device loads the replaced system library. The injected code decides what to do based on which app it is running inside. Two payloads activate depending on the app. The malware authors named them BufferA and BufferB in their own code. Both are embedded as fragments inside the replaced libandroid_runtime.so from Stage 5, assembled in memory at runtime, and deleted from disk immediately after loading, leaving no files behind. BufferA runs inside the system’s package installer and can silently install or uninstall apps. BufferB runs inside any app with internet access. 

BufferB is the campaign’s primary post-exploitation tool. It operates two independent C2 channels with separate encryption keys and beacon intervals. Both channels send device fingerprints to the C2 and receive task instructions in return. 

If all primary domains fail and three or more days pass without contact, a fallback routine activates between 1 and 4 AM, reaching out to api[.]googlserves[.]com for a fresh domain list. Because BufferB runs inside any app with internet access, it can be active in dozens of apps simultaneously on a single device. 

Figure 11: Injection logic selecting BufferA for the package installer and BufferB for all other apps.
Figure 11: Injection logic selecting BufferA for the package installer and BufferB for all other apps

Stage 8: The Theft 

The only task payload we recovered is PtfLibc, delivered to BufferB from Alibaba Cloud OSS. Its target is WhatsApp. 

PtfLibc copies WhatsApp’s encryption database, extracts the device’s Signal protocol identity keys and registration ID, and pulls the most recent signed prekey. It also reads 12 keys from WhatsApp’s local storage, including the phone number, push name, country code, and Google Drive backup account. For the client keypair, it tries multiple decryption methods depending on how the device stores the key. 

It sends the stolen data to api[.]googlserves[.]com through multiple layers of encryption and deletes the temporary database copy when done. 

With these keys and session data, an attacker can clone the victim’s WhatsApp session onto another device. 

Figure 12: Code accessing and copying WhatsApp’s encrypted Signal protocol databases for exfiltration.
Figure 12: Code accessing and copying WhatsApp’s encrypted Signal protocol databases for exfiltration

Infrastructure 

The campaign spreads its C2 communication across multiple domains, each serving a different function. 

fcm[.]androidlogs[.]com handles initial device enrollment. Once the plugin framework activates, stat[.]upload-logs[.]com takes over as the primary C2 for plugin delivery, device checkin, exploit distribution, and result reporting. config[.]updatesdk[.]com serves as its fallback. Exploit binaries are hosted separately on download[.]androidlogs[.]com, with an S3-accelerated endpoint (logserves[.]s3-accelerate[.]amazonaws[.]com) as the primary CDN. This endpoint returned 403 errors during our analysis. 

Task payloads for BufferB are hosted on Alibaba Cloud OSS (prod-log-oss-01[.]oss-ap-southeast-1[.]aliyuncs[.]com). PtfLibc beacons to api[.]googlserves[.]com, a domain designed to look like Google service traffic at a glance. 

The domain separation is deliberate. Taking down one domain does not affect the others. The C2 can update BufferB’s domain lists at runtime, and a fallback routine fetches fresh domains from hardcoded backup endpoints if all configured domains go silent for three or more days. 

Recommendations 

Because the rootkit writes to the system partition, a factory reset does not remove it. A reset wipes user data but leaves system files intact. Compromised devices require a full firmware reflash to return to a clean state. Blocking the C2 domains and beacon patterns listed in this report at the network level can disrupt the chain at multiple stages. 

Attribution  

Several indicators link NoVoice to the Android.Triada family. The property (os.config.ppgl.status) NoVoice sets to mark a device as compromised is a known indicator of compromise for Android.Triada.231, a variant that uses the same property to track installation state. Both NoVoice and Triada.231 persist by replacing libandroid_runtime.so and hooking system functions so that every app runs attacker code at launch. Whether NoVoice is a direct evolution of Triada.231, a fork of its codebase, or a separate group reusing proven techniques, the shared approach suggests access to a common toolchain. 

Conclusion 

What makes NoVoice dangerous is not any single technique. It is the engineering effort behind the full chain: a self-healing pipeline that goes from a Play Store install to code execution inside every app on the device, survives factory reset, and monitors its own installation. The operators built a delivery system, an infrastructure. 

We recovered one task. The framework is designed to accept any number of them, for any app, at any time. The C2 infrastructure remains active. We do not know what other objectives have been deployed before, during, or after our analysis. The WhatsApp session theft we observed may be the least of it. 

The rootkit’s persistence model, overwriting a system library inherited by every process, patching pre-compiled framework bytecode, and monitoring its own installation with a watchdog, makes remediation difficult. 

This research underscores McAfee’s ongoing role in identifying advanced mobile threats and working with platform partners to protect users before largescale harm occurs. 

References 

https://www.kaspersky.com/blog/triada-trojan/11481/ 

https://www.kaspersky.com/about/press-releases/kaspersky-discovers-keenadu-a-multifaceted-android-malware-that-can-come-preinstalled-on-new-devices 

https://www.humansecurity.com/learn/blog/badbox-peachpit-and-the-fraudulent-device-in-your-delivery-box/ 

Indicators of Compromise 

Command and Control Servers 

api.googlserves[.]com 

api.uplogconfig[.]com 

avatar.ttaeae[.]com 

awslog.oss-accelerate.aliyuncs[.]com 

check.updateconfig[.]com 

config.googleslb[.]com 

config.updatesdk[.]com 

dnskn.googlesapi[.]com 

download.androidlogs[.]com 

fcm.androidlogs[.]com 

log.logupload[.]com 

logserves.s3-accelerate.amazonaws[.]com 

prod-log-oss-01.oss-ap-southeast-1.aliyuncs[.]com 

sao.ttbebe[.]com 

stat.upload-logs[.]com 

upload.crash-report[.]com 

nzxsxn.98kk89[.]com 

98kk89[.]com 

Carrier App Samples 

03e62ac5080496c67676c0ef5f0bc50fc42fc31cf953538eda7d6ec6951979d8,com.filnishww.fluttbuber.storagecleaner, 

066a096a3716e02a6a40f0d7e6c1063baecbebc9cbcc91e7f55b2f82c0dad413,com.wififinder.wificonnect, 

0751decd391fa76d02329b0726c308206e58fc867f50283aa688d9fe0c70e835,com.wuniversal.lassistant, 

07a9d41c1c775def78a017cf1f6e65266382e76de0f05400b3296e2230979664,com.dynamicpuzzle.cvbfhf, 

0f28c49b24070a36dec09dd9d4b768e1ef6583b4891eca2e935a304ce704fcce,com.wgoddessg.sgallery, 

106edd06b6961c3d38edfefd2869ee05285f11b68befe145b124794d0e79e766,com.crazycodes.blendphoto, 

183e9174e51786be77d1341bcf7f05514f581823532028119c5844a8a5111848,com.colorbrickelim.inationl, 

1e0376330ff9e97f798870da8433c81e39f3591c82497ca1f6b5f00878d0221a,com.crazycodes.photomotion, 

1e7fe0ae7546162f23ff4f6e570f51b38562bf4f0ffd9305533b43d19574be38,com.swiftc.tcleans, 

1e8b048c8d32662f340787893d9ca824b039c14fb91bcc16e185a8bb872e0b80,com.mybatice.googcomlayou.phonecleaner, 

224e2395d3df96cf19e0b7be9731452da5b568026d81bd0981e48893f6a66859,com.glamorousg.sgoddesslys, 

2c2c965f3d091693bc6906fc2ed8d03ffccb84e0665841f2d073c2f0a09261bc,com.myapps.gooble.mobile1.maxclean, 

30504104f232a990f8226ff746b1718aafb727ce111d5a538962cc5e06c4259a,com.mybatice.smartersleep.junkfiles, 

3937b0bec287662fd82fca4693c8b3619b8c61eca7fe6efa7540c1ae291f8759,com.crazycodes.beautycam, 

4830a985f064974e6b5d19ae95d645d01fb57edd975a4fce5a1453c2ada70d4e,com.khanbro.gamestation, 

4f7825647bab001298f768302d0eeb6e0d639d401dc8b5bf60a4b9841a93c980,com.wBoothCash_18748294, 

4fbf1906fe02745cbf0350563440e9a05d19cd4a27c4fb6b67436392a18a0cd4,com.steppay.yrewards, 

54224288aa9fa3d4281fb91ad7b202fbc3e5708b173e319b6b450ad15bcdab43,com.scleanm.nmastery, 

594521e642fee75d474d8d0be839ebe9341f30196b19555882499145bf00746b,com.qwalkingr.grewards, 

721d92d30fbb90fe643507055baa4cce937c8659f1520be1bbce7f9669af6f84,com.modes2048.gamepro, 

7d90ee0be5eb63fbaa6839efdd6217b482576b1bab553731cac0b55f2fa1e6fa,com.jkesogeop.classicsudo, 

7f00991e63154a79ea220b713fcfb2ef8b8db923a75366a61e9bc30d9c355274,com.glamorousg.sgoddesslys, 

8cd77df7cf2242105b12297071ad1d11e91264f9de311d1b082666da19134476,com.wtoolboxp.xpro, 

974a5d005d3cfe4c63bd7a46ca72c6716c6c6de397d2e3e19b1730def31f7825,com.systmapp.mobile1.cleanmanager, 

98819230a6c3f5092517ada9652e9156e338acc27d29e4647b3cb69cddb668cb,com.crazycodes.airvpn, 

98db4904c3299b8ac383dd177c3cde87af25c088df1988f484427aab3b5c4e0d,com.wlifetoolp.lpro, 

9b9f55c4a68385e4a739c7d11159c9b4ab006660142331e8bdc477b5eba62aad,com.ulifea.eassistants, 

a02694b5de7a8a6ef3024d53e54a54a676f992bfa1e070f07827ab9b5dd1365c,com.priceper.km, 

a1e77c148f190b6bfdd40ce657722e902a31cedecab669dd6f78f38b6b18ddf7,crazycodes.notes.app, 

a430123efe9611f322fbc3c459fc5ec13abbb0def88ba3ec56a05a361a51a9ac,com.gbversion.gbplus.gblatestversion, 

ab6365bf7e6c7fba6867b44a80e8bf653c7b66ff91204ee3e2981b6532fea7ee,com.snowfindthesame.samethesnowswe, 

b4438ac1694e3a08a994750a7ac76399c48d5d3446e90ebebbea1f8694bf3dd5,com.guidely.earningapp, 

b8087e3535d395210b80637be35da6ae8e10450b6fb87de62a284d5d7397cd17,com.shcoob.groobe.timebuke, 

bf47dc1577c8b862c4e849a7ce52e143239f2f7274421befa902baf4bd1c4a19,com.wlifet.etoolbox, 

c332166f720e4d2f6f9b59993559df05281e7d2fbd56f90a7f2399a0ac620295,com.ebitans.tenstarbd, 

c509a98d0823add0c1440a7b043586eb5a8069fbb776ca36252f5b7653c92cb7,com.whabitn.tnotes, 

c517b26dfc8ffd5de7f49966ff3391475f80299ebc6ad9988bf166029cf76c91,com.filnishww.fluttbuber.storagecleaner, 

cf945c433aa80120be10566b9f1ae88e043f96872996f599b75bb57c74248e56,com.mfunt.ttetris, 

d72d96c6f299fe961dd98655e0468e45ed3ac03df0cfa499e27d4c399e304500,com.wififinder.wificonnect, 

db1168f2cb3b25ef65e06eb4e788ddda237a428fbce0725de1e9d70b36e96833,com.whomea.eassistan, 

ddc4da4c63c8bc7df53c3c7fe350b56ad31f313c7d95b472dc45a9fcf85273f0,com.mi2048nig.game, 

df00753933359d7369668eddeb0dc2565f075c78e4b46f3cabd2e8ff31eda42e,com.sportscash.xyz, 

e32c8a869585c107ccd1586b5edebc1d8eaa18017c2dd39b6267eec4db7f7410,com.biopops.mathly, 

e5b8d25ef612f0240ce28fbffd550fd4e0b9abdbf325e3ff85718e8312b70c2b,com.wdailyn.anova, 

e5f3aa5ef6b5b5fa94a921b55f52aa2c1011486b7370f1585deb6d571325ebcb,com.khan.pregnancyexercise, 

ec79443aa53864e4d322b8fa8fd4aad0ef878221f01e7d32512694ba24992aee,com.merge2048joy.joy, 

f654c5f926ebfcded4c0d07590972536280454e2501dc8a525390402fa945ff1,com.kgoddessv.svibe, 

f7c664ea66c43a82801ed7da23369af1e285857c1a4bf200147b716715f09d3f,com.chall2048enge.game, 

fc3b06c36feb38ed62f3034e428e814d6e1ac06ec1569ea22428374b8d15d848,com.jekunotesimple.notesimple, 

fd62c2bfa2277eff8787926f9976aa4a11235a18a9a543ced71a509c6ebf2bf2,com.game.ludoplay, 

The post Operation NoVoice: Rootkit Tells No Tales appeared first on McAfee Blog.

About Author

What do you feel about this?

Subscribe To InfoSec Today News

You have successfully subscribed to the newsletter

There was an error while trying to send your request. Please try again.

World Wide Crypto will use the information you provide on this form to be in touch with you and to provide updates and marketing.