Aller au contenu

Outils d'assemblage Web (WASM) Cheat Sheet

Copier toutes les commandes Générer PDF

Aperçu général

WebAssembly (WASM) est un format d'instruction binaire pour une machine virtuelle à pile, conçu comme une cible de compilation portable pour les langages de programmation. Comme WASM devient de plus en plus courant dans les applications web et même les logiciels malveillants, des outils spécialisés sont essentiels pour l'analyse, la décompilation et la recherche de sécurité.

C'est-à-dire Key Tools: wasm-decompile, Binaryen, WABT (WebAssembly Binary Toolkit), wasm2c, wasm-objdump et divers outils d'analyse par navigateur pour une analyse binaire complète de WASM.

Installation et configuration

WABT (WebAssembly Binary Toolkit)

# Install via package manager (Ubuntu/Debian)
sudo apt-get update
sudo apt-get install wabt

# Install via Homebrew (macOS)
brew install wabt

# Build from source
git clone --recursive https://github.com/WebAssembly/wabt
cd wabt
mkdir build && cd build
cmake ..
cmake --build .

# Add to PATH
export PATH=$PATH:/path/to/wabt/build

# Verify installation
wasm-objdump --version
wasm2wat --version
wat2wasm --version

# Install additional tools
sudo apt-get install nodejs npm
npm install -g @webassembly/wabt

Binaires

# Install via package manager
sudo apt-get install binaryen

# Install via npm
npm install -g binaryen

# Build from source
git clone https://github.com/WebAssembly/binaryen.git
cd binaryen
cmake . && make

# Verify installation
wasm-opt --version
wasm-dis --version
wasm-as --version

# Install Python bindings
pip install binaryen

# Test installation
python -c "import binaryen; print('Binaryen installed successfully')"
```_

### wasm-décompile et outils supplémentaires
```bash
# Install wasm-decompile (part of WABT)
# Already included with WABT installation

# Install wasm2c
git clone https://github.com/WebAssembly/wasm2c
cd wasm2c
make

# Install Wasmer (WASM runtime)
curl https://get.wasmer.io -sSfL | sh
source ~/.wasmer/wasmer.sh

# Install Wasmtime (another WASM runtime)
curl https://wasmtime.dev/install.sh -sSf | bash

# Install wasm-pack (for Rust)
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

# Install Emscripten (for C/C++ to WASM)
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
./emsdk install latest
./emsdk activate latest
source ./emsdk_env.sh

# Verify all tools
which wasm-objdump wasm-decompile wasm-opt wasmer wasmtime
```_

### Configuration des outils basés sur le navigateur
```bash
# Install browser extensions and tools
# Chrome DevTools WASM debugging (built-in)
# Firefox Developer Tools WASM support (built-in)

# Install wasm-pack for web development
npm install -g wasm-pack

# Install wasmtime-js for Node.js
npm install @bytecodealliance/wasmtime

# Set up local web server for WASM analysis
python3 -m http.server 8000
# or
npx serve .

# Install additional analysis tools
npm install -g wasm-strip
npm install -g wasm-snip
npm install -g twiggy  # WASM code size profiler

Analyse de base

Information et structure des dossiers

# Get basic file information
file sample.wasm
hexdump -C sample.wasm | head -20

# Check WASM magic number (0x00 0x61 0x73 0x6d)
xxd sample.wasm | head -1

# Get WASM module information
wasm-objdump -h sample.wasm

# Display all sections
wasm-objdump -s sample.wasm

# Show section details
wasm-objdump -x sample.wasm

# Display imports and exports
wasm-objdump -j import sample.wasm
wasm-objdump -j export sample.wasm

# Show function signatures
wasm-objdump -j type sample.wasm

# Display function bodies
wasm-objdump -j code sample.wasm

# Show data sections
wasm-objdump -j data sample.wasm

# Display custom sections
wasm-objdump -j custom sample.wasm

# Get detailed disassembly
wasm-objdump -d sample.wasm

# Show relocation information
wasm-objdump -r sample.wasm

# Display symbol table
wasm-objdump -t sample.wasm

La conversion WASM en WAT

# Convert WASM binary to WAT (WebAssembly Text)
wasm2wat sample.wasm -o sample.wat

# Convert with verbose output
wasm2wat sample.wasm -v -o sample.wat

# Convert with function names
wasm2wat sample.wasm --generate-names -o sample.wat

# Convert with inline exports
wasm2wat sample.wasm --inline-exports -o sample.wat

# Convert with folded expressions
wasm2wat sample.wasm --fold-exprs -o sample.wat

# Convert specific sections only
wasm2wat sample.wasm --no-debug-names -o sample.wat

# Convert with custom formatting
wasm2wat sample.wasm --no-check -o sample.wat

# Batch conversion
for file in *.wasm; do
    wasm2wat "$file" -o "${file%.wasm}.wat"
done

# Convert WAT back to WASM
wat2wasm sample.wat -o sample.wasm

# Validate WASM file
wat2wasm sample.wat --validate

Décompilation

# Decompile WASM to C-like pseudocode
wasm-decompile sample.wasm -o sample.dcmp

# Decompile with function names
wasm-decompile sample.wasm --generate-names -o sample.dcmp

# Decompile with inline exports
wasm-decompile sample.wasm --inline-exports -o sample.dcmp

# Decompile with variable names
wasm-decompile sample.wasm --name-all -o sample.dcmp

# Advanced decompilation options
wasm-decompile sample.wasm \
    --generate-names \
    --inline-exports \
    --fold-exprs \
    --name-all \
    -o sample_detailed.dcmp

# Decompile specific functions
wasm-decompile sample.wasm --func-index=0 -o function0.dcmp
wasm-decompile sample.wasm --func-name=main -o main_function.dcmp

# Batch decompilation
for file in *.wasm; do
    echo "Decompiling $file..."
    wasm-decompile "$file" --generate-names -o "${file%.wasm}.dcmp"
done

# Compare original and decompiled
diff -u original.c sample.dcmp

Techniques d'analyse avancées

