Skip to content

CameronBabcock/ObfTypes

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Defensive Toolkit

A header-only C++ library that prevents signature-based detection through compile-time polymorphic types. Each build generates different binary signatures for mathematically identical operations.

Overview

The Defensive Toolkit provides types like uint32_dt<Seed> that wrap standard integral types. These types overload operators (^, |, &, +, -, etc.) to dispatch to mathematically equivalent variants selected at compile-time based on a seed value. This creates signature variance while maintaining:

  • Zero runtime overhead - all variant selection is compile-time only
  • Mathematical correctness - Z3 formal verification proves equivalence
  • Easy integration - header-only, drop-in replacement for standard types

Quick Start

#include <defensive_toolkit/toolkit.h>

// SIMPLE: Use mono_* for consistent types that interoperate
mono_uint32 hash{2166136261u};
mono_uint32 value{42};

// Operations dispatch to variants (e.g., XOR might use: (a|b) & ~(a&b))
hash = hash ^ value;
hash = hash * 16777619u;

// Get underlying value
std::uint32_t result = hash.Get();

How It Works

Polymorphic Operations

Each arithmetic/bitwise operation has multiple mathematically equivalent implementations:

Operation Native Variant Examples
XOR a ^ b (a|b) & ~(a&b), (a&~b) | (~a&b), (a+b) - ((a&b)<<1)
OR a | b (a&b) + (a^b), (a^b) | (a&b)
AND a & b (a|b) - (a^b), ~(~a | ~b)
ADD a + b (a^b) + ((a&b)<<1), a - (~b) - 1
MUL a * c shift-add decomposition

Compile-Time Variant Selection

// Seed generation from __TIME__ and __DATE__
inline constexpr std::uint32_t DT_BUILD_SEED = /* varies each build */;

// Per-use unique seeds via __COUNTER__
#define DT_AUTO_SEED DT_UNIQUE_SEED(DT_BUILD_SEED)

// Template selects variant at compile-time
template<std::uint32_t Seed>
class PolyInt {
    T operator^(const PolyInt& other) const {
        constexpr auto variant = SelectVariant(Seed, OpType::XOR);
        return XorImpl<T, variant>::Apply(m_value, other.m_value);
    }
};

Signature Variance

Different builds select different variants:

Build 1 (10:15:30): XOR → Variant 2 → "(a&~b) | (~a&b)"
Build 2 (10:16:45): XOR → Variant 4 → "(a|b) - (a&b)"
Build 3 (10:18:00): XOR → Variant 0 → "a ^ b" (native)

Static signatures fail because each build produces different binary code for the same algorithm.

SIMD Support

Overview

The toolkit includes SIMD (Single Instruction, Multiple Data) variants for scalar operations. These variants use SIMD instructions to compute scalar results, adding another layer of signature variance.

Supported SIMD Instruction Sets

Platform Instruction Set Compiler Flag
x86_64 SSE2 -msse2
x86_64 AVX2 -mavx2
x86_64 AVX-512 -mavx512f
ARM64 NEON Auto-detected

SIMD Operation Variants

When SIMD is available, additional operation variants are included:

// Example: XOR with SSE2
template<IntegralType T>
T XorViaSIMD_SSE2(T a, T b) noexcept
{
    __m128i va = _mm_cvtsi32_si128(a);
    __m128i vb = _mm_cvtsi32_si128(b);
    __m128i result = _mm_xor_si128(va, vb);
    return _mm_cvtsi128_si32(result);
}

SIMD Usage Policy

Control when SIMD variants are used via compile-time configuration:

// Never use SIMD for scalar operations (default for compatibility)
#define DT_SIMD_USAGE ::defensive_toolkit::detail::SIMDUsage::Never

// Always use SIMD when available
#define DT_SIMD_USAGE ::defensive_toolkit::detail::SIMDUsage::Always

// Use SIMD probabilistically based on seed (~50% of the time)
#define DT_SIMD_USAGE ::defensive_toolkit::detail::SIMDUsage::Probabilistic

Default is Probabilistic, which uses SIMD approximately 50% of the time based on seed value.

SIMD Variant Count

Operation Scalar Variants +SSE2 +AVX2 +NEON
XOR 5 +1 +2 +1
OR 3 +1 +1 +1
AND 3 +1 +1 +1
ADD 3 +1 +2 +1
SUB 2 +1 +1 +1

Total XOR variants:

  • Scalar only: 5
  • With SSE2: 6
  • With AVX2: 7
  • With NEON: 6

Example: SIMD Demo

# Build and run SIMD demonstration
make SIMD=sse2
./build/simd_demo

The demo shows:

  • CPU feature detection
  • SIMD vector operations (128-bit, 256-bit, 512-bit)
  • Performance benchmarks
  • Polymorphic SIMD operations

Building

Requirements

  • C++26/latest compiler (Clang or GCC)
  • Google Test (for tests)
  • Python 3 + Z3 (for verification)

Build Examples

make                    # Build examples with Clang
make CXX=g++            # Build with GCC
make test               # Build and run tests
make verify             # Run Z3 formal verification

