Skip to content

Latest commit

 

History

History
337 lines (281 loc) · 13.2 KB

File metadata and controls

337 lines (281 loc) · 13.2 KB

Modular Architecture Documentation

Overview

The purl_diver PE shellcode extractor has been completely refactored into a modular architecture with 9 separate, well-defined modules. This transformation improves code maintainability, testability, and extensibility.

Architecture Diagram

┌─────────────────────────────────────────────────────────┐
│                     main.c                              │
│            (Application Entry Point)                    │
└─────────────────────────────────────────────────────────┘
                          │
          ┌───────────────┴───────────────┐
          │                               │
┌─────────▼──────────┐         ┌─────────▼──────────┐
│   options.c/h      │         │   error_codes.c/h  │
│  (Configuration)   │         │  (Error Handling)  │
└─────────┬──────────┘         └─────────┬──────────┘
          │                               │
          │         ┌─────────────────────┘
          │         │
┌─────────▼─────────▼──────────────────────────────────┐
│                  pe_parser.c/h                        │
│         (Core PE File Parsing & Context)              │
└─────────┬─────────────────────────────────────────────┘
          │
     ┌────┴────┬────────┬────────┬────────┬────────┐
     │         │        │        │        │        │
┌────▼────┐┌──▼───┐┌───▼───┐┌───▼──┐┌────▼───┐┌──▼───┐
│section_ ││hash_ ││entropy││output││import_ ││utils │
│analyzer ││algo  ││       ││format││export  ││      │
└─────────┘└──────┘└───────┘└──────┘└────────┘└──────┘

Module Descriptions

1. error_codes (include/error_codes.h, src/error_codes.c)

  • Purpose: Centralized error code definitions and error string mapping
  • Key Functions:
    • error_string() - Converts error codes to human-readable messages
  • Lines of Code: ~100
  • Dependencies: None

2. pe_types (include/pe_types.h)

  • Purpose: Cross-platform PE structure definitions
  • Key Features:
    • Automatic Windows.h inclusion on Windows
    • Manual PE structure definitions for Linux/macOS
    • Typedef definitions for portability
  • Lines of Code: ~450
  • Dependencies: Platform headers only

3. pe_parser (include/pe_parser.h, src/pe_parser.c)

  • Purpose: Core PE file parsing and validation
  • Key Functions:
    • validate_pe_structure() - Validates DOS/NT headers
    • initialize_pe_context() - Loads and initializes PE file
    • cleanup_pe_context() - Frees resources
    • find_executable_sections() - Locates executable sections
    • rva_to_file_offset() - RVA to file offset conversion
    • compare_sections() - qsort comparison for section sorting
    • safe_copy_section_name() - Safe section name extraction
    • safe_add_size() - Overflow-protected addition
  • Lines of Code: ~380
  • Dependencies: error_codes, pe_types

4. section_analyzer (include/section_analyzer.h, src/section_analyzer.c)

  • Purpose: Section filtering and overlap detection
  • Key Functions:
    • is_section_included() - Apply include/exclude filters
    • parse_section_name() - Parse comma-separated section names
    • free_section_filters() - Cleanup filter memory
    • detect_overlaps_and_calculate_size() - Overlap detection
  • Global Variables:
    • verbose, include_sections, exclude_sections, min_section_size
  • Lines of Code: ~180
  • Dependencies: pe_types, error_codes

5. hash_algorithms (include/hash_algorithms.h, src/hash_algorithms.c)

  • Purpose: Cryptographic hash calculation (MD5, SHA256)
  • Key Functions:
    • calculate_md5() - MD5 hash calculation
    • calculate_sha256() - SHA256 hash calculation
    • print_hash() - Format and print hash output
  • Implementation: Streaming algorithms for memory efficiency
  • Lines of Code: ~390
  • Dependencies: None (pure algorithm)

