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.
@@ -0,0 +1,164 @@
1
+ """
2
+ Manifest Parser for AndroidManifest.xml binary format analysis.
3
+
4
+ Handles parsing of compiled AndroidManifest.xml files which are in binary AXML format.
5
+ This module provides tools for extracting components, permissions, and intent filters.
6
+
7
+ Author: Security Research Team
8
+ License: Apache-2.0
9
+ """
10
+
11
+ from typing import Dict, List, Optional, Any
12
+ from dataclasses import dataclass
13
+
14
+
15
+ @dataclass
16
+ class Permission:
17
+ """Represents a permission declared in manifest."""
18
+ name: str
19
+ protection_level: str = "normal"
20
+ description: str = ""
21
+
22
+
23
+ @dataclass
24
+ class Component:
25
+ """Represents an Android component (Activity, Service, BroadcastReceiver, etc)."""
26
+ name: str
27
+ component_type: str # activity, service, receiver, provider
28
+ exported: bool = False
29
+ permissions: List[str] = None
30
+ intent_filters: List[Dict[str, Any]] = None
31
+
32
+
33
+ class ManifestParser:
34
+ """
35
+ Parser for Android manifest files.
36
+
37
+ Note: This is a simplified parser. For full AXML binary parsing,
38
+ use androguard library.
39
+ """
40
+
41
+ def __init__(self, manifest_path: str = None, manifest_data: bytes = None):
42
+ """
43
+ Initialize manifest parser.
44
+
45
+ Args:
46
+ manifest_path: Path to AndroidManifest.xml
47
+ manifest_data: Raw manifest bytes
48
+ """
49
+ self.manifest_path = manifest_path
50
+ self.manifest_data = manifest_data
51
+ self.components: List[Component] = []
52
+ self.permissions: List[Permission] = []
53
+ self.package_name: str = ""
54
+
55
+ def parse(self) -> bool:
56
+ """
57
+ Parse manifest file.
58
+
59
+ Returns:
60
+ True if parsing succeeded
61
+ """
62
+ try:
63
+ if self.manifest_path:
64
+ with open(self.manifest_path, 'rb') as f:
65
+ self.manifest_data = f.read()
66
+
67
+ if not self.manifest_data:
68
+ return False
69
+
70
+ self._extract_basic_info()
71
+ self._extract_components()
72
+ self._extract_permissions()
73
+
74
+ return True
75
+ except Exception as e:
76
+ print(f"Error parsing manifest: {str(e)}")
77
+ return False
78
+
79
+ def _extract_basic_info(self) -> None:
80
+ """Extract basic package information from manifest."""
81
+ # This is a simplified extraction - real parsing would decode AXML format
82
+ # Looking for package attribute pattern
83
+ try:
84
+ # Search for package name in manifest
85
+ if b'package=' in self.manifest_data:
86
+ idx = self.manifest_data.find(b'package=')
87
+ if idx != -1:
88
+ # Extract package name (simplified approach)
89
+ data_after = self.manifest_data[idx+8:idx+100]
90
+ end = data_after.find(b'\x00')
91
+ if end > 0:
92
+ self.package_name = data_after[:end].decode('utf-8', errors='ignore')
93
+ except Exception:
94
+ self.package_name = "Unknown"
95
+
96
+ def _extract_components(self) -> None:
97
+ """Extract Android components from manifest."""
98
+ # Simulate component extraction
99
+ component_types = {
100
+ b'<activity': 'activity',
101
+ b'<service': 'service',
102
+ b'<receiver': 'receiver',
103
+ b'<provider': 'provider',
104
+ }
105
+
106
+ for tag, comp_type in component_types.items():
107
+ offset = 0
108
+ while True:
109
+ offset = self.manifest_data.find(tag, offset)
110
+ if offset == -1:
111
+ break
112
+ # Extract component name (simplified)
113
+ offset += 1
114
+
115
+ def _extract_permissions(self) -> None:
116
+ """Extract declared permissions from manifest."""
117
+ # Simulate permission extraction
118
+ try:
119
+ offset = self.manifest_data.find(b'<uses-permission')
120
+ if offset != -1:
121
+ # Found permission declaration
122
+ pass
123
+ except Exception:
124
+ pass
125
+
126
+ def get_components(self, component_type: str = None) -> List[Component]:
127
+ """
128
+ Get components from manifest.
129
+
130
+ Args:
131
+ component_type: Filter by type (activity, service, receiver, provider)
132
+
133
+ Returns:
134
+ List of components
135
+ """
136
+ if not component_type:
137
+ return self.components
138
+ return [c for c in self.components if c.component_type == component_type]
139
+
140
+ def get_permissions(self) -> List[Permission]:
141
+ """Get declared permissions."""
142
+ return self.permissions
143
+
144
+ def get_package_name(self) -> str:
145
+ """Get package name."""
146
+ return self.package_name
147
+
148
+ def has_boot_receiver(self) -> bool:
149
+ """Check if manifest contains BOOT_COMPLETED receiver."""
150
+ return any(
151
+ 'BOOT_COMPLETED' in str(c.intent_filters or [])
152
+ for c in self.get_components('receiver')
153
+ )
154
+
155
+ def get_exported_components(self) -> List[Component]:
156
+ """Get all exported components (potential attack surface)."""
157
+ return [c for c in self.components if c.exported]
158
+
159
+ def print_summary(self) -> None:
160
+ """Print manifest summary."""
161
+ print(f"\nPackage: {self.package_name}")
162
+ print(f"Components: {len(self.components)}")
163
+ print(f"Permissions: {len(self.permissions)}")
164
+ print(f"Exported: {len(self.get_exported_components())}")
@@ -0,0 +1,217 @@
1
+ """
2
+ Signature Matcher for identifying known persistence patterns and attack signatures.
3
+
4
+ Provides pattern matching capabilities to identify known Android persistence
5
+ techniques, malware signatures, and suspicious code patterns.
6
+
7
+ Author: Security Research Team
8
+ License: Apache-2.0
9
+ """
10
+
11
+ from typing import Dict, List, Tuple, Optional
12
+ from dataclasses import dataclass
13
+ from enum import Enum
14
+
15
+
16
+ class SignatureSeverity(Enum):
17
+ """Severity levels for matched signatures."""
18
+ CRITICAL = 5
19
+ HIGH = 4
20
+ MEDIUM = 3
21
+ LOW = 2
22
+ INFO = 1
23
+
24
+
25
+ @dataclass
26
+ class Signature:
27
+ """Pattern signature definition."""
28
+ name: str
29
+ pattern: str # Regex or string pattern
30
+ description: str
31
+ severity: SignatureSeverity
32
+ cve: Optional[str] = None
33
+ references: List[str] = None
34
+
35
+
36
+ class SignatureMatcher:
37
+ """
38
+ Matcher for code and binary signatures.
39
+
40
+ Maintains a database of known persistence patterns and suspicious signatures
41
+ to identify potential threats during APK analysis.
42
+ """
43
+
44
+ def __init__(self):
45
+ """Initialize signature matcher with known patterns."""
46
+ self.signatures = self._load_signatures()
47
+
48
+ def _load_signatures(self) -> List[Signature]:
49
+ """
50
+ Load known persistence signatures.
51
+
52
+ Returns:
53
+ List of Signature objects
54
+ """
55
+ return [
56
+ Signature(
57
+ name="BootReceiver",
58
+ pattern="android.intent.action.BOOT_COMPLETED",
59
+ description="Broadcast receiver triggered on device boot",
60
+ severity=SignatureSeverity.HIGH,
61
+ ),
62
+ Signature(
63
+ name="AutoStartService",
64
+ pattern="startForeground|startService",
65
+ description="Auto-starting service detected",
66
+ severity=SignatureSeverity.MEDIUM,
67
+ ),
68
+ Signature(
69
+ name="ScreenOnReceiver",
70
+ pattern="android.intent.action.SCREEN_ON",
71
+ description="Receiver triggered on screen activation",
72
+ severity=SignatureSeverity.MEDIUM,
73
+ ),
74
+ Signature(
75
+ name="JobScheduler",
76
+ pattern="JobScheduler|JobService",
77
+ description="Job scheduling mechanism for background tasks",
78
+ severity=SignatureSeverity.MEDIUM,
79
+ ),
80
+ Signature(
81
+ name="WorkManager",
82
+ pattern="WorkManager|Worker",
83
+ description="WorkManager background task execution",
84
+ severity=SignatureSeverity.MEDIUM,
85
+ ),
86
+ Signature(
87
+ name="NativeExecution",
88
+ pattern="System\\.load|Runtime\\.exec",
89
+ description="Native code or shell command execution",
90
+ severity=SignatureSeverity.HIGH,
91
+ ),
92
+ Signature(
93
+ name="HiddenComponent",
94
+ pattern="android:enabled=\"false\"",
95
+ description="Disabled component that could be enabled at runtime",
96
+ severity=SignatureSeverity.MEDIUM,
97
+ ),
98
+ Signature(
99
+ name="KernelHook",
100
+ pattern="ptrace|syscall|mmap.*exec",
101
+ description="Potential kernel-level hook or manipulation",
102
+ severity=SignatureSeverity.CRITICAL,
103
+ ),
104
+ Signature(
105
+ name="ObfuscationIndicator",
106
+ pattern="aaaa|bbbb|cccc|\\u[0-9a-f]{4}",
107
+ description="Code obfuscation indicators",
108
+ severity=SignatureSeverity.MEDIUM,
109
+ ),
110
+ Signature(
111
+ name="ReflectionUsage",
112
+ pattern="reflect|getMethod|invoke|forName",
113
+ description="Reflection-based code execution",
114
+ severity=SignatureSeverity.MEDIUM,
115
+ ),
116
+ ]
117
+
118
+ def match_signatures(self, data: str) -> List[Tuple[Signature, List[str]]]:
119
+ """
120
+ Match signatures in provided data.
121
+
122
+ Args:
123
+ data: Text data to scan
124
+
125
+ Returns:
126
+ List of (Signature, matches) tuples
127
+ """
128
+ results = []
129
+
130
+ for signature in self.signatures:
131
+ matches = self._find_pattern(data, signature.pattern)
132
+ if matches:
133
+ results.append((signature, matches))
134
+
135
+ return results
136
+
137
+ def _find_pattern(self, text: str, pattern: str) -> List[str]:
138
+ """
139
+ Find all occurrences of pattern in text.
140
+
141
+ Args:
142
+ text: Text to search
143
+ pattern: Pattern to find
144
+
145
+ Returns:
146
+ List of matching strings
147
+ """
148
+ import re
149
+ try:
150
+ matches = re.findall(pattern, text, re.IGNORECASE)
151
+ return matches
152
+ except re.error:
153
+ # If regex fails, try literal string match
154
+ if pattern in text:
155
+ return [pattern]
156
+ return []
157
+
158
+ def get_risk_score(self, matches: List[Tuple[Signature, List[str]]]) -> float:
159
+ """
160
+ Calculate risk score based on signature matches.
161
+
162
+ Args:
163
+ matches: Matched signatures
164
+
165
+ Returns:
166
+ Risk score from 0 to 100
167
+ """
168
+ if not matches:
169
+ return 0.0
170
+
171
+ max_severity = max(m[0].severity.value for m in matches)
172
+ match_count = sum(len(m[1]) for m in matches)
173
+
174
+ base_score = (max_severity / 5) * 100
175
+ bonus = min(match_count * 5, 30) # Cap bonus at 30
176
+
177
+ return min(base_score + bonus, 100)
178
+
179
+ def generate_report(self, matches: List[Tuple[Signature, List[str]]]) -> str:
180
+ """
181
+ Generate human-readable report of matches.
182
+
183
+ Args:
184
+ matches: Matched signatures
185
+
186
+ Returns:
187
+ Formatted report string
188
+ """
189
+ lines = ["Signature Matching Report", "=" * 50]
190
+
191
+ risk_score = self.get_risk_score(matches)
192
+ lines.append(f"Overall Risk Score: {risk_score:.1f}/100")
193
+ lines.append("")
194
+
195
+ if not matches:
196
+ lines.append("No suspicious signatures detected.")
197
+ else:
198
+ lines.append(f"Found {len(matches)} signature matches:\n")
199
+
200
+ for signature, match_list in sorted(
201
+ matches,
202
+ key=lambda x: x[0].severity.value,
203
+ reverse=True
204
+ ):
205
+ lines.append(f"[{signature.severity.name}] {signature.name}")
206
+ lines.append(f" Description: {signature.description}")
207
+ lines.append(f" Matches: {len(match_list)}")
208
+ if len(match_list) <= 3:
209
+ for match in match_list:
210
+ lines.append(f" - {match}")
211
+ else:
212
+ for match in match_list[:3]:
213
+ lines.append(f" - {match}")
214
+ lines.append(f" ... and {len(match_list) - 3} more")
215
+ lines.append("")
216
+
217
+ return "\n".join(lines)