Outils d'analyse binaire

# Optimize WASM binary
wasm-opt sample.wasm -O3 -o sample_optimized.wasm

# Analyze optimization opportunities
wasm-opt sample.wasm --print-features

# Remove debug information
wasm-opt sample.wasm --strip-debug -o sample_stripped.wasm

# Minify WASM
wasm-opt sample.wasm -Oz --strip-debug -o sample_minified.wasm

# Validate WASM module
wasm-opt sample.wasm --validate

# Print module structure
wasm-opt sample.wasm --print

# Print function CFG
wasm-opt sample.wasm --print-function-map

# Analyze memory usage
wasm-opt sample.wasm --print-stack-ir

# Dead code elimination
wasm-opt sample.wasm --dce -o sample_dce.wasm

# Inline functions
wasm-opt sample.wasm --inline-functions-with-loops -o sample_inlined.wasm

# Constant folding
wasm-opt sample.wasm --const-hoisting -o sample_const.wasm

# Loop optimization
wasm-opt sample.wasm --optimize-level=3 --shrink-level=1 -o sample_loops.wasm

Mémoire et analyse des données

# Extract data sections
wasm-objdump -j data sample.wasm > data_sections.txt

# Analyze memory layout
wasm-objdump -s sample.wasm | grep -A 10 "Contents of section data"

# Extract strings from WASM
strings sample.wasm > extracted_strings.txt

# Search for specific patterns
hexdump -C sample.wasm | grep -i "password\|secret\|key"

# Analyze imports for API calls
wasm-objdump -j import sample.wasm | grep -E "(func|memory|table|global)"

# Check for suspicious imports
wasm-objdump -j import sample.wasm | grep -E "(eval|document|window|fetch|XMLHttpRequest)"

# Analyze exports
wasm-objdump -j export sample.wasm

# Extract function names
wasm-objdump -j export sample.wasm | grep "func" | awk '{print $4}'

# Analyze global variables
wasm-objdump -j global sample.wasm

# Check memory configuration
wasm-objdump -j memory sample.wasm

# Analyze table sections
wasm-objdump -j table sample.wasm

# Extract custom sections
wasm-objdump -j custom sample.wasm

Analyse du débit de contrôle

# Generate control flow graph
wasm-opt sample.wasm --print-function-map > cfg.txt

# Analyze function calls
wasm-objdump -d sample.wasm | grep "call"

# Find indirect calls
wasm-objdump -d sample.wasm | grep "call_indirect"

# Analyze branches
wasm-objdump -d sample.wasm | grep -E "(br|br_if|br_table)"

# Find loops
wasm-objdump -d sample.wasm | grep -E "(loop|block)"

# Analyze exception handling
wasm-objdump -d sample.wasm | grep -E "(try|catch|throw)"

# Extract function boundaries
awk '/^[0-9a-f]+.*func/ {print $0}' sample.wat

# Analyze stack operations
wasm-objdump -d sample.wasm | grep -E "(local\.|global\.|i32\.|i64\.|f32\.|f64\.)"

# Find return statements
wasm-objdump -d sample.wasm | grep "return"

# Analyze unreachable code
wasm-objdump -d sample.wasm | grep "unreachable"

Analyse de la sécurité

Techniques d'analyse des logiciels malveillants

# Check for obfuscation indicators
wasm-objdump -h sample.wasm | grep -E "(custom|unknown)"

# Analyze suspicious function names
wasm2wat sample.wasm --generate-names | grep -E "(eval|exec|shell|cmd)"

# Check for dynamic code generation
wasm-objdump -d sample.wasm | grep -E "(memory\.grow|table\.grow)"

# Analyze memory access patterns
wasm-objdump -d sample.wasm | grep -E "(load|store)" | head -20

# Check for anti-analysis techniques
wasm-objdump -d sample.wasm | grep -E "(unreachable|trap)"

# Analyze imports for suspicious APIs
wasm-objdump -j import sample.wasm | grep -v -E "(Math|console|Date)"

# Check for encrypted/encoded data
hexdump -C sample.wasm | grep -E "([0-9a-f]{2} ){16}" | head -10

# Analyze function complexity
wasm-decompile sample.wasm | grep -c "if\|while\|for"

# Check for self-modification
wasm-objdump -d sample.wasm | grep -E "(memory\.copy|memory\.fill)"

# Analyze exception handling for evasion
wasm-objdump -d sample.wasm | grep -E "(try|catch)" -A 5 -B 5

Détection de vulnérabilité

# Check for buffer overflow vulnerabilities
wasm-objdump -d sample.wasm | grep -E "(memory\.size|memory\.grow)" -A 3 -B 3

# Analyze bounds checking
wasm-decompile sample.wasm | grep -E "bounds|check|limit"

# Check for integer overflow
wasm-objdump -d sample.wasm | grep -E "(add|mul|div)" | head -10

# Analyze memory safety
wasm-opt sample.wasm --print-stack-ir | grep -E "load|store"

# Check for use-after-free patterns
wasm-decompile sample.wasm | grep -E "free|malloc|realloc" -A 5 -B 5

# Analyze type confusion
wasm-objdump -j type sample.wasm

# Check for format string vulnerabilities
strings sample.wasm | grep -E "%[sdxp]"

# Analyze input validation
wasm-decompile sample.wasm | grep -E "validate|sanitize|check"

# Check for race conditions
wasm-objdump -d sample.wasm | grep -E "(atomic|shared)"

# Analyze cryptographic usage
strings sample.wasm | grep -E "(crypto|hash|encrypt|decrypt|key|iv)"

Analyse de l'évacuation de la boîte à sable

# Check for DOM manipulation
wasm-objdump -j import sample.wasm | grep -E "(document|window|DOM)"

# Analyze file system access
wasm-objdump -j import sample.wasm | grep -E "(file|read|write|open)"

# Check for network access
wasm-objdump -j import sample.wasm | grep -E "(fetch|xhr|websocket|http)"

# Analyze process creation
wasm-objdump -j import sample.wasm | grep -E "(exec|spawn|process)"

