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.
- package/dist/index.js +143 -0
- package/package.json +27 -0
- package/skills/Android Ecosystem/Baseline Profile Generator/SKILL.md +277 -0
- package/skills/Android Ecosystem/Glance/SKILL.md +315 -0
- package/skills/Android Platform/Configuration/SKILL.md +201 -0
- package/skills/Android Platform/Filesystem/SKILL.md +216 -0
- package/skills/Android Platform/Lifecycle/SKILL.md +233 -0
- package/skills/Android Platform/Manifest/SKILL.md +226 -0
- package/skills/Android Platform/Process Death Recovery/SKILL.md +214 -0
- package/skills/Android Platform/Resources/SKILL.md +234 -0
- package/skills/Android Platform/SavedStateHandle/SKILL.md +217 -0
- package/skills/Android Platform/State Restoration/SKILL.md +210 -0
- package/skills/Architecture/Bounded Context/SKILL.md +207 -0
- package/skills/Architecture/Clean Architecture/SKILL.md +229 -0
- package/skills/Architecture/Domain Modeling/SKILL.md +236 -0
- package/skills/Architecture/Entity Design/SKILL.md +243 -0
- package/skills/Architecture/Feature Isolation/SKILL.md +216 -0
- package/skills/Architecture/MVI/SKILL.md +224 -0
- package/skills/Architecture/MVVM/SKILL.md +198 -0
- package/skills/Architecture/Modularization/SKILL.md +194 -0
- package/skills/Architecture/Offline First/SKILL.md +249 -0
- package/skills/Architecture/Repository Pattern/SKILL.md +216 -0
- package/skills/Architecture/Side Effect Management/SKILL.md +278 -0
- package/skills/Architecture/State Management/SKILL.md +229 -0
- package/skills/Architecture/Unidirectional Data Flow/SKILL.md +196 -0
- package/skills/Architecture/Use Case Design/SKILL.md +244 -0
- package/skills/Architecture/Value Object/SKILL.md +226 -0
- package/skills/Build Infrastructure/Build Orchestration/SKILL.md +257 -0
- package/skills/Build Infrastructure/Dependency Compatibility Resolver/SKILL.md +259 -0
- package/skills/Build Infrastructure/Environment Validator/SKILL.md +311 -0
- package/skills/Build System/Build Cache/SKILL.md +233 -0
- package/skills/Build System/Build Flavor Strategy/SKILL.md +171 -0
- package/skills/Build System/Build Variant/SKILL.md +215 -0
- package/skills/Build System/Convention Plugin/SKILL.md +288 -0
- package/skills/Build System/Dependency Management/SKILL.md +261 -0
- package/skills/Build System/Gradle/SKILL.md +284 -0
- package/skills/Build System/Incremental Build/SKILL.md +199 -0
- package/skills/Build System/KAPT/SKILL.md +198 -0
- package/skills/Build System/KSP/SKILL.md +263 -0
- package/skills/Build System/Module Dependency Graph Validation/SKILL.md +223 -0
- package/skills/Build System/Specialized/C++/SKILL.md +308 -0
- package/skills/Build System/Specialized/JNI/SKILL.md +306 -0
- package/skills/Build System/Specialized/NDK/SKILL.md +264 -0
- package/skills/Build System/Version Catalog/SKILL.md +304 -0
- package/skills/Concurrency/Background Processing/SKILL.md +185 -0
- package/skills/Concurrency/Channel/SKILL.md +207 -0
- package/skills/Concurrency/Coroutine/SKILL.md +200 -0
- package/skills/Concurrency/Flow/SKILL.md +179 -0
- package/skills/Concurrency/Mutex Strategy/SKILL.md +185 -0
- package/skills/Concurrency/SharedFlow/SKILL.md +171 -0
- package/skills/Concurrency/StateFlow/SKILL.md +175 -0
- package/skills/Concurrency/Structured Concurrency/SKILL.md +197 -0
- package/skills/Concurrency/Synchronization Policy/SKILL.md +192 -0
- package/skills/Core Language/Annotation Processing/SKILL.md +224 -0
- package/skills/Core Language/DSL/SKILL.md +186 -0
- package/skills/Core Language/Extension Functions Design/SKILL.md +191 -0
- package/skills/Core Language/Immutability/SKILL.md +156 -0
- package/skills/Core Language/KMP/SKILL.md +182 -0
- package/skills/Core Language/Kotlin/SKILL.md +187 -0
- package/skills/Core Language/Reactive State Management/SKILL.md +228 -0
- package/skills/Core Language/Reactive Streams/SKILL.md +235 -0
- package/skills/Core Language/Serialization/SKILL.md +191 -0
- package/skills/Data Layer/Cache Strategy/SKILL.md +261 -0
- package/skills/Data Layer/Conflict Resolution/SKILL.md +248 -0
- package/skills/Data Layer/DAO/SKILL.md +225 -0
- package/skills/Data Layer/DTO Mapping/SKILL.md +269 -0
- package/skills/Data Layer/DataStore/SKILL.md +264 -0
- package/skills/Data Layer/Database Versioning Strategy/SKILL.md +215 -0
- package/skills/Data Layer/Encrypted Database/SKILL.md +212 -0
- package/skills/Data Layer/File Storage/SKILL.md +247 -0
- package/skills/Data Layer/Indexing/SKILL.md +184 -0
- package/skills/Data Layer/Key-Value Store Strategy/SKILL.md +185 -0
- package/skills/Data Layer/Merge Strategy/SKILL.md +240 -0
- package/skills/Data Layer/Migration/SKILL.md +243 -0
- package/skills/Data Layer/Paging/SKILL.md +264 -0
- package/skills/Data Layer/Proto DataStore/SKILL.md +250 -0
- package/skills/Data Layer/Room/SKILL.md +244 -0
- package/skills/Data Layer/SQLite/SKILL.md +255 -0
- package/skills/Data Layer/Sync Engine/SKILL.md +268 -0
- package/skills/Dependency Injection/Dagger/SKILL.md +283 -0
- package/skills/Dependency Injection/Hilt/SKILL.md +345 -0
- package/skills/Dependency Injection/Koin/SKILL.md +282 -0
- package/skills/Developer Experience/Detekt/SKILL.md +272 -0
- package/skills/Developer Experience/Lint Rule/SKILL.md +281 -0
- package/skills/Google Ecosystem/Analytics/SKILL.md +281 -0
- package/skills/Google Ecosystem/Crashlytics/SKILL.md +234 -0
- package/skills/Google Ecosystem/Firebase/SKILL.md +200 -0
- package/skills/Google Ecosystem/Firebase Messaging/SKILL.md +266 -0
- package/skills/Media/Audio/SKILL.md +257 -0
- package/skills/Media/Camera/SKILL.md +229 -0
- package/skills/Media/CameraX/SKILL.md +295 -0
- package/skills/Media/ExoPlayer/SKILL.md +258 -0
- package/skills/Media/Video/SKILL.md +228 -0
- package/skills/Meta Skills/Domain Error Model/SKILL.md +238 -0
- package/skills/Meta Skills/Error Handling/SKILL.md +255 -0
- package/skills/Meta Skills/Error Mapping/SKILL.md +232 -0
- package/skills/Meta Skills/Failure Strategy/SKILL.md +294 -0
- package/skills/Meta Skills/Migration Strategy/SKILL.md +305 -0
- package/skills/Meta Skills/User Friendly Errors/SKILL.md +334 -0
- package/skills/Navigation/Deep Navigation/SKILL.md +209 -0
- package/skills/Navigation/Navigation/SKILL.md +215 -0
- package/skills/Navigation/Nested Navigation/SKILL.md +214 -0
- package/skills/Networking/API Contract/SKILL.md +220 -0
- package/skills/Networking/Authentication/SKILL.md +210 -0
- package/skills/Networking/Certificate Pinning/SKILL.md +167 -0
- package/skills/Networking/Fallback Strategy/SKILL.md +182 -0
- package/skills/Networking/Ktor/SKILL.md +219 -0
- package/skills/Networking/Multipart Upload/SKILL.md +213 -0
- package/skills/Networking/OkHttp/SKILL.md +193 -0
- package/skills/Networking/REST/SKILL.md +178 -0
- package/skills/Networking/Rate Limiting/SKILL.md +170 -0
- package/skills/Networking/Retrofit/SKILL.md +241 -0
- package/skills/Networking/Retry-Backoff/SKILL.md +181 -0
- package/skills/Networking/Server-Sent Events (SSE)/SKILL.md +196 -0
- package/skills/Networking/WebSocket/SKILL.md +224 -0
- package/skills/Observability/Crash Reporting/SKILL.md +219 -0
- package/skills/Observability/Logging/SKILL.md +168 -0
- package/skills/Observability/Metrics/SKILL.md +227 -0
- package/skills/Observability/Structured Logging/SKILL.md +234 -0
- package/skills/Performance/ANR Prevention/SKILL.md +192 -0
- package/skills/Performance/Allocation Optimization/SKILL.md +179 -0
- package/skills/Performance/App Startup/SKILL.md +183 -0
- package/skills/Performance/Baseline Profile/SKILL.md +205 -0
- package/skills/Performance/Battery Optimization/SKILL.md +192 -0
- package/skills/Performance/Benchmark/SKILL.md +182 -0
- package/skills/Performance/Bitmap Optimization/SKILL.md +178 -0
- package/skills/Performance/Compose Optimization/SKILL.md +187 -0
- package/skills/Performance/Heap Management/SKILL.md +184 -0
- package/skills/Performance/Macrobenchmark/SKILL.md +214 -0
- package/skills/Performance/Memory Leak Prevention/SKILL.md +218 -0
- package/skills/Performance/Rendering Performance/SKILL.md +205 -0
- package/skills/Performance/Startup Optimization/SKILL.md +219 -0
- package/skills/Security/Biometric/SKILL.md +224 -0
- package/skills/Security/Certificate Transparency/SKILL.md +158 -0
- package/skills/Security/Cryptography/SKILL.md +244 -0
- package/skills/Security/Encrypted Storage/SKILL.md +273 -0
- package/skills/Security/Frida Detection/SKILL.md +230 -0
- package/skills/Security/Hook Detection/SKILL.md +197 -0
- package/skills/Security/Keystore/SKILL.md +272 -0
- package/skills/Security/Network Security Config/SKILL.md +186 -0
- package/skills/Security/Obfuscation/SKILL.md +226 -0
- package/skills/Security/Proguard/SKILL.md +202 -0
- package/skills/Security/R8/SKILL.md +234 -0
- package/skills/Security/Reverse Engineering Resistance/SKILL.md +267 -0
- package/skills/Security/Root Detection/SKILL.md +220 -0
- package/skills/Security/Secure Networking/SKILL.md +220 -0
- package/skills/System Integration/AlarmManager/SKILL.md +182 -0
- package/skills/System Integration/App Widget/SKILL.md +182 -0
- package/skills/System Integration/Deep Link/SKILL.md +187 -0
- package/skills/System Integration/Foreground Service/SKILL.md +212 -0
- package/skills/System Integration/Notification/SKILL.md +237 -0
- package/skills/System Integration/WorkManager/SKILL.md +256 -0
- package/skills/System Integration/clipboard/SKILL.md +155 -0
- package/skills/System Integration/share-intent/SKILL.md +182 -0
- package/skills/Testing/Compose Testing/SKILL.md +296 -0
- package/skills/Testing/Espresso/SKILL.md +292 -0
- package/skills/Testing/Fake Data/SKILL.md +245 -0
- package/skills/Testing/Integration Testing/SKILL.md +288 -0
- package/skills/Testing/Mocking/SKILL.md +229 -0
- package/skills/Testing/Snapshot Testing/SKILL.md +259 -0
- package/skills/Testing/UI Testing/SKILL.md +293 -0
- package/skills/Testing/Unit Testing/SKILL.md +309 -0
- package/skills/UI System/Bottom Sheet Patterns/SKILL.md +279 -0
- package/skills/UI System/Compose/SKILL.md +296 -0
- package/skills/UI System/Compose Animation/SKILL.md +281 -0
- package/skills/UI System/Compose Multiplatform/SKILL.md +261 -0
- package/skills/UI System/Compose Navigation/SKILL.md +255 -0
- package/skills/UI System/Compose Performance/SKILL.md +274 -0
- package/skills/UI System/Design System/SKILL.md +217 -0
- package/skills/UI System/Empty State Strategy/SKILL.md +208 -0
- package/skills/UI System/Keyboard Navigation/SKILL.md +214 -0
- package/skills/UI System/Loading Strategy/SKILL.md +254 -0
- package/skills/UI System/Material 3/SKILL.md +279 -0
- package/skills/UI System/RTL/SKILL.md +179 -0
- package/src/index.ts +182 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: proguard
|
|
3
|
+
description: >
|
|
4
|
+
ProGuard rules reference for Android apps.
|
|
5
|
+
Load this skill when writing or debugging -keep rules, understanding
|
|
6
|
+
ProGuard rule syntax, maintaining proguard-rules.pro, or resolving
|
|
7
|
+
runtime crashes caused by missing keep rules.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# ProGuard
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
ProGuard is the rule language used by both the legacy ProGuard tool and its successor R8. In modern Android projects, you write ProGuard-syntax rules in `proguard-rules.pro`, and R8 consumes them. Understanding the rule syntax is essential for preventing runtime crashes in release builds.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Core Principles
|
|
18
|
+
|
|
19
|
+
- ProGuard rules **whitelist** — everything is removed unless kept
|
|
20
|
+
- Rules are **additive** — you cannot un-keep something kept by another rule
|
|
21
|
+
- Prefer **specific rules** over broad ones — broad rules defeat shrinking
|
|
22
|
+
- Rules affect **shrinking, obfuscation, and optimization** independently
|
|
23
|
+
- Test on a real device with a release/minified build before shipping
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Rule Syntax Reference
|
|
28
|
+
|
|
29
|
+
```proguard
|
|
30
|
+
# Keep a class and all its members
|
|
31
|
+
-keep class com.example.MyClass { *; }
|
|
32
|
+
|
|
33
|
+
# Keep a class name only (members may still be removed/renamed)
|
|
34
|
+
-keep class com.example.MyClass
|
|
35
|
+
|
|
36
|
+
# Keep members but allow class renaming
|
|
37
|
+
-keepclassmembers class com.example.MyClass {
|
|
38
|
+
public void myMethod();
|
|
39
|
+
private String myField;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# Keep class if it has certain members (conditional keep)
|
|
43
|
+
-keepclasseswithmembers class * {
|
|
44
|
+
public <init>(android.content.Context);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
# Keep class name if it has certain members (class name kept, members may be renamed)
|
|
48
|
+
-keepclasseswithmembernames class * {
|
|
49
|
+
native <methods>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Keep attributes (metadata)
|
|
53
|
+
-keepattributes *Annotation*
|
|
54
|
+
-keepattributes Signature
|
|
55
|
+
-keepattributes SourceFile,LineNumberTable
|
|
56
|
+
-keepattributes Exceptions
|
|
57
|
+
|
|
58
|
+
# Suppress warnings (use sparingly)
|
|
59
|
+
-dontwarn com.example.third.party.**
|
|
60
|
+
|
|
61
|
+
# Suppress notes
|
|
62
|
+
-dontnote com.example.third.party.**
|
|
63
|
+
|
|
64
|
+
# Rename source file attribute (for stack trace clarity)
|
|
65
|
+
-renamesourcefileattribute SourceFile
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Wildcards
|
|
71
|
+
|
|
72
|
+
```proguard
|
|
73
|
+
# * — matches any single package component or partial name (not dots)
|
|
74
|
+
-keep class com.example.*.Model { *; } # matches com.example.user.Model
|
|
75
|
+
# NOT com.example.user.detail.Model
|
|
76
|
+
|
|
77
|
+
# ** — matches any number of package components (including dots)
|
|
78
|
+
-keep class com.example.**.Model { *; } # matches both
|
|
79
|
+
|
|
80
|
+
# *** — matches any type (primitive or object)
|
|
81
|
+
# % — matches any primitive type
|
|
82
|
+
# <fields> — all fields
|
|
83
|
+
# <methods> — all methods
|
|
84
|
+
# <init> — constructors
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Common Rules by Library
|
|
90
|
+
|
|
91
|
+
```proguard
|
|
92
|
+
# ── Retrofit ──────────────────────────────────────────────────────────────
|
|
93
|
+
-keepattributes Signature
|
|
94
|
+
-keepattributes Exceptions
|
|
95
|
+
-keep interface * {
|
|
96
|
+
@retrofit2.http.* <methods>;
|
|
97
|
+
}
|
|
98
|
+
-dontwarn retrofit2.**
|
|
99
|
+
|
|
100
|
+
# ── OkHttp ────────────────────────────────────────────────────────────────
|
|
101
|
+
-dontwarn okhttp3.**
|
|
102
|
+
-dontwarn okio.**
|
|
103
|
+
-keep class okhttp3.** { *; }
|
|
104
|
+
-keep interface okhttp3.** { *; }
|
|
105
|
+
|
|
106
|
+
# ── Gson ──────────────────────────────────────────────────────────────────
|
|
107
|
+
-keepclassmembers class * {
|
|
108
|
+
@com.google.gson.annotations.SerializedName <fields>;
|
|
109
|
+
}
|
|
110
|
+
-keep class com.example.app.data.remote.dto.** { *; }
|
|
111
|
+
|
|
112
|
+
# ── kotlinx.serialization ─────────────────────────────────────────────────
|
|
113
|
+
-keepattributes *Annotation*, InnerClasses
|
|
114
|
+
-dontnote kotlinx.serialization.AnnotationsKt
|
|
115
|
+
-keepclassmembers class kotlinx.serialization.json.** {
|
|
116
|
+
*** Companion;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
# ── Room ──────────────────────────────────────────────────────────────────
|
|
120
|
+
-keep class * extends androidx.room.RoomDatabase
|
|
121
|
+
-keep @androidx.room.Entity class * { *; }
|
|
122
|
+
-keep @androidx.room.Dao interface * { *; }
|
|
123
|
+
|
|
124
|
+
# ── Hilt / Dagger ─────────────────────────────────────────────────────────
|
|
125
|
+
-keep class dagger.** { *; }
|
|
126
|
+
-keep class javax.inject.** { *; }
|
|
127
|
+
-keep @dagger.hilt.android.HiltAndroidApp class *
|
|
128
|
+
-keep @dagger.hilt.android.AndroidEntryPoint class *
|
|
129
|
+
-keep @dagger.hilt.InstallIn class *
|
|
130
|
+
|
|
131
|
+
# ── Parcelable ────────────────────────────────────────────────────────────
|
|
132
|
+
-keep class * implements android.os.Parcelable {
|
|
133
|
+
public static final android.os.Parcelable$Creator *;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# ── Enum ──────────────────────────────────────────────────────────────────
|
|
137
|
+
-keepclassmembers enum * {
|
|
138
|
+
public static **[] values();
|
|
139
|
+
public static ** valueOf(java.lang.String);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# ── Firebase ──────────────────────────────────────────────────────────────
|
|
143
|
+
-keep class com.google.firebase.** { *; }
|
|
144
|
+
-keep class com.google.android.gms.** { *; }
|
|
145
|
+
-dontwarn com.google.firebase.**
|
|
146
|
+
|
|
147
|
+
# ── Crashlytics ───────────────────────────────────────────────────────────
|
|
148
|
+
-keepattributes SourceFile,LineNumberTable
|
|
149
|
+
-keep public class * extends java.lang.Exception
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Diagnosing Missing Keep Rules
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# Run release build and check logcat for:
|
|
158
|
+
# E/AndroidRuntime: java.lang.ClassNotFoundException: com.example.MyClass
|
|
159
|
+
# → Add: -keep class com.example.MyClass { *; }
|
|
160
|
+
|
|
161
|
+
# E/AndroidRuntime: java.lang.NoSuchMethodException: myMethod
|
|
162
|
+
# → Add: -keepclassmembers class com.example.MyClass { void myMethod(); }
|
|
163
|
+
|
|
164
|
+
# Check usage.txt to see what was removed:
|
|
165
|
+
# app/build/outputs/mapping/release/usage.txt
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Rule Debugging
|
|
171
|
+
|
|
172
|
+
```proguard
|
|
173
|
+
# ✅ Print which rules were applied (outputs to build logs)
|
|
174
|
+
-printconfiguration build/outputs/mapping/release/configuration.txt
|
|
175
|
+
|
|
176
|
+
# ✅ Print which classes were kept
|
|
177
|
+
-printseeds build/outputs/mapping/release/seeds.txt
|
|
178
|
+
|
|
179
|
+
# ✅ Print which code was removed
|
|
180
|
+
-printusage build/outputs/mapping/release/usage.txt
|
|
181
|
+
|
|
182
|
+
# ✅ Print the mapping
|
|
183
|
+
-printmapping build/outputs/mapping/release/mapping.txt
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Anti-Patterns
|
|
189
|
+
|
|
190
|
+
- `-keep class com.example.** { *; }` — keeps all code, no shrinking benefit
|
|
191
|
+
- Writing rules without testing the release build — issues only surface at runtime
|
|
192
|
+
- `-dontwarn **` — suppresses all warnings including real problems
|
|
193
|
+
- Forgetting `-keepattributes Signature` — breaks Retrofit generic type resolution
|
|
194
|
+
- Not keeping DTO fields with Gson — deserialized objects have null fields in release
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Related Skills
|
|
199
|
+
- `r8` — R8 configuration and full mode
|
|
200
|
+
- `obfuscation` — obfuscation strategy and when to apply it
|
|
201
|
+
- `build-variant` — applying different rules per build type
|
|
202
|
+
- `gradle` — build configuration
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: r8
|
|
3
|
+
description: >
|
|
4
|
+
R8 compiler for Android app shrinking, obfuscation, and optimization.
|
|
5
|
+
Load this skill when configuring R8 rules, understanding how R8 differs
|
|
6
|
+
from ProGuard, enabling full mode, troubleshooting R8-related build or
|
|
7
|
+
runtime issues, or optimizing APK/AAB size with R8.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# R8
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
R8 is Android's default code shrinker, obfuscator, and optimizer — replacing ProGuard since AGP 3.4. R8 runs as part of the build pipeline and performs three tasks in one pass: **shrinking** (removes unused code), **obfuscation** (renames identifiers), and **optimization** (inlines, removes dead branches, optimizes bytecode).
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Core Principles
|
|
18
|
+
|
|
19
|
+
- R8 is enabled via `isMinifyEnabled = true` — not a separate tool invocation
|
|
20
|
+
- R8 Full Mode (`-fullmode`) is more aggressive — requires more explicit keep rules
|
|
21
|
+
- R8 consumes the same `-keep` rules as ProGuard — existing rules work
|
|
22
|
+
- R8 produces a `mapping.txt` — required for deobfuscating crash stack traces
|
|
23
|
+
- Test release builds — R8 transformations can break reflection-heavy code
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Build Configuration
|
|
28
|
+
|
|
29
|
+
```kotlin
|
|
30
|
+
// ✅ Standard R8 setup
|
|
31
|
+
android {
|
|
32
|
+
buildTypes {
|
|
33
|
+
release {
|
|
34
|
+
isMinifyEnabled = true
|
|
35
|
+
isShrinkResources = true // removes unused resources (requires minify)
|
|
36
|
+
proguardFiles(
|
|
37
|
+
getDefaultProguardFile("proguard-android-optimize.txt"), // base rules
|
|
38
|
+
"proguard-rules.pro" // app-specific rules
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ✅ Enable R8 full mode for maximum shrinking
|
|
44
|
+
// Add to gradle.properties:
|
|
45
|
+
// android.enableR8.fullMode=true
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## R8 vs ProGuard
|
|
52
|
+
|
|
53
|
+
| Feature | ProGuard | R8 |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| Shrinking | ✅ | ✅ |
|
|
56
|
+
| Obfuscation | ✅ | ✅ |
|
|
57
|
+
| Optimization | Basic | Advanced (inlining, constant propagation) |
|
|
58
|
+
| Build speed | Slower | Faster (single pass) |
|
|
59
|
+
| Kotlin support | Limited | Full |
|
|
60
|
+
| Full mode | ❌ | ✅ (opt-in) |
|
|
61
|
+
| Default since | AGP < 3.4 | AGP 3.4+ |
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## R8 Full Mode
|
|
66
|
+
|
|
67
|
+
```properties
|
|
68
|
+
# gradle.properties — enables more aggressive shrinking
|
|
69
|
+
android.enableR8.fullMode=true
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```proguard
|
|
73
|
+
# Full mode removes default constructors — keep them explicitly if needed
|
|
74
|
+
-keepclassmembers class com.example.app.data.remote.dto.** {
|
|
75
|
+
<init>(); # keep no-arg constructor for deserialization
|
|
76
|
+
<fields>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
# Full mode is stricter about interface default methods
|
|
80
|
+
-keep interface com.example.app.domain.repository.** { *; }
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Essential Keep Rules
|
|
86
|
+
|
|
87
|
+
```proguard
|
|
88
|
+
# ============================================================
|
|
89
|
+
# Reflection — anything accessed via reflection must be kept
|
|
90
|
+
# ============================================================
|
|
91
|
+
-keepclassmembers class * {
|
|
92
|
+
@com.google.gson.annotations.SerializedName <fields>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
# ============================================================
|
|
96
|
+
# kotlinx.serialization
|
|
97
|
+
# ============================================================
|
|
98
|
+
-keepattributes *Annotation*, InnerClasses
|
|
99
|
+
-keepclasseswithmembers class * {
|
|
100
|
+
@kotlinx.serialization.Serializable *;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
# ============================================================
|
|
104
|
+
# Preserve line numbers for crash reports
|
|
105
|
+
# ============================================================
|
|
106
|
+
-keepattributes SourceFile,LineNumberTable
|
|
107
|
+
-renamesourcefileattribute SourceFile
|
|
108
|
+
|
|
109
|
+
# ============================================================
|
|
110
|
+
# Keep generic signatures (needed by Retrofit)
|
|
111
|
+
# ============================================================
|
|
112
|
+
-keepattributes Signature
|
|
113
|
+
|
|
114
|
+
# ============================================================
|
|
115
|
+
# Native methods
|
|
116
|
+
# ============================================================
|
|
117
|
+
-keepclasseswithmembernames class * {
|
|
118
|
+
native <methods>;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
# ============================================================
|
|
122
|
+
# Custom views (used in XML layouts)
|
|
123
|
+
# ============================================================
|
|
124
|
+
-keep public class * extends android.view.View {
|
|
125
|
+
public <init>(android.content.Context);
|
|
126
|
+
public <init>(android.content.Context, android.util.AttributeSet);
|
|
127
|
+
public <init>(android.content.Context, android.util.AttributeSet, int);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
# ============================================================
|
|
131
|
+
# Suppress warnings for known-safe libraries
|
|
132
|
+
# ============================================================
|
|
133
|
+
-dontwarn kotlin.reflect.jvm.internal.**
|
|
134
|
+
-dontwarn org.slf4j.**
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## R8 Optimization Examples
|
|
140
|
+
|
|
141
|
+
```kotlin
|
|
142
|
+
// R8 inlines small functions — no performance penalty for utility wrappers
|
|
143
|
+
// Before optimization:
|
|
144
|
+
fun isValidEmail(email: String) = email.contains("@")
|
|
145
|
+
|
|
146
|
+
// R8 may inline this at call sites:
|
|
147
|
+
if (isValidEmail(input)) { ... }
|
|
148
|
+
// → if (input.contains("@")) { ... }
|
|
149
|
+
|
|
150
|
+
// R8 removes unreachable code:
|
|
151
|
+
if (BuildConfig.DEBUG) {
|
|
152
|
+
Log.d(TAG, "Debug only") // removed entirely in release build
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Checking What R8 Removed
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# ✅ Check which classes/methods were removed
|
|
162
|
+
# build/outputs/mapping/release/usage.txt — lists removed code
|
|
163
|
+
|
|
164
|
+
# ✅ Check final APK contents
|
|
165
|
+
# Android Studio → Build → Analyze APK
|
|
166
|
+
|
|
167
|
+
# ✅ R8 produces these files in build/outputs/mapping/release/:
|
|
168
|
+
# mapping.txt — obfuscated → original name mapping
|
|
169
|
+
# seeds.txt — classes/members that matched -keep rules
|
|
170
|
+
# usage.txt — code removed by shrinking
|
|
171
|
+
# configuration.txt — merged ProGuard rules applied
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Library AAR Keep Rules
|
|
177
|
+
|
|
178
|
+
```proguard
|
|
179
|
+
# ✅ If publishing an AAR library, define consumer rules
|
|
180
|
+
# In library module: src/main/proguard-consumer-rules.pro
|
|
181
|
+
# These are automatically applied to apps that use the library
|
|
182
|
+
|
|
183
|
+
-keep public class com.example.mylibrary.PublicApi { *; }
|
|
184
|
+
-keep public interface com.example.mylibrary.Callback { *; }
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
```kotlin
|
|
188
|
+
// ✅ Reference consumer rules in library build.gradle.kts
|
|
189
|
+
android {
|
|
190
|
+
defaultConfig {
|
|
191
|
+
consumerProguardFiles("proguard-consumer-rules.pro")
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Troubleshooting Checklist
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
R8 build succeeds but app crashes at runtime:
|
|
202
|
+
1. Check logcat for ClassNotFoundException, NoSuchMethodException
|
|
203
|
+
2. Add -keep rule for the missing class/method
|
|
204
|
+
3. Rebuild and test again
|
|
205
|
+
|
|
206
|
+
R8 build fails:
|
|
207
|
+
1. Check build output for "R8: error" messages
|
|
208
|
+
2. Look for conflicting keep rules
|
|
209
|
+
3. Try disabling full mode temporarily
|
|
210
|
+
|
|
211
|
+
APK too large after R8:
|
|
212
|
+
1. Enable isShrinkResources = true
|
|
213
|
+
2. Check if -keep rules are too broad
|
|
214
|
+
3. Use Android Studio APK Analyzer to identify large classes
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## Anti-Patterns
|
|
220
|
+
|
|
221
|
+
- `isMinifyEnabled = false` in release — ships unoptimized, unobfuscated code
|
|
222
|
+
- Broad `-keep com.example.app.** { *; }` rules — keeps entire package, defeats shrinking
|
|
223
|
+
- Not saving `mapping.txt` from each release — crashes become undecodable
|
|
224
|
+
- Only testing debug builds — R8 transformations only apply in release
|
|
225
|
+
- Using ProGuard when R8 is available — R8 is faster and more capable
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Related Skills
|
|
230
|
+
- `obfuscation` — obfuscation rules and strategy
|
|
231
|
+
- `proguard` — ProGuard rules reference for older setups
|
|
232
|
+
- `build-variant` — managing release/debug build types
|
|
233
|
+
- `gradle` — build configuration
|
|
234
|
+
- `reverse-engineering-resistance` — combining R8 with other protections
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: reverse-engineering-resistance
|
|
3
|
+
description: >
|
|
4
|
+
Reverse engineering resistance for Android apps.
|
|
5
|
+
Load this skill when designing a defense-in-depth security strategy,
|
|
6
|
+
combining multiple anti-tampering measures, protecting sensitive algorithms,
|
|
7
|
+
implementing APK integrity checks, or hardening an app against static analysis.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Reverse Engineering Resistance
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
Reverse engineering resistance is not a single technique — it's a layered strategy. Attackers use static analysis (decompiling the APK), dynamic analysis (runtime hooks, Frida), and debugging. No single measure stops a determined attacker, but raising the cost and complexity of an attack is a meaningful defense.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Core Principles
|
|
18
|
+
|
|
19
|
+
- **Defense in depth** — layer multiple techniques; no single measure is sufficient
|
|
20
|
+
- **Detect and respond** — detect analysis attempts and fail-secure
|
|
21
|
+
- **Move secrets server-side** — anything in the APK can be extracted
|
|
22
|
+
- **Obfuscate detection logic** — easy-to-read detection code is easy to bypass
|
|
23
|
+
- **Server-side integrity** — Play Integrity API provides the strongest client attestation
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Defense Layers
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Layer 1: Build-time protection
|
|
31
|
+
→ R8 obfuscation + shrinking
|
|
32
|
+
→ String encryption
|
|
33
|
+
→ Resource obfuscation
|
|
34
|
+
|
|
35
|
+
Layer 2: Runtime integrity checks
|
|
36
|
+
→ APK signature verification
|
|
37
|
+
→ Debugger detection
|
|
38
|
+
→ Emulator detection
|
|
39
|
+
→ Root detection
|
|
40
|
+
→ Hook detection (Xposed, Frida)
|
|
41
|
+
|
|
42
|
+
Layer 3: Server-side attestation
|
|
43
|
+
→ Play Integrity API
|
|
44
|
+
→ Certificate pinning
|
|
45
|
+
→ Short-lived tokens
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## APK Signature Verification
|
|
51
|
+
|
|
52
|
+
```kotlin
|
|
53
|
+
// ✅ Verify the app is signed with the expected certificate
|
|
54
|
+
class ApkIntegrityChecker @Inject constructor(
|
|
55
|
+
@ApplicationContext private val context: Context
|
|
56
|
+
) {
|
|
57
|
+
// Get your signing cert hash:
|
|
58
|
+
// keytool -printcert -file CERT.RSA | grep SHA256
|
|
59
|
+
private val expectedCertHash = "AA:BB:CC:DD:..." // replace with actual hash
|
|
60
|
+
|
|
61
|
+
fun isSignatureValid(): Boolean {
|
|
62
|
+
return try {
|
|
63
|
+
val packageInfo = context.packageManager.getPackageInfo(
|
|
64
|
+
context.packageName,
|
|
65
|
+
PackageManager.GET_SIGNING_CERTIFICATES
|
|
66
|
+
)
|
|
67
|
+
val signingInfo = packageInfo.signingInfo
|
|
68
|
+
val signatures = signingInfo?.apkContentsSigners ?: return false
|
|
69
|
+
|
|
70
|
+
signatures.any { signature ->
|
|
71
|
+
val digest = MessageDigest.getInstance("SHA-256")
|
|
72
|
+
val certHash = digest.digest(signature.toByteArray())
|
|
73
|
+
val hashHex = certHash.joinToString(":") { "%02X".format(it) }
|
|
74
|
+
hashHex == expectedCertHash
|
|
75
|
+
}
|
|
76
|
+
} catch (e: Exception) {
|
|
77
|
+
false
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Debugger Detection
|
|
86
|
+
|
|
87
|
+
```kotlin
|
|
88
|
+
// ✅ Detect if a debugger is attached
|
|
89
|
+
object DebuggerDetector {
|
|
90
|
+
fun isDebuggerAttached(): Boolean =
|
|
91
|
+
android.os.Debug.isDebuggerConnected() ||
|
|
92
|
+
android.os.Debug.waitingForDebugger()
|
|
93
|
+
|
|
94
|
+
fun isDebuggableBuild(): Boolean =
|
|
95
|
+
BuildConfig.DEBUG ||
|
|
96
|
+
(android.os.Debug.isDebuggerConnected())
|
|
97
|
+
|
|
98
|
+
// ✅ Check android:debuggable in manifest at runtime
|
|
99
|
+
fun isManifestDebuggable(context: Context): Boolean {
|
|
100
|
+
val appInfo = context.applicationInfo
|
|
101
|
+
return (appInfo.flags and android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE) != 0
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Emulator Detection
|
|
109
|
+
|
|
110
|
+
```kotlin
|
|
111
|
+
// ✅ Detect common Android emulators
|
|
112
|
+
object EmulatorDetector {
|
|
113
|
+
fun isEmulator(): Boolean =
|
|
114
|
+
checkBuildProperties() || checkEmulatorFiles() || checkTelephony()
|
|
115
|
+
|
|
116
|
+
private fun checkBuildProperties(): Boolean {
|
|
117
|
+
val indicators = listOf(
|
|
118
|
+
Build.FINGERPRINT.startsWith("generic"),
|
|
119
|
+
Build.FINGERPRINT.startsWith("unknown"),
|
|
120
|
+
Build.MODEL.contains("google_sdk", ignoreCase = true),
|
|
121
|
+
Build.MODEL.contains("Emulator", ignoreCase = true),
|
|
122
|
+
Build.MODEL.contains("Android SDK built for x86", ignoreCase = true),
|
|
123
|
+
Build.MANUFACTURER.contains("Genymotion", ignoreCase = true),
|
|
124
|
+
Build.HARDWARE.contains("goldfish", ignoreCase = true),
|
|
125
|
+
Build.HARDWARE.contains("ranchu", ignoreCase = true),
|
|
126
|
+
Build.PRODUCT.contains("sdk_gphone", ignoreCase = true),
|
|
127
|
+
Build.PRODUCT.contains("vbox86p", ignoreCase = true)
|
|
128
|
+
)
|
|
129
|
+
return indicators.any { it }
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
private fun checkEmulatorFiles(): Boolean {
|
|
133
|
+
val emulatorFiles = listOf(
|
|
134
|
+
"/dev/socket/qemud",
|
|
135
|
+
"/dev/qemu_pipe",
|
|
136
|
+
"/system/lib/libc_malloc_debug_qemu.so",
|
|
137
|
+
"/sys/qemu_trace",
|
|
138
|
+
"/system/bin/qemu-props"
|
|
139
|
+
)
|
|
140
|
+
return emulatorFiles.any { File(it).exists() }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
private fun checkTelephony(): Boolean {
|
|
144
|
+
// IMEI "000000000000000" is used by many emulators
|
|
145
|
+
return Build.SERIAL == "unknown" || Build.SERIAL == "0"
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## String Obfuscation Pattern
|
|
153
|
+
|
|
154
|
+
```kotlin
|
|
155
|
+
// ✅ Don't store sensitive strings as plain constants
|
|
156
|
+
// ❌ Bad — visible in decompiled APK
|
|
157
|
+
private const val API_SECRET = "my_secret_key_12345"
|
|
158
|
+
|
|
159
|
+
// ✅ Better — obfuscate with simple XOR (raise the bar, not a crypto solution)
|
|
160
|
+
object StringObfuscator {
|
|
161
|
+
private val key = byteArrayOf(0x4A, 0x3F, 0x2E, 0x1D, 0x5C)
|
|
162
|
+
|
|
163
|
+
fun decode(encoded: ByteArray): String {
|
|
164
|
+
return String(ByteArray(encoded.size) { i ->
|
|
165
|
+
(encoded[i].toInt() xor key[i % key.size].toInt()).toByte()
|
|
166
|
+
})
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ✅ Best — keep secrets server-side entirely; fetch at runtime with auth
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
## Tamper Response Strategy
|
|
176
|
+
|
|
177
|
+
```kotlin
|
|
178
|
+
// ✅ Consolidated tamper check at app startup
|
|
179
|
+
class TamperDetectionService @Inject constructor(
|
|
180
|
+
private val apkIntegrityChecker: ApkIntegrityChecker,
|
|
181
|
+
private val rootDetector: RootDetector,
|
|
182
|
+
private val hookDetector: HookDetector,
|
|
183
|
+
private val fridaDetector: FridaDetector
|
|
184
|
+
) {
|
|
185
|
+
sealed interface TamperStatus {
|
|
186
|
+
data object Clean : TamperStatus
|
|
187
|
+
data class Compromised(val reasons: List<String>) : TamperStatus
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
suspend fun evaluate(): TamperStatus = withContext(Dispatchers.IO) {
|
|
191
|
+
val reasons = mutableListOf<String>()
|
|
192
|
+
|
|
193
|
+
if (!apkIntegrityChecker.isSignatureValid()) reasons.add("invalid_signature")
|
|
194
|
+
if (DebuggerDetector.isDebuggerAttached()) reasons.add("debugger_attached")
|
|
195
|
+
if (EmulatorDetector.isEmulator()) reasons.add("emulator")
|
|
196
|
+
if (rootDetector.check().isRooted) reasons.add("rooted")
|
|
197
|
+
if (hookDetector.check().isHooked) reasons.add("hooked")
|
|
198
|
+
if (fridaDetector.check().isDetected) reasons.add("frida")
|
|
199
|
+
|
|
200
|
+
if (reasons.isEmpty()) TamperStatus.Clean
|
|
201
|
+
else TamperStatus.Compromised(reasons)
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ✅ Response options per risk level
|
|
206
|
+
fun respondToTamper(status: TamperStatus, sensitivity: AppSensitivity) {
|
|
207
|
+
if (status is TamperStatus.Compromised) {
|
|
208
|
+
when (sensitivity) {
|
|
209
|
+
AppSensitivity.HIGH -> {
|
|
210
|
+
// Banking, health: terminate
|
|
211
|
+
showTamperDialog()
|
|
212
|
+
finishAndRemoveTask()
|
|
213
|
+
}
|
|
214
|
+
AppSensitivity.MEDIUM -> {
|
|
215
|
+
// Restrict sensitive features only
|
|
216
|
+
disableSensitiveFeatures()
|
|
217
|
+
logThreatToServer(status.reasons)
|
|
218
|
+
}
|
|
219
|
+
AppSensitivity.LOW -> {
|
|
220
|
+
// Log silently
|
|
221
|
+
logThreatToServer(status.reasons)
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
## Play Integrity API (Strongest Attestation)
|
|
231
|
+
|
|
232
|
+
```kotlin
|
|
233
|
+
// ✅ Use Play Integrity for server-verified device integrity
|
|
234
|
+
// The server receives and verifies a signed token from Google
|
|
235
|
+
// Token contains: appIntegrity, deviceIntegrity, accountDetails
|
|
236
|
+
|
|
237
|
+
suspend fun requestIntegrityToken(context: Context, nonce: String): Result<String> =
|
|
238
|
+
runCatching {
|
|
239
|
+
val manager = IntegrityManagerFactory.create(context)
|
|
240
|
+
val request = IntegrityTokenRequest.builder().setNonce(nonce).build()
|
|
241
|
+
manager.requestIntegrityToken(request).await().token()
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Server checks:
|
|
245
|
+
// deviceRecognitionVerdict: MEETS_STRONG_INTEGRITY → real unmodified device
|
|
246
|
+
// appIntegrityVerdict: PLAY_RECOGNIZED → APK matches Play store version
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
## Anti-Patterns
|
|
252
|
+
|
|
253
|
+
- Relying on obfuscation alone — determined attackers can re-analyze obfuscated code
|
|
254
|
+
- Storing API secrets in APK — use server-side secrets with authenticated access
|
|
255
|
+
- Android `debuggable=true` in release builds — allows trivial debugging and memory inspection
|
|
256
|
+
- Single-point detection — check at startup only; also check before each sensitive operation
|
|
257
|
+
- No server-side validation — all client-side checks are bypassable given enough effort
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Related Skills
|
|
262
|
+
- `root-detection` — rooted device detection
|
|
263
|
+
- `hook-detection` — Xposed/LSPosed detection
|
|
264
|
+
- `frida-detection` — Frida dynamic instrumentation detection
|
|
265
|
+
- `obfuscation` — R8 obfuscation configuration
|
|
266
|
+
- `certificate-pinning` — network-level protection
|
|
267
|
+
- `cryptography` — protecting sensitive data
|