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.
Files changed (54) hide show
  1. packerscope-0.1.1/LICENSE +21 -0
  2. packerscope-0.1.1/PKG-INFO +157 -0
  3. packerscope-0.1.1/README.md +107 -0
  4. packerscope-0.1.1/packerscope/__init__.py +4 -0
  5. packerscope-0.1.1/packerscope/cli.py +266 -0
  6. packerscope-0.1.1/packerscope/config.py +278 -0
  7. packerscope-0.1.1/packerscope/constants.py +156 -0
  8. packerscope-0.1.1/packerscope/context.py +326 -0
  9. packerscope-0.1.1/packerscope/core/__init__.py +1 -0
  10. packerscope-0.1.1/packerscope/core/enums.py +360 -0
  11. packerscope-0.1.1/packerscope/core/interfaces.py +300 -0
  12. packerscope-0.1.1/packerscope/core/models.py +1101 -0
  13. packerscope-0.1.1/packerscope/detectors/__init__.py +37 -0
  14. packerscope-0.1.1/packerscope/detectors/entropy_detector.py +213 -0
  15. packerscope-0.1.1/packerscope/detectors/entrypoint_detector.py +202 -0
  16. packerscope-0.1.1/packerscope/detectors/heuristic_detector.py +529 -0
  17. packerscope-0.1.1/packerscope/detectors/iat_detector.py +183 -0
  18. packerscope-0.1.1/packerscope/detectors/pe_structure_detector.py +266 -0
  19. packerscope-0.1.1/packerscope/detectors/section_detector.py +181 -0
  20. packerscope-0.1.1/packerscope/detectors/signature_detector.py +228 -0
  21. packerscope-0.1.1/packerscope/detectors/yara_detector.py +239 -0
  22. packerscope-0.1.1/packerscope/exceptions.py +203 -0
  23. packerscope-0.1.1/packerscope/orchestrator.py +269 -0
  24. packerscope-0.1.1/packerscope/plugin_manager.py +246 -0
  25. packerscope-0.1.1/packerscope/reporters/__init__.py +8 -0
  26. packerscope-0.1.1/packerscope/reporters/csv_reporter.py +89 -0
  27. packerscope-0.1.1/packerscope/reporters/html_reporter.py +160 -0
  28. packerscope-0.1.1/packerscope/reporters/json_reporter.py +41 -0
  29. packerscope-0.1.1/packerscope/reporters/markdown_reporter.py +192 -0
  30. packerscope-0.1.1/packerscope/reporters/templates/__init__.py +1 -0
  31. packerscope-0.1.1/packerscope/signatures/__init__.py +1 -0
  32. packerscope-0.1.1/packerscope/signatures/peid_parser.py +161 -0
  33. packerscope-0.1.1/packerscope/signatures/yara_rules/__init__.py +1 -0
  34. packerscope-0.1.1/packerscope/unpackers/__init__.py +7 -0
  35. packerscope-0.1.1/packerscope/unpackers/dynamic_unpacker.py +149 -0
  36. packerscope-0.1.1/packerscope/unpackers/generic_unpacker.py +140 -0
  37. packerscope-0.1.1/packerscope/unpackers/upx_unpacker.py +125 -0
  38. packerscope-0.1.1/packerscope/utils/__init__.py +18 -0
  39. packerscope-0.1.1/packerscope/utils/concurrency.py +133 -0
  40. packerscope-0.1.1/packerscope/utils/disasm.py +328 -0
  41. packerscope-0.1.1/packerscope/utils/entropy.py +227 -0
  42. packerscope-0.1.1/packerscope/utils/hasher.py +174 -0
  43. packerscope-0.1.1/packerscope/utils/logger.py +169 -0
  44. packerscope-0.1.1/packerscope/utils/pe_parser.py +451 -0
  45. packerscope-0.1.1/packerscope/verification/__init__.py +1 -0
  46. packerscope-0.1.1/packerscope/verification/verifier.py +160 -0
  47. packerscope-0.1.1/packerscope.egg-info/PKG-INFO +157 -0
  48. packerscope-0.1.1/packerscope.egg-info/SOURCES.txt +52 -0
  49. packerscope-0.1.1/packerscope.egg-info/dependency_links.txt +1 -0
  50. packerscope-0.1.1/packerscope.egg-info/entry_points.txt +2 -0
  51. packerscope-0.1.1/packerscope.egg-info/requires.txt +33 -0
  52. packerscope-0.1.1/packerscope.egg-info/top_level.txt +1 -0
  53. packerscope-0.1.1/pyproject.toml +99 -0
  54. 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,4 @@
1
+ """PackerScope — Automatic packer detection, classification, and unpacking framework."""
2
+
3
+ __version__ = "0.1.1"
4
+ __author__ = "Salman Mallah"
@@ -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()