Skip to content

Latest commit

 

History

History
348 lines (278 loc) · 11 KB

File metadata and controls

348 lines (278 loc) · 11 KB

ShieldCore Architecture

Overview

ShieldCore is a cross-platform plugin protection framework that encrypts, signs, and loads plugins with runtime integrity and anti-tampering checks. The system is split into three main logical layers:

  1. Host Application — loads and validates plugins
  2. Plugin Layer — arbitrary code packaged as .plg files
  3. Packer Tool — converts DLLs/SOs into encrypted .plg packages

Module Structure

include/shieldcore/
├── common.hpp              # Shared types, file I/O, binary structures
├── crypto_utils.hpp        # AES-256-GCM, SHA256, RSA signing
├── plugin_loader.hpp       # In-memory DLL/SO loading
├── integrity_checker.hpp   # Host executable hash verification
├── anti_debug.hpp          # Debugger detection (startup + periodic)
├── http_client.hpp         # Backend validation, license activation
└── memory_protection.hpp   # Memory region verification

src/host/
├── main.cpp                # Entry point, orchestration
├── crypto_utils.cpp        # OpenSSL bindings
├── plugin_loader.cpp       # Platform-specific DLL/SO loading
├── integrity_checker.cpp   # SHA256 file hashing
├── anti_debug.cpp          # ptrace/IsDebuggerPresent detection
├── http_client.cpp         # libcurl JSON requests
└── memory_protection.cpp   # Memory page protection checks

src/plugin/
└── plugin.cpp              # Sample plugin exporting Execute()

src/tools/
└── packer.cpp              # Encrypts + signs plugins

Data Flow: Plugin Loading Pipeline

Host Startup
    ↓
[1] Anti-Debug Check (IsDebuggerPresent)
    ↓
[2] Host Self-Integrity Check (SHA256 of executable)
    ↓
[3] Load .plg File into Memory
    ↓
[4] Parse Plugin Header (magic, version, nonce, signature)
    ↓
[5] AES-256-GCM Decryption (using kDemoAesKey + nonce)
    ↓
[6] Compute SHA256 of Decrypted Payload
    ↓
[7] Verify Hash Matches Header Hash
    ↓
[8] RSA Signature Verification (using embedded public key)
    ↓
[9] Backend Validation (POST /validate with plugin_id + hwid)
    ↓
[10] In-Memory Plugin Load (dlopen/memfd or equivalent)
    ↓
[11] Resolve Execute() Symbol
    ↓
[12] Launch Periodic Checks:
     ├─ Debugger check every 5 seconds
     ├─ Memory protection check every 10 seconds
     └─ Host integrity check every 10 seconds
    ↓
[13] Call Execute()
    ↓
[14] Cleanup & Exit

Security Checkpoints

Checkpoint What Why When
Anti-debug ptrace(PTRACE_TRACEME) or IsDebuggerPresent() Prevent live inspection Startup + every 5s
Host integrity SHA256 of executable Detect tampering Startup + every 10s
Plugin magic Verify "SCPLG" header Reject corrupted/fake files On load
Decryption AES-256-GCM with nonce Ensure only authorized payloads load On load
Hash verification SHA256 match Detect payload tampering On load
Signature verification RSA-2048 signature Authenticate plugin origin On load
Backend validation Server response License/revocation check On load
Memory protection Page permissions Detect code injection/patching Every 10s during execution

Module Responsibilities

common.hpp

  • PluginHeader struct (packed binary layout, 321 bytes)
  • Helper utilities: toHex(), readFileBytes(), writeFileBytes()
  • Type aliases: ByteBuffer, size constants

crypto_utils.cpp

  • SHA256 hashing via OpenSSL
  • AES-256-GCM encrypt/decrypt (authenticated encryption)
  • RSA-2048 sign/verify using embedded PEM keys
  • Key management: embeddedPublicKeyPem(), embeddedPrivateKeyPem()

plugin_loader.cpp

  • Windows: LoadLibrary() with reflective DLL loading (stub)
  • Linux/macOS: memfd_create() + dlopen(/proc/self/fd/...)
  • Symbol resolution: dlsym() or GetProcAddress()
  • Handles cleanup on unload

integrity_checker.cpp

  • Compute SHA256 of files on disk
  • Compare against hardcoded or environment-provided hash
  • Exit if mismatch detected

anti_debug.cpp

  • Startup: Single check via ptrace(PTRACE_TRACEME) or IsDebuggerPresent()
  • Runtime: Background thread checking every 5 seconds
  • Exit immediately if debugger detected

http_client.cpp

  • validateWithBackend() — POST /validate with plugin_id + hwid
  • activateLicense() — POST /activate with license_key + nonce + timestamp
  • Nonce generation for replay protection
  • Parse JSON responses for valid / revoked status

memory_protection.cpp

  • Verify page protections (detect RWX regions)
  • Periodic thread checking every 10 seconds during plugin execution
  • Can expand to check for code caves, patched jumps, etc.

Threading Model

Three background threads run during plugin execution:

main thread:
  - Load plugin
  - Call Execute()
  - Wait for completion

integrityThread (spawned before Execute):
  - Every 10 seconds: verify host executable hash
  - Exit(1) if mismatch

debugCheckThread (spawned before Execute):
  - Every 5 seconds: check for attached debugger
  - Exit(1) if debugger present

memoryCheckThread (spawned before Execute):
  - Every 10 seconds: verify memory page protections
  - Can be extended for tampering detection

All three are stopped cleanly after Execute() returns.

Plugin Format (.plg)

Binary layout (little-endian, 321 byte header + payload):

[HEADER - 321 bytes]
  Magic (5 bytes): "SCPLG"
  Version (4 bytes): uint32
  Flags (4 bytes): uint32
  PayloadLength (8 bytes): uint64
  Nonce (12 bytes): AES-GCM nonce
  PayloadHash (32 bytes): SHA256 of decrypted payload
  Signature (256 bytes): RSA-2048 signature of hash

[PAYLOAD - variable length]
  Encrypted DLL/SO bytes (AES-256-GCM encrypted)

[TAG - 16 bytes]
  AES-GCM authentication tag

Key Material

  • AES Key: 32-byte hardcoded demo key (use SHIELDCORE_PRIVATE_KEY_PEM env var during development)
  • RSA Keys: Embedded PEM strings; override with SHIELDCORE_PRIVATE_KEY_PEM and SHIELDCORE_PUBLIC_KEY_PEM
  • Nonce: Random 12 bytes generated per encryption, stored in header

Cross-Platform Considerations

Linux

  • ptrace(PTRACE_TRACEME) for debugger detection
  • memfd_create() for in-memory plugin loading
  • /proc/self/exe for executable path resolution
  • GCC/Clang compilation

macOS

  • sysctl syscalls for debugger detection (not yet implemented; currently uses ptrace)
  • dlopen() with in-memory file descriptor approach
  • /proc/self/exe fallback to readlink(/proc/self/exe) or _dyld_get_image_name()
  • Clang compilation with -fPIC and -dynamiclib

Windows

  • IsDebuggerPresent() for debugger detection
  • LoadLibrary() from memory (reflective DLL loading not yet fully implemented)
  • GetProcAddress() for symbol resolution
  • MSVC or MinGW-w64 compilation

Build Configuration

Release Build (default)

make release
  • -O2 optimization
  • -DNDEBUG preprocessor flag
  • Link-time optimization (-flto) on Linux/macOS

Debug Build

make debug
  • -O0 no optimization
  • -g debug symbols
  • Full stack traces available

Hardened Release (strip symbols)

make release STRIP_SYMBOLS=1
  • Strips all debug symbols (-s linker flag)
  • Reduces binary size and reverse-engineering surface

How to Build

Prerequisites

# Ubuntu/WSL
sudo apt install build-essential make libssl-dev libcurl4-openssl-dev

# macOS
brew install openssl curl

# Windows (MSYS2)
pacman -S base-devel openssl curl

Compile

cd /path/to/project
make release

Binaries appear in build/:

  • hostapp (Linux/macOS) or hostapp.exe (Windows)
  • plugin.so (Linux), plugin.dylib (macOS), or plugin.dll (Windows)
  • packer or packer.exe

How to Run

Step 1: Generate Test Keys

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out build/test_private.pem
openssl pkey -in build/test_private.pem -pubout -out build/test_public.pem

Step 2: Export Keys to Environment

export SHIELDCORE_PRIVATE_KEY_PEM="$(cat build/test_private.pem)"
export SHIELDCORE_PUBLIC_KEY_PEM="$(cat build/test_public.pem)"

Step 3: Pack the Sample Plugin

./build/packer ./build/plugin.so ./build/plugin.plg

Output: build/plugin.plg (encrypted, signed package)

Step 4: Run the Host Against the Packed Plugin

./build/hostapp ./build/plugin.plg

Expected output:

ShieldCore plugin executed

Step 5 (Optional): Test Backend Validation

Start a mock backend server (e.g., HTTP server listening on port 8080 with a /validate endpoint):

./build/hostapp ./build/plugin.plg http://127.0.0.1:8080

The host will POST to /validate with:

{
  "plugin_id": "plugin.plg",
  "hwid": "linux-dev-hwid"
}

Respond with {"status": "valid"} to allow execution.

Environment Variables

Variable Purpose Example
SHIELDCORE_PRIVATE_KEY_PEM Override private key (packer signing) $(cat my_key.pem)
SHIELDCORE_PUBLIC_KEY_PEM Override public key (host verification) $(cat my_key.pub.pem)
SHIELDCORE_EXPECTED_HOST_HASH Expected SHA256 of host executable abc123def456...

Extending the Project

Add a Custom Plugin

  1. Write a C++ file with extern "C" void Execute() export
  2. Compile to .so / .dylib / .dll
  3. Pack with the packer tool
  4. Run with the host

Modify Anti-Tamper Logic

Add Backend Integration

  • Modify http_client.cpp to call real license API
  • Implement proper HWID generation for each platform
  • Add timestamp + nonce validation server-side

Build Different Variants

# Per-build randomization (future enhancement)
make release BUILD_VARIANT=v1

# Obfuscation flags (future enhancement)
make release OBFUSCATE=1

Security Limitations

  • Attacker with full system access can bypass all client-side protections
  • Private key material must be protected server-side
  • Backend API must use TLS and proper authentication
  • HWID generation is weak; improve with machine certificates or hardware attestation
  • This is an anti-piracy delay, not a security boundary

Next Steps

  1. Replace demo keys with production RSA-2048 key material
  2. Implement real backend for license validation and revocation
  3. Harden HWID generation (machine certificates, TPM, etc.)
  4. Add code obfuscation and CFI (control flow integrity) checks
  5. Implement per-build randomization to defeat pattern matching
  6. Add telemetry and abuse detection server-side