# SIMD builds (x86_64 only)
make SIMD=sse2          # Build with SSE2 SIMD support
make SIMD=avx2          # Build with AVX2 SIMD support
make SIMD=avx512        # Build with AVX-512 SIMD support

Note: ARM builds automatically use NEON SIMD instructions when available (no additional flags needed).

Build Targets

Target Description
make Build examples (default)
make examples Build example programs
make tests Build test suite
make test Build and run all tests
make verify Run Z3 verification
make clean Remove build artifacts
make info Show compiler information
make help Show all targets

Visual Studio Integration

Visual Studio solution and project files are provided for Windows development:

# Open in Visual Studio
start DefensiveToolkit.sln

Available configurations:

  • Debug/Release: Standard builds
  • Debug_SSE2/Release_SSE2: SSE2 SIMD builds
  • Debug_AVX2/Release_AVX2: AVX2 SIMD builds

Cross-Platform Support

Platform Architecture SIMD Support
Windows x86_64 SSE2, AVX2, AVX-512
Linux x86_64 SSE2, AVX2, AVX-512
macOS x86_64 SSE2, AVX2
macOS ARM64 (Apple Silicon) NEON
Linux ARM64 NEON

Usage

Macro Types (Recommended)

The easiest way to use the toolkit is with mono_* and poly_* macros:

#include <defensive_toolkit/toolkit.h>

// MONOMORPHIC: All instances share the same build seed
// - Same operation variants throughout the program
// - Types can interoperate (same underlying type)
// - Still varies build-to-build
mono_uint32 a{10};
mono_uint32 b{20};
auto c = a ^ b;  // Works - same type

// POLYMORPHIC: Each usage gets a unique seed
// - Different operation variants for maximum diversity
// - Types cannot interoperate (different underlying types)
poly_uint32 x{10};  // Unique seed
poly_uint32 y{20};  // Different unique seed
// x ^ y won't compile - different types
Macro Behavior Use Case
mono_uint32 Same seed for all Functions, classes, interoperating values
poly_uint32 Unique seed per use Maximum signature diversity

Available macro types:

  • Unsigned: mono_uint8, mono_uint16, mono_uint32, mono_uint64
  • Signed: mono_int8, mono_int16, mono_int32, mono_int64
  • Platform: mono_size, mono_uintptr, mono_intptr, mono_ptrdiff
  • Poly versions: Same names with poly_ prefix

Template Types (Advanced)

For explicit seed control, use template aliases:

#include <defensive_toolkit/toolkit.h>

using namespace defensive_toolkit;

// Fixed-width types with explicit seed
uint8_dt<Seed>   u8;
uint16_dt<Seed>  u16;
uint32_dt<Seed>  u32;
uint64_dt<Seed>  u64;

int8_dt<Seed>    i8;
int16_dt<Seed>   i16;
int32_dt<Seed>   i32;
int64_dt<Seed>   i64;

// Platform-specific types
size_dt<Seed>      sz;
uintptr_dt<Seed>   ptr;
ptrdiff_dt<Seed>   diff;

// Character types
char_dt<Seed>      c;
char16_dt<Seed>    c16;
char32_dt<Seed>    c32;

Seed Selection

// Build seed - changes every compilation
constexpr auto seed1 = DT_BUILD_SEED;

// Unique seed per use (uses __COUNTER__)
constexpr auto seed2 = DT_UNIQUE_SEED(DT_BUILD_SEED);
constexpr auto seed3 = DT_UNIQUE_SEED(DT_BUILD_SEED);
// seed2 != seed3

// Automatic unique seed (recommended)
uint32_dt<DT_AUTO_SEED> value{42};

Operations

mono_uint32 a{10};
mono_uint32 b{20};

// Bitwise operations
auto xorResult = a ^ b;
auto orResult = a | b;
auto andResult = a & b;
auto notResult = ~a;

// Arithmetic operations
auto sum = a + b;
auto diff = a - b;
auto product = a * b;  // Native multiplication

// Shift operations
auto shifted = a << 5;

// Multiplication by compile-time constant (provides signature variance)
auto fnvProduct = a.MultiplyBy<16777619u>();

// Compound assignment
a ^= b;
a += b;
a *= b;
a <<= 3;

// Comparison (always uses native - correctness critical)
bool equal = (a == b);
auto ordering = (a <=> b);

// Conversion
std::uint32_t native = a.Get();
std::uint32_t native2 = static_cast<std::uint32_t>(a);

Verification

Z3 Formal Verification

Prove mathematical equivalence of all variants:

cd verify
pip install -r requirements.txt
python3 verify_ops.py

All checks should return UNSAT (proven equivalent). See verify/README.md for details.

Verifying SIMD Operations

SIMD scalar operations (SSE2, AVX2, NEON) are mathematically equivalent to their scalar counterparts by design - they use the same underlying CPU instructions, just loaded/stored via SIMD registers. To verify:

  1. Functional Equivalence: SIMD intrinsics (_mm_xor_si128, veor_u32, etc.) map directly to native instructions

  2. Z3 Verification: SIMD variants can be verified by treating them as the native operation:

    # In verify_ops.py, SIMD variants are equivalent to variant 0 (native)
    simd_result = xor_native(a, b)  # Same as SIMD via registers
    solver.add(simd_result != xor_native(a, b))
    assert solver.check() == unsat  # Proven equivalent
  3. Runtime Testing: Unit tests verify SIMD operations produce identical results:

    make test  # Tests include SIMD variant verification

Note: The toolkit uses SIMD for signature variance (different instruction encodings), not for algorithm changes. Mathematical correctness is preserved.

Unit Tests

Runtime validation with random testing:

make test

Tests verify:

  • Operation correctness across 1000+ random inputs
  • Consistency across different seeds
  • Type behavior (construction, comparison, etc.)
  • Overflow behavior matches native types

Examples

Hash Function (Simple)

// Using mono_* for simple, interoperable types
mono_uint32 FNV1aHash(std::string_view str)
{
    mono_uint32 hash{2166136261u};

    for (unsigned char c : str)
    {
        hash = hash ^ mono_uint32{c};       // Variant XOR
        hash = hash.MultiplyBy<16777619u>(); // Variant MUL
    }

    return hash;
}

// All calls use same operation variants, results interoperate
auto hash1 = FNV1aHash("test");
auto hash2 = FNV1aHash("hello");
bool different = (hash1 != hash2);  // Works - same type

Hash Function (Template)

// Using templates for maximum diversity per call site
template<std::uint32_t Seed>
uint32_dt<Seed> FNV1aHash(std::string_view str)
{
    uint32_dt<Seed> hash{2166136261u};

    for (unsigned char c : str)
    {
        hash = hash ^ uint32_dt<Seed>{c};
        hash = hash.template MultiplyBy<16777619u>();
    }

    return hash;
}

// Each call site uses different operation variants
auto hash1 = FNV1aHash<DT_AUTO_SEED>("test");
auto hash2 = FNV1aHash<DT_AUTO_SEED>("test");
// hash1.Get() == hash2.Get() (same result, different code paths)

Full Example

See examples/string_lookup.cpp for a complete example using mono_uint32:

  • FNV-1a hash implementation
  • String lookup table class
  • CRC-32 checksum
  • Demonstrates interoperating monomorphic types

Run:

make examples
./build/string_lookup

Project Structure

ObfTypes/
├── include/defensive_toolkit/
│   ├── config.h                # Seed generation
│   ├── operations.h            # Operation variants
│   ├── types.h                 # PolyInt wrapper
│   ├── simd.h                  # SIMD vector types
│   ├── simd_scalar.h           # x86 SIMD scalar variants
│   ├── simd_scalar_neon.h      # ARM NEON scalar variants
│   └── toolkit.h               # Master include
├── examples/
│   ├── string_lookup.cpp       # Hash and lookup demo
│   ├── name_lookup.cpp         # Simple name hash demo
│   └── simd_demo.cpp           # SIMD demonstration
├── tests/
│   ├── test_operations.cpp     # Operation tests
│   └── test_types.cpp          # Type behavior tests
├── verify/
│   ├── verify_ops.py           # Z3 verification
│   ├── llm_prompt_z3_gen.md    # Auto-generate verification
│   ├── requirements.txt        # Python dependencies
│   └── README.md
├── DefensiveToolkit.sln        # Visual Studio solution
├── DefensiveToolkit.vcxproj    # VS project (library)
├── Examples.vcxproj            # VS project (examples)
├── Tests.vcxproj               # VS project (tests)
├── Makefile                    # Build system
├── .gitignore
└── README.md

Design Principles

  1. Zero Runtime Overhead - All polymorphism resolved at compile-time via templates
  2. Mathematical Correctness - Z3 proves variant equivalence before trusting implementation
  3. Build-Time Variance - Every compilation produces different binary via __TIME__/__DATE__
  4. Per-Use Variance - __COUNTER__ enables multiple variants in same build
  5. Type Safety - Concepts constrain templates; comparison operators never use variants
  6. No Exceptions - Pure header-only, types are infallible
  7. Modern C++26 - Leverages consteval, concepts, spaceship operator

Adding New Variants

  1. Add variant implementation to include/defensive_toolkit/operations.h
  2. Use verify/llm_prompt_z3_gen.md to generate Z3 verification
  3. Add verification to verify/verify_ops.py
  4. Run make verify to prove equivalence
  5. Run make test to validate runtime correctness

See verify/llm_prompt_z3_gen.md for automated workflow using LLMs.

Performance

  • Zero overhead: Release builds optimize to same performance as native operations
  • Binary size: Each variant adds ~10-50 bytes per instantiation
  • Compile time: Minimal impact (~5-10% increase)

Benchmark:

# Native loop
for (int i = 0; i < 1M; i++) result ^= data[i];

# PolyInt loop (same speed in -O2/-O3)
for (int i = 0; i < 1M; i++) result = result ^ PolyInt{data[i]};

Limitations

  • Requires C++26/latest (uses consteval, concepts, `<=>')
  • Header-only (can increase compile time for large projects)
  • No floating-point support (integral types only)

License

Copyright 2025 DOTW Limited. All rights reserved.

References

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors