Skip to content

Repository files navigation

ResourceExtractor

A command-line utility for extracting and replacing embedded assembly resources in ExcelDNA XLL add-in files. This tool enables code signing of assemblies that are packed inside XLL files, which is essential for enterprise deployment scenarios where all executables must be digitally signed.

The Problem

The starting XLL file that ExcelDNA uses contains pre-built .NET assemblies embedded as resources that are not signed. At runtime, ExcelDNA will extract and use these unsigned assemblies, often triggering tools like Microsoft Defender or InTune to block loading of unsigned assemblies and cause the addin to fail to load.

The Solution

ResourceExtractor allows you to:

  1. Extract embedded assemblies from an XLL file to disk
  2. Sign them using your preferred signing tool (signtool, AzureSignTool, etc.)
  3. Replace the resources in the XLL with the signed versions

The tool handles both ASSEMBLY (uncompressed) and ASSEMBLY_LZMA (LZMA-compressed) resource types, automatically decompressing and recompressing as needed.

Features

  • Extracts ASSEMBLY and ASSEMBLY_LZMA embedded resources from XLL files
  • Automatically decompresses LZMA resources for signing, recompresses on replacement
  • Skips already-signed assemblies to avoid redundant work (with -force override)
  • Adds .dll extension to extracted files for compatibility with signing tools
  • Cleans up extraction folders on each run to ensure fresh state
  • Non-zero exit codes on failure for CI/CD integration

Installation

Build from Source

Requires .NET 10 SDK.

dotnet publish -c Release -r win-x64

The output will be in bin/Release/net10.0/win-x64/publish/ResourceExtractor.exe.

Usage

Basic Commands

# Extract unsigned assemblies from an XLL file
ResourceExtractor.exe extract MyAddin.xll

# Replace assemblies with signed versions
ResourceExtractor.exe replace MyAddin.xll

# Extract all assemblies (even if already signed) for verification
ResourceExtractor.exe extract MyAddin.xll -force

Command Reference

extract

Extracts embedded assembly resources to folders alongside the XLL file.

ResourceExtractor.exe extract <file.xll> [-force]
  • Creates ASSEMBLY/ and ASSEMBLY_LZMA/ folders in the same directory as the XLL
  • Resources are saved with .dll extension (e.g., MYASSEMBLYMYASSEMBLY.dll)
  • LZMA-compressed resources are automatically decompressed
  • Already-signed assemblies are skipped unless -force is specified
  • Existing extraction folders are deleted before extraction

Options:

  • -force, -f, --force - Extract all resources regardless of signature status

replace

Replaces embedded resources with signed versions from the extraction folders.

ResourceExtractor.exe replace <file.xll>
  • Reads .dll files from ASSEMBLY/ and ASSEMBLY_LZMA/ folders
  • Strips .dll extension to match original resource names
  • LZMA resources are automatically recompressed before embedding
  • Only replaces resources that exist in the original XLL

Typical Workflow

# 1. Extract assemblies
ResourceExtractor.exe extract MyAddin-packed.xll

# 2. Sign the extracted DLLs
signtool sign /sha1 THUMBPRINT /tr http://timestamp.digicert.com /td sha256 ^
    ASSEMBLY\*.dll ASSEMBLY_LZMA\*.dll

# 3. Replace with signed versions
ResourceExtractor.exe replace MyAddin-packed.xll

# 4. Sign the XLL itself
signtool sign /sha1 THUMBPRINT /tr http://timestamp.digicert.com /td sha256 ^
    MyAddin-packed.xll

CI/CD Integration

Azure DevOps Pipeline Example

- task: PowerShell@2
  displayName: 'Sign XLL embedded assemblies'
  inputs:
    targetType: 'inline'
    script: |
      $xllFile = "$(Build.ArtifactStagingDirectory)/MyAddin-packed.xll"
      $resourceExtractor = "$(Build.SourcesDirectory)/Tools/ResourceExtractor.exe"
      
      # Extract unsigned assemblies
      & $resourceExtractor extract $xllFile
      
      # Collect files to sign
      $filesToSign = @()
      $filesToSign += Get-ChildItem -Path "ASSEMBLY" -Filter "*.dll" -ErrorAction SilentlyContinue
      $filesToSign += Get-ChildItem -Path "ASSEMBLY_LZMA" -Filter "*.dll" -ErrorAction SilentlyContinue
      
      if ($filesToSign.Count -gt 0) {
        # Sign with AzureSignTool
        azuresigntool sign -kvu "$(KeyVaultUrl)" -kvi "$(ClientId)" `
          -kvt "$(TenantId)" -kvs "$(ClientSecret)" -kvc "$(CertName)" `
          -tr http://timestamp.digicert.com $filesToSign.FullName
        
        # Replace with signed versions
        & $resourceExtractor replace $xllFile
      }
      
      # Clean up
      Remove-Item -Path "ASSEMBLY" -Recurse -Force -ErrorAction SilentlyContinue
      Remove-Item -Path "ASSEMBLY_LZMA" -Recurse -Force -ErrorAction SilentlyContinue

How It Works

Resource Types

ExcelDNA embeds assemblies as Windows PE resources with custom types:

  • ASSEMBLY - Uncompressed .NET assemblies
  • ASSEMBLY_LZMA - LZMA-compressed .NET assemblies (smaller XLL size)

Extraction Process

  1. Loads the XLL using Windows resource APIs (LoadLibraryEx)
  2. Enumerates resources of type ASSEMBLY and ASSEMBLY_LZMA
  3. For each resource:
    • Extracts the raw bytes
    • Decompresses if LZMA (5-byte header + 8-byte size + compressed data)
    • Checks for existing Authenticode signature
    • Writes to disk with .dll extension if unsigned (or if -force)

Replacement Process

  1. Reads signed DLLs from extraction folders
  2. For ASSEMBLY_LZMA resources, recompresses using LZMA
  3. Updates the XLL resources using Windows resource update APIs (BeginUpdateResource, UpdateResource, EndUpdateResource)

Signature Detection

The tool uses WinVerifyTrust to detect signatures. A resource is considered "signed" if it has any Authenticode signature present, regardless of trust chain validity. This means self-signed certificates and certificates not in the trust store are still detected as signed.

Requirements

  • Windows (uses Windows resource APIs)
  • .NET 10 Runtime (or self-contained build includes runtime)

Building

# Clone the repository
git clone https://github.com/yourusername/ResourceExtractor.git
cd ResourceExtractor

# Build framework-dependent (smaller, requires .NET 10 installed)
dotnet publish -c Release

# Build self-contained (larger, no runtime required)
dotnet publish -c Release -r win-x64 --self-contained true

License

MIT License - See LICENSE file for details.

Acknowledgments

  • ExcelDNA - The fantastic library that makes .NET Excel add-ins possible
  • LZMA SDK - For LZMA compression/decompression

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages