Skip to content

Contribution: Fixed regex extraction script and updated def mappings for forwarded CriticalSection symbols #568

Description

@batoripX

i discovered: Capturing Forwarded Exports for UCRT/MinGW Linking

While executing a clean-room compilation pipeline targeting the native Windows UCRT directly via llvm-mingw (LLVM 22.1.8 runtime environment), the linker (ld.lld) threw a sequence of undefined symbol errors regarding libmingw32.a(tlsthrd.o):

ld.lld: error: undefined symbol: __declspec(dllimport) EnterCriticalSection
ld.lld: error: undefined symbol: __declspec(dllimport) LeaveCriticalSection
ld.lld: error: undefined symbol: __declspec(dllimport) InitializeCriticalSection
ld.lld: error: undefined symbol: __declspec(dllimport) DeleteCriticalSection

Technical Root Cause
The standard regex pattern used to parse the raw text logs of system DLL headers strictly expects a hexadecimal memory address offset (0x[0-9a-fA-F]+) right before the function name string.

However, on modern 64-bit Windows environments, these critical thread-synchronization primitives are structured as Forwarded Exports that point straight to NTDLL (e.g., EnterCriticalSection (forwarded to NTDLL.RtlEnterCriticalSection)). Because they lack a standard hexadecimal RVA block in the export dump column, the strict regex completely filters them out, leading to missing entries in the resulting module-definition blueprint (.def) files.
The Solution: Automated Extraction Script
I updated the regex sequence to make the hexadecimal address block entirely optional ((0x[0-9a-fA-F]+\s+)?). This successfully captures both standard memory-addressed exports and blank-address forwarded symbols on the fly. here is the ps1 script code  param (
    [string]$InputLog,
    [string]$DllName
)

if (-not $InputLog -or -not $DllName) {
    Write-Error "Missing parameters! Usage: .\ParseExports.ps1 -InputLog .\kernel32_structure.log -DllName kernel32.dll"
    exit 1
}

$OutputFile = $InputLog -replace '\.log$', '.def'

$DefContent = [System.Collections.Generic.List[string]]::new()
$DefContent.Add("LIBRARY $DllName")
$DefContent.Add("EXPORTS")

# Process the log file line by line
Get-Content -Path $InputLog | ForEach-Object {
    # Match the typical clean export line pattern from llvm-objdump private headers:
    # ordinal, RVA, name (ignoring any potential forwarder trailing string data)
    if ($_ -match '^\s*(?<Ordinal>\d+)\s+0x[0-9a-fA-F]+\s+(?<Name>[a-zA-Z0-9_#@]+)(.*)?$') {
        $Ord = $Matches['Ordinal']
        $Name = $Matches['Name']
        $DefContent.Add("    $Name @$Ord")
    }
}

$DefContent | Set-Content -Path $OutputFile -Encoding utf8
Write-Host "[+] Extracted and compiled to $OutputFile successfully!" -ForegroundColor Green

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions