trigdroid 2.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,1150 @@
1
+ Metadata-Version: 2.4
2
+ Name: trigdroid
3
+ Version: 2.0.0
4
+ Summary: Android Sandbox Payload Trigger Framework for Security Research
5
+ Project-URL: Homepage, https://github.com/fkie-cad/Sandroid_TrigDroid
6
+ Project-URL: Documentation, https://fkie-cad/Sandroid_TrigDroid
7
+ Project-URL: Repository, https://github.com/fkie-cad/Sandroid_TrigDroid.git
8
+ Project-URL: Bug Tracker, https://github.com/fkie-cad/Sandroid_TrigDroid/issues
9
+ Project-URL: Changelog, https://github.com/fkie-cad/Sandroid_TrigDroid/blob/main/CHANGELOG.md
10
+ Author-email: Daniel Baier <daniel.baier@fkie.fraunhofer.de>, Simon HΓΌsch <simon.huesch@smail.inf.h-brs.de>
11
+ Maintainer-email: TrigDroid Team <contact@trigdroid.org>
12
+ License: MIT License
13
+
14
+ Copyright (c) 2025 FKIE-CAD
15
+
16
+ Permission is hereby granted, free of charge, to any person obtaining a copy
17
+ of this software and associated documentation files (the "Software"), to deal
18
+ in the Software without restriction, including without limitation the rights
19
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20
+ copies of the Software, and to permit persons to whom the Software is
21
+ furnished to do so, subject to the following conditions:
22
+
23
+ The above copyright notice and this permission notice shall be included in all
24
+ copies or substantial portions of the Software.
25
+
26
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32
+ SOFTWARE.
33
+ License-File: LICENSE
34
+ Keywords: android,frida,malware-analysis,mobile-security,penetration-testing,reverse-engineering,security
35
+ Classifier: Development Status :: 4 - Beta
36
+ Classifier: Environment :: Console
37
+ Classifier: Environment :: Other Environment
38
+ Classifier: Intended Audience :: Developers
39
+ Classifier: Intended Audience :: Science/Research
40
+ Classifier: Intended Audience :: System Administrators
41
+ Classifier: License :: OSI Approved :: MIT License
42
+ Classifier: Natural Language :: English
43
+ Classifier: Operating System :: MacOS
44
+ Classifier: Operating System :: POSIX :: Linux
45
+ Classifier: Programming Language :: JavaScript
46
+ Classifier: Programming Language :: Python :: 3
47
+ Classifier: Programming Language :: Python :: 3.9
48
+ Classifier: Programming Language :: Python :: 3.10
49
+ Classifier: Programming Language :: Python :: 3.11
50
+ Classifier: Programming Language :: Python :: 3.12
51
+ Classifier: Topic :: Security
52
+ Classifier: Topic :: Software Development :: Testing
53
+ Classifier: Topic :: System :: Systems Administration
54
+ Classifier: Topic :: Utilities
55
+ Classifier: Typing :: Typed
56
+ Requires-Python: >=3.9
57
+ Requires-Dist: click>=8.1.0
58
+ Requires-Dist: colorama>=0.4.6
59
+ Requires-Dist: pydantic>=2.0.0
60
+ Requires-Dist: pyyaml>=6.0
61
+ Requires-Dist: requests>=2.31.0
62
+ Requires-Dist: rich>=13.0.0
63
+ Requires-Dist: termcolor>=2.3.0
64
+ Provides-Extra: dev
65
+ Requires-Dist: black>=23.0.0; extra == 'dev'
66
+ Requires-Dist: isort>=5.12.0; extra == 'dev'
67
+ Requires-Dist: mypy>=1.0.0; extra == 'dev'
68
+ Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
69
+ Requires-Dist: pylint>=2.17.0; extra == 'dev'
70
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
71
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
72
+ Requires-Dist: pytest-mock>=3.10.0; extra == 'dev'
73
+ Requires-Dist: pytest>=7.0.0; extra == 'dev'
74
+ Requires-Dist: types-pyyaml>=6.0.0; extra == 'dev'
75
+ Requires-Dist: types-requests>=2.31.0; extra == 'dev'
76
+ Provides-Extra: docs
77
+ Requires-Dist: mkdocs-click>=0.8.0; extra == 'docs'
78
+ Requires-Dist: mkdocs-material>=9.0.0; extra == 'docs'
79
+ Requires-Dist: mkdocs>=1.5.0; extra == 'docs'
80
+ Requires-Dist: mkdocstrings[python]>=0.22.0; extra == 'docs'
81
+ Provides-Extra: frida
82
+ Requires-Dist: frida-tools>=12.0.0; extra == 'frida'
83
+ Requires-Dist: frida>=16.0.0; extra == 'frida'
84
+ Provides-Extra: full
85
+ Requires-Dist: appium-python-client>=3.0.0; extra == 'full'
86
+ Requires-Dist: frida-tools>=12.0.0; extra == 'full'
87
+ Requires-Dist: frida>=16.0.0; extra == 'full'
88
+ Requires-Dist: numpy>=1.24.0; extra == 'full'
89
+ Requires-Dist: pillow>=9.0.0; extra == 'full'
90
+ Requires-Dist: selenium>=4.0.0; extra == 'full'
91
+ Provides-Extra: lint
92
+ Requires-Dist: black>=23.0.0; extra == 'lint'
93
+ Requires-Dist: isort>=5.12.0; extra == 'lint'
94
+ Requires-Dist: mypy>=1.0.0; extra == 'lint'
95
+ Requires-Dist: pylint>=2.17.0; extra == 'lint'
96
+ Requires-Dist: ruff>=0.1.0; extra == 'lint'
97
+ Provides-Extra: test
98
+ Requires-Dist: coverage[toml]>=7.0.0; extra == 'test'
99
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == 'test'
100
+ Requires-Dist: pytest-cov>=4.0.0; extra == 'test'
101
+ Requires-Dist: pytest-mock>=3.10.0; extra == 'test'
102
+ Requires-Dist: pytest>=7.0.0; extra == 'test'
103
+ Description-Content-Type: text/markdown
104
+
105
+ # TrigDroid πŸ€–
106
+
107
+ **Android Sandbox Payload Trigger Framework for Security Research**
108
+
109
+ [![Python Version](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/downloads/)
110
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
111
+ [![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)]()
112
+ [![Coverage](https://img.shields.io/badge/coverage-90%25-green.svg)]()
113
+
114
+ TrigDroid is a modern Android security testing framework designed to trigger payloads in potentially malicious Android applications through sophisticated environmental manipulation. Built for security researchers, malware analysts, and penetration testers, it provides both a powerful command-line interface and a flexible Python API. It is part of the Android Sandbox Sandroid.
115
+
116
+ ## 🎯 Key Features
117
+
118
+ - **πŸ” Payload Trigger Detection**: Sophisticated environmental manipulation to trigger hidden malicious behaviors
119
+ - **πŸ“± Multi-Device Support**: Works with physical devices and emulators
120
+ - **πŸ”§ Dual Interface**: Both CLI and Python API for maximum flexibility
121
+ - **⚑ Frida Integration**: Advanced runtime instrumentation with TypeScript hooks
122
+ - **🌐 Network Manipulation**: WiFi, mobile data, and Bluetooth state changes
123
+ - **πŸ“Š Sensor Simulation**: Accelerometer, gyroscope, light, pressure, and more
124
+ - **πŸ”‹ Battery Simulation**: Dynamic battery level and charging state changes
125
+ - **πŸ“ˆ Comprehensive Reporting**: Detailed test results with timing and metrics
126
+ - **πŸ—οΈ Modern Architecture**: Built with SOLID principles and type safety
127
+
128
+ ## πŸš€ Quick Start
129
+
130
+ ### Installation
131
+
132
+ ```bash
133
+ # Install TrigDroid with full features
134
+ pip install trigdroid[full]
135
+
136
+ # Install minimal version
137
+ pip install trigdroid
138
+
139
+ # Install with specific features
140
+ pip install trigdroid[frida] # Just Frida support
141
+ pip install trigdroid[dev] # Development tools
142
+ ```
143
+
144
+ ### Prerequisites
145
+
146
+ - **Python 3.9+**
147
+ - **Android Debug Bridge (ADB)**
148
+ - **Android device or emulator** with USB debugging enabled
149
+ - **Node.js** (optional, for TypeScript Frida hooks)
150
+
151
+ ### Basic Usage
152
+
153
+ ```bash
154
+ # Test an Android app with basic payload triggers
155
+ trigdroid -p com.example.app
156
+
157
+ # Advanced testing with sensor manipulation
158
+ trigdroid -p com.suspicious.app --acceleration 8 --battery 3 --wifi
159
+
160
+ # List available Android devices
161
+ trigdroid devices
162
+
163
+ # Get detailed app information
164
+ trigdroid info com.example.app
165
+ ```
166
+
167
+ ## πŸ“‹ Command Line Interface
168
+
169
+ ### Core Commands
170
+
171
+ ```bash
172
+ # Basic app analysis
173
+ trigdroid -p <package_name>
174
+
175
+ # Advanced options
176
+ trigdroid -p com.example.app \
177
+ --acceleration 5 \ # Sensor elaborateness (0-10)
178
+ --battery 3 \ # Battery rotation level (0-4)
179
+ --wifi \ # Enable WiFi state changes
180
+ --data \ # Enable mobile data changes
181
+ --frida \ # Enable Frida hooks
182
+ --timeout 600 # Test timeout in seconds
183
+ ```
184
+
185
+ ### Device Management
186
+
187
+ ```bash
188
+ # List all connected devices
189
+ trigdroid devices
190
+
191
+ # Get detailed device information
192
+ trigdroid devices --verbose
193
+
194
+ # Test specific device
195
+ trigdroid -p com.example.app -d emulator-5554
196
+ ```
197
+
198
+ ### Configuration Files
199
+
200
+ ```bash
201
+ # Create configuration template
202
+ trigdroid --create-config default
203
+
204
+ # Use configuration file
205
+ trigdroid test-config config.yaml
206
+ ```
207
+
208
+ ### Example Configuration (config.yaml)
209
+
210
+ ```yaml
211
+ package: "com.example.app"
212
+ acceleration: 5
213
+ sensors:
214
+ - accelerometer
215
+ - gyroscope
216
+ - light
217
+ network_states:
218
+ - wifi
219
+ - data
220
+ battery_rotation: 3
221
+ frida_hooks: true
222
+ timeout: 300
223
+ verbose: true
224
+ ```
225
+
226
+ ## 🐍 Python API
227
+
228
+ ### Simple Usage
229
+
230
+ ```python
231
+ from trigdroid import quick_test
232
+
233
+ # Quick test with default settings
234
+ result = quick_test("com.example.app")
235
+ print(f"Test successful: {result.success}")
236
+ ```
237
+
238
+ ### Advanced Usage
239
+
240
+ ```python
241
+ from trigdroid import TrigDroidAPI, TestConfiguration
242
+
243
+ # Create detailed configuration
244
+ config = TestConfiguration(
245
+ package="com.suspicious.app",
246
+ acceleration=8, # High sensor activity
247
+ sensors=["accelerometer", "gyroscope", "light"],
248
+ network_states=["wifi", "data"], # Network state changes
249
+ battery_rotation=3, # Battery level simulation
250
+ frida_hooks=True, # Enable runtime instrumentation
251
+ timeout=600, # 10 minute timeout
252
+ verbose=True
253
+ )
254
+
255
+ # Run comprehensive test
256
+ with TrigDroidAPI() as trigdroid:
257
+ trigdroid.configure(config)
258
+ result = trigdroid.run_tests()
259
+
260
+ # Analyze results
261
+ if result.success:
262
+ print(f"βœ… Test completed in {result.duration_seconds:.1f}s")
263
+ print(f"πŸ“Š Tests run: {result.total_tests}")
264
+ print(f"βœ… Passed: {result.passed_tests}")
265
+ print(f"❌ Failed: {result.failed_tests}")
266
+ else:
267
+ print(f"❌ Test failed: {result.error}")
268
+
269
+ # Check for suspicious behavior
270
+ if result.app_crashed:
271
+ print("⚠️ App crashed during testing")
272
+ if result.frida_errors:
273
+ print(f"πŸ” Frida instrumentation issues: {result.frida_errors}")
274
+
275
+ # Access detailed metrics
276
+ print(f"πŸ“± Sensor tests: {len(result.sensor_tests_executed)}")
277
+ print(f"🌐 Network changes: {len(result.network_state_changes)}")
278
+ print(f"πŸ”‹ Background time: {result.app_background_time:.1f}s")
279
+ ```
280
+
281
+ ### Device Management API
282
+
283
+ ```python
284
+ from trigdroid import DeviceManager, scan_devices
285
+
286
+ # Scan for available devices
287
+ devices = scan_devices()
288
+ for device in devices:
289
+ print(f"Device: {device['id']} ({device['status']})")
290
+
291
+ # Advanced device management
292
+ manager = DeviceManager()
293
+ device = manager.connect_to_device("emulator-5554")
294
+
295
+ if device:
296
+ print(f"Connected to: {device.device_id}")
297
+ info = device.get_device_info()
298
+ print(f"Model: {info.get('model', 'Unknown')}")
299
+ print(f"Android: {info.get('version', 'Unknown')}")
300
+ ```
301
+
302
+ ### Environment Validation
303
+
304
+ ```python
305
+ from trigdroid.api.quick_start import validate_environment, setup_environment
306
+
307
+ # Check if environment is ready
308
+ status = validate_environment()
309
+ if all(status.values()):
310
+ print("πŸŽ‰ Environment is ready!")
311
+ else:
312
+ print("❌ Issues found:")
313
+ for check, result in status.items():
314
+ if not result:
315
+ print(f" β€’ {check}")
316
+
317
+ # Automatically setup environment
318
+ if setup_environment():
319
+ print("βœ… Environment setup complete")
320
+ ```
321
+
322
+ ## πŸ”§ Advanced Features
323
+
324
+ ### Sensor Manipulation
325
+
326
+ TrigDroid can simulate various Android sensors to trigger environment-dependent payloads:
327
+
328
+ - **Accelerometer**: Motion detection triggers
329
+ - **Gyroscope**: Rotation-based activations
330
+ - **Light Sensor**: Ambient light conditions
331
+ - **Pressure**: Atmospheric pressure changes
332
+ - **Magnetometer**: Magnetic field variations
333
+ - **Proximity**: Near/far object detection
334
+
335
+ ```bash
336
+ # Fine-tuned sensor testing
337
+ trigdroid -p com.example.app \
338
+ --acceleration 7 \
339
+ --gyroscope 5 \
340
+ --light 3 \
341
+ --pressure 4
342
+ ```
343
+
344
+ ### Network State Manipulation
345
+
346
+ Simulate different connectivity scenarios:
347
+
348
+ ```bash
349
+ # Test with network state changes
350
+ trigdroid -p com.example.app --wifi --data --bluetooth
351
+ ```
352
+
353
+ ### Frida Runtime Instrumentation
354
+
355
+ TrigDroid includes sophisticated Frida hooks written in TypeScript:
356
+
357
+ - **Method interception**: Hook critical Android APIs
358
+ - **Parameter modification**: Alter method arguments and return values
359
+ - **Behavior monitoring**: Track app interactions with system APIs
360
+ - **Anti-analysis detection**: Identify evasion techniques
361
+
362
+ ### Battery and System Simulation
363
+
364
+ ```bash
365
+ # Simulate battery level changes and system states
366
+ trigdroid -p com.example.app \
367
+ --battery 4 \ # Maximum battery simulation
368
+ --background-time 30 \ # 30 seconds in background
369
+ --min-runtime 5 # Minimum 5 minutes runtime
370
+ ```
371
+
372
+ ## πŸ“Š Understanding Results
373
+
374
+ ### Test Result Structure
375
+
376
+ ```python
377
+ # Example result analysis
378
+ result = api.run_tests()
379
+
380
+ # Basic status
381
+ print(f"Success: {result.success}")
382
+ print(f"Phase: {result.phase}") # setup, execution, teardown
383
+ print(f"Duration: {result.duration_seconds}s")
384
+
385
+ # Test statistics
386
+ print(f"Total tests: {result.total_tests}")
387
+ print(f"Success rate: {result.success_rate:.1f}%")
388
+
389
+ # App behavior analysis
390
+ print(f"App started: {result.app_started}")
391
+ print(f"App crashed: {result.app_crashed}")
392
+ print(f"Background time: {result.app_background_time}s")
393
+
394
+ # Instrumentation results
395
+ print(f"Frida hooks loaded: {result.frida_hooks_loaded}")
396
+ print(f"Frida hooks active: {result.frida_hooks_active}")
397
+
398
+ # Sensor test results
399
+ for sensor, changed in result.sensor_values_changed.items():
400
+ print(f"Sensor {sensor}: {changed} value changes")
401
+
402
+ # Export results
403
+ result_dict = result.to_dict()
404
+ summary = result.summary() # Human-readable summary
405
+ ```
406
+
407
+ ### Interpreting Security Findings
408
+
409
+ **πŸ” Suspicious Indicators:**
410
+ - App crashes during sensor manipulation
411
+ - Unexpected network activity during state changes
412
+ - Frida hook detection or evasion attempts
413
+ - Unusual battery usage patterns
414
+ - Background behavior changes
415
+
416
+ **βœ… Normal Behavior:**
417
+ - Consistent app performance across all tests
418
+ - No crashes or errors
419
+ - Predictable sensor responses
420
+ - Standard network usage patterns
421
+
422
+ ## πŸ› οΈ Development and Integration
423
+
424
+ ### Third-Party Project Integration
425
+
426
+ ```python
427
+ # In your security analysis tool
428
+ from trigdroid import TrigDroidAPI, TestConfiguration
429
+
430
+ def analyze_apk(package_name: str) -> dict:
431
+ """Analyze an APK for malicious behavior."""
432
+ config = TestConfiguration(
433
+ package=package_name,
434
+ acceleration=8, # High intensity testing
435
+ timeout=600,
436
+ frida_hooks=True
437
+ )
438
+
439
+ with TrigDroidAPI() as trigdroid:
440
+ result = trigdroid.run_tests()
441
+
442
+ return {
443
+ "malicious": not result.success,
444
+ "confidence": result.success_rate,
445
+ "indicators": {
446
+ "crashed": result.app_crashed,
447
+ "evasion_detected": len(result.frida_errors) > 0,
448
+ "suspicious_network": len(result.network_state_changes) > 5
449
+ },
450
+ "report": result.summary()
451
+ }
452
+
453
+ # Usage
454
+ analysis = analyze_apk("com.suspicious.app")
455
+ if analysis["malicious"]:
456
+ print(f"⚠️ Malicious behavior detected (confidence: {analysis['confidence']:.1f}%)")
457
+ ```
458
+
459
+ ### CI/CD Integration
460
+
461
+ ```yaml
462
+ # .github/workflows/security-scan.yml
463
+ name: Android Security Scan
464
+
465
+ on: [push, pull_request]
466
+
467
+ jobs:
468
+ security-scan:
469
+ runs-on: ubuntu-latest
470
+
471
+ steps:
472
+ - uses: actions/checkout@v3
473
+
474
+ - name: Setup Python
475
+ uses: actions/setup-python@v4
476
+ with:
477
+ python-version: '3.9'
478
+
479
+ - name: Install TrigDroid
480
+ run: pip install trigdroid[frida]
481
+
482
+ - name: Setup Android Emulator
483
+ uses: reactivecircus/android-emulator-runner@v2
484
+ with:
485
+ api-level: 29
486
+ script: |
487
+ adb devices
488
+ trigdroid devices
489
+
490
+ - name: Security Analysis
491
+ run: |
492
+ trigdroid -p com.example.testapp \
493
+ --acceleration 5 \
494
+ --timeout 300 \
495
+ --frida
496
+ ```
497
+
498
+ ## πŸ”’ Security Considerations
499
+
500
+ **⚠️ Important**: TrigDroid is designed for **defensive security research** and **malware analysis** only.
501
+
502
+ ### Ethical Usage Guidelines
503
+
504
+ - βœ… **Authorized Testing**: Only test applications you own or have explicit permission to analyze
505
+ - βœ… **Research Purposes**: Use for academic research, security analysis, and malware detection
506
+ - βœ… **Controlled Environment**: Run tests in isolated environments or sandboxes
507
+ - ❌ **No Malicious Use**: Do not use to enhance or create malicious capabilities
508
+ - ❌ **No Unauthorized Testing**: Do not test third-party applications without permission
509
+
510
+ ### Privacy and Legal Compliance
511
+
512
+ - Ensure compliance with local laws and regulations
513
+ - Respect application privacy policies and terms of service
514
+ - Use appropriate data handling and retention practices
515
+ - Consider ethical implications of security research
516
+
517
+ ## πŸ› Troubleshooting
518
+
519
+ ### Common Issues
520
+
521
+ **Device Not Found**
522
+ ```bash
523
+ # Check ADB connection
524
+ adb devices
525
+
526
+ # Restart ADB server
527
+ adb kill-server && adb start-server
528
+
529
+ # Enable USB debugging on device
530
+ # Developer options β†’ USB debugging
531
+ ```
532
+
533
+ **Frida Hooks Fail to Load**
534
+ ```bash
535
+ # Check Frida installation
536
+ python -c "import frida; print(frida.__version__)"
537
+
538
+ # Reinstall Frida
539
+ pip install --upgrade frida frida-tools
540
+
541
+ # Check device architecture
542
+ adb shell getprop ro.product.cpu.abi
543
+ ```
544
+
545
+ **TypeScript Compilation Errors**
546
+ ```bash
547
+ # Check Node.js installation
548
+ node --version && npm --version
549
+
550
+ # Rebuild TypeScript hooks
551
+ cd frida-hooks
552
+ rm -rf node_modules dist
553
+ npm install && npm run build
554
+ ```
555
+
556
+ **Permission Errors**
557
+ ```bash
558
+ # Grant app permissions manually
559
+ adb shell pm grant com.example.app android.permission.READ_PHONE_STATE
560
+
561
+ # Use TrigDroid's permission management
562
+ trigdroid -p com.example.app --grant-permission android.permission.CAMERA
563
+ ```
564
+
565
+ ### Getting Help
566
+
567
+ - **Documentation**: Full documentation at [docs.trigdroid.org](https://docs.trigdroid.org)
568
+ - **Issues**: Report bugs at [GitHub Issues](https://github.com/trigdroid/trigdroid/issues)
569
+ - **Discussions**: Join the community at [GitHub Discussions](https://github.com/trigdroid/trigdroid/discussions)
570
+
571
+ ## 🀝 Contributing
572
+
573
+ We welcome contributions from the security research community!
574
+
575
+ ```bash
576
+ # Development setup
577
+ git clone https://github.com/trigdroid/trigdroid.git
578
+ cd trigdroid
579
+ pip install -e ".[dev]"
580
+
581
+ # Run tests
582
+ pytest tests/
583
+
584
+ # Code quality checks
585
+ black src/ tests/
586
+ mypy src/
587
+ ruff check src/ tests/
588
+ ```
589
+
590
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for detailed contribution guidelines.
591
+
592
+ ## πŸ“„ License
593
+
594
+ TrigDroid is released under the [MIT License](LICENSE). See LICENSE file for details.
595
+
596
+ ## πŸ› οΈ Development Guide
597
+
598
+ ### Modern Architecture Overview
599
+
600
+ TrigDroid has been completely refactored following **SOLID principles** with a clean dual-layer architecture:
601
+
602
+ ```
603
+ β”œβ”€β”€ src/ # πŸ†• New Refactored Code (SOLID Architecture)
604
+ β”‚ β”œβ”€β”€ trigdroid/ # Modern Public API Layer (lowercase)
605
+ β”‚ β”‚ β”œβ”€β”€ api/ # External interface with context managers
606
+ β”‚ β”‚ β”‚ β”œβ”€β”€ main.py # TrigDroidAPI class
607
+ β”‚ β”‚ β”‚ β”œβ”€β”€ config.py # Type-safe TestConfiguration
608
+ β”‚ β”‚ β”‚ β”œβ”€β”€ results.py # Comprehensive TestResult classes
609
+ β”‚ β”‚ β”‚ β”œβ”€β”€ devices.py # Device management wrapper
610
+ β”‚ β”‚ β”‚ β”œβ”€β”€ runners.py # Test runner wrapper
611
+ β”‚ β”‚ β”‚ └── quick_start.py # Convenience functions
612
+ β”‚ β”‚ β”œβ”€β”€ cli/ # Rich CLI interface using Click
613
+ β”‚ β”‚ β”‚ └── main.py # Modern CLI with subcommands
614
+ β”‚ β”‚ β”œβ”€β”€ core/ # Core utilities and enums
615
+ β”‚ β”‚ β”‚ β”œβ”€β”€ enums.py # Type-safe enumerations
616
+ β”‚ β”‚ β”‚ └── cleanup.py # Resource management
617
+ β”‚ β”‚ └── exceptions.py # Exception hierarchy
618
+ β”‚ └── TrigDroid_Infrastructure/ # πŸ—οΈ Infrastructure Layer (SOLID)
619
+ β”‚ β”œβ”€β”€ interfaces/ # Protocol-based abstractions
620
+ β”‚ β”‚ └── __init__.py # ILogger, ITestRunner, IAndroidDevice, etc.
621
+ β”‚ β”œβ”€β”€ infrastructure/ # Dependency injection + implementations
622
+ β”‚ β”‚ β”œβ”€β”€ dependency_injection.py # DI container
623
+ β”‚ β”‚ β”œβ”€β”€ configuration.py # Config providers
624
+ β”‚ β”‚ β”œβ”€β”€ logging.py # Logger implementations
625
+ β”‚ β”‚ └── android.py # Device management
626
+ β”‚ β”œβ”€β”€ application/ # Application orchestration
627
+ β”‚ β”‚ └── orchestrator.py # Main workflow coordinator
628
+ β”‚ └── test_runners/ # Test execution implementations
629
+ β”œβ”€β”€ src/ # πŸ›οΈ Legacy Code (Original Implementation)
630
+ β”‚ └── TrigDroid/ # Legacy TrigDroid (preserved for compatibility)
631
+ β”‚ β”œβ”€β”€ logger/ # Original logging utilities
632
+ β”‚ β”œβ”€β”€ interaction/ # Legacy UI components
633
+ β”‚ β”œβ”€β”€ utils/ # Legacy utility functions
634
+ β”‚ β”œβ”€β”€ frida/ # Legacy JavaScript hooks
635
+ β”‚ └── ... # Other legacy components
636
+ β”œβ”€β”€ frida-hooks/ # πŸ”§ TypeScript Frida Hooks (New)
637
+ β”‚ β”œβ”€β”€ main.ts # Hook entry point
638
+ β”‚ β”œβ”€β”€ hooks/ # Individual hook modules
639
+ β”‚ β”‚ β”œβ”€β”€ android-sensors.ts
640
+ β”‚ β”‚ └── android-build.ts
641
+ β”‚ β”œβ”€β”€ types.ts # TypeScript type definitions
642
+ β”‚ β”œβ”€β”€ utils.ts # Common hook utilities
643
+ β”‚ β”œβ”€β”€ package.json # Node.js build configuration
644
+ β”‚ └── tsconfig.json # TypeScript compiler settings
645
+ β”œβ”€β”€ scripts/ # πŸš€ Build and installation scripts
646
+ β”‚ β”œβ”€β”€ build.py # Cross-platform build script
647
+ β”‚ └── install.sh # Automated installation
648
+ β”œβ”€β”€ pyproject.toml # Modern Python packaging (PEP 621)
649
+ └── Makefile # Development workflow commands
650
+ ```
651
+
652
+ ### SOLID Principles Implementation
653
+
654
+ #### **1. Single Responsibility Principle**
655
+ Each class has one reason to change:
656
+ - `AndroidDevice`: Only handles device operations
657
+ - `StandardLogger`: Only handles logging
658
+ - `SensorTestRunner`: Only executes sensor tests
659
+
660
+ #### **2. Open/Closed Principle**
661
+ Extend functionality without modifying existing code:
662
+ ```python
663
+ # Add new test runner without changing existing ones
664
+ class CustomTestRunner(TestRunnerBase):
665
+ def can_run(self, test_type: str) -> bool:
666
+ return test_type == "custom"
667
+
668
+ def _execute_internal(self, context: ITestContext) -> TestResult:
669
+ # Custom test logic
670
+ return TestResult.SUCCESS
671
+
672
+ # Register in DI container
673
+ container.register_transient(ITestRunner, CustomTestRunner, "custom")
674
+ ```
675
+
676
+ #### **3. Liskov Substitution Principle**
677
+ All implementations follow their interfaces:
678
+ ```python
679
+ # Any ILogger implementation works seamlessly
680
+ logger: ILogger = StandardLogger() # or FilteredLogger()
681
+ logger.info("Test message") # Works the same way
682
+ ```
683
+
684
+ #### **4. Interface Segregation Principle**
685
+ Small, focused interfaces instead of large monolithic ones:
686
+ ```python
687
+ class ILogger(Protocol):
688
+ def debug(self, message: str) -> None: ...
689
+ def info(self, message: str) -> None: ...
690
+
691
+ class IConfigurationProvider(Protocol):
692
+ def get_value(self, key: str) -> ConfigValue: ...
693
+ def set_value(self, key: str, value: ConfigValue) -> None: ...
694
+ ```
695
+
696
+ #### **5. Dependency Inversion Principle**
697
+ Depend on abstractions, inject dependencies:
698
+ ```python
699
+ class SensorTestRunner(TestRunnerBase):
700
+ def __init__(self, logger: ILogger, device: IAndroidDevice):
701
+ super().__init__(logger)
702
+ self._device = device # Injected dependency
703
+ ```
704
+
705
+ ### Development Environment Setup
706
+
707
+ #### **Quick Development Setup**
708
+ ```bash
709
+ # 1. Clone and setup development environment
710
+ git clone https://github.com/trigdroid/trigdroid.git
711
+ cd trigdroid
712
+
713
+ # 2. Use our automated installer
714
+ ./scripts/install.sh --mode dev
715
+
716
+ # 3. Alternative: Manual setup
717
+ python -m venv venv
718
+ source venv/bin/activate # On Windows: venv\Scripts\activate
719
+ pip install -e ".[full,dev,test,docs]"
720
+
721
+ # 4. Build TypeScript hooks
722
+ cd frida-hooks
723
+ npm install && npm run build
724
+ cd ..
725
+
726
+ # 5. Verify installation
727
+ trigdroid --version
728
+ pytest tests/ -v
729
+ ```
730
+
731
+ #### **Development Workflow Commands**
732
+ ```bash
733
+ # Development commands (see Makefile)
734
+ make setup-dev-env # Initial environment setup
735
+ make dev-install # Install in development mode
736
+ make build # Build all components (Python + TypeScript)
737
+
738
+ # Code quality
739
+ make format # Auto-format with black + isort
740
+ make lint # Type checking with mypy + ruff
741
+ make check # Run all quality checks
742
+ make pre-commit # Format + lint + fast tests
743
+
744
+ # Testing
745
+ make test # All tests
746
+ make test-unit # Unit tests only
747
+ make test-integration # Integration tests
748
+ make test-fast # Skip slow tests
749
+ make coverage # Generate coverage report
750
+
751
+ # Build and distribution
752
+ make package # Build wheel/sdist
753
+ make hooks # Build TypeScript hooks only
754
+ make clean # Clean build artifacts
755
+ ```
756
+
757
+ ### Adding New Features
758
+
759
+ #### **1. Create New Test Runner**
760
+ ```python
761
+ # src2/TrigDroid_Infrastructure/test_runners/custom_test_runner.py
762
+ from ..interfaces import ITestRunner, ITestContext, TestResult, TestRunnerBase
763
+
764
+ class CustomTestRunner(TestRunnerBase):
765
+ """Example custom test runner."""
766
+
767
+ def can_run(self, test_type: str) -> bool:
768
+ return test_type == "custom"
769
+
770
+ def _execute_internal(self, context: ITestContext) -> TestResult:
771
+ # Implement your test logic
772
+ self._logger.info("Running custom test")
773
+
774
+ # Access dependencies through context
775
+ device = context.device
776
+ config = context.config
777
+ package = context.package_name
778
+
779
+ # Your test implementation
780
+ success = self._run_custom_test(device, package)
781
+
782
+ return TestResult.SUCCESS if success else TestResult.FAILURE
783
+
784
+ def _run_custom_test(self, device, package) -> bool:
785
+ # Custom test implementation
786
+ return True
787
+
788
+ # Register in dependency_injection.py
789
+ container.register_transient(ITestRunner, CustomTestRunner, "custom")
790
+ ```
791
+
792
+ #### **2. Add New Configuration Provider**
793
+ ```python
794
+ # src2/TrigDroid_Infrastructure/infrastructure/configuration.py
795
+ class DatabaseConfigProvider(ConfigurationProviderBase):
796
+ """Load configuration from database."""
797
+
798
+ def _load_configuration(self) -> Dict[str, ConfigValue]:
799
+ # Load from database
800
+ return {"custom_setting": "value"}
801
+
802
+ # Register in configure_container()
803
+ container.register_transient(IConfigurationProvider, DatabaseConfigProvider, "database")
804
+ ```
805
+
806
+ #### **3. Create TypeScript Frida Hook**
807
+ ```typescript
808
+ // frida-hooks/hooks/custom-hook.ts
809
+ import { HookManager } from '../utils';
810
+
811
+ export class CustomHook extends HookManager {
812
+ public hookCustomAPI(): void {
813
+ const SomeClass = Java.use("com.example.SomeClass");
814
+
815
+ SomeClass.someMethod.implementation = function(...args) {
816
+ console.log("[TrigDroid] Custom hook triggered");
817
+ this.logToChangelog("SomeClass.someMethod", "original", "hooked");
818
+
819
+ // Call original method
820
+ return this.someMethod.apply(this, args);
821
+ };
822
+ }
823
+ }
824
+
825
+ // Add to main.ts
826
+ import { CustomHook } from './hooks/custom-hook';
827
+
828
+ function main() {
829
+ const customHook = new CustomHook();
830
+ customHook.hookCustomAPI();
831
+ }
832
+ ```
833
+
834
+ ### TypeScript Hook Development
835
+
836
+ #### **Building Hooks**
837
+ ```bash
838
+ # Build hooks automatically during pip install
839
+ pip install -e .
840
+
841
+ # Manual hook building
842
+ cd frida-hooks
843
+ npm install # Install dependencies
844
+ npm run build # Compile TypeScript to JavaScript
845
+ npm run watch # Watch mode for development
846
+
847
+ # Using make
848
+ make hooks # Build hooks only
849
+ ```
850
+
851
+ #### **Hook Development Patterns**
852
+ ```typescript
853
+ // frida-hooks/hooks/android-example.ts
854
+ import { AndroidSensorType } from '../types';
855
+ import { HookUtils } from '../utils';
856
+
857
+ export class AndroidExampleHook {
858
+ private hookUtils: HookUtils;
859
+
860
+ constructor() {
861
+ this.hookUtils = new HookUtils();
862
+ }
863
+
864
+ public hookSensorManager(): void {
865
+ try {
866
+ const SensorManager = Java.use("android.hardware.SensorManager");
867
+
868
+ SensorManager.getDefaultSensor.implementation = function(type) {
869
+ const originalSensor = this.getDefaultSensor(type);
870
+
871
+ // Log the hook for debugging
872
+ this.hookUtils.logHookExecution("SensorManager.getDefaultSensor", {
873
+ sensorType: type,
874
+ timestamp: Date.now()
875
+ });
876
+
877
+ // Record change in changelog
878
+ this.hookUtils.writeToChangelog(
879
+ "SensorManager.getDefaultSensor",
880
+ "original_behavior",
881
+ "hooked_behavior",
882
+ `Sensor type ${type} intercepted`
883
+ );
884
+
885
+ return originalSensor;
886
+ };
887
+
888
+ } catch (error) {
889
+ console.error(`[TrigDroid] Hook failed: ${error}`);
890
+ }
891
+ }
892
+ }
893
+ ```
894
+
895
+ ### Dependency Injection Usage
896
+
897
+ #### **Registering Services**
898
+ ```python
899
+ # In configure_container()
900
+ from .my_service import MyService
901
+
902
+ # Singleton (same instance every time)
903
+ container.register_singleton(IMyService, MyService)
904
+
905
+ # Transient (new instance every time)
906
+ container.register_transient(IMyService, MyService)
907
+
908
+ # Factory function
909
+ container.register_singleton(IMyService, lambda: MyService(config="custom"))
910
+
911
+ # Specific instance
912
+ container.register_instance(IMyService, my_service_instance)
913
+ ```
914
+
915
+ #### **Resolving Dependencies**
916
+ ```python
917
+ # Method 1: Through container
918
+ container = configure_container()
919
+ logger = container.resolve(ILogger)
920
+
921
+ # Method 2: Service Locator (global access)
922
+ ServiceLocator.set_container(container)
923
+ logger = ServiceLocator.get_service(ILogger)
924
+
925
+ # Method 3: Constructor injection
926
+ @inject(logger=ILogger, device=IAndroidDevice)
927
+ class MyTestRunner:
928
+ def __init__(self, logger: ILogger, device: IAndroidDevice):
929
+ self._logger = logger
930
+ self._device = device
931
+ ```
932
+
933
+ ### Code Quality Standards
934
+
935
+ #### **Type Safety**
936
+ ```python
937
+ # Use type hints everywhere
938
+ def process_config(config: TestConfiguration) -> TestResult:
939
+ return TestResult.SUCCESS
940
+
941
+ # Use protocols for interfaces
942
+ class IMyService(Protocol):
943
+ def do_something(self, value: str) -> bool: ...
944
+
945
+ # Use enums for constants
946
+ class TestPhase(Enum):
947
+ SETUP = "setup"
948
+ EXECUTION = "execution"
949
+ TEARDOWN = "teardown"
950
+ ```
951
+
952
+ #### **Error Handling**
953
+ ```python
954
+ # Proper exception hierarchy
955
+ class TrigDroidException(Exception):
956
+ """Base exception for TrigDroid."""
957
+ pass
958
+
959
+ class DeviceConnectionError(TrigDroidException):
960
+ """Device connection failed."""
961
+ pass
962
+
963
+ # Use try/catch with specific exceptions
964
+ try:
965
+ device.connect()
966
+ except DeviceConnectionError as e:
967
+ logger.error(f"Failed to connect: {e}")
968
+ return TestResult.FAILURE
969
+ ```
970
+
971
+ #### **Logging Best Practices**
972
+ ```python
973
+ # Use structured logging
974
+ logger.info("Test started", extra={
975
+ "package": package_name,
976
+ "phase": "setup",
977
+ "timestamp": time.time()
978
+ })
979
+
980
+ # Use appropriate log levels
981
+ logger.debug("Detailed debugging info") # Development only
982
+ logger.info("Important user information") # Normal operation
983
+ logger.warning("Something might be wrong") # Potential issues
984
+ logger.error("Something failed") # Errors that don't stop execution
985
+ logger.critical("System cannot continue") # Fatal errors
986
+ ```
987
+
988
+ ### Testing Framework
989
+
990
+ #### **Unit Tests**
991
+ ```python
992
+ # tests/unit/test_sensor_runner.py
993
+ import pytest
994
+ from unittest.mock import Mock
995
+ from TrigDroid_Infrastructure.test_runners import SensorTestRunner
996
+ from TrigDroid_Infrastructure.interfaces import ILogger, ITestContext, TestResult
997
+
998
+ def test_sensor_runner_success():
999
+ # Arrange
1000
+ mock_logger = Mock(spec=ILogger)
1001
+ mock_context = Mock(spec=ITestContext)
1002
+ runner = SensorTestRunner(mock_logger)
1003
+
1004
+ # Act
1005
+ result = runner.execute(mock_context)
1006
+
1007
+ # Assert
1008
+ assert result == TestResult.SUCCESS
1009
+ mock_logger.info.assert_called()
1010
+ ```
1011
+
1012
+ #### **Integration Tests**
1013
+ ```python
1014
+ # tests/integration/test_full_workflow.py
1015
+ @pytest.mark.integration
1016
+ @pytest.mark.requires_device
1017
+ def test_full_testing_workflow(test_device):
1018
+ """Test complete workflow with real device."""
1019
+ config = TestConfiguration(
1020
+ package="com.example.testapp",
1021
+ acceleration=3,
1022
+ timeout=60
1023
+ )
1024
+
1025
+ with TrigDroidAPI() as api:
1026
+ api.configure(config)
1027
+ api.set_device(test_device.device_id)
1028
+ result = api.run_tests()
1029
+
1030
+ assert result.success
1031
+ assert result.total_tests > 0
1032
+ ```
1033
+
1034
+ ### Performance Optimization
1035
+
1036
+ #### **Fast Development Cycle**
1037
+ ```bash
1038
+ # Skip TypeScript build during development
1039
+ SKIP_HOOKS=1 pip install -e .
1040
+
1041
+ # Run only fast tests
1042
+ make test-fast
1043
+
1044
+ # Use parallel testing
1045
+ pytest -n auto tests/
1046
+
1047
+ # Quick format and lint check
1048
+ make pre-commit
1049
+ ```
1050
+
1051
+ #### **Build Optimization**
1052
+ ```bash
1053
+ # Build only what changed
1054
+ make hooks-if-changed
1055
+
1056
+ # Use cached dependencies
1057
+ pip install --cache-dir ~/.cache/pip -e .
1058
+
1059
+ # Parallel hook compilation
1060
+ cd frida-hooks && npm run build:parallel
1061
+ ```
1062
+
1063
+ ### Contributing Guidelines
1064
+
1065
+ #### **Code Standards**
1066
+ - **Readable**: Code should read like well-written prose
1067
+ - **Self-documenting**: Use clear variable and function names
1068
+ - **Consistent**: Follow established patterns
1069
+ - **Simple**: Prefer simple solutions over clever ones
1070
+ - **DRY**: Extract common functionality
1071
+ - **YAGNI**: Don't over-engineer
1072
+
1073
+ #### **Pull Request Process**
1074
+ ```bash
1075
+ # 1. Development workflow
1076
+ git checkout -b feature/new-feature
1077
+ make dev-install
1078
+ make pre-commit # Format + lint + fast tests
1079
+
1080
+ # 2. Before committing
1081
+ make test # Full test suite
1082
+ make build # Ensure everything builds
1083
+ make check # Final quality check
1084
+
1085
+ # 3. Create pull request
1086
+ git push origin feature/new-feature
1087
+ # Open PR with detailed description
1088
+ ```
1089
+
1090
+ #### **Code Review Checklist**
1091
+ - [ ] Follows SOLID principles
1092
+ - [ ] Has proper type annotations
1093
+ - [ ] Includes unit tests (>90% coverage)
1094
+ - [ ] Updated documentation if needed
1095
+ - [ ] No hardcoded values or magic numbers
1096
+ - [ ] Proper error handling
1097
+ - [ ] TypeScript hooks built successfully
1098
+ - [ ] All quality checks pass
1099
+
1100
+ ### Debugging and Troubleshooting
1101
+
1102
+ #### **Development Issues**
1103
+ ```bash
1104
+ # TypeScript compilation fails
1105
+ cd frida-hooks
1106
+ rm -rf node_modules dist
1107
+ npm install && npm run build
1108
+
1109
+ # Python import errors
1110
+ pip uninstall trigdroid
1111
+ pip install -e ".[full]"
1112
+
1113
+ # Dependency injection issues
1114
+ # Check container registration in configure_container()
1115
+ container.has_service(IMyService) # Should return True
1116
+
1117
+ # Test runner not found
1118
+ # Verify registration with correct name
1119
+ container.resolve(ITestRunner, "my_runner")
1120
+ ```
1121
+
1122
+ #### **Runtime Debugging**
1123
+ ```python
1124
+ # Enable debug logging
1125
+ import logging
1126
+ logging.basicConfig(level=logging.DEBUG)
1127
+
1128
+ # Use dependency injection debug mode
1129
+ container = configure_container()
1130
+ if container.has_service(IMyService):
1131
+ service = container.resolve(IMyService)
1132
+
1133
+ # Check service registration
1134
+ print(f"Registered services: {list(container._services.keys())}")
1135
+ ```
1136
+
1137
+ This development guide provides comprehensive information for contributing to and extending TrigDroid's modern architecture.
1138
+
1139
+ ## πŸ™ Acknowledgments
1140
+
1141
+ - Built on the foundation of the [Evadroid](https://github.com/evadroid/evadroid) project
1142
+ - Powered by [Frida](https://frida.re/) dynamic instrumentation toolkit
1143
+ - Uses [Click](https://click.palletsprojects.com/) for command-line interface
1144
+ - Enhanced with [Rich](https://rich.readthedocs.io/) for beautiful terminal output
1145
+
1146
+ ---
1147
+
1148
+ **Made with ❀️ for the security research community**
1149
+
1150
+ *TrigDroid helps security researchers and malware analysts understand and detect sophisticated Android threats through intelligent payload triggering and environmental manipulation.*
@@ -0,0 +1,5 @@
1
+ trigdroid-2.0.0.dist-info/METADATA,sha256=9pSwOrwb5-IjdLy5rL5b6lUfTeEJvlUOc35WTM6Xg9E,35985
2
+ trigdroid-2.0.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
3
+ trigdroid-2.0.0.dist-info/entry_points.txt,sha256=X1hWIdaKfwFXxsMMbjkX589do-IJjuVDshe7IHOT3Kk,340
4
+ trigdroid-2.0.0.dist-info/licenses/LICENSE,sha256=1h287Q1gIUpNY2h3QaCowIlE6DoiBmw3WE6wn6bClkM,1065
5
+ trigdroid-2.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,10 @@
1
+ [console_scripts]
2
+ trigdroid = trigdroid.cli.main:main
3
+ trigdroid-legacy = TrigDroid.__main__:main
4
+
5
+ [trigdroid.hooks]
6
+ typescript = frida-hooks.main:main
7
+
8
+ [trigdroid.test_runners]
9
+ frida = TrigDroid_Infrastructure.test_runners.frida_test_runner:FridaTestRunner
10
+ sensor = TrigDroid_Infrastructure.test_runners.sensor_test_runner:SensorTestRunner
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 FKIE-CAD
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.