Guest User

Confidentialité Revolut (reverse)

a guest
Sep 14th, 2026
527
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 74.48 KB | Software | 0 0
  1. # Reverse engineering de Revolut Android 10.145 - synthèse consolidée
  2.  
  3. > Application étudiée : `com.revolut.revolut` - version `10.145` - `versionCode=1030145101`.
  4. >
  5. > Les observations portent principalement sur un POCO/Xiaomi `22101320G` (`redwood`) sous Android 15 / SDK 35, dans un environnement système modifié (rom Matrixx + KernelSu Next) accompagné d'un fork privé de l'agent android "Reins" embarquant un support llama.cpp avec endpoint openai, intégrant en son sein tout un environnement Termux dédié en capacité d'accéder aux droit superutilisateurs et sortir de sa sandbox à des fins d'étude. Ce dernier utilise le modèle de langage "Qwen 3.8-27B-abliterated.i1-Q4_K_S" fourni par un serveur local.
  6. >
  7. > Le document distingue systématiquement capacité statique, chemin de code réellement établi, observation runtime, donnée effectivement obtenue et éventuelle transmission réseau.
  8.  
  9. ---
  10.  
  11. ## Table des matières
  12.  
  13. 1. [Confidentialité et conséquences pour l'utilisateur final](#1-confidentialité-et-conséquences-pour-lutilisateur-final)
  14. 2. [Périmètre, méthode et hiérarchie des preuves](#2-périmètre-méthode-et-hiérarchie-des-preuves)
  15. 3. [Environnement matériel et système étudié](#3-environnement-matériel-et-système-étudié)
  16. 4. [État installé de Revolut 10.145](#4-état-installé-de-revolut-10145)
  17. 5. [Reproduction du crash et chronologie du démarrage](#5-reproduction-du-crash-et-chronologie-du-démarrage)
  18. 6. [Activité protégée, écran d'erreur et code 4674 / 0x1242](#6-activité-protégée-écran-derreur-et-code-4674--0x1242)
  19. 7. [Architecture de protection observée](#7-architecture-de-protection-observée)
  20. 8. [DexProtector : structure ELF, reconstruction et image native protégée](#8-dexprotector--structure-elf-reconstruction-et-image-native-protégée)
  21. 9. [Bitfield d'environnement DexProtector](#9-bitfield-denvironnement-dexprotector)
  22. 10. [Détection ADB - bit 0x200](#10-détection-adb---bit-0x200)
  23. 11. [Verified Boot, propriétés système et bit 0x40](#11-verified-boot-propriétés-système-et-bit-0x40)
  24. 12. [AndroidKeyStore, attestation, RootOfTrust et bit 0x40](#12-androidkeystore-attestation-rootoftrust-et-bit-0x40)
  25. 13. [Fourthline : contrôles root, hook, instrumentation et environnement](#13-fourthline--contrôles-root-hook-instrumentation-et-environnement)
  26. 14. [Credolab / Incognia : collecte et détection root réellement câblées](#14-credolab--incognia--collecte-et-détection-root-réellement-câblées)
  27. 15. [Second détecteur root : DeviceInfo.getIsRooted](#15-second-détecteur-root--deviceinfogetisrooted)
  28. 16. [Inventaire de paquets, QUERY_ALL_PACKAGES et applications ciblées](#16-inventaire-de-paquets-query_all_packages-et-applications-ciblées)
  29. 17. [Play Integrity, SafetyNet, Keystore et décision serveur](#17-play-integrity-safetynet-keystore-et-décision-serveur)
  30. 18. [Cartographie statique des SDK, DEX, permissions et API sensibles](#18-cartographie-statique-des-sdk-dex-permissions-et-api-sensibles)
  31. 19. [Bibliothèques natives et capacités observées](#19-bibliothèques-natives-et-capacités-observées)
  32. 20. [Assets, DEX embarqués et MessageGuardException](#20-assets-dex-embarqués-et-messageguardexception)
  33. 21. [Snapshots du processus Revolut et visibilité réelle des traces root](#21-snapshots-du-processus-revolut-et-visibilité-réelle-des-traces-root)
  34. 22. [Profils Android et espaces utilisateurs](#22-profils-android-et-espaces-utilisateurs)
  35. 23. [Matrice de causalité et conclusions de confiance](#23-matrice-de-causalité-et-conclusions-de-confiance)
  36. 24. [Critères de compatibilité à vérifier sur un autre appareil](#24-critères-de-compatibilité-à-vérifier-sur-un-autre-appareil)
  37. 25. [Artefacts et scripts d'analyse](#25-artefacts-et-scripts-danalyse)
  38. 26. [Limites méthodologiques et périmètre exclu](#26-limites-méthodologiques-et-périmètre-exclu)
  39. 27. [Résumé final](#27-résumé-final)
  40.  
  41. ---
  42.  
  43. # 1. Confidentialité et conséquences pour l'utilisateur final
  44.  
  45. ## 1.1. Constat général
  46.  
  47. Le reverse engineering de Revolut Android 10.145 met en évidence une surface d'observation de l'appareil particulièrement large. L'application ne se limite pas aux données strictement nécessaires à l'affichage d'un compte bancaire ou à l'exécution d'un paiement : elle embarque des mécanismes capables d'examiner de nombreux aspects de l'appareil, de son système, de ses applications, de ses identifiants, de ses interfaces matérielles et de son environnement d'exécution.
  48.  
  49. Pour l'utilisateur final, le problème de confidentialité principal est l'ampleur potentielle de cette collecte et de ce profilage technique. Les éléments présents dans le paquet permettent notamment d'observer ou d'inférer :
  50.  
  51. - l'identité logicielle détaillée de l'appareil ;
  52. - le modèle, la marque, le fabricant, le bootloader et le fingerprint Android ;
  53. - la version Android, le niveau de patch et diverses propriétés système ;
  54. - l'état de verrouillage et d'intégrité du démarrage ;
  55. - la présence de root, de frameworks d'injection ou d'outils de modification ;
  56. - la présence de certaines applications installées ;
  57. - les identifiants publicitaires et différents identifiants d'instance ;
  58. - des informations de téléphonie et de réseau ;
  59. - la localisation ;
  60. - les capteurs ;
  61. - la caméra et le microphone ;
  62. - la biométrie ;
  63. - les contacts ;
  64. - le presse-papiers et l'accessibilité dans certains chemins référencés ;
  65. - le NFC ;
  66. - l'état de capture ou d'enregistrement de l'écran ;
  67. - les caractéristiques du Keystore matériel et de l'attestation cryptographique de l'appareil.
  68.  
  69. Cette surface permet potentiellement de construire un profil technique très détaillé du terminal et de son environnement. Elle permet aussi de conditionner l'accès au service à des éléments qui dépassent la seule authentification bancaire : ROM, bootloader, ADB, root, applications installées, capacité d'attestation matérielle et autres propriétés de l'appareil.
  70.  
  71. ## 1.2. Permissions demandées
  72.  
  73. Le paquet installé demande 44 permissions. Parmi les plus significatives pour la vie privée :
  74.  
  75. - `android.permission.ACCESS_ADSERVICES_AD_ID` ;
  76. - `android.permission.ACCESS_ADSERVICES_ATTRIBUTION` ;
  77. - `android.permission.ACCESS_COARSE_LOCATION` ;
  78. - `android.permission.ACCESS_FINE_LOCATION` ;
  79. - `android.permission.ACCESS_WIFI_STATE` ;
  80. - `android.permission.BLUETOOTH_CONNECT` ;
  81. - `android.permission.BLUETOOTH_SCAN` ;
  82. - `android.permission.CAMERA` ;
  83. - `android.permission.DETECT_SCREEN_CAPTURE` ;
  84. - `android.permission.DETECT_SCREEN_RECORDING` ;
  85. - `android.permission.GET_ACCOUNTS` ;
  86. - `android.permission.HIGH_SAMPLING_RATE_SENSORS` ;
  87. - `android.permission.MANAGE_OWN_CALLS` ;
  88. - `android.permission.NFC` ;
  89. - `android.permission.QUERY_ALL_PACKAGES` ;
  90. - `android.permission.READ_CONTACTS` ;
  91. - `android.permission.WRITE_CONTACTS` ;
  92. - `android.permission.RECORD_AUDIO` ;
  93. - `android.permission.USE_BIOMETRIC` ;
  94. - `android.permission.USE_FINGERPRINT` ;
  95. - `android.permission.READ_EXTERNAL_STORAGE` ;
  96. - `android.permission.WRITE_EXTERNAL_STORAGE` ;
  97. - `com.google.android.gms.permission.AD_ID`.
  98.  
  99. Le relevé runtime ne montre pas toutes ces permissions comme accordées. Cinq permissions install/grant non-runtime ressortaient comme accordées, et les app-ops sensibles observées étaient majoritairement en `ignore` ou `default`, avec notamment `WAKE_LOCK` et `USE_BIOMETRIC` en `allow`. Cette distinction ne réduit pas la surface déclarée : elle signifie seulement qu'une permission demandée n'est pas forcément active dans le relevé considéré.
  100.  
  101. ## 1.3. Identifiants, attribution et télémétrie
  102.  
  103. Le code embarque plusieurs briques de mesure, d'attribution et de télémétrie, parmi lesquelles AppsFlyer, Firebase, Google Play Services, Sentry, Credolab, Incognia, Onfido, Incode et Twilio.
  104.  
  105. Des chaînes et chemins de code référencent notamment :
  106.  
  107. - `advertisingIdClientWrapper` ;
  108. - `appInstanceId` ;
  109. - `FirebaseAnalytics.appInstanceId` ;
  110. - `android_id` ;
  111. - `sha1_android_id` ;
  112. - `collectIMEI` ;
  113. - `collectIMEIForceByUser`.
  114.  
  115. La présence de ces références démontre une capacité de collecte ou de traitement d'identifiants dans les SDK embarqués. Elle ne prouve pas que chaque identifiant est obtenu ou envoyé pendant le lancement pré-crash analysé.
  116.  
  117. ## 1.4. Inventaire des applications installées
  118.  
  119. Revolut déclare `android.permission.QUERY_ALL_PACKAGES`. Le manifeste contient une liste explicite de familles d'applications liées au root, au hooking, au clonage, à l'émulation ou au contrôle distant : Magisk, KernelSU, SukiSU, APatch, Xposed/EdXposed, Substrate, SuperSU/Superuser, Lucky Patcher, App Cloner et d'autres outils.
  120.  
  121. Cette capacité d'inventaire est importante pour la confidentialité : la liste des logiciels présents sur un appareil révèle des usages et des choix techniques qui dépassent le strict fonctionnement d'une application bancaire.
  122.  
  123. ## 1.5. Profilage technique de l'appareil
  124.  
  125. Le split `feature_credit_data_monitoring_sdk` contient des collecteurs Credolab/Incognia pour :
  126.  
  127. `FINGERPRINT`, `TAGS`, `SDK_INT`, `RELEASE`, `SECURITY_PATCH`, `BOARD`, `BOOTLOADER`, `BRAND`, `DEVICE`, `MODEL`, `MANUFACTURER`, `PRODUCT`, `HARDWARE`.
  128.  
  129. Le PID Revolut a lui-même journalisé avant la terminaison protégée :
  130.  
  131. ```text
  132. Fingerprint: [20260614-202608251249 aarch64 35 POCO/redwood_global/redwood:14/UKQ1.240624.001/OS2.0.7.0.UMSMIXM:user/release-keys 5.4.302-Scarlet-v6.0]
  133. ```
  134.  
  135. Le fingerprint système et le noyau personnalisé font donc partie de l'état effectivement manipulé ou journalisé pendant le lancement.
  136.  
  137. ## 1.6. Root, intégrité et environnement
  138.  
  139. Plusieurs mécanismes distincts inspectent le système : DexProtector, Fourthline, Credolab, `DeviceInfo.getIsRooted()`, Play Integrity/SafetyNet et AndroidKeyStore/attestation.
  140.  
  141. Les indicateurs recherchés couvrent `su`, Magisk, KernelSU, APatch, Zygisk, Xposed, LSPosed, Riru, Frida, BusyBox, `test-keys`, `dev-keys`, SELinux, montages, `/proc/self/maps`, `/proc/mounts`, propriétés Android, état ADB, Verified Boot, bootloader et attestation TEE/KeyMint.
  142.  
  143. Du point de vue de l'utilisateur, cette inspection permet à Revolut de caractériser la manière dont il administre son propre terminal, et pas seulement de vérifier un identifiant de compte.
  144.  
  145. ## 1.7. Localisation, téléphonie, capteurs et interfaces sensibles
  146.  
  147. La cartographie DEX compte de nombreuses références à `FusedLocationProviderClient`, `LocationManager`, `ConnectivityManager`, `TelephonyManager`, `SensorManager`, caméra, microphone, NFC, Bluetooth et biométrie.
  148.  
  149. | Catégorie | Références d'API observées |
  150. | ------------------------------ | -------------------------: |
  151. | Localisation | 628 |
  152. | Biométrie | 384 |
  153. | Caméra / microphone / capteurs | 183 |
  154. | Keystore / attestation | 167 |
  155. | État réseau | 84 |
  156. | Téléphonie | 65 |
  157. | Accessibilité / presse-papiers | 60 |
  158. | Inventaire packages | 46 |
  159. | NFC | 29 |
  160. | Identifiants / Settings | 9 |
  161.  
  162. Ces nombres mesurent des références statiques et non des événements de collecte réels.
  163.  
  164. Le code référence aussi `AccessibilityManager.isEnabled()`, `getEnabledAccessibilityServiceList()`, `getInstalledAccessibilityServiceList()`, `ClipboardManager.getPrimaryClip()` et `getPrimaryClipDescription()`.
  165.  
  166. ## 1.8. Attestation matérielle et liaison à l'application
  167.  
  168. DexProtector utilise AndroidKeyStore et l'attestation X.509 Android. Le chemin reconstruit crée ou retrouve une clé EC P-256, utilise un challenge de 8 octets, parse l'OID Android Attestation `1.3.6.1.4.1.11129.2.1.17`, examine `RootOfTrust`, `deviceLocked`, `verifiedBootState`, classe la racine SPKI et vérifie une Application ID associée au package et au signataire APK.
  169.  
  170. Une clé persistante du namespace Revolut contient bien `com.revolut.revolut` dans son Application ID d'attestation et un digest de signature concordant avec les APK installés. Cette voie donne à l'application une vue matériellement attestée distincte des simples propriétés Android publiques.
  171.  
  172. ## 1.9. Ce qui est prouvé et ce qui ne l'est pas en matière de transmission
  173.  
  174. L'analyse sépare quatre niveaux :
  175.  
  176. 1. capacité statique présente ;
  177. 2. code effectivement appelé ;
  178. 3. donnée réellement obtenue ou stockée ;
  179. 4. transmission réseau établie.
  180.  
  181. | Information | Obtention locale | Usage local | Transmission réseau pré-crash établie |
  182. | ------------------------- | -------------------------- | ------------------------------ | ------------------------------------- |
  183. | État ADB | oui | bitfield / refus | non |
  184. | Verified Boot public | chemin établi | contrôle | non |
  185. | RootOfTrust matériel | oui sur certificat Revolut | contribue à `0x40` | non |
  186. | Build tags | chemin établi | bit `0x100` si condition vraie | non |
  187. | Lineage / GrapheneOS | chemin établi | bit `0x80` si condition vraie | non |
  188. | Statut DexProtector | oui | écran / QR | non |
  189. | Chaînes diagnostic Intent | oui dans l'UI locale | QR | non établi par `reGj()` |
  190.  
  191. La présence d'OkHttp, Retrofit, gRPC, Firebase ou Sentry ne suffit pas à prouver qu'une donnée précise a été transmise. L'absence de preuve de transmission avant le crash ne signifie pas non plus que les SDK n'envoient jamais ces données dans d'autres parcours.
  192.  
  193. ## 1.10. Conséquence pratique pour l'utilisateur
  194.  
  195. Revolut 10.145 peut faire dépendre l'accès à son application d'une analyse approfondie de l'appareil : configuration système, état matériel de démarrage, ADB, root, ROM, applications et attestation cryptographique. Le reverse engineering démontre aussi qu'un affichage superficiel `green`, `locked` et `release-keys` ne suffit pas lorsque l'attestation TEE/KeyMint révèle un état différent.
  196.  
  197. ---
  198.  
  199. # 2. Périmètre, méthode et hiérarchie des preuves
  200.  
  201. Les analyses consolidées datent principalement des 4 et 5 septembre 2026. Elles reposent sur ADB, logcat, dumpsys, Package Manager, inspection des APK/splits, baksmali, radare2/rabin2, Ghidra, scripts Python hors ligne, Termux intégré à Reins, observations root, parsing de certificats publics/Keystore et émulation locale bornée de fonctions natives avec Unicorn.
  202.  
  203. Les constats historiques fondamentaux ont été établis sans patcher l'APK Revolut, sans injecter du code dans son processus, sans MITM réseau et sans modifier ses certificats.
  204.  
  205. ### Niveau A - observé au runtime
  206.  
  207. - chargement de `libalice.so` puis `libdexprotector.so` ;
  208. - accès à `ro.debuggable` et `ro.lineage.build.version` ;
  209. - lancement de `ProtectedRevolutApplication$I` ;
  210. - écran « environnement non sécurisé » ;
  211. - code historique `4674 = 0x1242` ;
  212. - `TerminateException` et `APP CRASH(EXCEPTION)`.
  213.  
  214. ### Niveau B - mécanismes réellement implémentés et câblés
  215.  
  216. - détecteur root Credolab ;
  217. - `DeviceInfo.getIsRooted()` ;
  218. - Fourthline ;
  219. - inventaire de packages ;
  220. - Play Integrity/SafetyNet ;
  221. - AndroidKeyStore/attestation.
  222.  
  223. ### Niveau C - capacités statiques sans exécution pré-crash démontrée
  224.  
  225. - nombreux flux Onfido/Incode/Twilio ;
  226. - TensorFlow Lite ;
  227. - plusieurs chemins localisation/caméra/NFC ;
  228. - certaines télémétries et transmissions réseau.
  229.  
  230. # 3. Environnement matériel et système étudié
  231.  
  232. ## 3.1. Appareil
  233.  
  234. | Champ | Valeur |
  235. | -------------------------- | --------------------------------- |
  236. | Constructeur | Xiaomi |
  237. | Marque | POCO |
  238. | Modèle | `22101320G` |
  239. | Device | `redwood` |
  240. | SoC | QTI `SM7325` |
  241. | Android déclaré | 15 |
  242. | SDK | 35 |
  243. | Security patch système | `2025-09-01` |
  244. | Vendor security patch | `2025-06-01` |
  245. | ABI | `arm64-v8a, armeabi-v7a, armeabi` |
  246. | Premier niveau API produit | 31 |
  247. | SELinux | Enforcing |
  248. | Build public | `user` |
  249. | Tags publics | `release-keys` |
  250.  
  251. Propriétés publiques relevées :
  252.  
  253. ```text
  254. ro.secure=1
  255. ro.debuggable=0
  256. ro.adb.secure=1
  257. ro.boot.verifiedbootstate=green
  258. ro.boot.flash.locked=1
  259. ro.boot.vbmeta.device_state=locked
  260. ro.boot.veritymode=enforcing
  261. ro.boot.avb_version=1.2
  262. ```
  263.  
  264. ## 3.2. Noyau et root
  265.  
  266. Le noyau visible depuis une application non-root est :
  267.  
  268. ```text
  269. Linux localhost 5.4.302-Scarlet-v6.0 ... aarch64 Android
  270. ```
  271.  
  272. Le contexte root observé via Reins/Termux :
  273.  
  274. ```text
  275. uid=0(root)
  276. context=u:r:ksu:s0
  277. KernelSU 3.2.5-54-gcfac3be3:KernelSU
  278. ```
  279.  
  280. Éléments observés côté root :
  281.  
  282. - `/system/bin/su` présent et fonctionnel ;
  283. - `/data/adb/ksu` présent ;
  284. - `/data/adb/modules` présent ;
  285. - `/data/adb/apatch` absent ;
  286. - `/data/adb/magisk` absent ;
  287. - `/sbin/.magisk` absent ;
  288. - `/su/bin/su` absent ;
  289. - `/dev/kernelsu` absent au moment du relevé.
  290.  
  291. Un véritable processus Android Reins en contexte `untrusted_app` a vu :
  292.  
  293. ```text
  294. VISIBLE /system/bin/su
  295. NOT_VISIBLE /system/xbin/su
  296. NOT_VISIBLE /sbin/su
  297. NOT_VISIBLE /su/bin/su
  298. NOT_VISIBLE /sbin/.magisk
  299. NOT_VISIBLE /data/adb/magisk
  300. NOT_VISIBLE /data/adb/modules
  301. NOT_VISIBLE /data/adb/ksu
  302. NOT_VISIBLE /dev/kernelsu
  303. NOT_VISIBLE /data/adb/apatch
  304. uname -r 5.4.302-Scarlet-v6.0
  305. ```
  306.  
  307. La visibilité dépend donc du namespace et du contexte du processus. La présence root au niveau système ne se traduit pas nécessairement par un `File.exists()` positif depuis Revolut.
  308.  
  309. ## 3.3. Divergence Verified Boot
  310.  
  311. Les propriétés Android publiques indiquent `green/locked`, mais `/proc/cmdline` et `/proc/device-tree/chosen/bootargs` contiennent :
  312.  
  313. ```text
  314. androidboot.verifiedbootstate=orange
  315. ```
  316.  
  317. La machine expose donc deux vues différentes :
  318.  
  319. - état boot brut : `orange` ;
  320. - propriétés Android exposées : `green`, `locked`, `enforcing`.
  321.  
  322. Des couches de modification système présentes sur l'appareil réécrivent certaines propriétés avec `resetprop`. Cette divergence rend `getprop` insuffisant pour qualifier l'intégrité réelle du terminal.
  323.  
  324. Une trace noyau conservée par KernelSU montre :
  325.  
  326. ```text
  327. Init cannot set 'ro.boot.verifiedbootstate' to 'orange': Read-only property was already set
  328. ```
  329.  
  330. Cette ligne prouve une incohérence précoce, mais pas la valeur exacte qui était déjà positionnée.
  331.  
  332. ## 3.4. Divergence Android 15 / fingerprint Android 14
  333.  
  334. Le système expose :
  335.  
  336. ```text
  337. ro.build.version.release=15
  338. ro.build.version.sdk=35
  339. ro.build.id=BP1A.250505.005
  340. ```
  341.  
  342. Mais les fingerprints `build`, `system`, `vendor`, `product` et `system_ext` donnent :
  343.  
  344. ```text
  345. POCO/redwood_global/redwood:14/UKQ1.240624.001/OS2.0.7.0.UMSMIXM:user/release-keys
  346. ```
  347.  
  348. et :
  349.  
  350. ```text
  351. ro.build.description=redwood_global-user 14 UKQ1.240624.001 OS2.0.7.0.UMSMIXM release-keys
  352. ```
  353.  
  354. Les fichiers de propriétés réels révèlent en outre une base `userdebug` Lineage/MatrixX :
  355.  
  356. ```text
  357. ro.system.build.type=userdebug
  358. ro.build.type=userdebug
  359. ro.build.display.id=lineage_redwood-userdebug 15 BP1A.250505.005 ... release-keys
  360. ro.build.flavor=lineage_redwood-userdebug
  361. ro.lineage.device=redwood
  362. ro.matrixx.build.version=Matrixx-v11.8.0-Official-redwood-Gapps-202510031305
  363. ro.matrixx.display.version=Matrixx-15.0-redwood-v11.8.0
  364. ```
  365.  
  366. Cette coexistence de plusieurs identités système est pertinente puisque la protection cherche explicitement certaines propriétés de ROM et de build.
  367.  
  368. ---
  369.  
  370. # 4. État installé de Revolut 10.145
  371.  
  372. ## 4.1. Métadonnées principales
  373.  
  374. | Champ | Valeur |
  375. | ---------------------- | ----------------------------------------- |
  376. | Package | `com.revolut.revolut` |
  377. | `versionName` | `10.145` |
  378. | `versionCode` | `1030145101` |
  379. | `minSdk` | 28 |
  380. | `targetSdk` | 36 |
  381. | `compileSdkVersion` | 37 |
  382. | ABI principale | `arm64-v8a` |
  383. | Installateur | `com.android.vending` |
  384. | UID observé | `10701` |
  385. | Application | `com.revolut.ProtectedRevolutApplication` |
  386. | Activité login | `com.revolut.ui.login.pin.LoginActivity` |
  387. | `extractNativeLibs` | `false` |
  388. | `usesCleartextTraffic` | `false` |
  389. | `allowBackup` | `false` |
  390.  
  391. OpenGL ES 3.0 est déclaré requis. Les fonctions caméra, NFC, microphone, Bluetooth et autres capacités matérielles observées ne sont pas toutes déclarées obligatoires.
  392.  
  393. ## 4.2. APK et splits installés
  394.  
  395. Douze APK/splits ont été inventoriés :
  396.  
  397. - `base.apk` ;
  398. - `split_config.arm64_v8a.apk` ;
  399. - `split_config.fr.apk` ;
  400. - `split_config.xxhdpi.apk` ;
  401. - `split_feature_apm_multibanco_sdk_impl.apk` ;
  402. - `split_feature_apm_multibanco_sdk_impl.config.arm64_v8a.apk` ;
  403. - `split_feature_apm_multibanco_sdk_impl.config.xxhdpi.apk` ;
  404. - `split_feature_credit_data_monitoring_sdk.apk` ;
  405. - `split_feature_in_app_calls_twilio_retail.apk` ;
  406. - `split_feature_in_app_calls_twilio_retail.config.arm64_v8a.apk` ;
  407. - `split_feature_incode_sdk.apk` ;
  408. - `split_feature_incode_sdk.config.arm64_v8a.apk`.
  409.  
  410. Le `base.apk` contient 22 DEX ; l'inventaire global des splits en a compté 27.
  411.  
  412. ---
  413.  
  414. # 5. Reproduction du crash et chronologie du démarrage
  415.  
  416. ## 5.1. Lancement normal
  417.  
  418. Le lancement de :
  419.  
  420. ```text
  421. com.revolut.revolut/com.revolut.ui.login.pin.LoginActivity
  422. ```
  423.  
  424. est accepté par Android. Plusieurs reproductions ont retourné :
  425.  
  426. ```text
  427. Status: ok
  428. LaunchState: UNKNOWN (0)
  429. Activity: com.revolut.revolut/com.revolut.ui.login.pin.LoginActivity
  430. ```
  431.  
  432. Le processus principal disparaît ensuite en quelques dizaines à quelques centaines de millisecondes.
  433.  
  434. `ApplicationExitInfo` montre de nombreuses occurrences :
  435.  
  436. ```text
  437. reason=4 (APP CRASH(EXCEPTION))
  438. ```
  439.  
  440. Le crash est enregistré comme :
  441.  
  442. ```text
  443. FATAL EXCEPTION: main
  444. Process: com.revolut.revolut
  445. android.app.TerminateException$...
  446. ```
  447.  
  448. La classe `TerminateException` n'existe ni dans les `android.jar` SDK locaux ni dans le `/system/framework/framework.jar` réel du téléphone. Sa chaîne n'apparaît pas non plus en clair dans les DEX Revolut copiés. Elle appartient au comportement produit dynamiquement par la couche protégée.
  449.  
  450. ## 5.2. Séquence prouvée
  451.  
  452. 1. Android lance `LoginActivity`.
  453. 2. `ProtectedRevolutApplication.attachBaseContext()` s'exécute.
  454. 3. Une initialisation charge `libalice.so`.
  455. 4. `ProtectedRevolutApplication$I.bo()` vérifie si le processus se termine par `:p2235fa67286a712a679e1044`.
  456. 5. Dans le processus principal, `System.loadLibrary("dexprotector")` charge `libdexprotector.so`.
  457. 6. Des accès à `ro.debuggable` puis `ro.lineage.build.version` sont observés.
  458. 7. `ProtectedRevolutApplication.onCreate()` appelle une routine native `c()`.
  459. 8. Revolut tente d'ouvrir `ProtectedRevolutApplication$I`.
  460. 9. Android journalise un `Background activity launch blocked`.
  461. 10. Le thread principal se termine sur `TerminateException`.
  462. 11. ActivityManager enregistre `APP CRASH(EXCEPTION)`.
  463.  
  464. `bo()` n'est donc pas un test d'intégrité : il sert à reconnaître le processus dédié de l'activité d'erreur et à éviter d'y relancer la protection.
  465.  
  466. ## 5.3. Revalidation du 4 septembre 2026 à 19:49
  467.  
  468. | Heure | Observation |
  469. | ------------------- | -------------------------------------------------------- |
  470. | `19:49:32.597` | PID `25140` lancé |
  471. | `19:49:32.743` | `libalice.so` chargée |
  472. | `19:49:32.764` | `libdexprotector.so` chargée |
  473. | `19:49:32.772` | accès refusé à `ro.debuggable` |
  474. | `19:49:32.777` | accès refusé à `ro.lineage.build.version` |
  475. | `19:49:32.808-.813` | demande `ProtectedRevolutApplication$I`, `BAL_BLOCK` |
  476. | `19:49:32.816` | `TerminateException` |
  477. | `19:49:32.838` | processus mort |
  478. | `19:49:43.299` | watchdog `keystore2` inactif, sans attribution à Revolut |
  479.  
  480. Cette capture n'a pas relu le QR ni son entier. `0x1242` reste le code du lancement historique documenté, pas une nouvelle mesure à 19:49.
  481.  
  482. Des reproductions analogues ont été conservées à 12:29, 16:44, 17:58, 21:14, 21:31, 21:48 et 22:25. Toutes confirment l'ordre général :
  483.  
  484. ```text
  485. libalice.so
  486. -> libdexprotector.so
  487. -> sondes système
  488. -> ProtectedRevolutApplication$I
  489. -> TerminateException
  490. ```
  491.  
  492. Aucun SIGSEGV natif n'est observé comme cause immédiate.
  493.  
  494. ---
  495.  
  496. # 6. Activité protégée, écran d'erreur et code 4674 / 0x1242
  497.  
  498. ## 6.1. Écran réellement observé
  499.  
  500. La capture historique affichait :
  501.  
  502. > L'application ne peut pas s'exécuter, car l'environnement n'est pas sécurisé et vos données peuvent être en danger.
  503.  
  504. Le QR a été décodé hors ligne en :
  505.  
  506. ```text
  507. <identifiant-diagnostic> (anonymisé)
  508. 4674
  509. ```
  510.  
  511. `4674 = 0x1242`.
  512.  
  513. ## 6.2. Classification Java
  514.  
  515. `ProtectedRevolutApplication$I.laF(int)` applique :
  516.  
  517. ```text
  518. si code <= 0x1000 -> appareil/application incompatible
  519. sinon si code & 0x1 != 0 -> émulateur
  520. sinon si code & 0x10 != 0 -> installation non officielle
  521. sinon si code & 0x1ee != 0 -> environnement non sécurisé
  522. sinon -> appareil/application incompatible
  523. ```
  524.  
  525. Pour `0x1242` :
  526.  
  527. ```text
  528. 0x1242 & 0x1 = 0
  529. 0x1242 & 0x10 = 0
  530. 0x1242 & 0x1ee = 0x42
  531. ```
  532.  
  533. Le lancement est donc explicitement classé **environnement non sécurisé**, et non émulateur ou installation non officielle.
  534.  
  535. ## 6.3. Construction du QR et bridge natif
  536.  
  537. La native transmet le statut à un notifier identifié vers `0x82560`, qui passe l'entier à Java/JNI. L'activité protégée le lit dans l'extra :
  538.  
  539. ```text
  540. d6a6588daf2b0a92de41f122c557d510
  541. ```
  542.  
  543. `reGj()` concatène cet entier avec deux extras texte :
  544.  
  545. ```text
  546. 35bb9b08cb6e302c7338829816c2a2a5
  547. 35e3e802421d5280861b367f9d1e4a01
  548. ```
  549.  
  550. puis encode le résultat en UTF-8 pour générer le QR. Le chemin démontré est local :
  551.  
  552. ```text
  553. statut natif -> JNI/Intent -> activité d'erreur -> texte/QR
  554. ```
  555.  
  556. Cette logique ne démontre pas de transmission réseau.
  557.  
  558. ---
  559.  
  560. # 7. Architecture de protection observée
  561.  
  562. L'application combine plusieurs couches :
  563.  
  564. 1. sandbox Android standard ;
  565. 2. `ProtectedRevolutApplication` ;
  566. 3. `libalice.so` ;
  567. 4. DexProtector / `libdexprotector.so` ;
  568. 5. contrôles natifs Fourthline ;
  569. 6. SDK anti-fraude Credolab / Incognia ;
  570. 7. second détecteur root dans le split Multibanco ;
  571. 8. Play Integrity / SafetyNet ;
  572. 9. AndroidKeyStore / attestation ;
  573. 10. décisions métier potentiellement prises côté serveur.
  574.  
  575. Le crash immédiat intervient avant le chargement visible de `libfourthline-security.so`. Sa cause immédiate se situe donc dans la phase `ProtectedRevolutApplication` / `libalice.so` / DexProtector.
  576.  
  577. ---
  578.  
  579. # 8. DexProtector : structure ELF, reconstruction et image native protégée
  580.  
  581. ## 8.1. Bibliothèque originale
  582.  
  583. `libdexprotector.so` est un ELF64 AArch64, statique, stripped, PIC, NX, full RELRO, compilé avec LLD 17.0.2.
  584.  
  585. ```text
  586. taille : 443222 octets
  587. SHA-256 : 81bbed87aaa3fb03285d37bd04952dd18b0ba5f33e958e3f866791fa8034aeee
  588. ```
  589.  
  590. Le quatrième `PT_LOAD` protégé :
  591.  
  592. ```text
  593. offset fichier : 0x3638
  594. vaddr : 0xf638
  595. filesz : 0x68d16 = 429334
  596. memsz : 0x68d16
  597. SHA-256 : c3c29b0d376662e0c15f6133f77d633f53b31b1fdba69a704411f69fe838ca17
  598. ```
  599.  
  600. L'unique `.init_array` pointe vers `0x380`. Cette fonction exécute `prctl(PR_SET_DUMPABLE, 0)`, parcourt les program headers, sélectionne le quatrième `PT_LOAD` et le transmet à `0x243c`.
  601.  
  602. ## 8.2. Reconstruction d'une seconde image ELF
  603.  
  604. `0x243c` appelle `0x1298` pour reconstruire une image native, puis `0x1684` pour appliquer les relocations. Il traite les tags `DT_INIT_ARRAY`, `DT_FINI_ARRAY`, `DT_INIT_ARRAYSZ`, `DT_FINI_ARRAYSZ`, `DT_PLTRELSZ`, `DT_STRTAB`, `DT_SYMTAB`, `DT_RELA`, `DT_RELASZ`, `DT_STRSZ`, `DT_JMPREL`, `DT_RELRSZ`, `DT_RELR` et `DT_RELRENT`.
  605.  
  606. `0x1298` décode un en-tête protégé, réserve la mémoire avec `mmap`, reconstruit jusqu'à quatre segments, utilise `mprotect`, vérifie des checksums 16 bits, peut nommer des VMA avec `prctl(PR_SET_VMA, ...)`, et libère avec `munmap` en cas d'échec. La décompression reproduite hors ligne correspond à un schéma de type LZ4.
  607.  
  608. ## 8.3. Dérivation de clé liée au linker
  609.  
  610. La fonction `0x920` utilise `r_debug` et `r_brk` du linker Android. Le linker local analysé a le SHA-256 :
  611.  
  612. ```text
  613. a12e5824bbb6a2da45595b46396afce080e5a1c4f6dadccbe2790d513814da5f
  614. ```
  615.  
  616. L'émulation bornée avec Unicorn a produit :
  617.  
  618. ```text
  619. r_brk_bytes=c0035fd600000000
  620. final_pc=0x54000000
  621. derived_key=0f567a3629535da09e32c89fe26d159a851f97238b98893954ab9f48d377cf0c
  622. ```
  623.  
  624. En-tête décodé :
  625.  
  626. ```text
  627. 0x000ac000 0x00000000 0x00000040 0x00000008 0x0009d670
  628. 0x00098000 0x00008000 0x00004000 0x00000004
  629. ```
  630.  
  631. Le flux décodé fait 429330 octets, SHA-256 :
  632.  
  633. ```text
  634. c8491b253f378aca48d8248b6369f3df9fb5d3ffd198174a4b27c6669a40ea98
  635. ```
  636.  
  637. ## 8.4. Segments reconstruits
  638.  
  639. | Segment | Offset destination | Taille mémoire | Taille décompressée | Permissions | Checksum | Taille compressée |
  640. | ------- | -----------------: | -------------: | ------------------: | ----------: | -------: | ----------------: |
  641. | 0 | `0x0` | 611936 | 611936 | 5 | 381 | 418121 |
  642. | 1 | `0x99660` | 17224 | 17224 | 3 | 51960 | 5504 |
  643. | 2 | `0xa1980` | 24720 | 5200 | 3 | 40058 | 4996 |
  644. | 3 | `0xa8000` | 780 | 780 | 5 | 21269 | 577 |
  645.  
  646. Image reconstruite :
  647.  
  648. ```text
  649. taille : 704512 octets
  650. SHA-256 : ab936e6ccbd4c1ff76fe4fc8869a4d05118e97fb6bea1068743f39521cf245c0
  651. ```
  652.  
  653. Dépendances déclarées : `liblog.so`, `libandroid.so`, `libc.so`.
  654.  
  655. ## 8.5. Imports et inventaire profond
  656.  
  657. 24 imports dynamiques : `malloc`, `free`, `calloc`, `time`, `AAssetManager_openDir`, `AAssetDir_getNextFileName`, `AAsset_close`, `AAssetManager_open`, `AAsset_getBuffer`, `AAsset_getLength`, `AAssetDir_close`, `realloc`, `pthread_create`, `pthread_detach`, `sigaction`, `pthread_atfork`, `AAssetManager_fromJava`, `__android_log_write`, `arc4random_buf`, `__android_log_print`, `gmtime_r`, `getauxval`, `funopen`, `setvbuf`.
  658.  
  659. Inventaire :
  660.  
  661. ```text
  662. 160177 instructions désassemblées
  663. 5898 appels directs
  664. 10052 branches directes
  665. 284 syscalls repérés
  666. ```
  667.  
  668. Principaux syscalls : `close` 40, `exit_group` 9, `fchdir` 8, `fstat` 7, `getpid` 7, `getuid` 6, `ioctl` 18, `kill` 4, `mmap` 11, `mprotect` 11, `munmap` 12, `nanosleep` 11, `newfstatat` 28, `openat` 21, `prctl` 46, `read` 8, `readlinkat` 5, `write` 3.
  669.  
  670. # 9. Bitfield d'environnement DexProtector
  671.  
  672. L'analyse de l'image native reconstruite identifie une routine de synthèse qui assemble plusieurs indicateurs d'environnement dans un entier de statut. Les correspondances établies sont :
  673.  
  674. | Indicateur | Signification établie |
  675. | ---------- | ------------------------------------------------------------ |
  676. | `0x1` | catégorie liée à l'émulation |
  677. | `0x10` | catégorie liée à l'installation non officielle |
  678. | `0x40` | signal global d'environnement, alimenté notamment par Verified Boot et l'attestation KeyStore |
  679. | `0x80` | indicateurs de ROM/environnement, notamment Lineage ou GrapheneOS dans les chemins analysés |
  680. | `0x100` | tags de build `test-keys` ou `dev-keys` |
  681. | `0x200` | ADB actif |
  682. | `0x2` | agrégat automatiquement ajouté lorsqu'un des signaux `0x40`, `0x80` ou `0x100` est actif |
  683. | `0x1000` | marqueur de classe du statut environnement avant transport vers Java |
  684.  
  685. Le code historique :
  686.  
  687. ```text
  688. 0x1242 = 0x1000 | 0x200 | 0x40 | 0x2
  689. ```
  690.  
  691. ne contient ni `0x1` ni `0x10`, ce qui concorde avec la classification Java « environnement non sécurisé » plutôt qu'« émulateur » ou « installation non officielle ».
  692.  
  693. Les analyses ont également établi que `0x80` et `0x100` étaient absents du code historique. Cela concorde avec la vue publique observée au moment de l'analyse : build `release-keys` et propriétés de ROM ciblées masquées ou absentes.
  694.  
  695. Le CFG du wrapper natif confirme que la construction du bitfield précède une chaîne de contrôles supplémentaires, dont le chemin AndroidKeyStore/attestation. Plusieurs branches peuvent donc contribuer au statut final sans qu'une constante `0x1242` soit stockée telle quelle dans la bibliothèque.
  696.  
  697. ---
  698.  
  699. # 10. Détection ADB - bit 0x200
  700.  
  701. Le bit `0x200` est attribué à un prédicat natif de détection ADB.
  702.  
  703. Le chemin inspecte notamment :
  704.  
  705. - le port ADB TCP ;
  706. - l'état du service `adbd` ;
  707. - la configuration USB ;
  708. - l'état USB contenant `adb`.
  709.  
  710. Le code possède également un mécanisme natif de suivi autour de ce contrôle, et pas seulement un test ponctuel.
  711.  
  712. Sur le téléphone analysé, les relevés montrent un état ADB actif, notamment :
  713.  
  714. ```text
  715. persist.adb.tcp.port=5555
  716. init.svc.adbd=running
  717. init.svc.adb_root=running
  718. persist.sys.usb.config=adb
  719. sys.usb.config=adb
  720. sys.usb.state=adb
  721. sys.usb.adb.disabled=0
  722. settings global adb_enabled=1
  723. ```
  724.  
  725. Ces valeurs correspondent aux conditions recherchées par le prédicat. Le `0x200` du statut historique `0x1242` est donc expliqué par un état ADB effectivement présent.
  726.  
  727. ---
  728.  
  729. # 11. Verified Boot, propriétés système et bit 0x40
  730.  
  731. DexProtector possède un chemin natif qui inspecte notamment :
  732.  
  733. - `ro.boot.verifiedbootstate` ;
  734. - `ro.boot.flash.locked` ;
  735. - `ro.boot.vbmeta.device_state` ;
  736. - les mappings de propriétés via `/proc/self/maps`.
  737.  
  738. Le contrôle ne se limite pas à comparer une chaîne. Il vérifie également la cohérence du `prop_info` correspondant dans la zone de propriétés Android.
  739.  
  740. Une copie des property areas du téléphone a été analysée. La vue publique contenait :
  741.  
  742. ```text
  743. ro.boot.verifiedbootstate=green
  744. ro.boot.flash.locked=1
  745. ro.boot.vbmeta.device_state=locked
  746. ```
  747.  
  748. Le modèle local de la partie observable du validateur considère cette vue comme cohérente et ne produit pas de signal d'erreur. Cela réduit la probabilité que la seule propriété publique `green` explique le `0x40` historique.
  749.  
  750. En parallèle, l'état boot brut exposé par `/proc/cmdline` reste `orange`. Cette divergence confirme qu'il faut distinguer les propriétés Android visibles et l'état matériel réellement attesté.
  751.  
  752. Les snapshots du PID Revolut montrent que les zones de propriétés nécessaires sont bien mappées dans le processus. La libc Bionic locale confirme en outre que la résolution normale d'une propriété renvoie le vrai `prop_info` dans son `prop_area`.
  753.  
  754. Le statut final prouve que le global associé à `0x40` était actif, mais sa seule valeur finale ne permet pas de désigner le writer responsable. L'analyse KeyStore ci-dessous fournit cependant une correspondance directe avec une donnée réellement présente sous l'UID de Revolut.
  755.  
  756. ---
  757.  
  758. # 12. AndroidKeyStore, attestation, RootOfTrust et bit 0x40
  759.  
  760. ## 12.1. Chemin d'attestation
  761.  
  762. L'image DexProtector reconstruite contient un chemin AndroidKeyStore complet qui :
  763.  
  764. - interroge le service Keystore ;
  765. - utilise une clé EC P-256 ;
  766. - configure un challenge d'attestation de 8 octets ;
  767. - récupère une chaîne de certificats ;
  768. - parse l'extension Android Key Attestation ;
  769. - examine le niveau de sécurité ;
  770. - examine `RootOfTrust` ;
  771. - examine `deviceLocked` et `verifiedBootState` ;
  772. - classe la racine cryptographique de la chaîne ;
  773. - vérifie l'identité de l'application attestée.
  774.  
  775. L'OID recherché est :
  776.  
  777. ```text
  778. 1.3.6.1.4.1.11129.2.1.17
  779. ```
  780.  
  781. c'est-à-dire l'extension standard Android Key Attestation.
  782.  
  783. ## 12.2. Clé persistante liée à Revolut
  784.  
  785. Une clé persistante est présente dans le namespace Keystore2 correspondant à l'UID Revolut `10701`. Son alias est `<alias-anonymisé>` (anonymisé) et l'analyse statique montre qu'il correspond au schéma d'alias déterministe utilisé par DexProtector pour le processus Revolut.
  786.  
  787. Les métadonnées de la clé indiquent :
  788.  
  789. ```text
  790. ALGORITHM=EC
  791. EC_CURVE=P_256
  792. ORIGIN=GENERATED
  793. PURPOSE=SIGN
  794. DIGEST=SHA_2_256
  795. KEY_SIZE=256
  796. security_level=TRUSTED_ENVIRONMENT
  797. ```
  798.  
  799. Le certificat public d'attestation associé contient :
  800.  
  801. ```text
  802. Application ID: com.revolut.revolut
  803. device_locked=false
  804. verified_boot_state=2
  805. root_of_trust_source=teeEnforced
  806. ```
  807.  
  808. Le digest de signature de l'Application ID correspond au certificat de signature des APK Revolut installés :
  809.  
  810. ```text
  811. 9c9be07135e972780282c2e5d27da06ecb8ee3adfc75303917ddf66d6faaefa4
  812. ```
  813.  
  814. Le hash Verified Boot contenu dans l'attestation observée est (valeur anonymisée) :
  815.  
  816. ```text
  817. <hash-verified-boot> (anonymisé)
  818. ```
  819.  
  820. La chaîne publique est reconnue par le classificateur de DexProtector : l'échec ne s'explique donc pas par une simple racine d'attestation inconnue.
  821.  
  822. ## 12.3. Attestation indépendante du même appareil
  823.  
  824. Une attestation AndroidKeyStore locale effectuée hors Revolut via `app_process` a produit :
  825.  
  826. ```text
  827. generate_key_pair=success
  828. certificate_chain_length=4
  829. attestation_extension_present=true
  830. attestation_security_level=1:TrustedEnvironment
  831. keymaster_security_level=1:TrustedEnvironment
  832. root_of_trust_present=true
  833. root_of_trust_source=teeEnforced
  834. device_locked=false
  835. verified_boot_state=2:Unverified
  836. tee_os_version=150000
  837. tee_os_patch_level=202509
  838. tee_vendor_patch_level=20250601
  839. tee_boot_patch_level=202509
  840. ```
  841.  
  842. La racine terminale observée est la même famille de racine reconnue par DexProtector.
  843.  
  844. ## 12.4. Relation avec le bit 0x40
  845.  
  846. Le parser DexProtector possède une condition explicite sur `RootOfTrust` : un appareil annoncé comme non verrouillé ou dont le Verified Boot est `Unverified` active un drapeau interne consommé par le chemin qui positionne le signal global `0x40`.
  847.  
  848. Le certificat persistant attaché à l'UID de Revolut possède précisément ces deux caractéristiques :
  849.  
  850. ```text
  851. deviceLocked=false
  852. verifiedBootState=Unverified
  853. ```
  854.  
  855. Il existe donc une correspondance directe entre :
  856.  
  857. 1. le champ d'attestation attendu par le code ;
  858. 2. la valeur réellement présente dans un certificat associé à Revolut ;
  859. 3. le signal `0x40` présent dans le statut historique.
  860.  
  861. Le modèle du chemin de propriétés publiques, lui, ne reproduit pas d'erreur avec la vue `green/locked`. La cause pratique la plus solidement étayée du `0x40` historique est donc l'attestation TEE/KeyMint réellement retournée à Revolut, et non la simple apparence `green/locked` fournie par `getprop`.
  862.  
  863. ## 12.5. Keybox et racine publique
  864.  
  865. Le téléphone expose un overlay système de keybox certifiée actif. L'analyse n'a conservé que les certificats publics et leurs empreintes ; aucun matériel privé n'a été reporté. La racine publique résolue par cet overlay correspond à une racine reconnue par DexProtector, ce qui renforce la conclusion que le rejet porte sur les champs de `RootOfTrust` plutôt que sur une chaîne inconnue.
  866.  
  867. # 13. Fourthline : contrôles root, hook, instrumentation et environnement
  868.  
  869. `libfourthline-security.so` est une bibliothèque native AArch64 qui enregistre un `NativeSecurityChecker` via JNI. Son `JNI_OnLoad` récupère `JNIEnv`, cherche `com/fourthline/core/internal/security/NativeSecurityChecker` et enregistre la méthode native correspondante.
  870.  
  871. Imports utiles observés :
  872.  
  873. - `__system_property_get` ;
  874. - `popen` ;
  875. - `access` ;
  876. - `fopen` ;
  877. - `opendir` ;
  878. - `readlink`.
  879.  
  880. Les chaînes et contrôles natifs couvrent notamment :
  881.  
  882. ### Root classique
  883.  
  884. - `/system/bin/su` ;
  885. - `/system/xbin/su` ;
  886. - `/sbin/su` ;
  887. - `/su/bin/su` ;
  888. - `/data/local/bin/su` ;
  889. - `/data/local/xbin/su` ;
  890. - `busybox` ;
  891. - commandes `which su`.
  892.  
  893. ### Magisk / Zygisk / modules
  894.  
  895. - chemins Magisk sous `/data/adb` et `/sbin` ;
  896. - `/data/adb/modules` ;
  897. - marqueurs Zygisk ;
  898. - modules LSPosed / Riru.
  899.  
  900. ### Hooking et instrumentation
  901.  
  902. - Xposed / EdXposed ;
  903. - LSPosed ;
  904. - Riru ;
  905. - Substrate ;
  906. - Pine / SandHook / Whale ;
  907. - Frida, `frida-server`, `frida-gadget`, `libfrida`.
  908.  
  909. ### État système
  910.  
  911. - `ro.debuggable` ;
  912. - `ro.secure` ;
  913. - `ro.build.selinux` ;
  914. - `service.adb.root` ;
  915. - `/sys/fs/selinux/enforce` ;
  916. - `/proc/self/status` ;
  917. - `/proc/self/maps` ;
  918. - `/proc/self/mounts` ;
  919. - `/proc/mounts` ;
  920. - `/proc/%s/cmdline` ;
  921. - `/proc/self/fd` ;
  922. - `test-keys`.
  923.  
  924. Cette bibliothèque est bien livrée dans l'APK, mais elle n'est pas chargée visiblement avant le crash DexProtector immédiat dans le lancement de référence. Elle constitue donc une couche de contrôle additionnelle plutôt que la cause immédiate démontrée de `0x1242`.
  925.  
  926. ---
  927.  
  928. # 14. Credolab / Incognia : collecte et détection root réellement câblées
  929.  
  930. Le split `split_feature_credit_data_monitoring_sdk.apk` contient notamment :
  931.  
  932. - Credolab behavior `4.15.0-1768558695_moduleRelease` ;
  933. - Credolab Core `4.19.1` ;
  934. - Incognia.
  935.  
  936. ## 14.1. Détecteur root au niveau du code
  937.  
  938. Le détecteur n'est pas une simple collection de chaînes mortes. Le désassemblage montre un bloc root qui :
  939.  
  940. 1. recherche `su` via une fonction dédiée ;
  941. 2. utilise aussi `Runtime.exec(["which", "su"])` si nécessaire ;
  942. 3. recherche plusieurs composants Magisk ;
  943. 4. teste par `File.exists()` des chemins liés à Magisk, KernelSU, APatch et divers systèmes Superuser historiques ;
  944. 5. matérialise le résultat en booléen sérialisé.
  945.  
  946. La fonction de recherche de binaire parcourt explicitement des répertoires tels que :
  947.  
  948. - `/sbin/` ;
  949. - `/su/bin/` ;
  950. - `/system/bin/` ;
  951. - `/system/xbin/` ;
  952. - `/vendor/bin/` ;
  953. - `/product/bin/` ;
  954. - `/system_ext/bin/` ;
  955. - `/odm/bin/` ;
  956. - `/apex/com.android.runtime/bin/` ;
  957. - ainsi que les entrées du `PATH`.
  958.  
  959. Le chemin `/system/bin/su` fait donc partie des marqueurs réellement testés.
  960.  
  961. ## 14.2. Intégration dans le pipeline de collecte
  962.  
  963. Le descripteur associé au champ root couvre Android API 21 à 9999, donc SDK 35. Il est ajouté à la liste réelle de collecte, intégré au CoreModule Credolab et consommable par le moteur du SDK via son callback `extract`.
  964.  
  965. Conclusion : le paquet Revolut 10.145 embarque et câble effectivement un détecteur KernelSU/Magisk/APatch via un SDK tiers. Il n'est cependant pas démontré que ce callback soit invoqué avant le `TerminateException` du lancement très précoce, et il n'est pas nécessaire pour expliquer `0x1242`.
  966.  
  967. ---
  968.  
  969. # 15. Second détecteur root : DeviceInfo.getIsRooted
  970.  
  971. Le split `split_feature_apm_multibanco_sdk_impl.apk` contient un second mécanisme indépendant :
  972.  
  973. ```text
  974. sibs/connector/core/callbacks/model/DeviceInfo.getIsRooted()
  975. ```
  976.  
  977. La méthode exécute successivement plusieurs familles de contrôles et retourne `true` dès qu'une famille est positive :
  978.  
  979. - recherche de binaires root par `File.exists()` ;
  980. - recherche de paquets de gestion root avec `PackageManager.getPackageInfo()` ;
  981. - exécution de `getprop`, puis recherche de `ro.debuggable=[1]` ou `ro.secure=[0]` ;
  982. - recherche de fichiers Xposed ;
  983. - recherche de fichiers/répertoires considérés non autorisés ;
  984. - exécution de `mount` et détection de montages sensibles en `rw` ;
  985. - nouvelle passe de détection d'applications de gestion root.
  986.  
  987. Sur le téléphone étudié, `/system/bin/su` est visible depuis un processus Android `untrusted_app` Reins. Sa visibilité exacte depuis le PID Revolut peut toutefois différer selon le namespace et les mécanismes d'isolation en place.
  988.  
  989. ---
  990.  
  991. # 16. Inventaire de paquets, QUERY_ALL_PACKAGES et applications ciblées
  992.  
  993. Le manifeste demande :
  994.  
  995. ```text
  996. android.permission.QUERY_ALL_PACKAGES
  997. ```
  998.  
  999. Il référence explicitement plusieurs familles :
  1000.  
  1001. - Magisk ;
  1002. - KernelSU et variantes ;
  1003. - SukiSU ;
  1004. - APatch et variantes ;
  1005. - Xposed / EdXposed ;
  1006. - Substrate ;
  1007. - variantes SuperSU / Superuser ;
  1008. - Lucky Patcher ;
  1009. - App Cloner ;
  1010. - plusieurs émulateurs Android ;
  1011. - plusieurs outils de contrôle distant.
  1012.  
  1013. Deux paquets liés à l'écosystème KernelSU ont été observés sur le téléphone :
  1014.  
  1015. ```text
  1016. io.github.a13e300.ksuwebui
  1017. com.kowx712.supermanager
  1018. ```
  1019.  
  1020. Leurs noms exacts n'ont pas été retrouvés comme constantes dans les APK/splits/ELF Revolut inspectés. Le crash actuel ne peut donc pas être attribué factuellement à une comparaison directe avec ces deux noms précis.
  1021.  
  1022. ---
  1023.  
  1024. # 17. Play Integrity, SafetyNet, Keystore et décision serveur
  1025.  
  1026. ## 17.1. Play Integrity embarqué
  1027.  
  1028. Les DEX contiennent notamment des références à :
  1029.  
  1030. - `com.google.android.play.core.integrity` ;
  1031. - `StandardIntegrityManager` ;
  1032. - `IntegrityTokenRequest` ;
  1033. - `StandardIntegrityTokenRequest` ;
  1034. - `PrepareIntegrityTokenRequest` ;
  1035. - `StandardIntegrityTokenProvider` ;
  1036. - `ExpressIntegrityService` ;
  1037. - requêtes et warm-up de token d'intégrité.
  1038.  
  1039. Des références SafetyNet sont également présentes.
  1040.  
  1041. Le code contient aussi des indicateurs ou clés analytics liés au root et aux requêtes d'intégrité, par exemple :
  1042.  
  1043. - `IS_DEVICE_ROOTED_KEY` ;
  1044. - `ROOTED` ;
  1045. - `isRootedDevice` ;
  1046. - `is_rooted_device` ;
  1047. - `IntegrityTokenRequestsRetryAnalyticsEnabled` ;
  1048. - `EnableIntegrityTokenRetryAnalyticsFirebaseExperiment`.
  1049.  
  1050. Revolut dispose donc à la fois de contrôles locaux, de Play Integrity et de télémétrie relative à ces contrôles.
  1051.  
  1052. ## 17.2. Limite de causalité
  1053.  
  1054. Aucun verdict Play Integrity signé spécifique à Revolut n'a été obtenu pendant l'étude. Aucune requête Play Integrity n'est visible comme cause immédiate avant la terminaison historique. Il est donc incorrect d'attribuer le `TerminateException` immédiat à Play Integrity seul.
  1055.  
  1056. La politique serveur exacte ne peut pas être reconstruite depuis le seul client : elle peut dépendre du verdict, de la version de l'application, du compte ou d'autres signaux.
  1057.  
  1058. ## 17.3. StrongBox
  1059.  
  1060. Le code sait manipuler des notions de StrongBox, mais le téléphone n'annonce pas `android.hardware.strongbox_keystore` dans l'inventaire de features. Aucun besoin StrongBox n'est démontré comme condition du crash historique.
  1061.  
  1062. ---
  1063.  
  1064. # 18. Cartographie statique des SDK, DEX, permissions et API sensibles
  1065.  
  1066. ## 18.1. Familles de code détectées
  1067.  
  1068. | Famille | Types détectés |
  1069. | --------------- | -------------: |
  1070. | AppsFlyer | 564 |
  1071. | DexProtector | 16 |
  1072. | Facebook | 16 |
  1073. | Firebase | 215 |
  1074. | Google GMS | 9545 |
  1075. | gRPC | 21 |
  1076. | Incode | 1856 |
  1077. | ML Kit | 40 |
  1078. | OkHttp | 160 |
  1079. | Onfido | 4578 |
  1080. | Retrofit | 617 |
  1081. | Revolut | 134782 |
  1082. | Sentry | 526 |
  1083. | TensorFlow Lite | 134 |
  1084. | Twilio | 210 |
  1085.  
  1086. ## 18.2. Références d'API sensibles
  1087.  
  1088. | Catégorie | Nombre |
  1089. | ------------------------------ | -----: |
  1090. | Accessibilité / presse-papiers | 60 |
  1091. | Biométrie | 384 |
  1092. | Build props | 1 |
  1093. | Caméra / micro / capteurs | 183 |
  1094. | Code dynamique / processus | 22 |
  1095. | Keystore / attestation | 167 |
  1096. | Localisation | 628 |
  1097. | État réseau | 84 |
  1098. | NFC | 29 |
  1099. | Inventaire packages | 46 |
  1100. | Identifiants Settings | 9 |
  1101. | Téléphonie | 65 |
  1102.  
  1103. ## 18.3. Chaînes sensibles comptées
  1104.  
  1105. | Catégorie | Nombre |
  1106. | --------------------------------------- | -----: |
  1107. | Biométrie / NFC / caméra / localisation | 190 |
  1108. | Boot / build / intégrité | 475 |
  1109. | Identifiants appareil | 250 |
  1110. | Keystore / attestation / intégrité | 272 |
  1111. | Exécution native/dynamique | 1 |
  1112. | Réseau / transport | 871 |
  1113. | Inventaire applications | 106 |
  1114. | Play Integrity / SafetyNet | 18 |
  1115. | Root / frameworks | 161 |
  1116.  
  1117. ## 18.4. Appels DEX sensibles comptés
  1118.  
  1119. | Catégorie | Nombre |
  1120. | ------------------------------ | -----: |
  1121. | Accessibilité / presse-papiers | 136 |
  1122. | Biométrie | 209 |
  1123. | Build props | 3 |
  1124. | Caméra / micro / capteurs | 733 |
  1125. | Code dynamique / processus | 34 |
  1126. | Keystore / attestation | 363 |
  1127. | Localisation | 302 |
  1128. | Réseau | 160 |
  1129. | NFC | 42 |
  1130. | Inventaire packages | 212 |
  1131. | Identifiants Settings | 54 |
  1132. | Téléphonie | 88 |
  1133.  
  1134. Ces chiffres mesurent des références statiques. Ils incluent du code tiers et des chemins qui peuvent ne jamais être exécutés dans le scénario de crash étudié.
  1135.  
  1136. # 19. Bibliothèques natives et capacités observées
  1137.  
  1138. ## 19.1. Inventaire ARM64
  1139.  
  1140. 21 bibliothèques natives ont été inventoriées :
  1141.  
  1142. | Bibliothèque | Taille | SHA-256 |
  1143. | --------------------------------- | -------: | ------------------------------------------------------------ |
  1144. | `libNativeBridge.so` | 8250512 | `dbce54e598fcf7ca8f82d2bcde0c2b688e72733652315e9782f9e9ecdd29e95e` |
  1145. | `libalice.so` | 483576 | `a5bf3803653f6ca10f03dcde7ad134613525c95160a9b4c4b8344ec0ecb550bd` |
  1146. | `libandroidx.graphics.path.so` | 10096 | `41e9a793c43a0f4fddb19e33f346bace464f30f888ba7b9eaf96294ea115bfb6` |
  1147. | `libbitmap_effects.so` | 267256 | `d2adaf1b4a2906cd0516db8415df8bb9b1406919e436a07b5d882bab07b01798` |
  1148. | `libcamerakit_memfd_compat.so` | 23704 | `019ce81d6e30e831d3378490c0ecaf3b6f7192050e00aa2afa3e67ad81824f42` |
  1149. | `libdatastore_shared_counter.so` | 7112 | `d3e48717c9aa147e0ab21063ba0e8e0211cabf8bf40b222640829519edbf58e1` |
  1150. | `libdexprotector.so` | 443222 | `81bbed87aaa3fb03285d37bd04952dd18b0ba5f33e958e3f866791fa8034aeee` |
  1151. | `libfourthline-security.so` | 11784 | `abfb8be17ec0ec372acb93910d8452806a8d9baf57ae02f482d18bf27e3f389d` |
  1152. | `libimage_processing_util_jni.so` | 32528 | `0292b063fafccf734472cbb59588e756c2dbca907c72021ba6fbf126385508a2` |
  1153. | `librev_vision_android.so` | 17429056 | `9b31cd09a5af4b0c1c36cb94958149130c782e7965a61b54e7933b44aff95200` |
  1154. | `libsqlcipher.so` | 5186904 | `036c7311139c12f92244aa3c2660e818f586379b7fcc348f4a6b8f9f2eda5ffe` |
  1155. | `libsurface_util_jni.so` | 4896 | `a5c9d1928ea92ec7a94dff8cf666e7739cd734198b9f0ae1d4e28ea3c86516ba` |
  1156. | `libtensorflowlite_jni.so` | 4320528 | `85b97f038170c1c3137ba5e2f70d1435cc2cda5a7b07570e2192a38a0b5bdda8` |
  1157. | `libc++_shared.so` | 1253536 | `97176175c08bbd4af66836d290195797fff6c82e7b6261814fbe955f2f7ea02a` |
  1158. | `libmbway-lib.so` | 7399144 | `e83403453e44f4091290a4af9b225f50ca40e85b542e60c7dee21b9d2f70f9c7` |
  1159. | `libtwilio_voice_android_so.so` | 11446840 | `a7b3bca035ee9b0ea0fda7d18e3a454679cd0d17ceb68a82beab861d8e22d08a` |
  1160. | `libRecogKitAndroid.so` | 1203520 | `45728587b6201c6bced045455af7f02852add076408e332c4a46960fd6d1b8c9` |
  1161. | `liba94abe.so` | 103448 | `ad4cf6086838c2e483f7211552666c2376e5a15cecf4439229718401fc4d54d6` |
  1162. | `libd4a2.so` | 1138616 | `52cc132c340865309d22d585a6b80fc2406fb12184447378beef50627fe55d59` |
  1163. | `libsentry-android.so` | 16072 | `9cf26d69a0741bd2176b8a118aca3ecf9470849a6c437bf383b3034c44800657` |
  1164. | `libsentry.so` | 1219960 | `901b3c5eae3d59722f5d639b172c983b790bc038750070947b79ee2fe184248f` |
  1165.  
  1166. `libalice.so` importe notamment `getuid`, les API de propriétés système, `dlopen`, `dlsym`, `open`, `opendir`, `stat`, `kill`, `sigaction` et `syscall`. Elle contient aussi des chaînes relatives au fingerprint, au numéro de série et à l'attestation de clés. Ces primitives sont générales et ne suffisent pas, seules, à qualifier chaque usage.
  1167.  
  1168. `libsentry.so` contient des capacités de lecture de `/proc/self/maps`, d'unwinding, de `ptrace`, de fingerprint d'événement, de proxy HTTP/HTTPS et de stockage local Sentry. Leur présence ne prouve pas que toutes soient exécutées pendant le crash étudié.
  1169.  
  1170. ---
  1171.  
  1172. # 20. Assets, DEX embarqués et MessageGuardException
  1173.  
  1174. ## 20.1. Assets significatifs
  1175.  
  1176. | Asset | Taille | SHA-256 | Constat |
  1177. | ------------------------------- | -----: | ------------------------------------------------------------ | ----------------------------------------- |
  1178. | `assets/assets.dat` | 348054 | `c0b309d27b99923e04c84786b75d73007ca2e123e397ae085a4b8d3fd66e8463` | forte entropie, pas de signature évidente |
  1179. | `assets/dp_db.mp3` | 736397 | `e9c8c2bd0bdf705f8f3f23bc58e46e0435981cd157c06ec9f248de4431e6b1c4` | forte entropie, pas de MP3 utile |
  1180. | `assets/ict.dat` | 10171 | `84663187f4b4161f1c284da1c0a7ffa4fa751f71f878198b87670dbf2859b81e` | données de confiance/certificats |
  1181. | `res/raw/dexprotector_keep.xml` | 251 | `9a60cdbe78e0354b0eb822db145fd648fbf78b96326650a9f0d135c19b5eb81e` | ressources activité d'erreur |
  1182.  
  1183. Le rôle précis de `assets.dat` et `dp_db.mp3` dans le statut historique n'a pas été démontré.
  1184.  
  1185. ## 20.2. DEX embarqués dans l'image reconstruite
  1186.  
  1187. Deux blobs DEX parsables ont été trouvés : un canari de 300 octets à `0x6a80`, sans méthode, et un DEX de 780 octets à `0xa8000` contenant `com/revolut/MessageGuardException`.
  1188.  
  1189. SHA-256 du second :
  1190.  
  1191. ```text
  1192. 6de6032db6ff0183a3490a196b67c9c555a11c2fa4ba4bb266da7353e5b66768
  1193. ```
  1194.  
  1195. `MessageGuardException` hérite de `RuntimeException`, implémente `Runnable`, remplace sa stack trace par un tableau vide, relance l'exception elle-même dans `run()` et retourne seulement son message dans `toString()`.
  1196.  
  1197. Cette classe démontre une capacité à produire des erreurs volontairement difficiles à diagnostiquer. Elle ne doit pas être confondue avec l'exception réellement observée, `android.app.TerminateException$...`.
  1198.  
  1199. ---
  1200.  
  1201. # 21. Snapshots du processus Revolut et visibilité réelle des traces root
  1202.  
  1203. ## 21.1. Snapshot précoce
  1204.  
  1205. Un processus Revolut capturé avant terminaison présentait :
  1206.  
  1207. ```text
  1208. UID/GID : 10701
  1209. SELinux : u:r:untrusted_app:s0:c189,c258,c512,c768
  1210. exe : /system/bin/app_process64
  1211. TracerPid=0
  1212. Seccomp=2
  1213. Seccomp_filters=1
  1214. CapEff=0
  1215. ```
  1216.  
  1217. Aucune occurrence évidente n'a été trouvée dans `maps`, `mountinfo`, `status`, `cmdline`, `selinux_current` ou `exe` pour des marqueurs tels que `/data/adb`, KernelSU, Magisk, Zygisk, Xposed, Frida, `/system/bin/su` ou le nom du noyau Scarlet.
  1218.  
  1219. Cette absence dans les snapshots textuels n'exclut pas des tests de fichiers à la volée, des appels `uname`, l'analyse des propriétés, l'attestation ou Play Integrity.
  1220.  
  1221. ## 21.2. Série de 16 snapshots
  1222.  
  1223. Sur le PID `27354` :
  1224.  
  1225. - `cmdline=com.revolut.revolut` dès le premier sample ;
  1226. - même UID et contexte SELinux ;
  1227. - 11 à 22 threads ;
  1228. - mappings des splits APK visibles à partir du sample 1 ;
  1229. - aucun marqueur root évident dans les chaînes de `maps` ;
  1230. - les noms `libalice.so` et `libdexprotector.so` n'apparaissent pas dans les chaînes `maps`, malgré leur chargement prouvé par logcat.
  1231.  
  1232. Une bibliothèque chargée depuis un APK peut donc être exécutée sans apparaître sous son nom textuel évident dans `/proc/<pid>/maps`.
  1233.  
  1234. ## 21.3. Namespace et permissions de fichiers
  1235.  
  1236. Depuis une vue root sur `/proc/<pid>/root`, `/data/adb/ksu` et `/data/adb/modules` existent, mais les permissions observées comprennent :
  1237.  
  1238. ```text
  1239. /data 0771 system:system
  1240. /data/adb 0700 root:root
  1241. ```
  1242.  
  1243. Un UID applicatif comme `10701` ne peut normalement pas traverser `/data/adb` directement. Il serait donc incorrect de conclure qu'un simple `File.exists("/data/adb/ksu")` dans Revolut doit nécessairement réussir parce que root voit ce chemin.
  1244.  
  1245. # 22. Profils Android et espaces utilisateurs
  1246.  
  1247. Les espaces utilisateurs observés sont :
  1248.  
  1249. - User 0 : profil principal ;
  1250. - User 10 : Private space ;
  1251. - User 11 : `Revo` ;
  1252. - User 12 : cloneUser.
  1253.  
  1254. État Revolut :
  1255.  
  1256. - User 0 : installé, activité login résolue, crash reproductible ;
  1257. - User 11 : paquet marqué installé mais aucune activité résolue par `cmd package resolve-activity` ;
  1258. - User 10 / User 12 : pas d'installation exploitable observée.
  1259.  
  1260. Le test du profil secondaire n'établit donc ni succès ni échec des contrôles d'intégrité : l'installation User 11 n'était pas lançable normalement.
  1261.  
  1262. ---
  1263.  
  1264. # 23. Matrice de causalité et conclusions de confiance
  1265.  
  1266. ## 23.1. Faits de confiance élevée
  1267.  
  1268. - Revolut 10.145 est correctement fourni pour ARM64 ;
  1269. - le problème n'est pas une incompatibilité ABI simple ;
  1270. - `LoginActivity` démarre ;
  1271. - `libalice.so` puis `libdexprotector.so` sont chargées ;
  1272. - Revolut tente de lire `ro.debuggable` et `ro.lineage.build.version` ;
  1273. - `ProtectedRevolutApplication$I` est une véritable Activity interne ;
  1274. - le processus principal termine avec `TerminateException` ;
  1275. - Android classe la sortie en `APP CRASH(EXCEPTION)` ;
  1276. - l'écran historique sélectionne « environnement non sécurisé » ;
  1277. - le code historique est `4674 = 0x1242` ;
  1278. - `0x1242` contient `0x200`, `0x40`, `0x2` et `0x1000` ;
  1279. - `0x200` correspond à ADB actif ;
  1280. - `0x2` est un agrégat lié à la famille `0x40/0x80/0x100` ;
  1281. - le chemin KeyStore/RootOfTrust peut produire le signal `0x40` ;
  1282. - le certificat persistant du namespace Revolut présente précisément les valeurs RootOfTrust rejetées par ce chemin ;
  1283. - Fourthline contient des contrôles explicites root/hook/instrumentation ;
  1284. - Credolab contient un détecteur root fonctionnel et câblé ;
  1285. - le split Multibanco contient un second détecteur root ;
  1286. - Play Integrity/SafetyNet sont présents ;
  1287. - le manifeste autorise un inventaire large des packages.
  1288.  
  1289. ## 23.2. Explication la plus forte du statut historique
  1290.  
  1291. La combinaison la mieux étayée est :
  1292.  
  1293. ```text
  1294. ADB actif -> 0x200
  1295. RootOfTrust TEE non conforme -> 0x40
  1296. 0x40 actif -> agrégat 0x2
  1297. classe environnement -> 0x1000
  1298. => 0x1242
  1299. => catégorie UI « environnement non sécurisé »
  1300. ```
  1301.  
  1302. L'attestation observée contient :
  1303.  
  1304. ```text
  1305. deviceLocked=false
  1306. verifiedBootState=2:Unverified
  1307. ```
  1308.  
  1309. et le code DexProtector possède un contrôle explicite sur ces champs.
  1310.  
  1311. ## 23.3. Éléments pertinents mais non démontrés comme cause unique
  1312.  
  1313. Ne doivent pas être présentés comme déclencheurs uniques du crash :
  1314.  
  1315. - KernelSU ;
  1316. - `/system/bin/su` ;
  1317. - nom de noyau `Scarlet-v6.0` ;
  1318. - `/data/adb/ksu` ;
  1319. - `/data/adb/modules` ;
  1320. - présence de frameworks Zygisk/LSPosed ;
  1321. - noms particuliers des gestionnaires KernelSU installés ;
  1322. - fingerprint Android 14 sur système Android 15 ;
  1323. - absence de StrongBox ;
  1324. - Play Integrity ;
  1325. - le blocage BAL ;
  1326. - `ro.debuggable` seul ;
  1327. - `ro.lineage.build.version` seul ;
  1328. - Credolab seul ;
  1329. - Fourthline seul.
  1330.  
  1331. Ces éléments peuvent alimenter d'autres couches de décision sans être nécessaires pour expliquer le `0x1242` historique.
  1332.  
  1333. ## 23.4. Matrice résumée
  1334.  
  1335. | Élément | Présent / observé | Cause immédiate établie ? |
  1336. | --------------------------------- | ----------------- | ----------------------------------- |
  1337. | Android 15 / SDK 35 | oui | non |
  1338. | ARM64 | oui | compatible |
  1339. | SELinux Enforcing | oui | non |
  1340. | Build public `user/release-keys` | oui | non |
  1341. | Build réel `userdebug` | oui | non isolément |
  1342. | Verified Boot public `green` | oui | non isolément |
  1343. | Boot brut `orange` | oui | signal pertinent |
  1344. | Noyau custom | oui | non isolément |
  1345. | KernelSU | oui | non isolément |
  1346. | ADB actif | oui | oui pour le bit `0x200` |
  1347. | Attestation `deviceLocked=false` | oui | alimente le chemin `0x40` |
  1348. | Attestation `verifiedBootState=2` | oui | alimente le chemin `0x40` |
  1349. | Play Integrity présent | oui | pas comme cause immédiate démontrée |
  1350. | Fourthline présent | oui | pas chargé avant le crash immédiat |
  1351. | Credolab root detector | oui | pas nécessaire à `0x1242` |
  1352. | `DeviceInfo.getIsRooted()` | oui | pas nécessaire à `0x1242` |
  1353.  
  1354. ---
  1355.  
  1356. # 24. Critères de compatibilité à vérifier sur un autre appareil
  1357.  
  1358. Ce chapitre fournit des critères de diagnostic et de comparaison, pas une procédure de contournement.
  1359.  
  1360. ## 24.1. Exigences déclaratives
  1361.  
  1362. | Élément | Valeur |
  1363. | ----------------------------- | ----------- |
  1364. | Android minimum | API 28 |
  1365. | OpenGL ES | 3.0 requis |
  1366. | ABI de l'installation étudiée | ARM64 |
  1367. | Installateur observé | Google Play |
  1368.  
  1369. ## 24.2. Signaux à mesurer réellement
  1370.  
  1371. Pour comparer un autre appareil ou une autre ROM, les données les plus utiles sont :
  1372.  
  1373. - état de boot brut et propriétés Android exposées ;
  1374. - `deviceLocked` et `verifiedBootState` de l'attestation KeyMint/TEE ;
  1375. - cohérence entre version Android, fingerprint et build ;
  1376. - état ADB réel ;
  1377. - disponibilité et niveau de sécurité AndroidKeyStore ;
  1378. - code d'erreur affiché par Revolut ;
  1379. - classification de l'écran protégé ;
  1380. - logs de chargement des bibliothèques ;
  1381. - `ApplicationExitInfo`.
  1382.  
  1383. Le reverse engineering montre surtout qu'un simple `getprop ro.boot.verifiedbootstate=green` ne suffit pas à conclure à la compatibilité, car DexProtector dispose d'un chemin d'attestation matérielle indépendant des propriétés de surface.
  1384.  
  1385. # 25. Artefacts et scripts d'analyse
  1386.  
  1387. ## 25.1. Artefacts de preuve mentionnés dans le corpus
  1388.  
  1389. Les rapports s'appuient notamment sur les groupes d'artefacts suivants sous `build/revolut-readonly-analysis/` :
  1390.  
  1391. - structure APK et DEX : `apk_structure_summary.json`, `dex_keyword_summary.json`, `dex_xref_summary.json`, `apk_assets_inventory.json`, `asset_profile_summary.json` ;
  1392. - bibliothèques natives : `native_lib_summary.json`, `native_string_scan.json`, `revolut_native_library_inventory.json`, `revolut_native_string_capability_map.json` ;
  1393. - reconstruction DexProtector : `dexprotector_stream_decoded_scan.json`, `dexprotector_segments_parse.json`, `dexprotector_reconstructed_image.bin`, `dexprotector_reconstructed_image_summary.json`, `dexprotector_dynamic_parse.json`, `dexprotector_imports.json`, `dexprotector_string_xrefs.json` ;
  1394. - DEX internes : `messageguard_scan.json`, `dexprotector_embedded_dex_summary.json`, `dexprotector_embedded_dex_code.json`, `dexprotector_embedded_dex_code_summary.json` ;
  1395. - bitfield et chaînes de contrôle : `dexprotector_bit_context.json`, `dexprotector_status_bitfield_callers.json`, `dexprotector_bit_ops.json`, `dexprotector_check_chain.json`, `dexprotector_global_store_sites.json` ;
  1396. - chaînes décodées et imports : `dexprotector_decoded_security_strings.json`, `dexprotector_decoded_security_strings_unicorn.json`, `dexprotector_extra_decoded_strings.json`, `dexprotector_global_pointers.json`, `dexprotector_init_imports.json`, `dexprotector_import_callers.json`, `dexprotector_selected_import_targets.json`, `dexprotector_selected_got_symbols.json`, `dexprotector_selected_static_pointers.json` ;
  1397. - état appareil : `device_security_properties.json`, `device_boot_raw_state.json`, `device_feature_flags.json`, `termux_root_targeted_state.json`, `termux_root_targeted_state_summary.json` ;
  1398. - propriétés Android : `property_area_target_scan.json`, `boot_validator_property_area_model.json`, `proc_property_maps_summary.json`, `proc_bootloader_prop_addresses.json` ;
  1399. - KeyStore/attestation : `native_keystore_keyword_scan.json`, `dexprotector_keystore_string_sites.json`, `dexprotector_0x40_setter_sites.json`, `dexprotector_attestation_oid.json`, `termux_app_process_attestation_diag.json`, `termux_keymaster_metadata.json`, `framework_keymint_enums.json`, `revolut_existing_keystore_cert_summary.json`, `revolut_existing_cert_dp_flag_model.json`, `dexprotector_alias_hypotheses.json`, `dexprotector_builder_strings.json`, `dexprotector_alias_challenge_facts.json`, `revolut_signature_digest_check.json`, `existing_keystore_marker_scan.json` ;
  1400. - capacités globales : `revolut_app_capability_map.json`, `revolut_app_capability_map_summary.json`, `revolut_installed_package_state.json` ;
  1401. - captures contrôlées : `controlled_launch_broad_summary.json`, `controlled_launch_broad_logcat.txt`, snapshots `/proc`, copies ciblées des property areas, frameworks et bibliothèques nécessaires aux vérifications hors ligne ;
  1402. - inventaire natif approfondi : `dexprotector_deep_inventory.json`, `dexprotector_keystore_field_xrefs.json`, `dexprotector_entropy_string.json`.
  1403.  
  1404. Désassemblages ciblés cités :
  1405.  
  1406. - `revolut-readonly-dexprotector-adb-predicate-disasm.txt` ;
  1407. - `revolut-readonly-dexprotector-composite-property-rule-disasm.txt` ;
  1408. - `revolut-readonly-dexprotector-targeted-disasm.txt` ;
  1409. - `revolut-readonly-dexprotector-boot-adb-predicates-disasm.txt` ;
  1410. - `revolut-readonly-dexprotector-boot-helpers-disasm.txt` ;
  1411. - `revolut-readonly-dexprotector-version-globals-disasm.txt` ;
  1412. - `revolut-readonly-dexprotector-keystore-path-disasm.txt`.
  1413.  
  1414. ## 25.2. Helpers d'analyse sous `tool/android`
  1415.  
  1416. Le corpus cite notamment :
  1417.  
  1418. - `revolut-readonly-smali-xrefs.py`, `revolut-readonly-protected-app-methods.py`, `revolut-readonly-root-detector-methods.py` ;
  1419. - `revolut-readonly-dexprotector-elf.py`, `revolut-readonly-dexprotector-embedded-dex-code.py` ;
  1420. - `revolut-readonly-device-props.py`, `revolut-readonly-device-feature-flags.py`, `revolut-readonly-boot-raw-state.py` ;
  1421. - `revolut-readonly-native-cmdline-string-scan.py`, `revolut-readonly-native-keystore-keyword-scan.py` ;
  1422. - `revolut-readonly-dexprotector-keystore-string-sites.py`, `revolut-readonly-dexprotector-0x40-setter-sites.py` ;
  1423. - `revolut-readonly-property-area-scan.py`, `revolut-readonly-boot-validator-model.py`, `revolut-readonly-proc-property-maps.py`, `revolut-readonly-proc-bootloader-prop-addresses.py` ;
  1424. - `revolut-readonly-dexprotector-bit-semantic-disasm.py`, `revolut-readonly-dexprotector-boot-writer-disasm.py`, `revolut-readonly-dexprotector-extra-string-decode.py` ;
  1425. - `revolut-readonly-der-oid-decode.py` ;
  1426. - `revolut-readonly-termux-root-state.py`, `revolut-readonly-termux-root-summary.py`, `revolut-readonly-termux-attestation-app-process.py`, `revolut-readonly-termux-keymaster-metadata.py` ;
  1427. - `revolut-readonly-framework-keymint-enums.py`, `revolut-readonly-revolut-keystore-existing-cert.py`, `revolut-readonly-existing-cert-dp-flag-model.py` ;
  1428. - `revolut-readonly-dexprotector-alias-hypotheses.py`, `revolut-readonly-dexprotector-builder-strings.py`, `revolut-readonly-dexprotector-attestation-builders.py`, `revolut-readonly-dexprotector-attestation-facts-summary.py` ;
  1429. - `revolut-readonly-revolut-signature-digest.py` ;
  1430. - `revolut-readonly-dexprotector-import-targets.py`, `revolut-readonly-dexprotector-init-imports.py`, `revolut-readonly-dexprotector-got-symbols.py`, `revolut-readonly-dexprotector-static-pointers.py` ;
  1431. - `revolut-readonly-dexprotector-entropy-callbacks.py`, `revolut-readonly-dexprotector-entropy-string.py`, `revolut-readonly-dexprotector-random-core-disasm.py` ;
  1432. - `revolut-readonly-app-capability-map.py`, `revolut-readonly-installed-package-state.py`, `revolut-readonly-native-library-inventory.py`, `revolut-readonly-native-string-capability-map.py` ;
  1433. - `revolut-readonly-controlled-launch-broad-map.py`, `revolut-readonly-dexprotector-deep-inventory.py`, `revolut-readonly-dexprotector-keystore-field-xrefs.py` ;
  1434. - `revolut-readonly-write-capability-markdown.py`, `revolut-readonly-dexprotector-write-markdown.py`, `revolut-readonly-existing-keystore-marker-scan.py`, `revolut-readonly-dexprotector-keystore-disasm.py`.
  1435.  
  1436. `TERMUX_EMBEDDED_PLAN.md` décrit principalement l'infrastructure Reins/Termux. Pour cette étude, son apport pertinent est l'existence d'un environnement Unix local, d'un accès root contrôlé et d'outils d'analyse disponibles sur Android. Son architecture de développement propre n'est pas un résultat sur Revolut et n'est donc pas reproduite.
  1437.  
  1438. # 26. Limites méthodologiques et périmètre exclu
  1439.  
  1440. ## 26.1. Capacité statique ≠ exécution
  1441.  
  1442. Une classe, une permission, une chaîne, un import ou une API dans l'APK démontre une capacité ou une dépendance, pas son exécution à chaque lancement.
  1443.  
  1444. ## 26.2. Exécution ≠ transmission réseau
  1445.  
  1446. Même lorsqu'une donnée est obtenue localement, son envoi réseau doit être prouvé séparément. La simple présence d'OkHttp, Retrofit, gRPC, Firebase, AppsFlyer ou Sentry ne suffit pas à attribuer un transfert précis.
  1447.  
  1448. ## 26.3. Logs silencieux
  1449.  
  1450. L'absence de ligne Keystore/KeyMint dans logcat ne prouve pas l'absence d'une transaction. La revalidation de 19:49 confirme l'existence du service Keystore2 mais n'a pas attribué de transaction d'attestation précise à l'UID Revolut dans cette capture.
  1451.  
  1452. ## 26.4. Instrumentation intrusive non utilisée
  1453.  
  1454. La preuve instruction par instruction du passage live dans chaque branche de DexProtector exigerait une instrumentation du processus protégé ou du service Keystore. Cette instrumentation intrusive n'a pas été utilisée dans le corpus de référence.
  1455.  
  1456. ## 26.5. Évolution du contexte d'observation
  1457.  
  1458. Certaines observations tardives ont été faites dans un environnement système dont la vue exposée aux applications avait évolué par rapport au lancement historique `0x1242`. Elles ne doivent pas être utilisées pour réécrire rétroactivement les causes du crash historique. Inversement, un lancement réussi dans un contexte modifié ne supprime pas les capacités statiques de détection toujours présentes dans Revolut 10.145.
  1459.  
  1460. ## 26.7. Corpus lu
  1461.  
  1462. Les sept documents textuels/JSON indexés dans `docs/` ont été lus intégralement :
  1463.  
  1464. - `REVOLUT_ANDROID_INTEROPERABILITY_ANALYSIS.md` ;
  1465. - `REVOLUT_ANDROID_INTEROPERABILITY.md` ;
  1466. - `REVOLUT_ANDROID_READONLY_CAPABILITY_MAP.md` ;
  1467. - `REVOLUT_ANDROID_READONLY_DEXPROTECTOR_MAP.md` ;
  1468. - `REVOLUT_ANDROID_READONLY_INTEROP_ANALYSIS.md` ;
  1469. - `REVOLUT_READONLY_REVALIDATION_2026-09-04_1949.json` ;
  1470. - `TERMUX_EMBEDDED_PLAN.md`.
  1471.  
  1472. Le répertoire contient aussi `docs.zip`. Le workspace le traite comme un fichier binaire et refuse sa lecture directe ; il n'est pas indexé comme document textuel distinct par l'inspection du workspace. La présente consolidation couvre donc l'intégralité du corpus textuel/JSON accessible dans `docs/`.
  1473.  
  1474. ---
  1475.  
  1476. # 27. Résumé final
  1477.  
  1478. Le reverse engineering de Revolut Android 10.145 montre une application fortement protégée et dotée d'une surface d'inspection du terminal très étendue.
  1479.  
  1480. Sur le téléphone étudié, le lancement historique échoue volontairement pendant la phase DexProtector. Le statut capturé est :
  1481.  
  1482. ```text
  1483. 4674 = 0x1242
  1484. ```
  1485.  
  1486. Sa décomposition établie est :
  1487.  
  1488. ```text
  1489. 0x1000 marqueur de classe du statut environnement
  1490. 0x200 ADB actif
  1491. 0x40 signal d'environnement
  1492. 0x2 agrégat automatiquement ajouté à cause de la famille 0x40/0x80/0x100
  1493. ```
  1494.  
  1495. L'écran d'erreur classe ce statut comme :
  1496.  
  1497. ```text
  1498. environnement non sécurisé
  1499. ```
  1500.  
  1501. Le bit `0x200` est relié au contrôle ADB et correspond à l'état réel du téléphone lors de la capture.
  1502.  
  1503. Pour `0x40`, le chemin AndroidKeyStore/attestation est particulièrement probant. Le certificat persistant associé au namespace Revolut contient :
  1504.  
  1505. ```text
  1506. deviceLocked=false
  1507. verifiedBootState=2:Unverified
  1508. ```
  1509.  
  1510. et DexProtector possède un contrôle RootOfTrust sur ces champs. En parallèle, le modèle du chemin de propriétés publiques considère la vue `green/locked` comme cohérente. L'attestation matérielle fournit donc l'explication la plus solide du `0x40` historique.
  1511.  
  1512. Au-delà de cette phase immédiate, Revolut contient plusieurs couches supplémentaires capables de détecter root, KernelSU, Magisk, APatch, Zygisk, Xposed, LSPosed, Frida, état SELinux, montages, packages installés, propriétés de build et autres indicateurs système. Il embarque aussi Play Integrity, SafetyNet, plusieurs SDK de télémétrie et d'anti-fraude, ainsi que de nombreuses capacités relatives à la localisation, aux capteurs, à la téléphonie, aux identifiants, à la biométrie et à l'inventaire applicatif.
  1513.  
  1514. Pour l'utilisateur final, le constat de confidentialité est que Revolut 10.145 dispose d'une capacité technique importante pour profiler le terminal et son environnement, et peut conditionner l'accès à l'application à des caractéristiques matérielles et logicielles profondément internes à l'appareil. Le document distingue volontairement cette capacité de ce qui est effectivement exécuté, obtenu et transmis, afin de ne pas transformer des possibilités statiques en affirmations non démontrées.
Tags: revolut
Add Comment
Please, Sign In to add comment