# Check for memory access outside sandbox
wasm-objdump -d sample.wasm | grep -E "memory\.(load|store)" -A 2 | grep -E "offset="

# Analyze shared memory usage
wasm-objdump -j memory sample.wasm | grep "shared"

# Check for worker thread creation
wasm-objdump -j import sample.wasm | grep -E "(worker|thread)"

# Analyze timing attacks
wasm-objdump -j import sample.wasm | grep -E "(performance|time|date)"

# Check for side-channel attacks
wasm-decompile sample.wasm | grep -E "cache|timing|power"

Analyse dynamique

Analyse des temps d'exécution avec Wasmer

# Run WASM with Wasmer
wasmer run sample.wasm

# Run with arguments
wasmer run sample.wasm -- arg1 arg2 arg3

# Run with environment variables
ENV_VAR=value wasmer run sample.wasm

# Enable debugging
wasmer run sample.wasm --debug

# Run with memory limits
wasmer run sample.wasm --max-memory=1GB

# Run with CPU limits
wasmer run sample.wasm --max-time=30s

# Enable tracing
wasmer run sample.wasm --trace

# Run with custom imports
wasmer run sample.wasm --mapdir=/host:/guest

# Profile execution
time wasmer run sample.wasm

# Run with different engines
wasmer run sample.wasm --backend=cranelift
wasmer run sample.wasm --backend=llvm
wasmer run sample.wasm --backend=singlepass

Analyse des temps d'exécution avec Wasmtime

# Run WASM with Wasmtime
wasmtime run sample.wasm

# Run with arguments
wasmtime run sample.wasm -- arg1 arg2 arg3

# Enable debugging
wasmtime run --debug sample.wasm

# Run with memory limits
wasmtime run --max-memory=1073741824 sample.wasm  # 1GB

# Enable optimization
wasmtime run --optimize sample.wasm

# Run with fuel limits (instruction counting)
wasmtime run --fuel=1000000 sample.wasm

# Enable epoch interruption
wasmtime run --epoch-interruption sample.wasm

# Run with WASI support
wasmtime run --wasi-modules=wasi_snapshot_preview1 sample.wasm

# Profile execution
wasmtime run --profile=jitdump sample.wasm

# Run with custom configuration
wasmtime run --config wasmtime.toml sample.wasm

# Enable memory profiling
wasmtime run --profile=perfmap sample.wasm

Analyse dynamique basée sur le navigateur

// Load and analyze WASM in browser
async function analyzeWasm(wasmUrl) {
    try {
        // Fetch WASM binary
        const response = await fetch(wasmUrl);
        const wasmBytes = await response.arrayBuffer();

        // Compile WASM module
        const wasmModule = await WebAssembly.compile(wasmBytes);

        // Get module imports
        const imports = WebAssembly.Module.imports(wasmModule);
        console.log('Imports:', imports);

        // Get module exports
        const exports = WebAssembly.Module.exports(wasmModule);
        console.log('Exports:', exports);

        // Create instance with instrumented imports
        const instrumentedImports = createInstrumentedImports();
        const wasmInstance = await WebAssembly.instantiate(wasmModule, instrumentedImports);

        // Analyze exported functions
        for (const exp of exports) {
            if (exp.kind === 'function') {
                console.log(`Function: ${exp.name}`);

                // Hook function calls
                const originalFunc = wasmInstance.exports[exp.name];
                wasmInstance.exports[exp.name] = function(...args) {
                    console.log(`Calling ${exp.name} with args:`, args);
                    const result = originalFunc.apply(this, args);
                    console.log(`${exp.name} returned:`, result);
                    return result;
                };
            }
        }

        return wasmInstance;

    } catch (error) {
        console.error('WASM analysis failed:', error);
    }
}

// Create instrumented imports for monitoring
function createInstrumentedImports() {
    return {
        env: {
            // Hook memory operations
            memory: new WebAssembly.Memory({ initial: 256, maximum: 256 }),

            // Hook console operations
            console_log: function(ptr, len) {
                const memory = new Uint8Array(this.memory.buffer);
                const str = new TextDecoder().decode(memory.slice(ptr, ptr + len));
                console.log('WASM console.log:', str);
            },

            // Hook mathematical operations
            Math_random: function() {
                const value = Math.random();
                console.log('WASM Math.random():', value);
                return value;
            },

            // Hook date operations
            Date_now: function() {
                const value = Date.now();
                console.log('WASM Date.now():', value);
                return value;
            }
        },

        // Hook WASI operations
        wasi_snapshot_preview1: {
            fd_write: function(fd, iovs, iovs_len, nwritten) {
                console.log(`WASM fd_write: fd=${fd}, iovs_len=${iovs_len}`);
                return 0;
            },

            proc_exit: function(code) {
                console.log(`WASM proc_exit: code=${code}`);
            }
        }
    };
}

// Memory analysis functions
function analyzeWasmMemory(wasmInstance) {
    const memory = wasmInstance.exports.memory;
    if (!memory) return;

    const buffer = new Uint8Array(memory.buffer);

    // Search for strings
    const strings = [];
    let currentString = '';
    for (let i = 0; i < buffer.length; i++) {
        if (buffer[i] >= 32 && buffer[i] <= 126) {
            currentString += String.fromCharCode(buffer[i]);
        } else {
            if (currentString.length > 4) {
                strings.push(currentString);
            }
            currentString = '';
        }
    }

    console.log('Found strings:', strings);

    // Analyze memory patterns
    const patterns = {
        nullBytes: 0,
        highEntropy: 0,
        repeatingPatterns: 0
    };

    for (let i = 0; i < buffer.length; i++) {
        if (buffer[i] === 0) patterns.nullBytes++;
        if (buffer[i] > 127) patterns.highEntropy++;
        if (i > 0 && buffer[i] === buffer[i-1]) patterns.repeatingPatterns++;
    }

    console.log('Memory patterns:', patterns);
}

// Function call tracing
function traceWasmExecution(wasmInstance) {
    const callStack = [];

    // Hook all exported functions
    for (const [name, func] of Object.entries(wasmInstance.exports)) {
        if (typeof func === 'function') {
            wasmInstance.exports[name] = function(...args) {
                callStack.push({ function: name, args: args, timestamp: Date.now() });
                console.log(`→ ${name}(${args.join(', ')})`);

                try {
                    const result = func.apply(this, args);
                    console.log(`← ${name} returned: ${result}`);
                    return result;
                } catch (error) {
                    console.log(`← ${name} threw: ${error}`);
                    throw error;
                } finally {
                    callStack.pop();
                }
            };
        }
    }

    return callStack;
}

Scénarios d'analyse automatisés

Script d'analyse complète WASM

#!/usr/bin/env python3
"""
Comprehensive WASM Analysis Tool
Analyzes WebAssembly binaries for security issues and malware indicators
"""

import os
import sys
import subprocess
import json
import hashlib
import re
from pathlib import Path

class WasmAnalyzer:
    def __init__(self, wasm_file):
        self.wasm_file = Path(wasm_file)
        self.analysis_results = {
            'file_info': {},
            'structure': {},
            'security': {},
            'strings': [],
            'functions': [],
            'imports': [],
            'exports': [],
            'suspicious_indicators': []
        }

    def analyze(self):
        """Perform comprehensive WASM analysis"""

        print(f"Analyzing WASM file: {self.wasm_file}")

        # Basic file analysis
        self.analyze_file_info()

        # Structure analysis
        self.analyze_structure()

        # Security analysis
        self.analyze_security()

        # Extract strings
        self.extract_strings()

        # Analyze functions
        self.analyze_functions()

        # Analyze imports/exports
        self.analyze_imports_exports()

        # Generate report
        self.generate_report()

        return self.analysis_results

    def analyze_file_info(self):
        """Analyze basic file information"""

        stat = self.wasm_file.stat()

        with open(self.wasm_file, 'rb') as f:
            content = f.read()

        self.analysis_results['file_info'] = {
            'size': stat.st_size,
            'md5': hashlib.md5(content).hexdigest(),
            'sha1': hashlib.sha1(content).hexdigest(),
            'sha256': hashlib.sha256(content).hexdigest(),
            'magic': content[:4].hex() if len(content) >= 4 else '',
            'valid_magic': content[:4] == b'\x00asm'
        }

    def analyze_structure(self):
        """Analyze WASM structure"""

        try:
            # Get section information
            result = subprocess.run(['wasm-objdump', '-h', str(self.wasm_file)], 
                                  capture_output=True, text=True)

            if result.returncode == 0:
                sections = []
                for line in result.stdout.split('\n'):
                    if 'Section Details:' in line or line.strip().startswith('Type'):
                        continue
                    if line.strip() and not line.startswith('Sections:'):
                        sections.append(line.strip())

                self.analysis_results['structure']['sections'] = sections

            # Get detailed structure
            result = subprocess.run(['wasm-objdump', '-x', str(self.wasm_file)], 
                                  capture_output=True, text=True)

            if result.returncode == 0:
                self.analysis_results['structure']['detailed'] = result.stdout

        except FileNotFoundError:
            print("Warning: wasm-objdump not found. Install WABT for detailed analysis.")

    def analyze_security(self):
        """Perform security analysis"""

        security_issues = []

        # Check for suspicious imports
        try:
            result = subprocess.run(['wasm-objdump', '-j', 'import', str(self.wasm_file)], 
                                  capture_output=True, text=True)

            if result.returncode == 0:
                suspicious_imports = [
                    'eval', 'Function', 'document', 'window', 'XMLHttpRequest',
                    'fetch', 'WebSocket', 'Worker', 'SharedArrayBuffer'
                ]

                for imp in suspicious_imports:
                    if imp in result.stdout:
                        security_issues.append(f"Suspicious import: {imp}")

        except FileNotFoundError:
            pass

        # Check for obfuscation indicators
        with open(self.wasm_file, 'rb') as f:
            content = f.read()

        # High entropy check (possible encryption/obfuscation)
        entropy = self.calculate_entropy(content)
        if entropy > 7.5:
            security_issues.append(f"High entropy detected: {entropy:.2f}")

        # Check for unusual section names
        try:
            result = subprocess.run(['wasm-objdump', '-j', 'custom', str(self.wasm_file)], 
                                  capture_output=True, text=True)

            if result.returncode == 0 and result.stdout.strip():
                security_issues.append("Custom sections detected (possible obfuscation)")

        except FileNotFoundError:
            pass

        self.analysis_results['security']['issues'] = security_issues

    def calculate_entropy(self, data):
        """Calculate Shannon entropy of data"""

        if not data:
            return 0

        # Count byte frequencies
        frequencies = {}
        for byte in data:
            frequencies[byte] = frequencies.get(byte, 0) + 1

        # Calculate entropy
        entropy = 0
        data_len = len(data)

        for count in frequencies.values():
            probability = count / data_len
            if probability > 0:
                entropy -= probability * (probability.bit_length() - 1)

        return entropy

    def extract_strings(self):
        """Extract strings from WASM binary"""

        try:
            result = subprocess.run(['strings', str(self.wasm_file)], 
                                  capture_output=True, text=True)

            if result.returncode == 0:
                strings = [s.strip() for s in result.stdout.split('\n') if len(s.strip()) > 3]
                self.analysis_results['strings'] = strings[:100]  # Limit to first 100

                # Check for suspicious strings
                suspicious_patterns = [
                    r'password', r'secret', r'key', r'token', r'auth',
                    r'eval\(', r'document\.', r'window\.', r'\.exe',
                    r'cmd\.exe', r'powershell', r'bash', r'sh\s'
                ]

                for string in strings:
                    for pattern in suspicious_patterns:
                        if re.search(pattern, string, re.IGNORECASE):
                            self.analysis_results['suspicious_indicators'].append(
                                f"Suspicious string: {string}"
                            )

        except FileNotFoundError:
            print("Warning: strings command not found")

    def analyze_functions(self):
        """Analyze WASM functions"""

        try:
            # Get function information
            result = subprocess.run(['wasm-objdump', '-j', 'code', str(self.wasm_file)], 
                                  capture_output=True, text=True)

            if result.returncode == 0:
                functions = []
                current_function = None

                for line in result.stdout.split('\n'):
                    if 'func[' in line:
                        if current_function:
                            functions.append(current_function)
                        current_function = {'name': line.strip(), 'instructions': []}
                    elif current_function and line.strip():
                        current_function['instructions'].append(line.strip())

                if current_function:
                    functions.append(current_function)

                self.analysis_results['functions'] = functions

        except FileNotFoundError:
            pass

    def analyze_imports_exports(self):
        """Analyze imports and exports"""

        try:
            # Analyze imports
            result = subprocess.run(['wasm-objdump', '-j', 'import', str(self.wasm_file)], 
                                  capture_output=True, text=True)

            if result.returncode == 0:
                imports = []
                for line in result.stdout.split('\n'):
                    if 'import[' in line or 'func' in line or 'memory' in line:
                        imports.append(line.strip())

                self.analysis_results['imports'] = imports

            # Analyze exports
            result = subprocess.run(['wasm-objdump', '-j', 'export', str(self.wasm_file)], 
                                  capture_output=True, text=True)

            if result.returncode == 0:
                exports = []
                for line in result.stdout.split('\n'):
                    if 'export[' in line or 'func' in line:
                        exports.append(line.strip())

                self.analysis_results['exports'] = exports

        except FileNotFoundError:
            pass

    def generate_report(self):
        """Generate analysis report"""

        report_file = self.wasm_file.with_suffix('.analysis.json')

        with open(report_file, 'w') as f:
            json.dump(self.analysis_results, f, indent=2)

        print(f"Analysis report saved to: {report_file}")

        # Print summary
        print("\n=== WASM Analysis Summary ===")
        print(f"File: {self.wasm_file}")
        print(f"Size: {self.analysis_results['file_info']['size']} bytes")
        print(f"SHA256: {self.analysis_results['file_info']['sha256']}")
        print(f"Valid WASM: {self.analysis_results['file_info']['valid_magic']}")

        if self.analysis_results['security']['issues']:
            print(f"\nSecurity Issues ({len(self.analysis_results['security']['issues'])}):")
            for issue in self.analysis_results['security']['issues']:
                print(f"  - {issue}")

        if self.analysis_results['suspicious_indicators']:
            print(f"\nSuspicious Indicators ({len(self.analysis_results['suspicious_indicators'])}):")
            for indicator in self.analysis_results['suspicious_indicators'][:10]:
                print(f"  - {indicator}")

        print(f"\nFunctions: {len(self.analysis_results['functions'])}")
        print(f"Imports: {len(self.analysis_results['imports'])}")
        print(f"Exports: {len(self.analysis_results['exports'])}")
        print(f"Strings: {len(self.analysis_results['strings'])}")

