Control planes ODM Longcheer/Unisoc in Android system processes. │ 3 apps injected into system_server (UID 1000) and com.android.phone (UID 1001) │ provide total radio control (WiFi + cellular + baseband) NOT a CVE. NOT an exploit. Architecture of ODM control that enables │ the attack vector WITHOUT 0-days
All three applications are signed with the identical Longcheer certificate (SHA-1: b0c7dc5f6277b80abad48c6fe6965c9a260a380c), which is a self-signed X.509 v3 certificate issued on September 15, 2023, and valid until January 31, 2051.
- Package Name:
com.unisoc.wifi - Installation Path:
/system_ext/priv-app/UniWifi - Shared User ID:
android.uid.system(UID 1000) - Key Permissions:
FLAG_HARDWARE_ACCELERATED, full access to hidden APIs. - Signature Status: Verified (Scheme v3)
- Signer Certificate:
- Subject/Issuer:
CN=Longcheer, OU=Longcheer, O=Longcheer, L=ShangHai, ST=ShangHai, C=CN - Validity: 2023-09-15 to 2051-01-31
- SHA-256:
4cfe803b578fd6958d236e494248585eccbc5c33a5113bda7ff1a47351e4118d - Algorithm: SHA256withRSA
- Subject/Issuer:
- Package Name:
com.unisoc.phone - Installation Path:
/system_ext/priv-app/UniTelephony - Shared User ID:
android.uid.phone(UID 1001) - Key Permissions: Full access to hidden APIs (same as UniWifi).
- Signature Status: Verified (Scheme v3)
- Signer Certificate: Identical to
UniWifiApp(Longcheer self-signed cert, SHA-1:b0c7dc5f...).
- Package Name:
com.android.unisoc.telephony.server - Installation Path:
/system_ext/priv-app/radio_interactor_service - Signature Status: Verified (Scheme v3)
- Signer Certificate: Identical to the other two apps (Longcheer self-signed cert, SHA-1:
b0c7dc5f...).
- Certificate Authority: The certificate is a self-signed root CA issued by Longcheer (the ODM manufacturer for these Unisoc devices). It is not issued by a public CA.
- Privilege Level: Because these apps reside in
/system_ext/priv-appand use system-level shared UIDs (android.uid.systemandandroid.uid.phone), they bypass standard Android permission checks and have elevated privileges over the Wi-Fi and Telephony subsystems. - X.509 Implementation: The presence of
org.bouncycastle.asn1.x509.X509ObjectIdentifiersin the decompiled code indicates the apps use the Bouncy Castle library to handle ASN.1/X.509 parsing, likely for internal certificate validation or provisioning logic.
These 3 apps are not user apps. They are ODM control planes running inside the Android core processes:
com.unisoc.wifiruns assystem(UID 1000,process="system") - same process asWifiServiceImplcom.unisoc.phone+radio_interactor_servicerun ascom.android.phone(UID 1001,persistent=trueimmortal)
This means Longcheer code is injected into system_server and com.android.phone via UniWifiInjector singleton which returns the same instance of WifiManager, WifiP2pManager and ContentResolver used by the framework. It calls handleBootCompleted() immediately in constructor, no BOOT_COMPLETED wait, directBootAware - active before user unlock.
Combined with custom android.security.net.config.ConfigNetworkSecurityPolicy and KeyStoreCertificateSource (BouncyCastle ASN.1/X.509 impl, TrustedCertificateIndex, no-op handleTrustStorageUpdate()), the ODM can override cleartext, Certificate Transparency and trust anchor resolution. This is what explains the net/host unreachable for DoT (9.9.9.9:853) observed in the pcap.
All extracted directly from jar:
-
Signature: Same self-signed Longcheer cert v3 in all 3
- Serial:
228526b0d1ef90c3b8ed568a49c3714f6a39506b - SHA-1:
b0c7dc5f6277b80abad48c6fe6965c9a260a380c - SHA-256:
4cfe803b578fd6958d236e494248585eccbc5c33a5113bda7ff1a47351e4118d - Issuer:
CN=Longcheer, O=Longcheer, L=ShangHai, C=CN / release@Longcheer.com - Validity: 2023-09-15 to 2051-01-31, SHA256withRSA
apksigner verify --print-certs /system_ext/priv-app/UniWifi/UniWifi.apk apksigner verify --print-certs /system_ext/priv-app/UniTelephony/UniTelephony.apk apksigner verify --print-certs /system_ext/priv-app/radio_interactor_service/*.apk - Serial:
-
Paths & UIDs:
/system_ext/priv-app/UniWifi->android.uid.system(1000)/system_ext/priv-app/UniTelephony->android.uid.phone(1001)/system_ext/priv-app/radio_interactor_service-> same cert
-
Behavioral IO:
- Listens to:
CARRIER_CONFIG_CHANGED,SIM_STATE_CHANGED,WIFI_STATE_CHANGED,LOCALE_CHANGED,ACTION_SHUTDOWN CARRIER_CONFIG_CHANGED-> re-evaluates Passpoint SSIDsSIM_STATE_CHANGED-> updates P2P name = f(IMEI) viaUniWifiUtils.getDefaultNameViaImei()and IMSI for PasspointUniWifiApConfigStore.setDefaultTetherSsid()->SoftApConfiguration.Builder.setSsid(imei-derived)+ writesSettings.Global wifi_tether_default_ssid
- Listens to:
-
Network artifacts:
- Passpoint auto-join without consent:
builder.setIsInitialAutojoinEnabled(true)inUniWifiCarrierNetworkManager - Pre-installed Passpoint FQDN/realm from carrier config (OTA updatable by carrier without FOTA)
- IPv6 RA
fd00:2:fd00:1::/64+ RDNSSfd00:2:fd00:1:fd00:1:fd00:1-> wcn chr blocks DoT
- Passpoint auto-join without consent:
Boot (pre-unlock, directBootAware)
│
▼
UniWifiApp.onCreate()
│
├── Log: "Boot Successfully!"
│
└── new UniWifiController(this)
│
├── new UniWifiInjector(context)
│ ├── HandlerThread "UniWifiHandlerThread"
│ ├── UniWifiConfigStore (P2P/IMEI)
│ ├── UniWifiApConfigStore (AP/Passpoint)
│ ├── UniWifiCarrierNetworkManager (auto-join)
│ └── UniWifiCountryCode
│
└── handleBootCompleted()
├── registerReceiver: SHUTDOWN, LOCALE_CHANGED,
│ WIFI_STATE_CHANGED, SIM_STATE_CHANGED,
│ CARRIER_CONFIG_CHANGED
├── UniWifiCountryCode.handleLocaleChanged()
├── UniWifiApConfigStore.handleReceiveBootCompleted()
└── UniWifiCarrierNetworkManager.handleReceiveBootCompleted()
├── loadUniWifiConfig() → SSIDs hardcodeados
└── loadCarrierConfig() → SSIDs del carrier (OTA)
Validated
// setDefaultTetherSsid():
int sku = Integer.parseInt(SystemProperties.get("ro.boot.sku"));
// 1. Si Settings.Global "wifi_tether_default_ssid" is from SKIP // 2. Si no → genera SSID desde IMEI (prefix match) o resource default // 3. WifiManager.setSoftApConfiguration(builder.setSsid(ssid).build()) // 4. Settings.Global.putString("wifi_tether_default_ssid", ssid)
UniWifiUtils.getDefaultNameViaImei() // Input: String[3] = {prefix1, prefix2, prefix3} (IMEI prefixes) // Logic: String imei = TelephonyManager.getDeviceId(); if (imei.startsWith(prefix1)) return prefix1_name; else return prefix3_name; // default
Forensic Checklist for any researcher
-
Same cert? apksigner verify --print-certs /system_ext/priv-app/UniWifi/.apk apksigner verify --print-certs /system_ext/priv-app/UniTelephony/.apk Expect SHA-256 4cfe803b578fd6958d236e494248585eccbc5c33a5113bda7ff1a47351e4118d
-
Passpoint profiles preinstalled? dumpsys wifi | grep -A30 Passpoint dumpsys wifi | grep -A10 NetworkSuggestion
-
Carrier config OTA? dumpsys telephony.registry | grep carrier_config content query --uri content://telephony/carrier_config | grep wifi
-
P2P name settings get global wifi_p2p_default_device_name getprop ro.boot.sku getprop ro.boot.hardware.sku
-
NetworkSecurityPolicy impl? dexdump -d /system/framework/framework.jar | grep ConfigNetworkSecurityPolicy If present -> Longcheer impl, not AOSP
-
KeyStore source logcat -b all -s KeyStoreCertificateSource Containment (defensive)
- Block OMACP push at carrier level, audit APN changes
- Disable Passpoint auto-join:
settings put global wifi_networks_available_notification_on 0+ remove suggestions viacmd wifi - Monitor
fd00:2::/32RA viaip -6 route+ndp -a - Use DoH via app (not system) to bypass wcn chr block
- YARA: hash `4cfe803b578fd6958d236e494248585eccbc5c33a5113bda7ff1a47351e4118d
UniWifiInjector (singleton) ├── getWifiManager() → Context.getSystemService(WifiManager.class) │ → Es la MISMA instancia que usa WifiServiceImpl │ → No es una copia. Es el framework. ├── getWifiP2pManager() → Context.getSystemService(WifiP2pManager.class) │ → Mismo ├── getContentResolver() → context.getContentResolver() │ → Mismo ├── getUniWifiApConfigStore() → [COMPONENTE LONGCHEER] ├── getUniWifiCarrierNetworkManager() → [COMPONENTE LONGCHEER] ├── getUniWifiConfigStore() → [COMPONENTE LONGCHEER] └── getUniWifiCountryCode() → [COMPONENTE LONGCHEER]
┌─────────────────────────────────────────────────────────────────────┐ │ LAYER 1: HARDWARE (feature declaration) │ │ android.hardware.wifi.passpoint → "el chip soporta 802.11u" │ │ android.hardware.wifi.direct → "el chip soporta WiFi P2P" │ │ → Solo metadata. No ejecuta nada. │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ LAYER 2: DAEMONS (vendor + apex) │ │ /vendor/bin/hw/hostapd → AP side (emite ANQP, gestiona clientes) │ │ /apex/com.android.wifi/bin/wpa_supplicant_mainline → STA side │ │ → Son daemons, no apps. No tienen UI. │ │ → Son controlados vía AIDL por el framework. │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ LAYER 3: FRAMEWORK (system_server, UID 1000) │ │ WifiServiceImpl → gestiona redes, tethering, VPN │ │ Tethering → SoftAP + portal cautivo │ │ → Es el framework estándar de Android. │ └─────────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────────┐ │ LAYER 4: ODM CODE INJECTED (com.unisoc.wifi, process="system") │ │ │ │ UniWifiController (ORCHESTRATOR) │ │ • Escucha: SHUTDOWN, LOCALE_CHANGED, WIFI_STATE_CHANGED, │ │ SIM_STATE_CHANGED, CARRIER_CONFIG_CHANGED │ │ • On boot → dispara todo el stack │ │ │ │ UniWifiInjector (DEPENDENCY CONTAINER) │ │ • HandlerThread "UniWifiHandlerThread" │ │ • Crea e inyecta: │ │ ├── UniWifiApConfigStore → Config del AP (SSID, Passpoint) │ │ ├── UniWifiCarrierNetworkManager → Passpoint auto-join │ │ ├── UniWifiConfigStore → P2P name (IMEI-based) │ │ └── UniWifiCountryCode → Country code WiFi │ │ • Expone: WifiManager, WifiP2pManager, ContentResolver │ │ (son las INSTANCIAS del framework, no copies) │ └─────────────────────────────────────────────────────────────────────┘
- YARA: rule longcheer_same_cert { strings: $a = "4cfe803b578fd6958d236e494248585eccbc5c33a5113bda7ff1a47351e4118d" $b = "228526b0d1ef90c3b8ed568a49c3714f6a39506b" }
NOT a CVE. NOT an exploit. It's ODM architecture, but it enables attack vectors without 0-days:
-
No-interaction radio control: WiFi AP, P2P, Passpoint, Tethering, VPN, CA installer, APN rewrite, AT commands (
AT+SPASENGMD) all inside UID 1000/1001. Immutable, survives factory reset. -
Identifiable device: Tether SSID and WiFi Direct device name derived from IMEI via
getDefaultNameViaImei()+ro.boot.sku. Any scanner can track the device. Overridable if attacker hasWRITE_SECURE_SETTINGS(which these apps have). -
Carrier can reconfigure remotely:
loadCarrierConfig()vsloadUniWifiConfig(). Hardcoded SSIDs need FOTA, but carrier config is OTA. Telcel/Tigo can push new Passpoint FQDN/realm/EAP/IMSI remotely. Consumer never sees prompt due to auto-join. -
Trust control: Custom
ConfigNetworkSecurityPolicy+KeyStoreCertificateSourcewithTrustedCertificateIndexand BouncyCastleX509ObjectIdentifiers(OID 2.5.4.3, 1.3.14.3.2.26, 1.3.6.1.5.5.7) means ODM does its own ASN.1/X.509 parsing for internal cert validation/provisioning (Passpoint OSU, OMACP).handleTrustStorageUpdate()is no-op, so system trust updates are ignored. -
Monetization chain on top: Separate app
com.dti.amx(DT Ignite, Speedy Movil cert SHA-2567d7226772d4f6d778fef53a36be15ad78d8d9d4bc4ce00c5f2e3216c19480fa0, v1/v2 152 warnings SHA1withRSA) withINSTALL_PACKAGES, READ_PRIVILEGED_PHONE_STATE, QUERY_ALL_PACKAGES, DOWNLOAD_WITHOUT_NOTIFICATION, WRITE_SECURE_SETTINGS, providerscom.kochava.preinstall,com.oem.attribution,com.dti.amx.attribution.adjust. This does silent install on first boot (see link to DIGITAL_TURBINE_IGNITE repo). Combined with radio control, it explains the Guanajuato kiosk vector: OMACP APN rewrite -> RA fd00::/64 -> DoT block -> QUIC 443 SNI cloning.
For consumer: You cannot uninstall, disable, or audit these apps without root. They run as system and phone. Even with VPN always-on and private DNS, wcn chr (UniSoC WiFi daemon) can block/redirect at HAL level. This is the cost of low-cost Unisoc T606 ODM devices.
Defensive disclosure - forensic reproducibility only.