Files

131 lines
5.1 KiB
Markdown

---
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 <name> | Select Id,Path
# Or search MSIX install
Get-ChildItem "C:\Program Files\WindowsApps" -Directory -Filter "*<name>*"
```
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%\<ServiceName>\<package-name>\<version>\dist` at runtime.
Check if the app has been run:
```powershell
Get-ChildItem "$env:TEMP" -Directory -Filter "*<ServiceName>*"
Get-ChildItem "$env:TEMP\*<ServiceName>*\*" -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