Location Tracking & Geofencing¶
Collecting device location data for victim surveillance, movement tracking, and geographically targeted malware activation. Location is a core data collection capability for every major spyware family and a common activation gate for banking trojans that only operate in target countries. Unlike most attack techniques that require complex permission escalation, coarse location data can be derived without any device permissions at all through IP geolocation and cell tower inference.
See also: ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION, ACCESS_BACKGROUND_LOCATION, Anti-Analysis Techniques, Play Store Evasion
Requirements
| Requirement | Details |
|---|---|
| Fine location | ACCESS_FINE_LOCATION runtime permission (GPS, ~3m accuracy) |
| Coarse location | ACCESS_COARSE_LOCATION runtime permission (cell/WiFi, ~50-300m) |
| Background location | ACCESS_BACKGROUND_LOCATION (Android 10+), separate grant |
| Cell tower info | ACCESS_FINE_LOCATION for getAllCellInfo(), READ_PHONE_STATE for older APIs |
| No permission needed | IP geolocation (server-side), SIM operator codes, system locale |
Malware that only needs to know the victim's country does not need location permissions at all. TelephonyManager.getSimCountryIso() and server-side IP geolocation provide country-level targeting with zero permissions.
Location Data Collection Methods¶
GPS (Fine Location)¶
The most precise location source, accurate to approximately 3 meters outdoors. Requires ACCESS_FINE_LOCATION and a clear view of satellites. Indoor accuracy degrades significantly.
FusedLocationProvider Request
FusedLocationProviderClient client = LocationServices.getFusedLocationProviderClient(context);
LocationRequest request = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
.setInterval(30000)
.setFastestInterval(10000);
client.requestLocationUpdates(request, new LocationCallback() {
@Override
public void onLocationResult(LocationResult result) {
Location loc = result.getLastLocation();
exfiltrate(loc.getLatitude(), loc.getLongitude(), loc.getAccuracy());
}
}, Looper.getMainLooper());
FusedLocationProviderClient from Google Play Services is the preferred API for most malware because it intelligently combines GPS, WiFi, cell towers, and sensors to maximize accuracy while minimizing battery drain. Heavy GPS polling is a telltale sign of surveillance -- legitimate apps use the fused provider.
Network-Based (Coarse Location)¶
Cell tower triangulation and WiFi BSSID lookup provide 50-300 meter accuracy with ACCESS_COARSE_LOCATION. Faster than GPS (no satellite lock required) and works indoors.
Android 12 introduced the choice between approximate and precise location in the permission dialog. Malware requesting only ACCESS_COARSE_LOCATION still gets sufficient accuracy for city-level tracking and movement pattern analysis.
Cell Tower Information¶
TelephonyManager exposes cell tower identifiers that can be resolved to geographic coordinates using public databases like OpenCellID or Google's geolocation API.
Cell Tower Info Extraction
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
List<CellInfo> cells = tm.getAllCellInfo();
for (CellInfo cell : cells) {
if (cell instanceof CellInfoLte) {
CellIdentityLte id = ((CellInfoLte) cell).getCellIdentity();
int mcc = id.getMccString() != null ? Integer.parseInt(id.getMccString()) : -1;
int mnc = id.getMncString() != null ? Integer.parseInt(id.getMncString()) : -1;
int lac = id.getTac();
int cid = id.getCi();
exfiltrate(mcc, mnc, lac, cid);
}
}
The MCC/MNC/LAC/CID tuple uniquely identifies a cell tower. Malware exfiltrates these raw values to C2, where server-side lookup resolves them to coordinates. This provides 100-1000 meter accuracy without any GPS usage or Google Play Services dependency.
WiFi BSSID Mapping¶
WiFi access point MAC addresses (BSSIDs) are mapped to geographic coordinates in databases maintained by Google, Apple, and Mozilla. Scanning nearby WiFi networks and sending BSSIDs to a geolocation API provides 10-50 meter accuracy indoors.
Since Android 9, WiFi scan results are throttled (4 scans per 2 minutes in foreground, 1 scan per 30 minutes in background). Since Android 10, ACCESS_FINE_LOCATION is required to get scan results at all.
Passive Location Provider¶
The passive location provider piggybacks on location requests made by other apps. Instead of actively querying GPS or network providers, the malware registers for passive updates and receives location data whenever any other app on the device requests location.
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
lm.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, locationListener);
This is stealthier than active location requests because it generates zero additional battery drain and no GPS activation. The tradeoff is unpredictable update frequency -- the malware only gets data when other apps happen to request location.
IP Geolocation¶
Server-side IP geolocation requires no device permission. The C2 server resolves the client's public IP to a geographic location using commercial databases (MaxMind, IPinfo, IP2Location). Accuracy ranges from city-level (ISP-assigned IP blocks) to country-level.
Malware uses IP geolocation for two purposes:
- Activation gating: C2 checks the client IP before delivering payloads, blocking requests from non-target countries or known VPN/cloud IP ranges
- Approximate tracking: logging victim IP-derived location when precise GPS data is unavailable
Background Location Tracking¶
Background location is the critical capability for persistent surveillance. Each Android version has progressively restricted background location access, and each restriction has forced malware to develop new workarounds.
Android Version Restrictions¶
| Version | Restriction | Impact |
|---|---|---|
| Android 8 (API 26) | Background location throttled to a few updates per hour | Active GPS polling from background services degraded |
| Android 8 (API 26) | Background service execution limits | Long-running location services killed; foreground service required |
| Android 10 (API 29) | ACCESS_BACKGROUND_LOCATION as separate permission |
User must explicitly grant background location in addition to foreground |
| Android 11 (API 30) | Background location can only be granted from Settings | No runtime dialog -- user must navigate to Settings manually |
| Android 12 (API 31) | Approximate vs precise location choice in permission dialog | User can grant only approximate location, degrading tracking precision |
| Android 13 (API 33) | Foreground service type location must be declared |
Manifest must include foregroundServiceType="location" |
| Android 14 (API 34) | USE_EXACT_ALARM restricted |
Periodic alarm-based location polling requires justification |
| Android 15 (API 35) | Enhanced background location audit in Play Store review | Play Store scrutiny on ACCESS_BACKGROUND_LOCATION usage increased |
Malware Workarounds¶
Foreground service with location type: The primary workaround. The malware starts a foreground service declared with foregroundServiceType="location" and maintains a persistent (often misleading) notification. The notification is disguised as a system process, battery optimization, or connectivity service.
WorkManager periodic requests: Scheduling periodic location collection through WorkManager, which survives process death and respects Doze mode constraints while still executing periodically. Each work request grabs the last known location and exfiltrates it.
PeriodicWorkRequest locationWork = new PeriodicWorkRequest.Builder(
LocationWorker.class, 15, TimeUnit.MINUTES)
.setConstraints(new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build())
.build();
WorkManager.getInstance(context).enqueueUniquePeriodicWork(
"loc_sync", ExistingPeriodicWorkPolicy.KEEP, locationWork);
Boot receiver with location request: Registering RECEIVE_BOOT_COMPLETED to restart location tracking after device reboot. The receiver starts a foreground service that re-registers for location updates.
Accessibility-based auto-grant: Malware with accessibility service access navigates the Settings UI to grant ACCESS_BACKGROUND_LOCATION without genuine user consent. This is particularly effective on Android 11+ where background location requires a Settings toggle rather than a runtime dialog -- the accessibility service opens the Settings page and taps the correct option.
AlarmManager wake-ups: Using exact alarms (AlarmManager.setExactAndAllowWhileIdle()) to wake the device from Doze mode and capture a location fix at fixed intervals.
Stalkerware Persistence¶
Commercial stalkerware (Cerberus rebranded as "monitoring software", FlexiSpy, mSpy, Cocospy) maintains persistent background location through a combination of techniques:
- Foreground service with
IMPORTANCE_MINnotification (barely visible) REQUEST_IGNORE_BATTERY_OPTIMIZATIONSto avoid Doze mode killingRECEIVE_BOOT_COMPLETEDfor restart after reboot- Device admin enrollment to resist uninstallation
- Hiding the app icon from the launcher
The result is continuous location streaming at 30-second to 5-minute intervals, surviving reboots, battery optimization, and user attempts to find and remove the app.
Geofencing-Based Malware Activation¶
Geofencing is the most operationally significant use of location in Android malware. Rather than tracking victims, it determines where malware should and should not operate. This is both an evasion technique (avoid analysis environments) and a targeting mechanism (only attack users in profitable regions).
SIM Operator Code Checks (MCC/MNC)¶
The most reliable device-side geofencing method. The Mobile Country Code (MCC) and Mobile Network Code (MNC) are set by the physical SIM card and cannot be spoofed without inserting a SIM from the target country.
TelephonyManager tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
String simCountry = tm.getSimCountryIso();
String networkCountry = tm.getNetworkCountryIso();
String simOperator = tm.getSimOperator();
| API | What It Returns | Spoofing Difficulty |
|---|---|---|
getSimCountryIso() |
ISO country code from SIM (e.g., "de") | High -- requires physical SIM from that country |
getNetworkCountryIso() |
ISO country code from current network registration | High -- VPN does not change this |
getSimOperator() |
MCC+MNC string (e.g., "26201" for Telekom DE) | High -- SIM-bound |
getNetworkOperator() |
MCC+MNC of current network | High -- requires roaming on target network |
This is why banking trojans targeting German banks check for German SIMs, and analysts need German SIMs to trigger the malware. MITRE ATT&CK documents this as T1627.001 (Geofencing).
Locale-Based Activation¶
String language = Locale.getDefault().getLanguage();
String country = Locale.getDefault().getCountry();
Checking the system language and region. Less reliable than SIM checks because users can change these in Settings, but useful as an additional signal. Some families combine locale with SIM checks -- both must match for activation.
Server-Side IP Geofencing¶
The C2 server checks the client's source IP against geolocation databases before delivering payloads. This is the hardest geofencing method for analysts to bypass because the logic runs entirely server-side.
Behaviors include:
- Returning HTTP 404 or empty responses for non-target IPs
- Serving a benign APK to non-target regions and a malicious APK to targets
- Blocking known VPN, cloud, and datacenter IP ranges
- Requiring the device IP to match the SIM country (IP from Germany + German SIM = deliver payload)
GPS-Based Geofencing¶
Some families use the Android GeofencingClient API or manual coordinate comparison to activate only within specific geographic boundaries.
GeofencingClient client = LocationServices.getGeofencingClient(context);
Geofence geofence = new Geofence.Builder()
.setRequestId("target_zone")
.setCircularRegion(latitude, longitude, radiusMeters)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
.build();
This is less common in commodity malware (GPS requires permissions and satellite access) but observed in targeted spyware that activates surveillance only when the victim enters specific locations.
Real-World Geofencing Examples¶
| Family | Geofencing Method | Targeting Behavior |
|---|---|---|
| Anatsa | SIM country + IP geofencing | Targets UK, DE, ES, SK, SI, CZ. Avoids Eastern European and Chinese IP ranges |
| Mandrake | C2-side geofencing | Server decides payload delivery based on device profile. Non-target regions never receive malware |
| GoldPickaxe | SIM + locale | Specifically targets Thailand and Vietnam. Checks for Thai/Vietnamese SIM and language |
| Anatsa | Installed app check | Scans for banking apps from target countries as secondary geofence |
| GodFather | System language | Avoids activating on devices with Russian, Azerbaijani, Uzbek, Kazakh, Kyrgyz, Tajik, Armenian, or Belarusian languages |
| Cerberus | SIM + locale | Avoided activating in CIS countries |
| Mamont | SIM country | Targets Russian-speaking countries specifically |
Location as Intelligence¶
Stalkerware and Physical Surveillance¶
Stalkerware apps provide real-time location dashboards showing victim movements on a map. Features include:
- Live GPS tracking with configurable polling intervals
- Location history with timestamped breadcrumb trails
- Geofence alerts when the victim enters or leaves defined areas
- Address resolution (reverse geocoding) for each location point
- Speed and altitude data for travel pattern analysis
Kaspersky reported 13,279 unique users affected by stalkerware in 2024, but actual numbers are higher because stalkerware is designed to be undetectable.
State-Sponsored Location Collection¶
Every major state-sponsored spyware platform collects location as a core surveillance function:
| Family | Location Capability | Details |
|---|---|---|
| Pegasus | GPS + cell + WiFi | Real-time location streaming and historical tracking. Amnesty International's forensic methodology documents location collection as a standard capability |
| Predator | GPS + cell + WiFi | Five-module architecture includes location harvesting |
| Hermit | GPS + cell tower | RCS Lab spyware collects location alongside call/SMS interception |
| FinSpy | GPS + cell + WiFi | Location tracking module with configurable intervals |
| EagleMsgSpy | GPS | Chinese law enforcement surveillance tool with location collection |
Combining Location with Sensor Data¶
Advanced spyware correlates location with other sensor data to build comprehensive victim profiles:
- Accelerometer + location: Determine if the victim is walking, driving, or stationary
- WiFi probe requests + location: Map physical movements through WiFi networks encountered
- Barometer + GPS: Floor-level positioning inside buildings
- Cell tower transitions: Movement patterns even without GPS
Location Spoofing Detection¶
Some malware and many commercial apps check for mock location providers to detect analysis environments where analysts use fake GPS to trigger geofenced behavior.
Detection Techniques¶
private boolean isMockLocation(Location location) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
return location.isFromMockProvider();
}
return Settings.Secure.getString(
getContentResolver(),
Settings.Secure.ALLOW_MOCK_LOCATION).equals("1");
}
| Check | API | Android Version |
|---|---|---|
| Mock provider flag | Location.isFromMockProvider() |
18+ |
| Mock location setting | Settings.Secure.ALLOW_MOCK_LOCATION |
Deprecated in API 23, removed in 31 |
| Mock provider flag (new) | Location.isMock() |
31+ |
| Provider list check | LocationManager.getProviders() for non-standard providers |
All |
Why This Matters for Analysis¶
Analysts frequently use mock location apps (Fake GPS, GPS Joystick) or emulator location spoofing to trigger geofenced malware. If the malware checks isFromMockProvider(), mock location will not work. Workarounds:
- Use Frida to hook
isFromMockProvider()and returnfalse - Use a rooted device with Magisk module that patches the mock location flag at framework level
- Use a physical device with a SIM from the target country instead of mock GPS
- Modify the emulator's location through the emulator controls (not via mock provider API)
Families with Location Tracking¶
| Family | Type | Location Methods | Purpose |
|---|---|---|---|
| Pegasus | State-sponsored | GPS, cell, WiFi, passive | Real-time surveillance, movement tracking |
| Predator | State-sponsored | GPS, cell, WiFi | Targeted surveillance |
| Hermit | State-sponsored | GPS, cell tower | Law enforcement surveillance |
| FinSpy | State-sponsored | GPS, cell, WiFi | Dissident/journalist targeting |
| SpyNote | RAT | GPS, cell | Full device surveillance including location |
| SpyAgent | Spyware | GPS | Location tracking alongside crypto wallet theft |
| PJobRAT | Targeted | GPS | Military/government personnel targeting |
| GuardZoo | Targeted | GPS | Military targeting in Middle East |
| AridSpy | Targeted | GPS | Middle East espionage |
| BoneSpy | State-sponsored | GPS, cell | Russian-linked surveillance of Central Asian targets |
| PlainGnome | State-sponsored | GPS | Gamaredon group, targets Russian-speaking in former Soviet states |
| KoSpy | State-sponsored | GPS | North Korean APT targeting Korean/English speakers |
| LightSpy | State-sponsored | GPS, WiFi | Chinese-linked, modular with dedicated location plugin |
| EagleMsgSpy | Law enforcement | GPS | Chinese police surveillance tool |
| GoldPickaxe | Banking | SIM + locale geofencing | Activates only in Thailand/Vietnam |
| Anatsa | Banking | SIM + IP geofencing | Targets specific European countries |
| GodFather | Banking | Language check | Avoids CIS countries |
| Cerberus | Banking | SIM + locale | Avoided CIS countries |
| SpyLoan | Predatory lending | GPS | Collects victim location for intimidation and debt collection |
| DCHSpy | Spyware | GPS | Location tracking as part of surveillance suite |
| FireScam | Spyware | GPS | Telegram impersonation with location exfiltration |
Android Version Timeline¶
| Version | API | Year | Location Change |
|---|---|---|---|
| 1.0 | 1 | 2008 | ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION introduced |
| 6.0 | 23 | 2015 | Location becomes runtime permission (user must grant explicitly) |
| 8.0 | 26 | 2017 | Background location throttled to a few updates per hour |
| 8.0 | 26 | 2017 | Background service execution limits kill persistent location services |
| 9.0 | 28 | 2018 | WiFi scan throttling (foreground: 4/2min, background: 1/30min) |
| 10 | 29 | 2019 | ACCESS_BACKGROUND_LOCATION introduced as separate permission |
| 10 | 29 | 2019 | ACCESS_FINE_LOCATION required for WiFi scan results |
| 11 | 30 | 2020 | Background location can only be granted from Settings (not runtime dialog) |
| 11 | 30 | 2020 | One-time location permission option added |
| 12 | 31 | 2021 | Approximate vs precise location choice in permission dialog |
| 12 | 31 | 2021 | Location.isMock() replaces deprecated isFromMockProvider() |
| 12 | 31 | 2021 | Bluetooth scan no longer requires location permission |
| 13 | 33 | 2022 | Foreground service type location must be declared in manifest |
| 14 | 34 | 2023 | Stricter foreground service type enforcement |
| 15 | 35 | 2024 | Enhanced background location audit, Play Store review hardened |
Detection During Analysis¶
Static Indicators
ACCESS_FINE_LOCATION,ACCESS_COARSE_LOCATION,ACCESS_BACKGROUND_LOCATIONin manifestforegroundServiceType="location"in service declarationFusedLocationProviderClientorLocationManager.requestLocationUpdates()callsTelephonyManager.getAllCellInfo(),getCellLocation(), orgetNeighboringCellInfo()TelephonyManager.getSimCountryIso(),getNetworkCountryIso(),getSimOperator()for geofencingLocale.getDefault()combined with conditional execution pathsGeofencingClientorGeofence.BuilderusageWifiManager.getScanResults()for BSSID-based positioningLocationManager.PASSIVE_PROVIDERusage (piggyback tracking)
Dynamic Indicators
- Periodic location requests from a background service or WorkManager
- Cell tower info (
getAllCellInfo()) exfiltrated to C2 - WiFi scan results sent to external geolocation APIs
- Network requests to IP geolocation services (ipinfo.io, ip-api.com, MaxMind)
- Location data appearing in C2 traffic (latitude/longitude pairs, MCC/MNC/LAC/CID tuples)
- App behavior changing based on SIM country or system locale
- Foreground service with location type running with misleading notification text
Frida: Hook Location Updates
Java.perform(function() {
var LocationManager = Java.use("android.location.LocationManager");
LocationManager.requestLocationUpdates.overload(
"java.lang.String", "long", "float", "android.location.LocationListener"
).implementation = function(provider, minTime, minDist, listener) {
console.log("[*] requestLocationUpdates: provider=" + provider +
" interval=" + minTime + "ms minDist=" + minDist + "m");
return this.requestLocationUpdates(provider, minTime, minDist, listener);
};
var TelephonyManager = Java.use("android.telephony.TelephonyManager");
TelephonyManager.getSimCountryIso.implementation = function() {
var result = this.getSimCountryIso();
console.log("[*] getSimCountryIso() = " + result);
return result;
};
TelephonyManager.getNetworkCountryIso.implementation = function() {
var result = this.getNetworkCountryIso();
console.log("[*] getNetworkCountryIso() = " + result);
return result;
};
TelephonyManager.getSimOperator.implementation = function() {
var result = this.getSimOperator();
console.log("[*] getSimOperator() = " + result);
return result;
};
});
Frida: Spoof SIM Country for Geofence Bypass
Java.perform(function() {
var TelephonyManager = Java.use("android.telephony.TelephonyManager");
TelephonyManager.getSimCountryIso.implementation = function() {
console.log("[*] getSimCountryIso() spoofed to 'de'");
return "de";
};
TelephonyManager.getNetworkCountryIso.implementation = function() {
console.log("[*] getNetworkCountryIso() spoofed to 'de'");
return "de";
};
TelephonyManager.getSimOperator.implementation = function() {
console.log("[*] getSimOperator() spoofed to '26201'");
return "26201";
};
});
This spoofs device-side checks but will not bypass server-side IP geofencing. Combine with a VPN exit node in the target country for full geofence bypass. Note that some families cross-validate SIM country against IP geolocation, so both must be consistent.