--- name: reverse-winui3-app description: "Use when reverse engineering a WinUI 3 / .NET 8 MSIX-packaged Windows application — covers ILSpy decompilation, PRI/XBF extraction, and pkg-compiled Node.js backend identification" --- # Reverse Engineering WinUI 3 + pkg-compiled Node.js Applications ## When to use - Target is a Windows MSIX-packaged app (WinUI 3 / Windows App SDK) - App has a separate backend executable compiled with `pkg` - Need to extract C# source, XAML (XBF), and JS backend code ## Phase 1: Locate and identify the target ```powershell # Find running process Get-Process | Select Id,Path # Or search MSIX install Get-ChildItem "C:\Program Files\WindowsApps" -Directory -Filter "**" ``` Identify app type by inspecting install directory: - `coreclr.dll`, `hostfxr.dll`, `*.dll` (managed) → .NET / WinUI 3 - `VibeProxy.exe` (small, ~400KB) = native launcher; `VibeProxy.dll` (large) = main logic - `Watchers\*.exe` (large, 100MB+) = likely pkg-compiled Node.js backend ## Phase 2: Decompile .NET frontend (ILSpy) ```bash # Install ILSpy CLI dotnet tool install --global ilspycmd # Decompile with BAML support and reference path ilspycmd "target.dll" -p -o "output_dir" --decompile-baml -r "install_dir" # Fix csproj: LangVersion=preview, add HintPath for all references # Copy dependency DLLs to libs/ folder (include large ones >15MB separately) # Fix CS0136 (duplicate variable names) by renaming # Build: dotnet build -c Release ``` Key limitations: - WinUI 3 XAML compiles to XBF (not BAML) — `--decompile-baml` won't extract it - `Unknown result type` comments in 60+ files = WinRT projection IL artifacts - Smoke-launch fails: WinRT activation requires MSIX package identity ## Phase 3: Extract XBF from resources.pri ```bash # Find makepri.exe in Windows SDK # Dump PRI to XML (detailed format for Base64 data) makepri.exe dump /if "install_dir\resources.pri" /of "dump.xml" /dt detailed /o ``` ```powershell # Extract XBF binaries from PRI dump XML [xml]$xml = Get-Content "dump.xml" -Raw $xbfNodes = $xml.SelectNodes("//NamedResource[contains(@name,'.xbf')]") foreach ($node in $xbfNodes) { $name = $node.GetAttribute("name") $uri = $node.GetAttribute("uri") $pathPart = $uri -replace "ms-resource://[^/]+/Files/", "" $candidate = $node.SelectSingleNode("Candidate") if ($candidate.GetAttribute("type") -eq "EmbeddedData") { $b64 = $candidate.SelectSingleNode("Base64Value") $bytes = [System.Convert]::FromBase64String($b64.InnerText) [System.IO.File]::WriteAllBytes($outPath, $bytes) } } ``` XBF v2.1 format notes: - Header: magic(4) + metaSize(uint32) + nodeSize(uint32) = 12 bytes - Metadata at byte 12: version(2x uint32) + 6 table offsets (uint64, relative to byte 12) + hash (64 bytes) - Table entries are 12 bytes each (not 8 as in v1.0) - Node encoding differs from v1.0 — no existing tool supports v2.1 - Best path to XAML: runtime-hook `LoadComponent` in live app, serialize visual tree ## Phase 4: Identify and extract pkg-compiled backend ### Identify pkg binary ``` PE header: NOT .NET (no CLR header) Search for: "process.versions.pkg", "pkg/prelude", "/snapshot/" NODE_SEA_FUSE:0 = disabled (stock Node, not SEA) pkg version found in binary (e.g., '6.22.0') ``` ### Extract via runtime unpacking pkg extracts itself to `%TEMP%\\\\dist` at runtime. Check if the app has been run: ```powershell Get-ChildItem "$env:TEMP" -Directory -Filter "**" Get-ChildItem "$env:TEMP\**\*" -Recurse ``` If found, copy the `dist` directory — it contains: - `server.js` / `server-ws.mjs` (entry points, readable) - `.build/next/` (Next.js standalone build) - `.build/next/server/chunks/` (minified but readable JS, 3000+ files) - `.build/next/server/app/api/` (API routes) - `package.json` (identifies upstream package + version) ### Match with upstream Check `package.json` name + version against GitHub: - If MIT/Apache licensed → full source available upstream - Clone the repo for complete TypeScript source (2,600+ .ts/.tsx files) - Compare extracted dist with git clone to find local modifications ## Phase 5: Full inventory checklist | Item | Source | Method | |------|--------|--------| | C# source | VibeProxy.dll | ILSpy decompile | | XBF (compiled XAML) | resources.pri | makepri dump + extract | | Assets/icons | MSIX install dir | Copy | | Locales | MSIX install dir | Copy (check all dirs) | | config.yaml | Watchers/ | Copy | | Backend dist | %TEMP% (runtime) | Copy after app run | | Backend source | GitHub (if MIT) | git clone | | Build verification | dotnet build | Must be GREEN | | Sourcemaps | .build/next/ | Usually absent in prod | ## Common pitfalls - Don't claim "full source" without building and smoke-testing - XBF v2.1 ≠ BAML — `--decompile-baml` produces nothing for WinUI 3 - SEA fuse `:0` means stock Node.js, not SEA — look for `process.versions.pkg` instead - 210MB gzip trailing data in PE = pkg payload, not SEA blob - Sourcemaps in production Next.js builds are typically empty (`{"sources":[],"sections":[]}`) - WinUI 3 apps cannot launch standalone without MSIX registration