def main():
    if len(sys.argv) != 2:
        print("Usage: python wasm_analyzer.py <wasm_file>")
        sys.exit(1)

    wasm_file = sys.argv[1]

    if not os.path.exists(wasm_file):
        print(f"Error: File {wasm_file} not found")
        sys.exit(1)

    analyzer = WasmAnalyzer(wasm_file)
    results = analyzer.analyze()

if __name__ == "__main__":
    main()

Scénario d'analyse du lot WASM

#!/bin/bash
# Batch WASM analysis script

WASM_DIR="$1"
OUTPUT_DIR="$2"

if [ -z "$WASM_DIR" ] || [ -z "$OUTPUT_DIR" ]; then
    echo "Usage: $0 <wasm_directory> <output_directory>"
    exit 1
fi

mkdir -p "$OUTPUT_DIR"

echo "Starting batch WASM analysis..."
echo "Input directory: $WASM_DIR"
echo "Output directory: $OUTPUT_DIR"

# Find all WASM files
find "$WASM_DIR" -name "*.wasm" -type f | while read -r wasm_file; do
    echo "Processing: $wasm_file"

    # Create output subdirectory
    rel_path=$(realpath --relative-to="$WASM_DIR" "$wasm_file")
    output_subdir="$OUTPUT_DIR/$(dirname "$rel_path")"
    mkdir -p "$output_subdir"

    # Base name for output files
    base_name=$(basename "$wasm_file" .wasm)
    output_base="$output_subdir/$base_name"

    # Basic analysis
    echo "  - Basic analysis..."
    wasm-objdump -h "$wasm_file" > "${output_base}_headers.txt" 2>/dev/null
    wasm-objdump -x "$wasm_file" > "${output_base}_details.txt" 2>/dev/null

    # Disassembly
    echo "  - Disassembly..."
    wasm-objdump -d "$wasm_file" > "${output_base}_disasm.txt" 2>/dev/null

    # Decompilation
    echo "  - Decompilation..."
    wasm-decompile "$wasm_file" -o "${output_base}_decompiled.c" 2>/dev/null

    # WAT conversion
    echo "  - WAT conversion..."
    wasm2wat "$wasm_file" -o "${output_base}.wat" 2>/dev/null

    # String extraction
    echo "  - String extraction..."
    strings "$wasm_file" > "${output_base}_strings.txt" 2>/dev/null

    # Security analysis
    echo "  - Security analysis..."
    {
        echo "=== IMPORTS ==="
        wasm-objdump -j import "$wasm_file" 2>/dev/null
        echo -e "\n=== EXPORTS ==="
        wasm-objdump -j export "$wasm_file" 2>/dev/null
        echo -e "\n=== SUSPICIOUS STRINGS ==="
        strings "$wasm_file" | grep -E -i "(eval|document|window|fetch|xhr|websocket|crypto|password|secret|key)" 2>/dev/null
    } > "${output_base}_security.txt"

    # File hash
    echo "  - Computing hashes..."
    {
        echo "MD5: $(md5sum "$wasm_file" | cut -d' ' -f1)"
        echo "SHA1: $(sha1sum "$wasm_file" | cut -d' ' -f1)"
        echo "SHA256: $(sha256sum "$wasm_file" | cut -d' ' -f1)"
        echo "Size: $(stat -c%s "$wasm_file") bytes"
    } > "${output_base}_hashes.txt"

    echo "  - Complete!"
