
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.

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.

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.

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.


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.

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.

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.

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.

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.

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.

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.

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 large‑scale harm occurs.
References
https://www.kaspersky.com/blog/triada-trojan/11481/
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.