6. entropy (include/entropy.h, src/entropy.c)

  • Purpose: Shannon entropy calculation
  • Key Functions:
    • calculate_entropy() - Computes entropy (0.0-8.0 bits/byte)
  • Use Case: Detect packed/encrypted/compressed sections
  • Lines of Code: ~30
  • Dependencies: Math library (log)

7. output_formats (include/output_formats.h, src/output_formats.c)

  • Purpose: Multiple output format support
  • Key Functions:
    • output_as_c_array() - Generate C source code
    • output_as_python() - Generate Python byte string
    • output_as_hex_dump() - Traditional hex dump with ASCII
    • output_as_json() - JSON with metadata
  • Lines of Code: ~120
  • Dependencies: pe_types, entropy

8. import_export_analyzer (include/import_export_analyzer.h, src/import_export_analyzer.c)

  • Purpose: Import/Export table analysis
  • Key Functions:
    • analyze_imports() - Parse IAT/INT, display imported DLLs/functions
    • analyze_exports() - Parse EAT, display exported functions
    • print_imports_exports_summary() - Combined analysis
  • Lines of Code: ~240
  • Dependencies: pe_types, pe_parser

9. utils (include/utils.h, src/utils.c)

  • Purpose: Utility functions for batch processing and UI
  • Key Functions:
    • add_input_file() - Manage batch file list
    • interactive_section_selection() - User prompts for section selection
  • Lines of Code: ~120
  • Dependencies: pe_types, section_analyzer

10. options (include/options.h, src/options.c)

  • Purpose: Command-line argument parsing and global configuration
  • Key Structures:
    • ProgramOptions - Global configuration state
    • OutputFormat enum - Output format types
  • Key Functions:
    • init_options() - Initialize defaults
    • parse_arguments() - Command-line parsing
    • print_usage() - Help message
    • print_version() - Version information
  • Lines of Code: ~180
  • Dependencies: section_analyzer, error_codes

11. main (src/main.c)

  • Purpose: Application entry point and orchestration
  • Key Functions:
    • extract_shellcode() - Main extraction pipeline
    • main() - Entry point, argument parsing, cleanup
  • Lines of Code: ~250
  • Dependencies: All modules

Build System

Makefile Targets

make                 # Build modular version (recommended)
make modular         # Build full modular version (purl_diver)
make legacy          # Build legacy monolithic version
make demo            # Build simple demo version
make clean           # Remove build artifacts
make help            # Show all targets

Module Compilation

The modular build compiles all modules together:

MODULAR_SRC = src/main.c \
              src/error_codes.c \
              src/pe_parser.c \
              src/hash_algorithms.c \
              src/entropy.c \
              src/section_analyzer.c \
              src/output_formats.c \
              src/import_export_analyzer.c \
              src/utils.c \
              src/options.c

Binary Output

  • Binary Name: purl_diver
  • Size: ~43KB (optimized)
  • Warnings: 0 (clean compilation)
  • Platform: Cross-platform (Linux, macOS, Windows with MinGW)

Code Metrics

Metric Monolithic Modular Improvement
Total Files 1 21 +2000%
Total LOC 2386 ~2440 +2% (documentation overhead)
Modules 0 9
Headers 0 10
Compilation Warnings 14 0 -100%
Testability Low High Significant
Maintainability Low High Significant

Dependency Graph

main.c
 ├── options.h
 │    └── section_analyzer.h
 │         └── pe_types.h
 ├── error_codes.h
 ├── pe_parser.h
 │    ├── pe_types.h
 │    └── error_codes.h
 ├── section_analyzer.h (via options)
 ├── output_formats.h
 │    ├── pe_types.h
 │    └── entropy.h
 ├── hash_algorithms.h
 ├── entropy.h
 └── import_export_analyzer.h
      ├── pe_types.h
      └── pe_parser.h

Testing

Functional Test

