KWAJ Format Guide
Purpose
This guide provides comprehensive documentation for working with Microsoft KWAJ compressed installation files using Cabriolet. KWAJ is a flexible compression format used in Microsoft installation packages with support for multiple compression algorithms.
Concepts
What is a KWAJ File?
KWAJ files are Microsoft’s installation file compression format, commonly used for:
-
Software installation packages
-
Setup program data files
-
Compressed installer components
-
Distribution archives
-
Single-file compression with metadata
The format supports multiple compression methods and can embed original filenames.
KWAJ File Structure
A KWAJ file has a flexible header structure:
┌─────────────────────────┐
│ KWAJ Header │ Variable-length header
│ - Signature: "KWAJ" │
│ - Compression type │
│ - Data offset │
│ - Optional filename │
│ - Optional length │
│ - Optional extra data │
├─────────────────────────┤
│ Compressed Data │ Compressed with selected algorithm
│ │
└─────────────────────────┘KWAJ Header: Contains signature, compression method, and optional metadata.
Compression Type: Identifies which algorithm is used (none, XOR, SZDD, MSZIP).
Optional Fields: Filename, uncompressed length, and extra data can be included.
Compressed Data: File content compressed with the specified algorithm.
Compression Support
KWAJ files support multiple compression algorithms:
-
None (Type 0) - Uncompressed storage
-
XOR (Type 1) - Simple XOR obfuscation
-
SZDD (Type 2) - LZSS compression
-
MSZIP (Type 3) - DEFLATE compression
For detailed algorithm information, see link:.
Basic Operations
Extracting KWAJ Files
Decompress KWAJ files:
# Auto-detect output filename from header
cabriolet extract setup.kwj
# Specify output filename
cabriolet extract setup.kwj setup.exe
# For explicit format specification:
cabriolet extract --format kwaj setup.kwj setup.exerequire 'cabriolet'
decompressor = Cabriolet::KWAJ::Decompressor.new
header = decompressor.open('setup.kwj')
# Auto-detect output name from embedded filename
output = decompressor.auto_output_filename('setup.kwj', header)
# Extract the file
bytes = decompressor.extract(header, 'setup.kwj', output)
decompressor.close(header)
puts "Extracted to #{output} (#{bytes} bytes)"Extracted setup.kwj to setup.exe (1,048,576 bytes)Getting KWAJ Information
Display KWAJ file metadata:
cabriolet info setup.kwj
# For explicit format specification:
cabriolet info --format kwaj setup.kwjrequire 'cabriolet'
decompressor = Cabriolet::KWAJ::Decompressor.new
header = decompressor.open('setup.kwj')
puts "Compression: #{header.compression_name}"
puts "Data offset: #{header.data_offset}"
puts "Uncompressed size: #{header.length || 'unknown'}"
puts "Original filename: #{header.filename}" if header.filename
if header.extra && !header.extra.empty?
puts "Extra data: #{header.extra_length} bytes"
end
decompressor.close(header)KWAJ File Information
==================================================
Filename: setup.kwj
Compression: MSZIP
Data offset: 64 bytes
Uncompressed size: 1,048,576 bytes
Original filename: setup.exe
Extra data: 16 bytes
Version: 1.0.0.0Compressing Files
Create KWAJ compressed files:
# Compress with default SZDD compression
cabriolet create setup.kwj setup.exe
# Compress with MSZIP
cabriolet create --compression=mszip setup.kwj setup.exe
# Include original filename in header
cabriolet create --filename=setup.exe data.kwj setup.exe
# Include extra metadata
cabriolet create --extra-data="Version: 1.0" setup.kwj setup.exe
# For explicit format specification:
cabriolet create --format kwaj --compression=mszip setup.kwj setup.exerequire 'cabriolet'
compressor = Cabriolet::KWAJ::Compressor.new
# Compress with MSZIP
bytes = compressor.compress(
'setup.exe',
'setup.kwj',
compression: :mszip,
filename: 'setup.exe',
include_length: true
)
puts "Compressed to setup.kwj (#{bytes} bytes)"Advanced Features
Compression Method Selection
Choose the appropriate compression method:
require 'cabriolet'
compressor = Cabriolet::KWAJ::Compressor.new
# No compression (fast, larger)
bytes = compressor.compress(
'data.bin',
'data.kwj',
compression: :none
)
# XOR obfuscation (very fast, minimal size reduction)
bytes = compressor.compress(
'data.bin',
'data.kwj',
compression: :xor
)
# SZDD compression (fast, good for text)
bytes = compressor.compress(
'readme.txt',
'readme.kwj',
compression: :szdd
)
# MSZIP compression (best ratio, slower)
bytes = compressor.compress(
'large.dat',
'large.kwj',
compression: :mszip
)Filename Embedding
Embed original filename for automatic reconstruction:
require 'cabriolet'
compressor = Cabriolet::KWAJ::Compressor.new
# Compress with embedded filename
bytes = compressor.compress(
'original-name.exe',
'compressed.kwj',
compression: :mszip,
filename: 'original-name.exe'
)
# Later, extract with automatic filename
decompressor = Cabriolet::KWAJ::Decompressor.new
header = decompressor.open('compressed.kwj')
if header.filename
puts "Original filename: #{header.filename}"
output = header.filename
else
output = 'compressed.out'
end
decompressor.extract(header, 'compressed.kwj', output)
decompressor.close(header)Extra Data Storage
Store custom metadata:
require 'cabriolet'
compressor = Cabriolet::KWAJ::Compressor.new
# Compress with extra metadata
metadata = "Version: 2.0.1\nBuild: 12345"
bytes = compressor.compress(
'setup.exe',
'setup.kwj',
compression: :mszip,
extra_data: metadata
)
# Read extra data
decompressor = Cabriolet::KWAJ::Decompressor.new
header = decompressor.open('setup.kwj')
if header.extra && !header.extra.empty?
puts "Metadata:"
puts header.extra
end
decompressor.close(header)Length Field Control
Control whether uncompressed length is stored:
require 'cabriolet'
compressor = Cabriolet::KWAJ::Compressor.new
# Include length in header
bytes = compressor.compress(
'data.bin',
'data.kwj',
compression: :mszip,
include_length: true
)
# Omit length (saves 4 bytes in header)
bytes = compressor.compress(
'data.bin',
'data-nolen.kwj',
compression: :mszip,
include_length: false
)Performance Optimization
Compression Algorithm Selection
Choose compression based on file type:
| Algorithm | Best For | Trade-off |
|---|---|---|
None | Pre-compressed files, testing | No compression, fastest |
XOR | Obfuscation, speed | Minimal compression, very fast |
SZDD | Text files, moderate size | Good for text, fast |
MSZIP | General purpose, maximum compression | Best ratio, slower |
Header Size Optimization
Minimize header size:
require 'cabriolet'
compressor = Cabriolet::KWAJ::Compressor.new
# Minimal header (no filename, no length, no extra)
bytes = compressor.compress(
'data.bin',
'minimal.kwj',
compression: :mszip
)
# Full header (filename, length, extra data)
bytes = compressor.compress(
'data.bin',
'full.kwj',
compression: :mszip,
filename: 'data.bin',
include_length: true,
extra_data: 'metadata'
)Common Use Cases
Installation File Compression
Compress setup files:
require 'cabriolet'
compressor = Cabriolet::KWAJ::Compressor.new
# Compress installer components
['setup.exe', 'readme.txt', 'license.txt'].each do |file|
output = "#{file}.kwj"
bytes = compressor.compress(
file,
output,
compression: :mszip,
filename: file,
include_length: true
)
puts "Compressed #{file} → #{output} (#{bytes} bytes)"
endBatch Extraction
Extract multiple KWAJ files:
require 'cabriolet'
require 'fileutils'
decompressor = Cabriolet::KWAJ::Decompressor.new
FileUtils.mkdir_p('extracted')
Dir.glob('installer/*.kwj').each do |kwaj_file|
header = decompressor.open(kwaj_file)
# Use embedded filename or generate from .kwj name
output = if header.filename
File.join('extracted', header.filename)
else
File.join('extracted', File.basename(kwaj_file, '.kwj'))
end
bytes = decompressor.extract(header, kwaj_file, output)
decompressor.close(header)
puts "Extracted: #{File.basename(output)} (#{bytes} bytes)"
endVersion Information Storage
Store version data in extra field:
require 'cabriolet'
require 'json'
compressor = Cabriolet::KWAJ::Compressor.new
# Create version metadata
version_info = {
version: '2.1.0',
build: 12345,
date: '2024-01-15'
}
# Compress with version info
bytes = compressor.compress(
'application.exe',
'application.kwj',
compression: :mszip,
filename: 'application.exe',
extra_data: JSON.generate(version_info)
)
# Extract and read version
decompressor = Cabriolet::KWAJ::Decompressor.new
header = decompressor.open('application.kwj')
if header.extra
info = JSON.parse(header.extra)
puts "Version: #{info['version']}"
puts "Build: #{info['build']}"
puts "Date: #{info['date']}"
end
decompressor.close(header)Troubleshooting
Common Errors
"Invalid KWAJ signature"
The file is not a valid KWAJ file:
# Check file signature
hexdump -C setup.kwj | head -1
# Should show: "KWAJ" (4B 57 41 4A)"Unsupported compression type"
Unknown compression method:
cabriolet info setup.kwj # Shows compression type"Decompression failed"
Corrupted compressed data. Check compression type:
cabriolet info setup.kwj
# Verify compression method is supported"Missing filename in header"
No embedded filename. Specify output explicitly:
cabriolet extract setup.kwj output.exeValidation
Verify KWAJ file integrity:
require 'cabriolet'
begin
decompressor = Cabriolet::KWAJ::Decompressor.new
header = decompressor.open('setup.kwj')
puts "KWAJ file is valid"
puts "Compression: #{header.compression_name}"
puts "Data offset: #{header.data_offset}"
if header.length
puts "Uncompressed size: #{header.length} bytes"
end
decompressor.close(header)
rescue Cabriolet::FormatError => e
puts "Invalid KWAJ file: #{e.message}"
rescue Cabriolet::CorruptionError => e
puts "Corrupted KWAJ: #{e.message}"
endBest practices
-
Include metadata: Store filename and length for better usability
-
Choose appropriate compression:
-
MSZIP for general files
-
SZDD for text files
-
None for pre-compressed files
-
XOR only for obfuscation
-
-
Use extra data wisely: Store version info or checksums
-
Validate after compression: Test extraction before distribution
-
Handle missing filename: Always have fallback for output naming
-
Document compression method: Note which algorithm was used
Format Specifications
File Signature
KWAJ files start with the signature "KWAJ":
Offset Bytes Description
0x0000 4 Signature: "KWAJ" (0x4B 0x57 0x41 0x4A)
0x0004 2 Compression method
0x0006 2 Data offset (header size)
0x0008 ... Optional fields (flags determine presence)Compression Types
-
0- None (uncompressed) -
1- XOR (simple obfuscation) -
2- SZDD (LZSS compression) -
3- MSZIP (DEFLATE compression)
Optional Fields
Header can include:
-
Filename: Null-terminated string
-
Length: 4-byte uncompressed size
-
Extra data: Variable-length custom data
For complete format specifications, see Format Specifications.
Next steps
-
Learn about compression algorithms
-
Review extracting files
-
Study Ruby API