android-sdd 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. package/dist/index.js +143 -0
  2. package/package.json +27 -0
  3. package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
  4. package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
  5. package/skills/Android Platform/Configuration/SKILL.md +201 -0
  6. package/skills/Android Platform/Filesystem/SKILL.md +216 -0
  7. package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
  8. package/skills/Android Platform/Manifest/SKILL.md +226 -0
  9. package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
  10. package/skills/Android Platform/Resources/SKILL.md +234 -0
  11. package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
  12. package/skills/Android Platform/State Restoration/SKILL.md +210 -0
  13. package/skills/Architecture/Bounded Context/SKILL.md +207 -0
  14. package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
  15. package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
  16. package/skills/Architecture/Entity Design/SKILL.md +243 -0
  17. package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
  18. package/skills/Architecture/MVI/SKILL.md +224 -0
  19. package/skills/Architecture/MVVM/SKILL.md +198 -0
  20. package/skills/Architecture/Modularization/SKILL.md +194 -0
  21. package/skills/Architecture/Offline First/SKILL.md +249 -0
  22. package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
  23. package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
  24. package/skills/Architecture/State Management/SKILL.md +229 -0
  25. package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
  26. package/skills/Architecture/Use Case Design/SKILL.md +244 -0
  27. package/skills/Architecture/Value Object/SKILL.md +226 -0
  28. package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
  29. package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
  30. package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
  31. package/skills/Build System/Build Cache/SKILL.md +233 -0
  32. package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
  33. package/skills/Build System/Build Variant/SKILL.md +215 -0
  34. package/skills/Build System/Convention Plugin/SKILL.md +288 -0
  35. package/skills/Build System/Dependency Management/SKILL.md +261 -0
  36. package/skills/Build System/Gradle/SKILL.md +284 -0
  37. package/skills/Build System/Incremental Build/SKILL.md +199 -0
  38. package/skills/Build System/KAPT/SKILL.md +198 -0
  39. package/skills/Build System/KSP/SKILL.md +263 -0
  40. package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
  41. package/skills/Build System/Specialized/C++/SKILL.md +308 -0
  42. package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
  43. package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
  44. package/skills/Build System/Version Catalog/SKILL.md +304 -0
  45. package/skills/Concurrency/Background Processing/SKILL.md +185 -0
  46. package/skills/Concurrency/Channel/SKILL.md +207 -0
  47. package/skills/Concurrency/Coroutine/SKILL.md +200 -0
  48. package/skills/Concurrency/Flow/SKILL.md +179 -0
  49. package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
  50. package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
  51. package/skills/Concurrency/StateFlow/SKILL.md +175 -0
  52. package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
  53. package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
  54. package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
  55. package/skills/Core Language/DSL/SKILL.md +186 -0
  56. package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
  57. package/skills/Core Language/Immutability/SKILL.md +156 -0
  58. package/skills/Core Language/KMP/SKILL.md +182 -0
  59. package/skills/Core Language/Kotlin/SKILL.md +187 -0
  60. package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
  61. package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
  62. package/skills/Core Language/Serialization/SKILL.md +191 -0
  63. package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
  64. package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
  65. package/skills/Data Layer/DAO/SKILL.md +225 -0
  66. package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
  67. package/skills/Data Layer/DataStore/SKILL.md +264 -0
  68. package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
  69. package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
  70. package/skills/Data Layer/File Storage/SKILL.md +247 -0
  71. package/skills/Data Layer/Indexing/SKILL.md +184 -0
  72. package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
  73. package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
  74. package/skills/Data Layer/Migration/SKILL.md +243 -0
  75. package/skills/Data Layer/Paging/SKILL.md +264 -0
  76. package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
  77. package/skills/Data Layer/Room/SKILL.md +244 -0
  78. package/skills/Data Layer/SQLite/SKILL.md +255 -0
  79. package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
  80. package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
  81. package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
  82. package/skills/Dependency Injection/Koin/SKILL.md +282 -0
  83. package/skills/Developer Experience/Detekt/SKILL.md +272 -0
  84. package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
  85. package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
  86. package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
  87. package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
  88. package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
  89. package/skills/Media/Audio/SKILL.md +257 -0
  90. package/skills/Media/Camera/SKILL.md +229 -0
  91. package/skills/Media/CameraX/SKILL.md +295 -0
  92. package/skills/Media/ExoPlayer/SKILL.md +258 -0
  93. package/skills/Media/Video/SKILL.md +228 -0
  94. package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
  95. package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
  96. package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
  97. package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
  98. package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
  99. package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
  100. package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
  101. package/skills/Navigation/Navigation/SKILL.md +215 -0
  102. package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
  103. package/skills/Networking/API Contract/SKILL.md +220 -0
  104. package/skills/Networking/Authentication/SKILL.md +210 -0
  105. package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
  106. package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
  107. package/skills/Networking/Ktor/SKILL.md +219 -0
  108. package/skills/Networking/Multipart Upload/SKILL.md +213 -0
  109. package/skills/Networking/OkHttp/SKILL.md +193 -0
  110. package/skills/Networking/REST/SKILL.md +178 -0
  111. package/skills/Networking/Rate Limiting/SKILL.md +170 -0
  112. package/skills/Networking/Retrofit/SKILL.md +241 -0
  113. package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
  114. package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
  115. package/skills/Networking/WebSocket/SKILL.md +224 -0
  116. package/skills/Observability/Crash Reporting/SKILL.md +219 -0
  117. package/skills/Observability/Logging/SKILL.md +168 -0
  118. package/skills/Observability/Metrics/SKILL.md +227 -0
  119. package/skills/Observability/Structured Logging/SKILL.md +234 -0
  120. package/skills/Performance/ANR Prevention/SKILL.md +192 -0
  121. package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
  122. package/skills/Performance/App Startup/SKILL.md +183 -0
  123. package/skills/Performance/Baseline Profile/SKILL.md +205 -0
  124. package/skills/Performance/Battery Optimization/SKILL.md +192 -0
  125. package/skills/Performance/Benchmark/SKILL.md +182 -0
  126. package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
  127. package/skills/Performance/Compose Optimization/SKILL.md +187 -0
  128. package/skills/Performance/Heap Management/SKILL.md +184 -0
  129. package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
  130. package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
  131. package/skills/Performance/Rendering Performance/SKILL.md +205 -0
  132. package/skills/Performance/Startup Optimization/SKILL.md +219 -0
  133. package/skills/Security/Biometric/SKILL.md +224 -0
  134. package/skills/Security/Certificate Transparency/SKILL.md +158 -0
  135. package/skills/Security/Cryptography/SKILL.md +244 -0
  136. package/skills/Security/Encrypted Storage/SKILL.md +273 -0
  137. package/skills/Security/Frida Detection/SKILL.md +230 -0
  138. package/skills/Security/Hook Detection/SKILL.md +197 -0
  139. package/skills/Security/Keystore/SKILL.md +272 -0
  140. package/skills/Security/Network Security Config/SKILL.md +186 -0
  141. package/skills/Security/Obfuscation/SKILL.md +226 -0
  142. package/skills/Security/Proguard/SKILL.md +202 -0
  143. package/skills/Security/R8/SKILL.md +234 -0
  144. package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
  145. package/skills/Security/Root Detection/SKILL.md +220 -0
  146. package/skills/Security/Secure Networking/SKILL.md +220 -0
  147. package/skills/System Integration/AlarmManager/SKILL.md +182 -0
  148. package/skills/System Integration/App Widget/SKILL.md +182 -0
  149. package/skills/System Integration/Deep Link/SKILL.md +187 -0
  150. package/skills/System Integration/Foreground Service/SKILL.md +212 -0
  151. package/skills/System Integration/Notification/SKILL.md +237 -0
  152. package/skills/System Integration/WorkManager/SKILL.md +256 -0
  153. package/skills/System Integration/clipboard/SKILL.md +155 -0
  154. package/skills/System Integration/share-intent/SKILL.md +182 -0
  155. package/skills/Testing/Compose Testing/SKILL.md +296 -0
  156. package/skills/Testing/Espresso/SKILL.md +292 -0
  157. package/skills/Testing/Fake Data/SKILL.md +245 -0
  158. package/skills/Testing/Integration Testing/SKILL.md +288 -0
  159. package/skills/Testing/Mocking/SKILL.md +229 -0
  160. package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
  161. package/skills/Testing/UI Testing/SKILL.md +293 -0
  162. package/skills/Testing/Unit Testing/SKILL.md +309 -0
  163. package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
  164. package/skills/UI System/Compose/SKILL.md +296 -0
  165. package/skills/UI System/Compose Animation/SKILL.md +281 -0
  166. package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
  167. package/skills/UI System/Compose Navigation/SKILL.md +255 -0
  168. package/skills/UI System/Compose Performance/SKILL.md +274 -0
  169. package/skills/UI System/Design System/SKILL.md +217 -0
  170. package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
  171. package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
  172. package/skills/UI System/Loading Strategy/SKILL.md +254 -0
  173. package/skills/UI System/Material 3/SKILL.md +279 -0
  174. package/skills/UI System/RTL/SKILL.md +179 -0
  175. package/src/index.ts +182 -0
  176. package/tsconfig.json +19 -0
@@ -0,0 +1,273 @@
1
+ ---
2
+ name: encrypted-storage
3
+ description: >
4
+ Encrypted local storage for Android apps.
5
+ Load this skill when storing sensitive data (tokens, user credentials,
6
+ personal info) locally, using EncryptedSharedPreferences, EncryptedFile,
7
+ or integrating encryption with DataStore or Room.
8
+ ---
9
+
10
+ # Encrypted Storage
11
+
12
+ ## Overview
13
+ Encrypted Storage protects sensitive data at rest on the device. Android provides `EncryptedSharedPreferences` and `EncryptedFile` via the Security library, both backed by the Android Keystore. For DataStore and Room, encryption is applied at the file or database level.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - **Never store sensitive data in plain SharedPreferences or files**
20
+ - Use **Android Keystore** as the key provider — keys never leave the secure hardware
21
+ - **EncryptedSharedPreferences** for key-value sensitive data (tokens, settings)
22
+ - **EncryptedFile** for sensitive file content
23
+ - **SQLCipher** for encrypted Room databases
24
+ - Encryption keys are **generated once and stored in Keystore** — never hardcoded
25
+
26
+ ---
27
+
28
+ ## EncryptedSharedPreferences
29
+
30
+ ```kotlin
31
+ // ✅ Create encrypted SharedPreferences
32
+ fun createEncryptedPreferences(context: Context): SharedPreferences {
33
+ val masterKey = MasterKey.Builder(context)
34
+ .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
35
+ .build()
36
+
37
+ return EncryptedSharedPreferences.create(
38
+ context,
39
+ "secure_prefs", // file name
40
+ masterKey,
41
+ EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
42
+ EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
43
+ )
44
+ }
45
+
46
+ // ✅ Wrapper for type-safe access
47
+ class SecurePreferences @Inject constructor(
48
+ @ApplicationContext context: Context
49
+ ) {
50
+ private val prefs = createEncryptedPreferences(context)
51
+
52
+ var authToken: String?
53
+ get() = prefs.getString(KEY_AUTH_TOKEN, null)
54
+ set(value) = prefs.edit {
55
+ if (value != null) putString(KEY_AUTH_TOKEN, value)
56
+ else remove(KEY_AUTH_TOKEN)
57
+ }
58
+
59
+ var refreshToken: String?
60
+ get() = prefs.getString(KEY_REFRESH_TOKEN, null)
61
+ set(value) = prefs.edit {
62
+ if (value != null) putString(KEY_REFRESH_TOKEN, value)
63
+ else remove(KEY_REFRESH_TOKEN)
64
+ }
65
+
66
+ fun clearAll() = prefs.edit { clear() }
67
+
68
+ companion object {
69
+ private const val KEY_AUTH_TOKEN = "auth_token"
70
+ private const val KEY_REFRESH_TOKEN = "refresh_token"
71
+ }
72
+ }
73
+ ```
74
+
75
+ ---
76
+
77
+ ## EncryptedFile
78
+
79
+ ```kotlin
80
+ // ✅ Write encrypted file
81
+ fun writeEncryptedFile(context: Context, fileName: String, content: ByteArray) {
82
+ val masterKey = MasterKey.Builder(context)
83
+ .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
84
+ .build()
85
+
86
+ val file = File(context.filesDir, fileName)
87
+ if (file.exists()) file.delete() // EncryptedFile cannot overwrite
88
+
89
+ val encryptedFile = EncryptedFile.Builder(
90
+ context,
91
+ file,
92
+ masterKey,
93
+ EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
94
+ ).build()
95
+
96
+ encryptedFile.openFileOutput().use { output ->
97
+ output.write(content)
98
+ }
99
+ }
100
+
101
+ // ✅ Read encrypted file
102
+ fun readEncryptedFile(context: Context, fileName: String): ByteArray {
103
+ val masterKey = MasterKey.Builder(context)
104
+ .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
105
+ .build()
106
+
107
+ val file = File(context.filesDir, fileName)
108
+ val encryptedFile = EncryptedFile.Builder(
109
+ context,
110
+ file,
111
+ masterKey,
112
+ EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
113
+ ).build()
114
+
115
+ return encryptedFile.openFileInput().use { it.readBytes() }
116
+ }
117
+ ```
118
+
119
+ ---
120
+
121
+ ## Encrypted DataStore
122
+
123
+ ```kotlin
124
+ // ✅ Encrypt DataStore using EncryptedFile as backing storage
125
+ // Add dependency: androidx.security:security-crypto-ktx
126
+
127
+ fun createEncryptedDataStore(context: Context): DataStore<Preferences> {
128
+ val masterKey = MasterKey.Builder(context)
129
+ .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
130
+ .build()
131
+
132
+ return PreferenceDataStoreFactory.createWithPath(
133
+ produceFile = {
134
+ val file = File(context.filesDir, "secure_datastore.preferences_pb")
135
+ // For true file-level encryption, wrap with EncryptedFile
136
+ file.toOkioPath()
137
+ }
138
+ )
139
+ }
140
+
141
+ // ✅ Simpler: use Tink-based DataStore encryption
142
+ // (preferred for production)
143
+ class EncryptedDataStoreManager @Inject constructor(
144
+ private val dataStore: DataStore<Preferences>
145
+ ) {
146
+ private val TOKEN_KEY = stringPreferencesKey("auth_token")
147
+ private val USER_ID_KEY = stringPreferencesKey("user_id")
148
+
149
+ val authToken: Flow<String?> = dataStore.data
150
+ .catch { if (it is IOException) emit(emptyPreferences()) else throw it }
151
+ .map { it[TOKEN_KEY] }
152
+
153
+ suspend fun saveAuthToken(token: String) {
154
+ dataStore.edit { it[TOKEN_KEY] = token }
155
+ }
156
+
157
+ suspend fun clearAuth() {
158
+ dataStore.edit {
159
+ it.remove(TOKEN_KEY)
160
+ it.remove(USER_ID_KEY)
161
+ }
162
+ }
163
+ }
164
+ ```
165
+
166
+ ---
167
+
168
+ ## Encrypted Room (SQLCipher)
169
+
170
+ ```kotlin
171
+ // ✅ SQLCipher for encrypted Room database
172
+ // dependency: net.zetetic:android-database-sqlcipher
173
+ // dependency: androidx.sqlite:sqlite-ktx
174
+
175
+ @Database(entities = [...], version = 1)
176
+ abstract class SecureDatabase : RoomDatabase() {
177
+ abstract fun sensitiveDataDao(): SensitiveDataDao
178
+ }
179
+
180
+ fun buildSecureDatabase(context: Context, passphrase: ByteArray): SecureDatabase {
181
+ val factory = SupportFactory(passphrase)
182
+ return Room.databaseBuilder(context, SecureDatabase::class.java, "secure.db")
183
+ .openHelperFactory(factory)
184
+ .build()
185
+ }
186
+
187
+ // ✅ Derive passphrase from Keystore-backed key
188
+ class DatabaseKeyProvider @Inject constructor(
189
+ @ApplicationContext private val context: Context
190
+ ) {
191
+ fun getPassphrase(): ByteArray {
192
+ val keyAlias = "db_key"
193
+ val keyStore = KeyStore.getInstance("AndroidKeyStore").also { it.load(null) }
194
+
195
+ if (!keyStore.containsAlias(keyAlias)) {
196
+ val keyGenerator = KeyGenerator.getInstance(
197
+ KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"
198
+ )
199
+ keyGenerator.init(
200
+ KeyGenParameterSpec.Builder(
201
+ keyAlias,
202
+ KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
203
+ )
204
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
205
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
206
+ .setKeySize(256)
207
+ .build()
208
+ )
209
+ keyGenerator.generateKey()
210
+ }
211
+
212
+ // Derive a stable passphrase from the key
213
+ val key = keyStore.getKey(keyAlias, null) as SecretKey
214
+ return key.encoded ?: ByteArray(32).also { SecureRandom().nextBytes(it) }
215
+ }
216
+ }
217
+ ```
218
+
219
+ ---
220
+
221
+ ## Hilt Setup
222
+
223
+ ```kotlin
224
+ @Module
225
+ @InstallIn(SingletonComponent::class)
226
+ object StorageModule {
227
+
228
+ @Provides
229
+ @Singleton
230
+ fun provideSecurePreferences(
231
+ @ApplicationContext context: Context
232
+ ): SecurePreferences = SecurePreferences(context)
233
+
234
+ @Provides
235
+ @Singleton
236
+ fun provideSecureDatabase(
237
+ @ApplicationContext context: Context,
238
+ keyProvider: DatabaseKeyProvider
239
+ ): SecureDatabase = buildSecureDatabase(context, keyProvider.getPassphrase())
240
+ }
241
+ ```
242
+
243
+ ---
244
+
245
+ ## What to Encrypt
246
+
247
+ | Data | Storage | Encrypted? |
248
+ |---|---|---|
249
+ | Auth token / refresh token | EncryptedSharedPreferences | ✅ Always |
250
+ | User PII (name, email, phone) | Encrypted Room / SQLCipher | ✅ Always |
251
+ | Payment data | Encrypted Room | ✅ Always |
252
+ | App preferences (theme, language) | Regular DataStore | ❌ Not needed |
253
+ | Cached images | File cache | ❌ Usually not needed |
254
+ | Health/medical data | Encrypted Room | ✅ Always |
255
+
256
+ ---
257
+
258
+ ## Anti-Patterns
259
+
260
+ - Storing tokens in plain `SharedPreferences` — readable by rooted devices and backup extraction
261
+ - Hardcoding encryption keys in source code — use Android Keystore
262
+ - Storing the encryption key alongside the encrypted data — defeats the purpose
263
+ - Using `MODE_WORLD_READABLE` or `MODE_WORLD_WRITEABLE` for any file
264
+ - Encrypting non-sensitive data — unnecessary overhead; encrypt only what needs protection
265
+
266
+ ---
267
+
268
+ ## Related Skills
269
+ - `keystore` — Android Keystore key generation and management
270
+ - `datastore` — DataStore patterns
271
+ - `room` — Room database setup
272
+ - `encrypted-database` — full SQLCipher Room setup
273
+ - `secure-networking` — protecting data in transit
@@ -0,0 +1,230 @@
1
+ ---
2
+ name: frida-detection
3
+ description: >
4
+ Frida dynamic instrumentation detection for Android apps.
5
+ Load this skill when detecting Frida gadget injection, Frida server
6
+ presence, or dynamic instrumentation attempts targeting the app at runtime.
7
+ ---
8
+
9
+ # Frida Detection
10
+
11
+ ## Overview
12
+ Frida is a dynamic instrumentation toolkit widely used by security researchers and attackers to hook functions, inspect memory, and bypass security checks at runtime. Unlike Xposed (which requires a framework installed on the device), Frida can be injected into a process dynamically. Detecting it requires checking for its artifacts at multiple levels.
13
+
14
+ ---
15
+
16
+ ## Core Principles
17
+
18
+ - **Frida leaves multiple traces** — process maps, port, libraries, named pipes
19
+ - **Check at multiple points** — startup and before sensitive operations
20
+ - **Frida can hide itself** — combine with server-side integrity (Play Integrity API)
21
+ - **Native checks are harder to bypass** — JNI-based detection is more resilient than Java
22
+ - **No detection is perfect** — the goal is to raise the cost of attack
23
+
24
+ ---
25
+
26
+ ## Java-Level Detection
27
+
28
+ ```kotlin
29
+ class FridaDetector @Inject constructor() {
30
+
31
+ data class FridaCheckResult(
32
+ val isDetected: Boolean,
33
+ val signals: List<String>
34
+ )
35
+
36
+ fun check(): FridaCheckResult {
37
+ val signals = mutableListOf<String>()
38
+
39
+ if (checkFridaServer()) signals.add("frida_server_port")
40
+ if (checkFridaFiles()) signals.add("frida_files")
41
+ if (checkProcessMaps()) signals.add("frida_in_maps")
42
+ if (checkFridaNamedPipes()) signals.add("frida_named_pipes")
43
+ if (checkFridaLibraries()) signals.add("frida_libraries")
44
+
45
+ return FridaCheckResult(
46
+ isDetected = signals.isNotEmpty(),
47
+ signals = signals
48
+ )
49
+ }
50
+
51
+ // ✅ Check if Frida server is listening on its default port (27042)
52
+ private fun checkFridaServer(): Boolean {
53
+ return try {
54
+ Socket().use { socket ->
55
+ socket.connect(InetSocketAddress("127.0.0.1", 27042), 100)
56
+ true // connection succeeded — Frida server likely running
57
+ }
58
+ } catch (e: Exception) {
59
+ false
60
+ }
61
+ }
62
+
63
+ // ✅ Check for Frida-related files
64
+ private fun checkFridaFiles(): Boolean {
65
+ val fridaPaths = listOf(
66
+ "/data/local/tmp/frida-server",
67
+ "/data/local/tmp/frida",
68
+ "/sdcard/frida-server",
69
+ "/system/bin/frida-server"
70
+ )
71
+ return fridaPaths.any { File(it).exists() }
72
+ }
73
+
74
+ // ✅ Check /proc/self/maps for Frida agent
75
+ private fun checkProcessMaps(): Boolean {
76
+ return try {
77
+ File("/proc/self/maps").readLines().any { line ->
78
+ line.contains("frida", ignoreCase = true) ||
79
+ line.contains("gum-js-loop", ignoreCase = true) ||
80
+ line.contains("gmain", ignoreCase = true) ||
81
+ line.contains("linjector", ignoreCase = true)
82
+ }
83
+ } catch (e: Exception) {
84
+ false
85
+ }
86
+ }
87
+
88
+ // ✅ Check for Frida named pipes in /proc/self/fd
89
+ private fun checkFridaNamedPipes(): Boolean {
90
+ return try {
91
+ val fdDir = File("/proc/self/fd")
92
+ fdDir.listFiles()?.any { fd ->
93
+ try {
94
+ val link = fd.canonicalPath
95
+ link.contains("frida", ignoreCase = true) ||
96
+ link.contains("linjector", ignoreCase = true)
97
+ } catch (e: Exception) {
98
+ false
99
+ }
100
+ } ?: false
101
+ } catch (e: Exception) {
102
+ false
103
+ }
104
+ }
105
+
106
+ // ✅ Check loaded libraries for Frida signature
107
+ private fun checkFridaLibraries(): Boolean {
108
+ return try {
109
+ File("/proc/self/maps").readLines().any { line ->
110
+ line.contains("frida-agent", ignoreCase = true) ||
111
+ line.contains("frida-gadget", ignoreCase = true)
112
+ }
113
+ } catch (e: Exception) {
114
+ false
115
+ }
116
+ }
117
+ }
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Native Detection (JNI — More Resilient)
123
+
124
+ ```c
125
+ // ✅ native-lib.cpp — harder to hook than Java methods
126
+ #include <jni.h>
127
+ #include <string>
128
+ #include <fstream>
129
+ #include <sys/socket.h>
130
+ #include <netinet/in.h>
131
+ #include <arpa/inet.h>
132
+ #include <unistd.h>
133
+
134
+ extern "C" JNIEXPORT jboolean JNICALL
135
+ Java_com_example_app_security_FridaDetectorNative_checkFridaPort(JNIEnv*, jobject) {
136
+ int sock = socket(AF_INET, SOCK_STREAM, 0);
137
+ if (sock < 0) return JNI_FALSE;
138
+
139
+ struct sockaddr_in addr{};
140
+ addr.sin_family = AF_INET;
141
+ addr.sin_port = htons(27042);
142
+ inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);
143
+
144
+ struct timeval tv{};
145
+ tv.tv_sec = 0;
146
+ tv.tv_usec = 100000; // 100ms timeout
147
+ setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
148
+ setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
149
+
150
+ bool detected = connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == 0;
151
+ close(sock);
152
+ return detected ? JNI_TRUE : JNI_FALSE;
153
+ }
154
+
155
+ extern "C" JNIEXPORT jboolean JNICALL
156
+ Java_com_example_app_security_FridaDetectorNative_checkProcessMaps(JNIEnv*, jobject) {
157
+ std::ifstream maps("/proc/self/maps");
158
+ std::string line;
159
+ while (std::getline(maps, line)) {
160
+ if (line.find("frida") != std::string::npos ||
161
+ line.find("gum-js-loop") != std::string::npos ||
162
+ line.find("frida-agent") != std::string::npos) {
163
+ return JNI_TRUE;
164
+ }
165
+ }
166
+ return JNI_FALSE;
167
+ }
168
+ ```
169
+
170
+ ```kotlin
171
+ // ✅ Kotlin wrapper for native checks
172
+ class FridaDetectorNative {
173
+ external fun checkFridaPort(): Boolean
174
+ external fun checkProcessMaps(): Boolean
175
+
176
+ companion object {
177
+ init { System.loadLibrary("native-lib") }
178
+ }
179
+ }
180
+ ```
181
+
182
+ ---
183
+
184
+ ## Combined Security Check
185
+
186
+ ```kotlin
187
+ // ✅ Unified check combining all detectors
188
+ class AppSecurityChecker @Inject constructor(
189
+ private val fridaDetector: FridaDetector,
190
+ private val hookDetector: HookDetector,
191
+ private val rootDetector: RootDetector
192
+ ) {
193
+ data class SecurityReport(
194
+ val passed: Boolean,
195
+ val threats: List<String>
196
+ )
197
+
198
+ suspend fun runChecks(): SecurityReport = withContext(Dispatchers.IO) {
199
+ val threats = mutableListOf<String>()
200
+
201
+ val frida = fridaDetector.check()
202
+ val hooks = hookDetector.check()
203
+ val root = rootDetector.check()
204
+
205
+ if (frida.isDetected) threats.addAll(frida.signals.map { "frida:$it" })
206
+ if (hooks.isHooked) threats.addAll(hooks.signals.map { "hook:$it" })
207
+ if (root.isRooted) threats.addAll(root.signals.map { "root:$it" })
208
+
209
+ SecurityReport(passed = threats.isEmpty(), threats = threats)
210
+ }
211
+ }
212
+ ```
213
+
214
+ ---
215
+
216
+ ## Anti-Patterns
217
+
218
+ - Only checking port 27042 — Frida server can be configured to use any port
219
+ - Java-only detection — Frida can hook Java methods; native checks are more reliable
220
+ - Checking only at startup — Frida can be attached after the app starts
221
+ - Crashing the app on detection without explanation — show a clear message
222
+ - No server-side corroboration — client detection alone is bypassable
223
+
224
+ ---
225
+
226
+ ## Related Skills
227
+ - `hook-detection` — Xposed/LSPosed framework detection
228
+ - `root-detection` — rooted device detection
229
+ - `reverse-engineering-resistance` — broader anti-tampering strategy
230
+ - `obfuscation` — obfuscating detection logic
@@ -0,0 +1,197 @@
1
+ ---
2
+ name: hook-detection
3
+ description: >
4
+ Runtime hook detection for Android apps.
5
+ Load this skill when detecting Xposed framework, LSPosed, or other
6
+ hooking frameworks that intercept method calls, alter app behavior,
7
+ or bypass security checks at runtime.
8
+ ---
9
+
10
+ # Hook Detection
11
+
12
+ ## Overview
13
+ Hooking frameworks like Xposed and LSPosed allow attackers to intercept and modify method calls at runtime — bypassing authentication, logging sensitive data, or altering app behavior. Hook detection identifies the presence of these frameworks before they can tamper with critical operations.
14
+
15
+ ---
16
+
17
+ ## Core Principles
18
+
19
+ - **Detect early** — check before any sensitive operation, not just at startup
20
+ - **Multiple signals** — Xposed/LSPosed can hide some traces but rarely all
21
+ - **Combine with root detection** — hooking frameworks typically require root
22
+ - **Server-side validation** — client-side detection can be bypassed; pair with Play Integrity
23
+ - **Fail-secure** — block sensitive operations if hooks are detected
24
+
25
+ ---
26
+
27
+ ## Xposed / LSPosed Detection
28
+
29
+ ```kotlin
30
+ class HookDetector @Inject constructor(
31
+ @ApplicationContext private val context: Context
32
+ ) {
33
+ data class HookCheckResult(
34
+ val isHooked: Boolean,
35
+ val signals: List<String>
36
+ )
37
+
38
+ fun check(): HookCheckResult {
39
+ val signals = mutableListOf<String>()
40
+
41
+ if (checkXposedInstaller()) signals.add("xposed_installer")
42
+ if (checkXposedBridge()) signals.add("xposed_bridge")
43
+ if (checkStackTrace()) signals.add("xposed_stack_trace")
44
+ if (checkXposedFiles()) signals.add("xposed_files")
45
+ if (checkLoadedModules()) signals.add("loaded_modules")
46
+
47
+ return HookCheckResult(
48
+ isHooked = signals.isNotEmpty(),
49
+ signals = signals
50
+ )
51
+ }
52
+
53
+ // ✅ Check for Xposed installer apps
54
+ private fun checkXposedInstaller(): Boolean {
55
+ val xposedPackages = listOf(
56
+ "de.robv.android.xposed.installer",
57
+ "org.meowcat.edxposed.manager",
58
+ "io.github.lsposed.manager",
59
+ "com.android.xposed"
60
+ )
61
+ return xposedPackages.any { pkg ->
62
+ try {
63
+ context.packageManager.getPackageInfo(pkg, 0)
64
+ true
65
+ } catch (e: PackageManager.NameNotFoundException) {
66
+ false
67
+ }
68
+ }
69
+ }
70
+
71
+ // ✅ Check if XposedBridge class is loadable
72
+ private fun checkXposedBridge(): Boolean {
73
+ return try {
74
+ Class.forName("de.robv.android.xposed.XposedBridge")
75
+ true
76
+ } catch (e: ClassNotFoundException) {
77
+ false
78
+ }
79
+ }
80
+
81
+ // ✅ Check stack trace for Xposed method wrappers
82
+ private fun checkStackTrace(): Boolean {
83
+ return try {
84
+ throw Exception("stack_check")
85
+ } catch (e: Exception) {
86
+ val stackTrace = e.stackTrace.joinToString { it.className }
87
+ stackTrace.contains("XposedBridge") ||
88
+ stackTrace.contains("de.robv.android.xposed")
89
+ }
90
+ }
91
+
92
+ // ✅ Check for Xposed-related files on disk
93
+ private fun checkXposedFiles(): Boolean {
94
+ val xposedFiles = listOf(
95
+ "/system/lib/libxposed_art.so",
96
+ "/system/lib64/libxposed_art.so",
97
+ "/system/xposed.prop",
98
+ "/data/data/de.robv.android.xposed.installer"
99
+ )
100
+ return xposedFiles.any { File(it).exists() }
101
+ }
102
+
103
+ // ✅ Check loaded libraries for hooking framework signatures
104
+ private fun checkLoadedModules(): Boolean {
105
+ return try {
106
+ val mapsFile = File("/proc/self/maps")
107
+ mapsFile.readLines().any { line ->
108
+ line.contains("XposedBridge") ||
109
+ line.contains("libxposed") ||
110
+ line.contains("edxp") ||
111
+ line.contains("lspd")
112
+ }
113
+ } catch (e: Exception) {
114
+ false
115
+ }
116
+ }
117
+ }
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Runtime Integrity Check
123
+
124
+ ```kotlin
125
+ // ✅ Verify critical method hasn't been hooked by checking its declaring class
126
+ fun isMethodHooked(methodName: String, clazz: Class<*>): Boolean {
127
+ return try {
128
+ val method = clazz.getDeclaredMethod(methodName)
129
+ // If method is hooked, its class will be XposedBridge's handler class
130
+ method.declaringClass != clazz
131
+ } catch (e: Exception) {
132
+ false
133
+ }
134
+ }
135
+
136
+ // ✅ Self-check on a known method
137
+ fun checkOwnMethodIntegrity(): Boolean {
138
+ return try {
139
+ val method = MySecurityClass::class.java.getDeclaredMethod("checkOwnMethodIntegrity")
140
+ method.declaringClass == MySecurityClass::class.java
141
+ } catch (e: Exception) {
142
+ false
143
+ }
144
+ }
145
+ ```
146
+
147
+ ---
148
+
149
+ ## Response Strategy
150
+
151
+ ```kotlin
152
+ @HiltViewModel
153
+ class SecurityViewModel @Inject constructor(
154
+ private val hookDetector: HookDetector,
155
+ private val rootDetector: RootDetector
156
+ ) : ViewModel() {
157
+
158
+ val securityStatus: StateFlow<SecurityStatus> = flow {
159
+ val hookResult = hookDetector.check()
160
+ val rootResult = rootDetector.check()
161
+
162
+ emit(SecurityStatus(
163
+ isHooked = hookResult.isHooked,
164
+ isRooted = rootResult.isRooted,
165
+ hookSignals = hookResult.signals,
166
+ rootSignals = rootResult.signals
167
+ ))
168
+ }.stateIn(viewModelScope, SharingStarted.Eagerly, SecurityStatus())
169
+ }
170
+
171
+ data class SecurityStatus(
172
+ val isHooked: Boolean = false,
173
+ val isRooted: Boolean = false,
174
+ val hookSignals: List<String> = emptyList(),
175
+ val rootSignals: List<String> = emptyList()
176
+ ) {
177
+ val isCompromised: Boolean get() = isHooked || isRooted
178
+ }
179
+ ```
180
+
181
+ ---
182
+
183
+ ## Anti-Patterns
184
+
185
+ - Only checking for Xposed installer — LSPosed and EdXposed use different package names
186
+ - Not checking at runtime before sensitive ops — startup-only check can be bypassed
187
+ - Crashing without explanation — show a clear message before blocking
188
+ - Trusting client-side detection alone — combine with Play Integrity API for server validation
189
+ - Storing detection result in a predictable variable — hooked code can modify it
190
+
191
+ ---
192
+
193
+ ## Related Skills
194
+ - `frida-detection` — Frida-specific detection
195
+ - `root-detection` — detecting rooted devices
196
+ - `reverse-engineering-resistance` — broader anti-tampering measures
197
+ - `obfuscation` — making detection logic harder to analyze