done

# Generate summary report
echo "Generating summary report..."
{
    echo "WASM Batch Analysis Summary"
    echo "=========================="
    echo "Analysis Date: $(date)"
    echo "Input Directory: $WASM_DIR"
    echo "Output Directory: $OUTPUT_DIR"
    echo ""

    total_files=$(find "$WASM_DIR" -name "*.wasm" -type f | wc -l)
    echo "Total WASM files analyzed: $total_files"

    echo ""
    echo "File Sizes:"
    find "$WASM_DIR" -name "*.wasm" -type f -exec stat -c"%s %n" {} \; | sort -n | tail -10

    echo ""
    echo "Most Common Imports:"
    find "$OUTPUT_DIR" -name "*_security.txt" -exec grep -h "import\[" {} \; | sort | uniq -c | sort -nr | head -10

    echo ""
    echo "Most Common Exports:"
    find "$OUTPUT_DIR" -name "*_security.txt" -exec grep -h "export\[" {} \; | sort | uniq -c | sort -nr | head -10

} > "$OUTPUT_DIR/analysis_summary.txt"

echo "Batch analysis complete!"
echo "Summary report: $OUTPUT_DIR/analysis_summary.txt"

Intégration avec les flux de travail d'ingénierie inverse

WASM dans le pipeline d'analyse des logiciels malveillants

#!/usr/bin/env python3
"""
WASM Malware Analysis Pipeline
Integrates WASM analysis into broader malware analysis workflow
"""

import os
import json
import subprocess
import tempfile
from pathlib import Path

class WasmMalwareAnalyzer:
    def __init__(self, sample_path):
        self.sample_path = Path(sample_path)
        self.temp_dir = Path(tempfile.mkdtemp())
        self.analysis_results = {}

    def analyze_sample(self):
        """Analyze WASM sample for malware indicators"""

        # Extract WASM from various containers
        wasm_files = self.extract_wasm_files()

        if not wasm_files:
            print("No WASM files found in sample")
            return

        # Analyze each WASM file
        for wasm_file in wasm_files:
            print(f"Analyzing WASM file: {wasm_file}")

            analysis = {
                'static_analysis': self.static_analysis(wasm_file),
                'behavioral_analysis': self.behavioral_analysis(wasm_file),
                'threat_assessment': self.threat_assessment(wasm_file)
            }

            self.analysis_results[str(wasm_file)] = analysis

        # Generate IOCs
        self.generate_iocs()

        # Create YARA rules
        self.create_yara_rules()

        return self.analysis_results

    def extract_wasm_files(self):
        """Extract WASM files from various containers"""

        wasm_files = []

        # Direct WASM file
        if self.sample_path.suffix == '.wasm':
            return [self.sample_path]

        # Extract from HTML/JS files
        if self.sample_path.suffix in ['.html', '.htm', '.js']:
            wasm_files.extend(self.extract_from_web_files())

        # Extract from archives
        if self.sample_path.suffix in ['.zip', '.tar', '.gz']:
            wasm_files.extend(self.extract_from_archives())

        # Search for embedded WASM
        wasm_files.extend(self.find_embedded_wasm())

        return wasm_files

    def extract_from_web_files(self):
        """Extract WASM from HTML/JS files"""

        wasm_files = []

        with open(self.sample_path, 'r', encoding='utf-8', errors='ignore') as f:
            content = f.read()

        # Look for WASM loading patterns
        patterns = [
            r'fetch\(["\']([^"\']*\.wasm)["\']',
            r'WebAssembly\.instantiateStreaming\([^,]*["\']([^"\']*\.wasm)["\']',
            r'new\s+WebAssembly\.Module\([^)]*["\']([^"\']*\.wasm)["\']'
        ]

        import re
        for pattern in patterns:
            matches = re.findall(pattern, content)
            for match in matches:
                wasm_path = self.sample_path.parent / match
                if wasm_path.exists():
                    wasm_files.append(wasm_path)

        return wasm_files

    def extract_from_archives(self):
        """Extract WASM from archive files"""

        wasm_files = []
        extract_dir = self.temp_dir / "extracted"
        extract_dir.mkdir(exist_ok=True)

        try:
            if self.sample_path.suffix == '.zip':
                subprocess.run(['unzip', '-q', str(self.sample_path), '-d', str(extract_dir)])
            elif self.sample_path.suffix in ['.tar', '.gz']:
                subprocess.run(['tar', '-xf', str(self.sample_path), '-C', str(extract_dir)])

            # Find WASM files in extracted content
            for wasm_file in extract_dir.rglob('*.wasm'):
                wasm_files.append(wasm_file)

        except subprocess.CalledProcessError:
            pass

        return wasm_files

    def find_embedded_wasm(self):
        """Find embedded WASM in binary files"""

        wasm_files = []

        with open(self.sample_path, 'rb') as f:
            content = f.read()

        # Look for WASM magic bytes
        wasm_magic = b'\x00asm'
        offset = 0

        while True:
            pos = content.find(wasm_magic, offset)
            if pos == -1:
                break

            # Extract potential WASM module
            wasm_data = self.extract_wasm_module(content, pos)
            if wasm_data:
                wasm_file = self.temp_dir / f"embedded_{pos:08x}.wasm"
                with open(wasm_file, 'wb') as f:
                    f.write(wasm_data)
                wasm_files.append(wasm_file)

            offset = pos + 4

        return wasm_files

    def extract_wasm_module(self, data, start_pos):
        """Extract complete WASM module from binary data"""

        if start_pos + 8 > len(data):
            return None

        # Check magic and version
        if data[start_pos:start_pos+4] != b'\x00asm':
            return None

        version = int.from_bytes(data[start_pos+4:start_pos+8], 'little')
        if version != 1:
            return None

        # Parse sections to find module end
        pos = start_pos + 8

        while pos < len(data):
            if pos + 1 > len(data):
                break

            section_id = data[pos]
            pos += 1

            # Parse LEB128 size
            size, size_bytes = self.parse_leb128(data, pos)
            if size is None:
                break

            pos += size_bytes

            if pos + size > len(data):
                break

            pos += size

            # If we've read all standard sections, we're done
            if section_id == 0:  # Custom section at end
                break

        return data[start_pos:pos]

    def parse_leb128(self, data, pos):
        """Parse LEB128 encoded integer"""

        result = 0
        shift = 0
        size = 0

        while pos + size < len(data):
            byte = data[pos + size]
            size += 1

            result |= (byte & 0x7f) << shift
            shift += 7

            if (byte & 0x80) == 0:
                break

            if size > 5:  # Prevent infinite loop
                return None, 0

        return result, size

    def static_analysis(self, wasm_file):
        """Perform static analysis on WASM file"""

        analysis = {
            'file_info': {},
            'structure': {},
            'suspicious_indicators': [],
            'capabilities': []
        }

        # File information
        stat = wasm_file.stat()
        with open(wasm_file, 'rb') as f:
            content = f.read()

        analysis['file_info'] = {
            'size': stat.st_size,
            'sha256': hashlib.sha256(content).hexdigest(),
            'entropy': self.calculate_entropy(content)
        }

        # Analyze imports for capabilities
        try:
            result = subprocess.run(['wasm-objdump', '-j', 'import', str(wasm_file)], 
                                  capture_output=True, text=True)

            if result.returncode == 0:
                imports = result.stdout

                # Check for suspicious capabilities
                if 'fetch' in imports or 'XMLHttpRequest' in imports:
                    analysis['capabilities'].append('network_access')
                    analysis['suspicious_indicators'].append('Network access capability')

                if 'document' in imports or 'window' in imports:
                    analysis['capabilities'].append('dom_manipulation')
                    analysis['suspicious_indicators'].append('DOM manipulation capability')

                if 'eval' in imports or 'Function' in imports:
                    analysis['capabilities'].append('code_execution')
                    analysis['suspicious_indicators'].append('Dynamic code execution capability')

                if 'crypto' in imports:
                    analysis['capabilities'].append('cryptography')

                if 'Worker' in imports:
                    analysis['capabilities'].append('worker_threads')
                    analysis['suspicious_indicators'].append('Worker thread capability')

        except FileNotFoundError:
            pass

        # Check for obfuscation
        if analysis['file_info']['entropy'] > 7.5:
            analysis['suspicious_indicators'].append('High entropy (possible obfuscation)')

        return analysis

    def behavioral_analysis(self, wasm_file):
        """Perform behavioral analysis using sandboxed execution"""

        analysis = {
            'execution_trace': [],
            'network_activity': [],
            'file_operations': [],
            'api_calls': []
        }

        # Create instrumented environment
        instrumented_js = self.create_instrumented_environment()

        # Run WASM in controlled environment
        try:
            with tempfile.NamedTemporaryFile(mode='w', suffix='.js', delete=False) as f:
                f.write(instrumented_js)
                js_file = f.name

            # Execute with Node.js
            result = subprocess.run(['node', js_file, str(wasm_file)], 
                                  capture_output=True, text=True, timeout=30)

            if result.returncode == 0:
                # Parse execution trace
                for line in result.stdout.split('\n'):
                    if line.startswith('TRACE:'):
                        analysis['execution_trace'].append(line[6:])
                    elif line.startswith('NETWORK:'):
                        analysis['network_activity'].append(line[8:])
                    elif line.startswith('API:'):
                        analysis['api_calls'].append(line[4:])

            os.unlink(js_file)

        except (subprocess.TimeoutExpired, FileNotFoundError):
            analysis['execution_trace'].append('Execution timeout or Node.js not available')

        return analysis

    def create_instrumented_environment(self):
        """Create instrumented JavaScript environment for WASM execution"""

        return """
const fs = require('fs');
const path = require('path');

// Instrumented WebAssembly implementation
const originalInstantiate = WebAssembly.instantiate;
WebAssembly.instantiate = async function(source, imports) {
    console.log('TRACE: WebAssembly.instantiate called');

    // Instrument imports
    if (imports) {
        for (const [module, funcs] of Object.entries(imports)) {
            for (const [name, func] of Object.entries(funcs)) {
                if (typeof func === 'function') {
                    imports[module][name] = function(...args) {
                        console.log(`API: ${module}.${name}(${args.join(', ')})`);
                        return func.apply(this, args);
                    };
                }
            }
        }
    }

    const result = await originalInstantiate.call(this, source, imports);

    // Instrument exports
    if (result.instance && result.instance.exports) {
        for (const [name, func] of Object.entries(result.instance.exports)) {
            if (typeof func === 'function') {
                result.instance.exports[name] = function(...args) {
                    console.log(`TRACE: Calling exported function ${name}(${args.join(', ')})`);
                    return func.apply(this, args);
                };
            }
        }
    }

    return result;
};

// Instrument fetch for network monitoring
global.fetch = function(url, options) {
    console.log(`NETWORK: fetch(${url})`);
    return Promise.reject(new Error('Network access blocked in sandbox'));
};

// Load and run WASM file
async function runWasm() {
    try {
        const wasmFile = process.argv[2];
        if (!wasmFile) {
            console.error('Usage: node script.js <wasm_file>');
            return;
        }

        const wasmBuffer = fs.readFileSync(wasmFile);
        console.log(`TRACE: Loading WASM file: ${wasmFile} (${wasmBuffer.length} bytes)`);

        const wasmModule = await WebAssembly.instantiate(wasmBuffer, {
            env: {
                console_log: (ptr, len) => console.log('TRACE: console.log called'),
                memory: new WebAssembly.Memory({ initial: 256, maximum: 256 })
            }
        });

        console.log('TRACE: WASM module loaded successfully');

        // Try to call main function if it exists
        if (wasmModule.instance.exports.main) {
            console.log('TRACE: Calling main function');
            wasmModule.instance.exports.main();
        }

    } catch (error) {
        console.log(`TRACE: Error: ${error.message}`);
    }
}

runWasm();
"""

    def threat_assessment(self, wasm_file):
        """Assess threat level of WASM file"""

        threat_score = 0
        threat_indicators = []

        # Get analysis results
        static = self.analysis_results.get(str(wasm_file), {}).get('static_analysis', {})
        behavioral = self.analysis_results.get(str(wasm_file), {}).get('behavioral_analysis', {})

        # Score based on capabilities
        capabilities = static.get('capabilities', [])

        if 'network_access' in capabilities:
            threat_score += 30
            threat_indicators.append('Network access capability')

        if 'code_execution' in capabilities:
            threat_score += 40
            threat_indicators.append('Dynamic code execution')

        if 'dom_manipulation' in capabilities:
            threat_score += 20
            threat_indicators.append('DOM manipulation')

        if 'worker_threads' in capabilities:
            threat_score += 15
            threat_indicators.append('Worker thread creation')

        # Score based on suspicious indicators
        suspicious = static.get('suspicious_indicators', [])
        threat_score += len(suspicious) * 10

        # Score based on entropy
        entropy = static.get('file_info', {}).get('entropy', 0)
        if entropy > 7.5:
            threat_score += 25
            threat_indicators.append('High entropy (obfuscation)')

        # Determine threat level
        if threat_score >= 80:
            threat_level = 'HIGH'
        elif threat_score >= 50:
            threat_level = 'MEDIUM'
        elif threat_score >= 20:
            threat_level = 'LOW'
        else:
            threat_level = 'MINIMAL'

        return {
            'threat_level': threat_level,
            'threat_score': threat_score,
            'threat_indicators': threat_indicators
        }

    def generate_iocs(self):
        """Generate Indicators of Compromise"""

        iocs = {
            'file_hashes': [],
            'network_indicators': [],
            'behavioral_indicators': []
        }

        for wasm_file, analysis in self.analysis_results.items():
            # File hash IOCs
            file_info = analysis.get('static_analysis', {}).get('file_info', {})
            if 'sha256' in file_info:
                iocs['file_hashes'].append(file_info['sha256'])

            # Network IOCs
            network_activity = analysis.get('behavioral_analysis', {}).get('network_activity', [])
            iocs['network_indicators'].extend(network_activity)

            # Behavioral IOCs
            capabilities = analysis.get('static_analysis', {}).get('capabilities', [])
            iocs['behavioral_indicators'].extend(capabilities)

        # Save IOCs
        ioc_file = self.temp_dir / 'iocs.json'
        with open(ioc_file, 'w') as f:
            json.dump(iocs, f, indent=2)

        print(f"IOCs saved to: {ioc_file}")
        return iocs

    def create_yara_rules(self):
        """Create YARA rules for detected threats"""

        yara_rules = []

        for wasm_file, analysis in self.analysis_results.items():
            threat = analysis.get('threat_assessment', {})

            if threat.get('threat_level') in ['HIGH', 'MEDIUM']:
                rule_name = f"WASM_Malware_{Path(wasm_file).stem}"

                rule = f"""
rule {rule_name}
{{
    meta:
        description = "Suspicious WASM file detected"
        threat_level = "{threat.get('threat_level')}"
        threat_score = {threat.get('threat_score')}

    strings:
        $magic = {{ 00 61 73 6D }}  // WASM magic

    condition:
        $magic at 0
}}
"""
                yara_rules.append(rule)

        # Save YARA rules
        if yara_rules:
            yara_file = self.temp_dir / 'wasm_malware.yar'
            with open(yara_file, 'w') as f:
                f.write('\n'.join(yara_rules))

            print(f"YARA rules saved to: {yara_file}")

        return yara_rules

