android-persistence 1.0.0__py3-none-any.whl
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.
- android_persistence/__init__.py +29 -0
- android_persistence/data_parser.py +325 -0
- android_persistence/defensive_mitigations.py +378 -0
- android_persistence/persistence_detector.py +441 -0
- android_persistence/report_generator.py +326 -0
- android_persistence/utils/__init__.py +3 -0
- android_persistence/utils/hex_analyzer.py +162 -0
- android_persistence/utils/logger.py +74 -0
- android_persistence/utils/manifest_parser.py +164 -0
- android_persistence/utils/signature_matcher.py +217 -0
- android_persistence-1.0.0.dist-info/METADATA +484 -0
- android_persistence-1.0.0.dist-info/RECORD +16 -0
- android_persistence-1.0.0.dist-info/WHEEL +5 -0
- android_persistence-1.0.0.dist-info/entry_points.txt +2 -0
- android_persistence-1.0.0.dist-info/licenses/LICENSE +21 -0
- android_persistence-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Defensive Mitigations - Mitigation strategies and defensive recommendations.
|
|
3
|
+
|
|
4
|
+
Provides comprehensive mitigation strategies for Android persistence mechanisms
|
|
5
|
+
and security vulnerabilities. Includes defensive techniques for system hardening.
|
|
6
|
+
|
|
7
|
+
DISCLAIMER: These mitigations are intended for defensive purposes on authorized
|
|
8
|
+
systems only. Improper implementation may affect system functionality.
|
|
9
|
+
|
|
10
|
+
Author: Security Research Team
|
|
11
|
+
License: Apache-2.0
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from typing import Dict, List, Optional, Set
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from enum import Enum
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MitigationType(Enum):
|
|
20
|
+
"""Types of mitigation strategies."""
|
|
21
|
+
DETECTION = "detection"
|
|
22
|
+
PREVENTION = "prevention"
|
|
23
|
+
HARDENING = "hardening"
|
|
24
|
+
MONITORING = "monitoring"
|
|
25
|
+
RESPONSE = "response"
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class Mitigation:
|
|
30
|
+
"""Single mitigation strategy."""
|
|
31
|
+
name: str
|
|
32
|
+
description: str
|
|
33
|
+
mitigation_type: MitigationType
|
|
34
|
+
implementation: str
|
|
35
|
+
effectiveness: int # 0-100
|
|
36
|
+
difficulty: int # 0-100 (implementation difficulty)
|
|
37
|
+
references: List[str] = None
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class MitigationStrategies:
|
|
41
|
+
"""
|
|
42
|
+
Central repository of defensive mitigation strategies.
|
|
43
|
+
|
|
44
|
+
Provides tailored recommendations based on detected persistence mechanisms
|
|
45
|
+
and threat profiles.
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
>>> mitigations = MitigationStrategies()
|
|
49
|
+
>>> strategies = mitigations.get_mitigations_for_threat('broadcast_receiver')
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def __init__(self):
|
|
53
|
+
"""Initialize mitigation strategies database."""
|
|
54
|
+
self.mitigations = self._load_mitigations()
|
|
55
|
+
|
|
56
|
+
def _load_mitigations(self) -> Dict[str, List[Mitigation]]:
|
|
57
|
+
"""
|
|
58
|
+
Load comprehensive mitigation strategies database.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
Dictionary mapping threat types to mitigation lists
|
|
62
|
+
"""
|
|
63
|
+
return {
|
|
64
|
+
'broadcast_receiver': [
|
|
65
|
+
Mitigation(
|
|
66
|
+
name="Explicit Intent Usage",
|
|
67
|
+
description="Replace implicit broadcasts with explicit intents",
|
|
68
|
+
mitigation_type=MitigationType.PREVENTION,
|
|
69
|
+
implementation="""
|
|
70
|
+
// Instead of implicit broadcast:
|
|
71
|
+
// Intent i = new Intent("com.example.ACTION");
|
|
72
|
+
|
|
73
|
+
// Use explicit broadcast:
|
|
74
|
+
Intent intent = new Intent(context, MyReceiver.class);
|
|
75
|
+
intent.setAction("com.example.ACTION");
|
|
76
|
+
context.sendBroadcast(intent);
|
|
77
|
+
""",
|
|
78
|
+
effectiveness=95,
|
|
79
|
+
difficulty=30,
|
|
80
|
+
),
|
|
81
|
+
Mitigation(
|
|
82
|
+
name="Permission-Based Access Control",
|
|
83
|
+
description="Restrict receiver access with signature-level permissions",
|
|
84
|
+
mitigation_type=MitigationType.PREVENTION,
|
|
85
|
+
implementation="""
|
|
86
|
+
<receiver android:name=".MyReceiver"
|
|
87
|
+
android:permission="com.example.permission.RECEIVE_EVENT">
|
|
88
|
+
<intent-filter>
|
|
89
|
+
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
|
90
|
+
</intent-filter>
|
|
91
|
+
</receiver>
|
|
92
|
+
|
|
93
|
+
<!-- In another app that needs to send: -->
|
|
94
|
+
<uses-permission android:name="com.example.permission.RECEIVE_EVENT"/>
|
|
95
|
+
""",
|
|
96
|
+
effectiveness=80,
|
|
97
|
+
difficulty=25,
|
|
98
|
+
),
|
|
99
|
+
Mitigation(
|
|
100
|
+
name="Context-Aware Receiver Registration",
|
|
101
|
+
description="Register receivers dynamically only when needed",
|
|
102
|
+
mitigation_type=MitigationType.HARDENING,
|
|
103
|
+
implementation="""
|
|
104
|
+
// Register in onCreate:
|
|
105
|
+
IntentFilter filter = new IntentFilter();
|
|
106
|
+
filter.addAction(Intent.ACTION_BOOT_COMPLETED);
|
|
107
|
+
registerReceiver(myReceiver, filter);
|
|
108
|
+
|
|
109
|
+
// Unregister in onDestroy:
|
|
110
|
+
unregisterReceiver(myReceiver);
|
|
111
|
+
""",
|
|
112
|
+
effectiveness=85,
|
|
113
|
+
difficulty=40,
|
|
114
|
+
),
|
|
115
|
+
],
|
|
116
|
+
'service': [
|
|
117
|
+
Mitigation(
|
|
118
|
+
name="Implement StartId Management",
|
|
119
|
+
description="Properly handle service stopSelf() with correct startId",
|
|
120
|
+
mitigation_type=MitigationType.HARDENING,
|
|
121
|
+
implementation="""
|
|
122
|
+
@Override
|
|
123
|
+
public int onStartCommand(Intent intent, int flags, int startId) {
|
|
124
|
+
// Do work
|
|
125
|
+
|
|
126
|
+
// Only stop if this is the latest request
|
|
127
|
+
if (shouldStop) {
|
|
128
|
+
stopSelf(startId);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return START_NOT_STICKY; // Don't restart
|
|
132
|
+
}
|
|
133
|
+
""",
|
|
134
|
+
effectiveness=90,
|
|
135
|
+
difficulty=20,
|
|
136
|
+
),
|
|
137
|
+
Mitigation(
|
|
138
|
+
name="Background Service Limitations",
|
|
139
|
+
description="Use WorkManager or JobScheduler with constraints",
|
|
140
|
+
mitigation_type=MitigationType.HARDENING,
|
|
141
|
+
implementation="""
|
|
142
|
+
// Use WorkManager instead of Service:
|
|
143
|
+
PeriodicWorkRequest workRequest =
|
|
144
|
+
new PeriodicWorkRequest.Builder(
|
|
145
|
+
MyWorker.class,
|
|
146
|
+
15, // minimum interval
|
|
147
|
+
TimeUnit.MINUTES)
|
|
148
|
+
.addTag("background_work")
|
|
149
|
+
.build();
|
|
150
|
+
|
|
151
|
+
WorkManager.getInstance(context)
|
|
152
|
+
.enqueueUniquePeriodicWork(
|
|
153
|
+
"work_name",
|
|
154
|
+
ExistingPeriodicWorkPolicy.REPLACE,
|
|
155
|
+
workRequest);
|
|
156
|
+
""",
|
|
157
|
+
effectiveness=85,
|
|
158
|
+
difficulty=35,
|
|
159
|
+
),
|
|
160
|
+
Mitigation(
|
|
161
|
+
name="Service Permission Declaration",
|
|
162
|
+
description="Declare services with explicit permissions",
|
|
163
|
+
mitigation_type=MitigationType.PREVENTION,
|
|
164
|
+
implementation="""
|
|
165
|
+
<service android:name=".MyService"
|
|
166
|
+
android:permission="com.example.INTERNAL_SERVICE">
|
|
167
|
+
<intent-filter>
|
|
168
|
+
<action android:name="com.example.MY_ACTION"/>
|
|
169
|
+
</intent-filter>
|
|
170
|
+
</service>
|
|
171
|
+
""",
|
|
172
|
+
effectiveness=80,
|
|
173
|
+
difficulty=15,
|
|
174
|
+
),
|
|
175
|
+
],
|
|
176
|
+
'native_library': [
|
|
177
|
+
Mitigation(
|
|
178
|
+
name="Native Code Validation",
|
|
179
|
+
description="Use runtime code integrity verification for native libs",
|
|
180
|
+
mitigation_type=MitigationType.DETECTION,
|
|
181
|
+
implementation="""
|
|
182
|
+
// Compute hash of loaded native library
|
|
183
|
+
public boolean verifyNativeLibrary(String libName) {
|
|
184
|
+
try {
|
|
185
|
+
System.loadLibrary(libName);
|
|
186
|
+
String libPath = findLoadedLibraryPath(libName);
|
|
187
|
+
String hash = calculateFileHash(libPath);
|
|
188
|
+
return hash.equals(expectedHash);
|
|
189
|
+
} catch (Exception e) {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
""",
|
|
194
|
+
effectiveness=75,
|
|
195
|
+
difficulty=60,
|
|
196
|
+
),
|
|
197
|
+
Mitigation(
|
|
198
|
+
name="Selinux Policy Enforcement",
|
|
199
|
+
description="Implement strict SELinux policy for native code",
|
|
200
|
+
mitigation_type=MitigationType.HARDENING,
|
|
201
|
+
implementation="""
|
|
202
|
+
# SELinux policy for native library access
|
|
203
|
+
type app_native_t;
|
|
204
|
+
type app_data_t;
|
|
205
|
+
|
|
206
|
+
allow app_native_t app_data_t:file { read write };
|
|
207
|
+
deny app_native_t kernel_t:capability sys_ptrace;
|
|
208
|
+
neverallow app_native_t device_t:chr_file write;
|
|
209
|
+
""",
|
|
210
|
+
effectiveness=90,
|
|
211
|
+
difficulty=80,
|
|
212
|
+
),
|
|
213
|
+
Mitigation(
|
|
214
|
+
name="Address Space Layout Randomization (ASLR)",
|
|
215
|
+
description="Enable ASLR to prevent memory exploitation",
|
|
216
|
+
mitigation_type=MitigationType.HARDENING,
|
|
217
|
+
implementation="""
|
|
218
|
+
# ASLR is enabled by default on modern Android
|
|
219
|
+
# Verify via:
|
|
220
|
+
adb shell cat /proc/sys/kernel/randomize_va_space
|
|
221
|
+
# Should output: 2 (full ASLR)
|
|
222
|
+
""",
|
|
223
|
+
effectiveness=85,
|
|
224
|
+
difficulty=5,
|
|
225
|
+
),
|
|
226
|
+
],
|
|
227
|
+
'job_scheduler': [
|
|
228
|
+
Mitigation(
|
|
229
|
+
name="Job Constraints Implementation",
|
|
230
|
+
description="Enforce strict constraints on scheduled jobs",
|
|
231
|
+
mitigation_type=MitigationType.HARDENING,
|
|
232
|
+
implementation="""
|
|
233
|
+
JobInfo job = new JobInfo.Builder(JOB_ID, componentName)
|
|
234
|
+
.setRequiresDeviceIdle(true)
|
|
235
|
+
.setRequiresCharging(true)
|
|
236
|
+
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
|
|
237
|
+
.setPeriodic(24 * 60 * 60 * 1000) // 24 hours minimum
|
|
238
|
+
.build();
|
|
239
|
+
""",
|
|
240
|
+
effectiveness=80,
|
|
241
|
+
difficulty=25,
|
|
242
|
+
),
|
|
243
|
+
Mitigation(
|
|
244
|
+
name="Job Execution Monitoring",
|
|
245
|
+
description="Monitor and log all job executions",
|
|
246
|
+
mitigation_type=MitigationType.MONITORING,
|
|
247
|
+
implementation="""
|
|
248
|
+
public class MonitoredJobService extends JobService {
|
|
249
|
+
@Override
|
|
250
|
+
public boolean onStartJob(JobParameters params) {
|
|
251
|
+
logJobExecution("started", params.getJobId());
|
|
252
|
+
doWork();
|
|
253
|
+
logJobExecution("completed", params.getJobId());
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
""",
|
|
258
|
+
effectiveness=70,
|
|
259
|
+
difficulty=30,
|
|
260
|
+
),
|
|
261
|
+
],
|
|
262
|
+
'intent_filter': [
|
|
263
|
+
Mitigation(
|
|
264
|
+
name="Explicit Component Targeting",
|
|
265
|
+
description="Always use explicit intents instead of implicit",
|
|
266
|
+
mitigation_type=MitigationType.PREVENTION,
|
|
267
|
+
implementation="""
|
|
268
|
+
// Bad: implicit intent
|
|
269
|
+
Intent intent = new Intent("com.example.ACTION");
|
|
270
|
+
startActivity(intent);
|
|
271
|
+
|
|
272
|
+
// Good: explicit intent
|
|
273
|
+
Intent intent = new Intent(this, TargetActivity.class);
|
|
274
|
+
startActivity(intent);
|
|
275
|
+
""",
|
|
276
|
+
effectiveness=95,
|
|
277
|
+
difficulty=20,
|
|
278
|
+
),
|
|
279
|
+
Mitigation(
|
|
280
|
+
name="Intent Filter Export Control",
|
|
281
|
+
description="Carefully control intent filter export attribute",
|
|
282
|
+
mitigation_type=MitigationType.HARDENING,
|
|
283
|
+
implementation="""
|
|
284
|
+
<!-- Only export if truly necessary -->
|
|
285
|
+
<activity android:name=".PublicActivity"
|
|
286
|
+
android:exported="true">
|
|
287
|
+
<intent-filter>
|
|
288
|
+
<action android:name="android.intent.action.MAIN"/>
|
|
289
|
+
</intent-filter>
|
|
290
|
+
</activity>
|
|
291
|
+
|
|
292
|
+
<!-- Default: not exported -->
|
|
293
|
+
<activity android:name=".PrivateActivity">
|
|
294
|
+
</activity>
|
|
295
|
+
""",
|
|
296
|
+
effectiveness=85,
|
|
297
|
+
difficulty=15,
|
|
298
|
+
),
|
|
299
|
+
],
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
def get_mitigations_for_threat(self, threat_type: str) -> List[Mitigation]:
|
|
303
|
+
"""
|
|
304
|
+
Get mitigations for specific threat type.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
threat_type: Type of threat (e.g., 'broadcast_receiver')
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
List of applicable mitigations
|
|
311
|
+
"""
|
|
312
|
+
threat_type = threat_type.lower().replace(' ', '_')
|
|
313
|
+
return self.mitigations.get(threat_type, [])
|
|
314
|
+
|
|
315
|
+
def get_mitigations_by_type(self, mitigation_type: MitigationType) -> List[Mitigation]:
|
|
316
|
+
"""
|
|
317
|
+
Get all mitigations of specific type.
|
|
318
|
+
|
|
319
|
+
Args:
|
|
320
|
+
mitigation_type: Type of mitigation to filter by
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
List of mitigations
|
|
324
|
+
"""
|
|
325
|
+
results = []
|
|
326
|
+
for mitigations in self.mitigations.values():
|
|
327
|
+
results.extend([m for m in mitigations if m.mitigation_type == mitigation_type])
|
|
328
|
+
return results
|
|
329
|
+
|
|
330
|
+
def get_highest_effectiveness_mitigation(
|
|
331
|
+
self,
|
|
332
|
+
threat_type: str
|
|
333
|
+
) -> Optional[Mitigation]:
|
|
334
|
+
"""Get most effective mitigation for threat."""
|
|
335
|
+
mitigations = self.get_mitigations_for_threat(threat_type)
|
|
336
|
+
if not mitigations:
|
|
337
|
+
return None
|
|
338
|
+
return max(mitigations, key=lambda m: m.effectiveness)
|
|
339
|
+
|
|
340
|
+
def get_easiest_mitigations(self, threat_type: str, count: int = 3) -> List[Mitigation]:
|
|
341
|
+
"""Get easiest-to-implement mitigations."""
|
|
342
|
+
mitigations = self.get_mitigations_for_threat(threat_type)
|
|
343
|
+
sorted_mitigations = sorted(mitigations, key=lambda m: m.difficulty)
|
|
344
|
+
return sorted_mitigations[:count]
|
|
345
|
+
|
|
346
|
+
def generate_mitigation_report(self, threats: List[str]) -> str:
|
|
347
|
+
"""
|
|
348
|
+
Generate mitigation recommendations report.
|
|
349
|
+
|
|
350
|
+
Args:
|
|
351
|
+
threats: List of detected threat types
|
|
352
|
+
|
|
353
|
+
Returns:
|
|
354
|
+
Formatted mitigation report
|
|
355
|
+
"""
|
|
356
|
+
lines = ["MITIGATION RECOMMENDATIONS REPORT", "=" * 70]
|
|
357
|
+
lines.append("")
|
|
358
|
+
|
|
359
|
+
all_mitigations: Set[str] = set()
|
|
360
|
+
|
|
361
|
+
for threat in threats:
|
|
362
|
+
mitigations = self.get_mitigations_for_threat(threat)
|
|
363
|
+
if mitigations:
|
|
364
|
+
lines.append(f"\n{threat.upper()}")
|
|
365
|
+
lines.append("-" * 50)
|
|
366
|
+
|
|
367
|
+
for mitigation in mitigations:
|
|
368
|
+
lines.append(f"\n {mitigation.name}")
|
|
369
|
+
lines.append(f" Type: {mitigation.mitigation_type.value}")
|
|
370
|
+
lines.append(f" Effectiveness: {mitigation.effectiveness}%")
|
|
371
|
+
lines.append(f" Difficulty: {mitigation.difficulty}/100")
|
|
372
|
+
lines.append(f" Description: {mitigation.description}")
|
|
373
|
+
all_mitigations.add(mitigation.name)
|
|
374
|
+
|
|
375
|
+
lines.append("\n" + "=" * 70)
|
|
376
|
+
lines.append(f"Total Unique Mitigations: {len(all_mitigations)}")
|
|
377
|
+
|
|
378
|
+
return "\n".join(lines)
|