packerscope 0.1.1__tar.gz
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.
- packerscope-0.1.1/LICENSE +21 -0
- packerscope-0.1.1/PKG-INFO +157 -0
- packerscope-0.1.1/README.md +107 -0
- packerscope-0.1.1/packerscope/__init__.py +4 -0
- packerscope-0.1.1/packerscope/cli.py +266 -0
- packerscope-0.1.1/packerscope/config.py +278 -0
- packerscope-0.1.1/packerscope/constants.py +156 -0
- packerscope-0.1.1/packerscope/context.py +326 -0
- packerscope-0.1.1/packerscope/core/__init__.py +1 -0
- packerscope-0.1.1/packerscope/core/enums.py +360 -0
- packerscope-0.1.1/packerscope/core/interfaces.py +300 -0
- packerscope-0.1.1/packerscope/core/models.py +1101 -0
- packerscope-0.1.1/packerscope/detectors/__init__.py +37 -0
- packerscope-0.1.1/packerscope/detectors/entropy_detector.py +213 -0
- packerscope-0.1.1/packerscope/detectors/entrypoint_detector.py +202 -0
- packerscope-0.1.1/packerscope/detectors/heuristic_detector.py +529 -0
- packerscope-0.1.1/packerscope/detectors/iat_detector.py +183 -0
- packerscope-0.1.1/packerscope/detectors/pe_structure_detector.py +266 -0
- packerscope-0.1.1/packerscope/detectors/section_detector.py +181 -0
- packerscope-0.1.1/packerscope/detectors/signature_detector.py +228 -0
- packerscope-0.1.1/packerscope/detectors/yara_detector.py +239 -0
- packerscope-0.1.1/packerscope/exceptions.py +203 -0
- packerscope-0.1.1/packerscope/orchestrator.py +269 -0
- packerscope-0.1.1/packerscope/plugin_manager.py +246 -0
- packerscope-0.1.1/packerscope/reporters/__init__.py +8 -0
- packerscope-0.1.1/packerscope/reporters/csv_reporter.py +89 -0
- packerscope-0.1.1/packerscope/reporters/html_reporter.py +160 -0
- packerscope-0.1.1/packerscope/reporters/json_reporter.py +41 -0
- packerscope-0.1.1/packerscope/reporters/markdown_reporter.py +192 -0
- packerscope-0.1.1/packerscope/reporters/templates/__init__.py +1 -0
- packerscope-0.1.1/packerscope/signatures/__init__.py +1 -0
- packerscope-0.1.1/packerscope/signatures/peid_parser.py +161 -0
- packerscope-0.1.1/packerscope/signatures/yara_rules/__init__.py +1 -0
- packerscope-0.1.1/packerscope/unpackers/__init__.py +7 -0
- packerscope-0.1.1/packerscope/unpackers/dynamic_unpacker.py +149 -0
- packerscope-0.1.1/packerscope/unpackers/generic_unpacker.py +140 -0
- packerscope-0.1.1/packerscope/unpackers/upx_unpacker.py +125 -0
- packerscope-0.1.1/packerscope/utils/__init__.py +18 -0
- packerscope-0.1.1/packerscope/utils/concurrency.py +133 -0
- packerscope-0.1.1/packerscope/utils/disasm.py +328 -0
- packerscope-0.1.1/packerscope/utils/entropy.py +227 -0
- packerscope-0.1.1/packerscope/utils/hasher.py +174 -0
- packerscope-0.1.1/packerscope/utils/logger.py +169 -0
- packerscope-0.1.1/packerscope/utils/pe_parser.py +451 -0
- packerscope-0.1.1/packerscope/verification/__init__.py +1 -0
- packerscope-0.1.1/packerscope/verification/verifier.py +160 -0
- packerscope-0.1.1/packerscope.egg-info/PKG-INFO +157 -0
- packerscope-0.1.1/packerscope.egg-info/SOURCES.txt +52 -0
- packerscope-0.1.1/packerscope.egg-info/dependency_links.txt +1 -0
- packerscope-0.1.1/packerscope.egg-info/entry_points.txt +2 -0
- packerscope-0.1.1/packerscope.egg-info/requires.txt +33 -0
- packerscope-0.1.1/packerscope.egg-info/top_level.txt +1 -0
- packerscope-0.1.1/pyproject.toml +99 -0
- packerscope-0.1.1/setup.cfg +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Salman Mallah
|
|
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.
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: packerscope
|
|
3
|
+
Version: 0.1.1
|
|
4
|
+
Summary: Automatic packer detection, classification, and unpacking framework for Windows PE files.
|
|
5
|
+
Author-email: Salman Mallah <mallahsalman06@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/salmanmallah/packerscope
|
|
8
|
+
Project-URL: Repository, https://github.com/salmanmallah/packerscope
|
|
9
|
+
Project-URL: Issues, https://github.com/salmanmallah/packerscope/issues
|
|
10
|
+
Keywords: packer,unpacker,pe,malware,reverse-engineering,binary-analysis
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Information Technology
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: Security
|
|
19
|
+
Classifier: Topic :: Software Development :: Disassemblers
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.13
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: pefile>=2024.8.26
|
|
25
|
+
Requires-Dist: pydantic>=2.9
|
|
26
|
+
Requires-Dist: pydantic-settings>=2.5
|
|
27
|
+
Requires-Dist: click>=8.1
|
|
28
|
+
Requires-Dist: rich>=13.9
|
|
29
|
+
Requires-Dist: structlog>=24.4
|
|
30
|
+
Requires-Dist: pyyaml>=6.0
|
|
31
|
+
Requires-Dist: colorama>=0.4.6; sys_platform == "win32"
|
|
32
|
+
Provides-Extra: analysis
|
|
33
|
+
Requires-Dist: lief>=0.15; extra == "analysis"
|
|
34
|
+
Requires-Dist: capstone>=5.0; extra == "analysis"
|
|
35
|
+
Provides-Extra: yara
|
|
36
|
+
Requires-Dist: yara-python>=4.5; extra == "yara"
|
|
37
|
+
Provides-Extra: dynamic
|
|
38
|
+
Requires-Dist: frida>=16.5; extra == "dynamic"
|
|
39
|
+
Requires-Dist: qiling>=1.4; extra == "dynamic"
|
|
40
|
+
Provides-Extra: hashing
|
|
41
|
+
Requires-Dist: ppdeep>=20200723; extra == "hashing"
|
|
42
|
+
Provides-Extra: dev
|
|
43
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
44
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
45
|
+
Requires-Dist: pytest-mock>=3.14; extra == "dev"
|
|
46
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
47
|
+
Provides-Extra: all
|
|
48
|
+
Requires-Dist: packerscope[analysis,dynamic,hashing,yara]; extra == "all"
|
|
49
|
+
Dynamic: license-file
|
|
50
|
+
|
|
51
|
+
# PackerScope
|
|
52
|
+
|
|
53
|
+
**PackerScope** is a production-quality Python framework for automatic packer detection, classification, and unpacking of Windows PE files. It is designed for defensive security research, malware analysis labs, and educational purposes.
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
- **Multi-layered Detection Pipeline:**
|
|
58
|
+
- **Entropy Analysis:** Measures Shannon entropy across the whole file, sections, and via a sliding window.
|
|
59
|
+
- **Section Analysis:** Detects anomalous section names (e.g., `UPX0`, `.vmp0`), extreme virtual-to-raw size ratios, and abnormal permissions (RWX).
|
|
60
|
+
- **IAT Analysis:** Analyzes Import Address Table sparseness and suspicious API usage (e.g., `LoadLibrary`, `VirtualAlloc`).
|
|
61
|
+
- **Entry Point Analysis:** Disassembles entry point instructions using Capstone to detect jump chains, NOP sleds, and push/ret trampolines.
|
|
62
|
+
- **Structure Analysis:** Identifies anomalies in PE optional and file headers, missing directories, or invalid timestamps.
|
|
63
|
+
- **Signature Matching:** Fast byte-pattern matching using PEiD-style `userdb.txt` databases.
|
|
64
|
+
- **YARA Scanning:** Deep static analysis utilizing community or custom YARA rules.
|
|
65
|
+
- **Heuristics Engine:** Aggregates weak signals across all modules to form a high-confidence final verdict.
|
|
66
|
+
|
|
67
|
+
- **Automated Unpacking (Pluggable):**
|
|
68
|
+
- **UPXUnpacker:** Native fast decompression using the `upx` system binary.
|
|
69
|
+
- **GenericStaticUnpacker:** Template for static algorithmic decompression (aPLib, LZMA).
|
|
70
|
+
- **DynamicUnpacker:** Template for dynamic unpacking via emulation (Qiling/Unicorn) or instrumentation (Frida).
|
|
71
|
+
|
|
72
|
+
- **Verification Subsystem:** Automatically verifies the success of an unpacking attempt by checking PE validity, entropy reduction, IAT restoration, and section normalization.
|
|
73
|
+
|
|
74
|
+
- **Reporting:** Generates comprehensive analysis reports in JSON, CSV, Markdown, and HTML formats.
|
|
75
|
+
|
|
76
|
+
- **Developer-Friendly:** Written in modern Python 3.13+, completely type-hinted, and modular using Pydantic v2 data models and the Blackboard design pattern (`PEContext`).
|
|
77
|
+
|
|
78
|
+
## Requirements
|
|
79
|
+
|
|
80
|
+
- Python 3.13+
|
|
81
|
+
- Windows (Primary target OS, though the framework runs on Linux/macOS)
|
|
82
|
+
- Recommended external tools: `upx`, Capstone
|
|
83
|
+
|
|
84
|
+
## Installation
|
|
85
|
+
|
|
86
|
+
1. Clone the repository or navigate to the framework directory.
|
|
87
|
+
2. Install the required dependencies:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
pip install -r requirements.txt
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
*(Optional)* For advanced features like disassembly and dynamic analysis, you can install optional dependency groups defined in `pyproject.toml`.
|
|
94
|
+
|
|
95
|
+
## Usage
|
|
96
|
+
|
|
97
|
+
PackerScope provides an easy-to-use Command Line Interface (CLI):
|
|
98
|
+
|
|
99
|
+
### Analyze a Single File
|
|
100
|
+
```bash
|
|
101
|
+
python -m packerscope.cli scan samples/malware.exe --format json,html --output results/
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Batch Analyze a Directory
|
|
105
|
+
```bash
|
|
106
|
+
python -m packerscope.cli batch samples/ --workers 8 --format csv
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### View Quick PE Information
|
|
110
|
+
```bash
|
|
111
|
+
python -m packerscope.cli info samples/malware.exe
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Architecture
|
|
115
|
+
|
|
116
|
+
1. **Orchestrator:** Manages the entire pipeline (Initialization → Detection → Verdict → Unpack → Verify → Report).
|
|
117
|
+
2. **PEContext:** The central Blackboard state object. Parsed PE data and detector results are shared here.
|
|
118
|
+
3. **Plugin Manager:** Dynamically discovers and loads detectors, unpackers, and reporters from the framework and external directories.
|
|
119
|
+
4. **Detectors:** Implement `BaseDetector`. Executed in priority order.
|
|
120
|
+
5. **Unpackers:** Implement `BaseUnpacker`. Selected dynamically based on the final packer verdict.
|
|
121
|
+
|
|
122
|
+
## Project Structure
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
packer_identifier_framework/
|
|
126
|
+
├── packerscope/
|
|
127
|
+
│ ├── cli.py # Command-line interface
|
|
128
|
+
│ ├── config.py # Central configuration (Pydantic Settings)
|
|
129
|
+
│ ├── constants.py # Thresholds and heuristics constants
|
|
130
|
+
│ ├── context.py # PEContext (Blackboard state)
|
|
131
|
+
│ ├── exceptions.py # Custom exceptions
|
|
132
|
+
│ ├── orchestrator.py # Pipeline execution logic
|
|
133
|
+
│ ├── plugin_manager.py # Plugin discovery and registration
|
|
134
|
+
│ ├── core/ # Interfaces, Enums, and Pydantic Models
|
|
135
|
+
│ ├── detectors/ # Detection modules (Entropy, IAT, YARA, etc.)
|
|
136
|
+
│ ├── reporters/ # Output generators (JSON, CSV, HTML, MD)
|
|
137
|
+
│ ├── signatures/ # PEiD signature parsing
|
|
138
|
+
│ ├── unpackers/ # Unpacking strategies
|
|
139
|
+
│ ├── utils/ # Helpers (disasm, entropy, hasher, pe_parser)
|
|
140
|
+
│ └── verification/ # Unpack verification logic
|
|
141
|
+
├── plugins/ # Directory for custom third-party plugins
|
|
142
|
+
├── tests/ # Unit and Integration tests
|
|
143
|
+
├── pyproject.toml # Project metadata and dependencies
|
|
144
|
+
└── requirements.txt # Flat dependency list
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Running Tests
|
|
148
|
+
|
|
149
|
+
PackerScope comes with a comprehensive test suite covering core models, utility functions, detectors, config, and orchestrator integration.
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
pytest tests/ -v
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Disclaimer
|
|
156
|
+
|
|
157
|
+
**Educational and Research Purposes Only.** This framework is intended strictly for defensive security research, malware analysis, and educational use within isolated malware analysis lab environments. Do not use this tool on systems or files you do not have permission to analyze.
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# PackerScope
|
|
2
|
+
|
|
3
|
+
**PackerScope** is a production-quality Python framework for automatic packer detection, classification, and unpacking of Windows PE files. It is designed for defensive security research, malware analysis labs, and educational purposes.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Multi-layered Detection Pipeline:**
|
|
8
|
+
- **Entropy Analysis:** Measures Shannon entropy across the whole file, sections, and via a sliding window.
|
|
9
|
+
- **Section Analysis:** Detects anomalous section names (e.g., `UPX0`, `.vmp0`), extreme virtual-to-raw size ratios, and abnormal permissions (RWX).
|
|
10
|
+
- **IAT Analysis:** Analyzes Import Address Table sparseness and suspicious API usage (e.g., `LoadLibrary`, `VirtualAlloc`).
|
|
11
|
+
- **Entry Point Analysis:** Disassembles entry point instructions using Capstone to detect jump chains, NOP sleds, and push/ret trampolines.
|
|
12
|
+
- **Structure Analysis:** Identifies anomalies in PE optional and file headers, missing directories, or invalid timestamps.
|
|
13
|
+
- **Signature Matching:** Fast byte-pattern matching using PEiD-style `userdb.txt` databases.
|
|
14
|
+
- **YARA Scanning:** Deep static analysis utilizing community or custom YARA rules.
|
|
15
|
+
- **Heuristics Engine:** Aggregates weak signals across all modules to form a high-confidence final verdict.
|
|
16
|
+
|
|
17
|
+
- **Automated Unpacking (Pluggable):**
|
|
18
|
+
- **UPXUnpacker:** Native fast decompression using the `upx` system binary.
|
|
19
|
+
- **GenericStaticUnpacker:** Template for static algorithmic decompression (aPLib, LZMA).
|
|
20
|
+
- **DynamicUnpacker:** Template for dynamic unpacking via emulation (Qiling/Unicorn) or instrumentation (Frida).
|
|
21
|
+
|
|
22
|
+
- **Verification Subsystem:** Automatically verifies the success of an unpacking attempt by checking PE validity, entropy reduction, IAT restoration, and section normalization.
|
|
23
|
+
|
|
24
|
+
- **Reporting:** Generates comprehensive analysis reports in JSON, CSV, Markdown, and HTML formats.
|
|
25
|
+
|
|
26
|
+
- **Developer-Friendly:** Written in modern Python 3.13+, completely type-hinted, and modular using Pydantic v2 data models and the Blackboard design pattern (`PEContext`).
|
|
27
|
+
|
|
28
|
+
## Requirements
|
|
29
|
+
|
|
30
|
+
- Python 3.13+
|
|
31
|
+
- Windows (Primary target OS, though the framework runs on Linux/macOS)
|
|
32
|
+
- Recommended external tools: `upx`, Capstone
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
1. Clone the repository or navigate to the framework directory.
|
|
37
|
+
2. Install the required dependencies:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install -r requirements.txt
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
*(Optional)* For advanced features like disassembly and dynamic analysis, you can install optional dependency groups defined in `pyproject.toml`.
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
PackerScope provides an easy-to-use Command Line Interface (CLI):
|
|
48
|
+
|
|
49
|
+
### Analyze a Single File
|
|
50
|
+
```bash
|
|
51
|
+
python -m packerscope.cli scan samples/malware.exe --format json,html --output results/
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Batch Analyze a Directory
|
|
55
|
+
```bash
|
|
56
|
+
python -m packerscope.cli batch samples/ --workers 8 --format csv
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### View Quick PE Information
|
|
60
|
+
```bash
|
|
61
|
+
python -m packerscope.cli info samples/malware.exe
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Architecture
|
|
65
|
+
|
|
66
|
+
1. **Orchestrator:** Manages the entire pipeline (Initialization → Detection → Verdict → Unpack → Verify → Report).
|
|
67
|
+
2. **PEContext:** The central Blackboard state object. Parsed PE data and detector results are shared here.
|
|
68
|
+
3. **Plugin Manager:** Dynamically discovers and loads detectors, unpackers, and reporters from the framework and external directories.
|
|
69
|
+
4. **Detectors:** Implement `BaseDetector`. Executed in priority order.
|
|
70
|
+
5. **Unpackers:** Implement `BaseUnpacker`. Selected dynamically based on the final packer verdict.
|
|
71
|
+
|
|
72
|
+
## Project Structure
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
packer_identifier_framework/
|
|
76
|
+
├── packerscope/
|
|
77
|
+
│ ├── cli.py # Command-line interface
|
|
78
|
+
│ ├── config.py # Central configuration (Pydantic Settings)
|
|
79
|
+
│ ├── constants.py # Thresholds and heuristics constants
|
|
80
|
+
│ ├── context.py # PEContext (Blackboard state)
|
|
81
|
+
│ ├── exceptions.py # Custom exceptions
|
|
82
|
+
│ ├── orchestrator.py # Pipeline execution logic
|
|
83
|
+
│ ├── plugin_manager.py # Plugin discovery and registration
|
|
84
|
+
│ ├── core/ # Interfaces, Enums, and Pydantic Models
|
|
85
|
+
│ ├── detectors/ # Detection modules (Entropy, IAT, YARA, etc.)
|
|
86
|
+
│ ├── reporters/ # Output generators (JSON, CSV, HTML, MD)
|
|
87
|
+
│ ├── signatures/ # PEiD signature parsing
|
|
88
|
+
│ ├── unpackers/ # Unpacking strategies
|
|
89
|
+
│ ├── utils/ # Helpers (disasm, entropy, hasher, pe_parser)
|
|
90
|
+
│ └── verification/ # Unpack verification logic
|
|
91
|
+
├── plugins/ # Directory for custom third-party plugins
|
|
92
|
+
├── tests/ # Unit and Integration tests
|
|
93
|
+
├── pyproject.toml # Project metadata and dependencies
|
|
94
|
+
└── requirements.txt # Flat dependency list
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Running Tests
|
|
98
|
+
|
|
99
|
+
PackerScope comes with a comprehensive test suite covering core models, utility functions, detectors, config, and orchestrator integration.
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pytest tests/ -v
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Disclaimer
|
|
106
|
+
|
|
107
|
+
**Educational and Research Purposes Only.** This framework is intended strictly for defensive security research, malware analysis, and educational use within isolated malware analysis lab environments. Do not use this tool on systems or files you do not have permission to analyze.
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""PackerScope command-line interface.
|
|
2
|
+
|
|
3
|
+
Provides the ``packerscope`` CLI for single file analysis and batch
|
|
4
|
+
scanning.
|
|
5
|
+
|
|
6
|
+
Usage:
|
|
7
|
+
packerscope scan sample.exe
|
|
8
|
+
packerscope scan --format json,html samples/
|
|
9
|
+
packerscope batch samples/ --workers 8
|
|
10
|
+
packerscope info sample.exe
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import sys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
import click
|
|
19
|
+
from rich.console import Console
|
|
20
|
+
from rich.panel import Panel
|
|
21
|
+
from rich.table import Table
|
|
22
|
+
from rich.text import Text
|
|
23
|
+
|
|
24
|
+
from packerscope import __version__
|
|
25
|
+
from packerscope.config import Config
|
|
26
|
+
from packerscope.orchestrator import Orchestrator
|
|
27
|
+
from packerscope.utils.logger import setup_logging
|
|
28
|
+
|
|
29
|
+
console = Console()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@click.group()
|
|
33
|
+
@click.version_option(__version__, prog_name="PackerScope")
|
|
34
|
+
@click.option("--verbose", "-v", is_flag=True, help="Enable verbose logging")
|
|
35
|
+
@click.option("--debug", is_flag=True, help="Enable debug logging")
|
|
36
|
+
@click.option("--json-log", is_flag=True, help="Output logs in JSON format")
|
|
37
|
+
@click.option("--log-file", type=click.Path(), default=None, help="Log to file")
|
|
38
|
+
@click.pass_context
|
|
39
|
+
def main(ctx: click.Context, verbose: bool, debug: bool, json_log: bool, log_file: str | None) -> None:
|
|
40
|
+
"""PackerScope — Automatic packer detection, classification & unpacking."""
|
|
41
|
+
level = "DEBUG" if debug else ("INFO" if verbose else "WARNING")
|
|
42
|
+
log_path = Path(log_file) if log_file else None
|
|
43
|
+
setup_logging(level=level, log_file=log_path, json_output=json_log)
|
|
44
|
+
ctx.ensure_object(dict)
|
|
45
|
+
ctx.obj["config"] = Config()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@main.command()
|
|
49
|
+
@click.argument("target", type=click.Path(exists=True))
|
|
50
|
+
@click.option("--format", "-f", "formats", default="json", help="Report formats (comma-separated: json,csv,md,html)")
|
|
51
|
+
@click.option("--output", "-o", type=click.Path(), default=None, help="Output directory")
|
|
52
|
+
@click.option("--no-unpack", is_flag=True, help="Skip unpacking step")
|
|
53
|
+
@click.option("--no-verify", is_flag=True, help="Skip unpacking verification")
|
|
54
|
+
@click.pass_context
|
|
55
|
+
def scan(
|
|
56
|
+
ctx: click.Context,
|
|
57
|
+
target: str,
|
|
58
|
+
formats: str,
|
|
59
|
+
output: str | None,
|
|
60
|
+
no_unpack: bool,
|
|
61
|
+
no_verify: bool,
|
|
62
|
+
) -> None:
|
|
63
|
+
"""Analyze a PE file or directory for packer detection."""
|
|
64
|
+
config: Config = ctx.obj["config"]
|
|
65
|
+
|
|
66
|
+
if output:
|
|
67
|
+
config.output_dir = Path(output)
|
|
68
|
+
if no_unpack:
|
|
69
|
+
config.enable_unpack = False
|
|
70
|
+
if no_verify:
|
|
71
|
+
config.enable_verification = False
|
|
72
|
+
|
|
73
|
+
# Parse report formats
|
|
74
|
+
from packerscope.core.enums import ReportFormat
|
|
75
|
+
fmt_map = {"json": ReportFormat.JSON, "csv": ReportFormat.CSV, "md": ReportFormat.MARKDOWN, "html": ReportFormat.HTML}
|
|
76
|
+
config.report_formats = [fmt_map[f.strip()] for f in formats.split(",") if f.strip() in fmt_map]
|
|
77
|
+
|
|
78
|
+
target_path = Path(target)
|
|
79
|
+
orch = Orchestrator(config)
|
|
80
|
+
|
|
81
|
+
if target_path.is_file():
|
|
82
|
+
_analyze_single(orch, target_path)
|
|
83
|
+
elif target_path.is_dir():
|
|
84
|
+
_analyze_directory(orch, target_path, config)
|
|
85
|
+
else:
|
|
86
|
+
console.print(f"[red]Error: {target} is not a valid file or directory[/red]")
|
|
87
|
+
sys.exit(1)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@main.command()
|
|
91
|
+
@click.argument("directory", type=click.Path(exists=True, file_okay=False))
|
|
92
|
+
@click.option("--workers", "-w", type=int, default=None, help="Number of concurrent workers")
|
|
93
|
+
@click.option("--format", "-f", "formats", default="json,csv", help="Report formats")
|
|
94
|
+
@click.option("--output", "-o", type=click.Path(), default=None, help="Output directory")
|
|
95
|
+
@click.pass_context
|
|
96
|
+
def batch(
|
|
97
|
+
ctx: click.Context,
|
|
98
|
+
directory: str,
|
|
99
|
+
workers: int | None,
|
|
100
|
+
formats: str,
|
|
101
|
+
output: str | None,
|
|
102
|
+
) -> None:
|
|
103
|
+
"""Batch-analyze all PE files in a directory."""
|
|
104
|
+
config: Config = ctx.obj["config"]
|
|
105
|
+
if output:
|
|
106
|
+
config.output_dir = Path(output)
|
|
107
|
+
|
|
108
|
+
from packerscope.core.enums import ReportFormat
|
|
109
|
+
fmt_map = {"json": ReportFormat.JSON, "csv": ReportFormat.CSV, "md": ReportFormat.MARKDOWN, "html": ReportFormat.HTML}
|
|
110
|
+
config.report_formats = [fmt_map[f.strip()] for f in formats.split(",") if f.strip() in fmt_map]
|
|
111
|
+
|
|
112
|
+
dir_path = Path(directory)
|
|
113
|
+
pe_files = _find_pe_files(dir_path)
|
|
114
|
+
|
|
115
|
+
if not pe_files:
|
|
116
|
+
console.print(f"[yellow]No PE files found in {directory}[/yellow]")
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
console.print(f"[cyan]Found {len(pe_files)} PE file(s) to analyze[/cyan]")
|
|
120
|
+
orch = Orchestrator(config)
|
|
121
|
+
orch.initialize()
|
|
122
|
+
|
|
123
|
+
reports = orch.analyze_batch(pe_files, max_workers=workers)
|
|
124
|
+
|
|
125
|
+
# Summary table
|
|
126
|
+
table = Table(title="Batch Analysis Results", show_lines=True)
|
|
127
|
+
table.add_column("File", style="cyan")
|
|
128
|
+
table.add_column("Packed", justify="center")
|
|
129
|
+
table.add_column("Packer", style="yellow")
|
|
130
|
+
table.add_column("Confidence")
|
|
131
|
+
table.add_column("Duration")
|
|
132
|
+
|
|
133
|
+
for r in reports:
|
|
134
|
+
packed_str = "[red]YES[/red]" if r.verdict.is_packed else "[green]NO[/green]"
|
|
135
|
+
table.add_row(
|
|
136
|
+
r.file_name,
|
|
137
|
+
packed_str,
|
|
138
|
+
r.verdict.packer.value,
|
|
139
|
+
f"{r.verdict.confidence:.1%}",
|
|
140
|
+
f"{r.analysis_duration_seconds:.2f}s",
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
console.print(table)
|
|
144
|
+
packed_count = sum(1 for r in reports if r.verdict.is_packed)
|
|
145
|
+
console.print(f"\n[bold]Summary:[/bold] {packed_count}/{len(reports)} files detected as packed")
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@main.command()
|
|
149
|
+
@click.argument("file", type=click.Path(exists=True))
|
|
150
|
+
@click.pass_context
|
|
151
|
+
def info(ctx: click.Context, file: str) -> None:
|
|
152
|
+
"""Show quick PE file information (no detection pipeline)."""
|
|
153
|
+
from packerscope.context import PEContext
|
|
154
|
+
|
|
155
|
+
file_path = Path(file)
|
|
156
|
+
with PEContext(file_path) as pctx:
|
|
157
|
+
pctx.initialize()
|
|
158
|
+
|
|
159
|
+
console.print(Panel(f"[bold cyan]{file_path.name}[/bold cyan]", subtitle=f"{file_path}"))
|
|
160
|
+
|
|
161
|
+
if pctx.metadata:
|
|
162
|
+
m = pctx.metadata
|
|
163
|
+
table = Table(title="File Metadata")
|
|
164
|
+
table.add_column("Property", style="cyan")
|
|
165
|
+
table.add_column("Value")
|
|
166
|
+
table.add_row("MD5", m.md5)
|
|
167
|
+
table.add_row("SHA256", m.sha256)
|
|
168
|
+
table.add_row("Imphash", m.imphash or "N/A")
|
|
169
|
+
table.add_row("Size", f"{m.file_size:,} bytes")
|
|
170
|
+
table.add_row("Machine", m.machine_type)
|
|
171
|
+
console.print(table)
|
|
172
|
+
|
|
173
|
+
if pctx.pe and pctx.pe.is_valid:
|
|
174
|
+
pe = pctx.pe
|
|
175
|
+
sec_table = Table(title="Sections")
|
|
176
|
+
sec_table.add_column("Name")
|
|
177
|
+
sec_table.add_column("VSize", justify="right")
|
|
178
|
+
sec_table.add_column("RSize", justify="right")
|
|
179
|
+
sec_table.add_column("Entropy", justify="right")
|
|
180
|
+
from packerscope.utils.entropy import calculate_entropy
|
|
181
|
+
for sec in pe.sections:
|
|
182
|
+
data = sec.data if sec.data else b""
|
|
183
|
+
ent = calculate_entropy(data) if data else 0.0
|
|
184
|
+
sec_table.add_row(
|
|
185
|
+
sec.name, f"{sec.virtual_size:,}",
|
|
186
|
+
f"{sec.raw_size:,}", f"{ent:.4f}",
|
|
187
|
+
)
|
|
188
|
+
console.print(sec_table)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _analyze_single(orch: Orchestrator, file_path: Path) -> None:
|
|
192
|
+
"""Analyze a single file and print results."""
|
|
193
|
+
try:
|
|
194
|
+
report = orch.analyze(file_path)
|
|
195
|
+
_print_report(report)
|
|
196
|
+
except Exception as e:
|
|
197
|
+
console.print(f"[red]Analysis failed: {e}[/red]")
|
|
198
|
+
sys.exit(1)
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _analyze_directory(orch: Orchestrator, dir_path: Path, config: Config) -> None:
|
|
202
|
+
"""Analyze all PE files in a directory."""
|
|
203
|
+
pe_files = _find_pe_files(dir_path)
|
|
204
|
+
if not pe_files:
|
|
205
|
+
console.print(f"[yellow]No PE files found in {dir_path}[/yellow]")
|
|
206
|
+
return
|
|
207
|
+
|
|
208
|
+
console.print(f"[cyan]Found {len(pe_files)} PE file(s)[/cyan]")
|
|
209
|
+
orch.initialize()
|
|
210
|
+
for f in pe_files:
|
|
211
|
+
try:
|
|
212
|
+
report = orch.analyze(f)
|
|
213
|
+
_print_report_summary(report)
|
|
214
|
+
except Exception as e:
|
|
215
|
+
console.print(f"[red]{f.name}: Error — {e}[/red]")
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _find_pe_files(directory: Path) -> list[Path]:
|
|
219
|
+
"""Recursively find PE files in a directory."""
|
|
220
|
+
extensions = {".exe", ".dll", ".sys", ".drv", ".ocx", ".scr"}
|
|
221
|
+
files = []
|
|
222
|
+
for ext in extensions:
|
|
223
|
+
files.extend(directory.rglob(f"*{ext}"))
|
|
224
|
+
return sorted(files)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _print_report(report) -> None:
|
|
228
|
+
"""Print a detailed Rich-formatted analysis report."""
|
|
229
|
+
v = report.verdict
|
|
230
|
+
color = "red" if v.is_packed else "green"
|
|
231
|
+
status = "PACKED" if v.is_packed else "NOT PACKED"
|
|
232
|
+
|
|
233
|
+
console.print(Panel(
|
|
234
|
+
Text.from_markup(f"[bold {color}]{status}[/bold {color}]"),
|
|
235
|
+
title=f"{report.file_name}",
|
|
236
|
+
subtitle=f"Confidence: {v.confidence:.1%} ({v.confidence_level.value})",
|
|
237
|
+
))
|
|
238
|
+
|
|
239
|
+
if v.is_packed:
|
|
240
|
+
console.print(f" Packer: [yellow bold]{v.packer.value}[/yellow bold]")
|
|
241
|
+
|
|
242
|
+
if v.reasons:
|
|
243
|
+
console.print("\n [bold]Reasons:[/bold]")
|
|
244
|
+
for r in v.reasons[:8]:
|
|
245
|
+
console.print(f" • {r}")
|
|
246
|
+
|
|
247
|
+
console.print(f"\n MD5: [dim]{report.metadata.md5}[/dim]")
|
|
248
|
+
console.print(f" SHA256: [dim]{report.metadata.sha256}[/dim]")
|
|
249
|
+
console.print(f" Size: {report.metadata.file_size:,} bytes")
|
|
250
|
+
console.print(f" Time: {report.analysis_duration_seconds:.3f}s")
|
|
251
|
+
console.print()
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _print_report_summary(report) -> None:
|
|
255
|
+
"""Print a one-line summary for batch processing."""
|
|
256
|
+
v = report.verdict
|
|
257
|
+
status = "[red]PACKED[/red]" if v.is_packed else "[green]CLEAN[/green]"
|
|
258
|
+
packer = f" [{v.packer.value}]" if v.is_packed else ""
|
|
259
|
+
console.print(
|
|
260
|
+
f" {report.file_name:40s} {status}{packer} "
|
|
261
|
+
f"({v.confidence:.0%}) {report.analysis_duration_seconds:.2f}s"
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
if __name__ == "__main__":
|
|
266
|
+
main()
|