def main():
    if len(sys.argv) != 2:
        print("Usage: python wasm_malware_analyzer.py <sample_file>")
        sys.exit(1)

    sample_file = sys.argv[1]

    if not os.path.exists(sample_file):
        print(f"Error: File {sample_file} not found")
        sys.exit(1)

    analyzer = WasmMalwareAnalyzer(sample_file)
    results = analyzer.analyze_sample()

    # Print summary
    print("\n=== WASM Malware Analysis Summary ===")
    for wasm_file, analysis in results.items():
        threat = analysis.get('threat_assessment', {})
        print(f"\nFile: {wasm_file}")
        print(f"Threat Level: {threat.get('threat_level', 'UNKNOWN')}")
        print(f"Threat Score: {threat.get('threat_score', 0)}")

        indicators = threat.get('threat_indicators', [])
        if indicators:
            print("Threat Indicators:")
            for indicator in indicators:
                print(f"  - {indicator}")

if __name__ == "__main__":
    main()

Ressources et documentation

Ressources officielles

  • Site officiel de l'Assemblée - Documentation officielle de l'Assemblée Web
  • [WABT GitHub Repository] (LINK_15) - Boîte à outils binaire de l'assemblage Web
  • [Binaryen GitHub Repository] (LINK_15) - Optimisateur et compilateur de l'assemblage Web
  • Spécification de l'assemblage Web - Spécification officielle WASM

Ressources pédagogiques

Recherche sur la sécurité

  • [WASM Malware Analysis] (LINK_15) - Techniques d'analyse des logiciels malveillants
  • [WASM Inverse Engineering] (LINK_15) - Méthodes RE
  • [Sécurité du navigateur et WASM] (LINK_15) - Considérations relatives à la sécurité du navigateur

Outils et services publics

  • Wasmer - Durée d'exécution de l'assemblage Web universel
  • [Wasmtime] (LINK_15) - Déroulement de l'assemblage Web autonome
  • wasm-pack - Déroulement de l'assemblage Web
  • Emscripten - C/C++ au compilateur de l'assemblage Web