$ ./purl_diver -v --hash cshost.exe cshost_modular.bin
[INFO] Entry point RVA: 0x4E8490
[INFO] Machine type: x64 (64-bit)
[INFO] Number of sections: 3
[INFO] Input file is an executable.
[DEBUG] Processing section 'UPX1': RVA=0x30F000, Raw=0x200, Size=0x1D9800
[INFO] Entry point is in section 'UPX1'
[+] Found 1 executable sections:
  - Section 'UPX1': 1939456 bytes at offset 0x200
[+] Success: Extracted 1939456 bytes from 1 sections to 'cshost_modular.bin'
[INFO] SHA256: 11b59d4dce0cc77910d2307f478fbc50a6167c3250cbd12434ecd950b0db1b18

Comparison with Monolithic Version

Feature Monolithic Modular Status
PE Parsing PASS
Section Extraction PASS
Hash Calculation PASS
Output Formats PASS
Import/Export Analysis PASS
Binary Size 48KB 43KB Better
Compilation Warnings 14 0 Better

Benefits of Modular Architecture

1. Improved Maintainability

  • Each module has a single, well-defined responsibility
  • Changes to one module don't affect others
  • Easier to locate and fix bugs

2. Better Testability

  • Individual modules can be unit tested in isolation
  • Mock dependencies for focused testing
  • Clear interfaces enable test automation

3. Enhanced Readability

  • Logical organization matches mental model
  • Header files serve as documentation
  • Reduced cognitive load for developers

4. Increased Extensibility

  • New modules can be added without modifying existing code
  • Interfaces define clear extension points
  • Plugin architecture possible

5. Compilation Efficiency

  • Incremental compilation: only changed modules recompile
  • Parallel compilation possible with advanced build systems
  • Faster development iteration

6. Code Reusability

  • Modules can be used in other projects
  • Examples: hash_algorithms, entropy, pe_parser
  • Reduced code duplication

Future Enhancements

Phase 2 Optimizations (Planned)

  1. Unified Error Handling: Implement goto cleanup pattern
  2. Eliminate Double File Read: Use mmap or single-pass reading
  3. Logging System: Structured logging with levels
  4. Unit Test Framework: Comprehensive test suite with >80% coverage

Phase 3 Performance (Planned)

  1. Memory-Mapped I/O: Use mmap for large files
  2. Optimize Entropy: Precomputed lookup tables
  3. Cache Optimizations: Section name caching
  4. Fuzzing Harness: AFL/libFuzzer integration

Phase 4 Polish (Planned)

  1. Configuration File Support: YAML/JSON config
  2. API Documentation: Doxygen-generated docs
  3. CONTRIBUTING.md: Contribution guidelines
  4. Release Automation: GitHub Actions for releases

Migration Guide

For Developers

If you're maintaining or extending purl_diver, the modular architecture makes common tasks easier:

Adding a New Output Format

  1. Add new enum value to OutputFormat in include/options.h
  2. Implement output function in src/output_formats.c
  3. Add function declaration to include/output_formats.h
  4. Add case to switch statement in src/main.c
  5. Update parse_arguments() in src/options.c

Adding a New Analysis Module

  1. Create include/new_module.h with function declarations
  2. Create src/new_module.c with implementation
  3. Add to MODULE_SRC in Makefile
  4. Include header in src/main.c
  5. Call functions in appropriate places

Debugging Specific Functionality

Instead of searching through 2386 lines, navigate directly to the relevant module:

  • PE parsing issues → src/pe_parser.c
  • Hash mismatches → src/hash_algorithms.c
  • Section filtering → src/section_analyzer.c

Conclusion

The modular refactoring represents a significant quality improvement while maintaining 100% functional compatibility with the original monolithic implementation. The new architecture provides a solid foundation for future enhancements and makes purl_diver a professional-grade, enterprise-ready tool.

Total Development Time: 1 session Lines of Code Added: ~2440 (modular) + ~800 (documentation) Test Status: ✅ All functional tests passing Compilation: ✅ Zero warnings Backward Compatibility: ✅ 100% feature parity