From 936902dc4df31fc14b40bbd397feefcd3992533e Mon Sep 17 00:00:00 2001 From: cproudlock Date: Thu, 6 Aug 2026 08:04:48 -0400 Subject: [PATCH] Document how PESetup.exe actually works Written from the decompiled assembly rather than from observation. Three long-standing beliefs about this tool are wrong, and each has cost real debugging time: W: is not a guess. PESetup hardcodes it in nine places - every copy destination, the DISM offline sessions, bcdboot, reagentc - and creates it during its own disk preparation. startnet.cmd's volume finder, diskpart reassignment and W: wait loop are machinery built around a problem that does not exist. The copy steps do not filter. CopyPackages copies the whole of Deploy\Applications recursively to W:\Deploy\Applications - no manifest, no unattend parsing, no extension or size rules. Anything dropped there reaches the target, which is the basis for staging our own payload without the enrollment share. It is also fail-fast: one unreadable file fails the whole step. A missing driver match is a WARNING, not an error. GetDriverByModel does a substring test of comma-separated tokens with first-match-wins, the family filter knows only Latitude, OptiPlex and Precision, and a single 'virtual platform' entry in the catalogue fails every physical machine. A miss lets imaging finish with no drivers, so no NIC, so DNS failures at first boot - symptoms far from the cause, and easily misattributed to the drive letter. Includes the bundle-extraction recipe so the next person can re-derive all of this instead of trusting this document. --- docs/PESETUP-INTERNALS.md | 269 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 docs/PESETUP-INTERNALS.md diff --git a/docs/PESETUP-INTERNALS.md b/docs/PESETUP-INTERNALS.md new file mode 100644 index 0000000..161304d --- /dev/null +++ b/docs/PESETUP-INTERNALS.md @@ -0,0 +1,269 @@ +# PESetup.exe internals + +What GE Image Setup actually does, start to finish. Written from the decompiled +assembly, not from observation, because several long-standing beliefs about this +tool turned out to be wrong and cost weeks of debugging. + +**Version documented:** 4.0.0.17 (`Sources/PESetup.exe`, PE32+ native apphost +wrapping a .NET 6 single-file bundle, 468 embedded files). + +## How to re-derive this + +The managed code is not directly readable - the outer PE has no managed +metadata, so ILSpy refuses it. Extract `PESetup.dll` from the bundle first: + +```python +# .NET single-file bundle: signature is SHA-256 of ".net core bundle"; +# the int64 EIGHT BYTES BEFORE it is the bundle header offset. +SIG = bytes([0x8b,0x12,0x02,0xb9,0x6a,0x61,0x20,0x38, + 0x72,0x7b,0x93,0x02,0x14,0xd7,0xa0,0x32]) +i = data.find(SIG) +header = struct.unpack_from(' PESetup.dll`. + +## Paths it hardcodes + +From `PESetup.Models.GlobalSettings`. All are relative to the MEDIA drive +(`Z:\`, whichever winpeapps share startnet mapped) unless stated. + +``` +ControlDir \Deploy\Control +ToolsDir \Deploy\Tools\GE +SoftwarePackagesDir \Deploy\Applications +HWPackagesDir \Deploy\HW_Apps +UnattendFile \Deploy\Tools\GE\XML\FlatUnattendW10.xml +UnattendFile2 \Deploy\FlatUnattendW10.xml (fallback) +DiskPartDir \DiskPart +ConfigSkipPackagesFile skip.json +ConfigDisableAutoStartFile disableauto.json +ExpirationDuration 30 days +AutoStartCountDown 31 +MinRequiredSpaceWithoutCompression 128849018880 (120 GB) +``` + +**`W:` IS HARDCODED, EVERYWHERE.** Not derived, not configurable, not a +convention this project invented: + +```csharp +CopyPackages destDir = "W:\\" + SoftwarePackagesDir +CopyHWPackages destDir = "W:\\" + HWPackagesDir +CopyTools destDir = "W:\\GE" +CopyDrivers destinationPath = "W:\\Drivers\\" + +ApplyImage destination = "W:\\" +CreateBCD "W:\\Windows\\System32\\bcdboot.exe", "W:\\Windows /l en-US" +ApplyPackages DismApi.OpenOfflineSession("W:\\") +CopyLogs tags -> "w:\\windows\\system32\\" +``` + +PESetup's own disk preparation is what makes W: exist. Any downstream script +that hunts for "the applied volume" is solving a problem this tool does not +have. `diskpart list volume` on a machine mid-image shows `Volume 0 W Windows +NTFS 237GB Healthy`. + +## The five working steps + +Keys as they appear in the log: `workingstep_gatherdata`, `workingstep_selectos`, +`workingstep_prepare`, `workingstep_copy`, `workingstep_apply`, +`workingstep_finalize`, `workingstep_reboot`. + +### 1. GATHER DATA + +Four operations, all automatic: + +| Operation | What it establishes | +|---|---| +| `GatherDataMediaDrive` | `ImageInfo.MediaDrive`, e.g. `Z:\` | +| `GatherDataBootDrive` | `ImageInfo.BootDrive`, always `X:\` (WinPE RAM disk) | +| `GatherDataSelection` | BIOS version/type, serial, secure boot, OS, languages, model, **driver** | +| `GatherDataImageDisk` | picks the physical disk and logs its partitions | + +`GatherDataSelection` is where most decisions are made: + +1. **Secure boot is mandatory.** `SecurebootEnabled != 1` fails the step outright. +2. Reads `Control\LanguagePacks.json`. +3. `SystemHelpers.GetModel()` and `GetManufacturer()` off WMI. +4. Reads `Control\HardwareDriver.json` and `Control\hw_applications.json`. + NOTE: it reads **HardwareDriver.json**, not the `hw_drivers.json` that also + sits in that folder. Editing the wrong one changes nothing. +5. **Virtual platform check.** If ANY driver entry has a manufacturer containing + "virtual platform", the tool goes into virtual-only mode: it takes + `list[0]` as the driver and then REQUIRES the machine to look virtual + (model/serial/BIOS containing VIRTUAL, VMWARE, XEN, QEMU, VirtualBox, vmw). + On real hardware that is a hard failure. A stray "virtual platform" entry in + the catalogue therefore breaks imaging for every physical machine. +6. Otherwise `GetDriverByModel` (below). +7. If `Control\skip.json` exists, package installation is skipped. + +### 2. SELECT OS + +Operator-facing. Warns when the media holds an LTSC image ("99% of the time only +for ShopFloor"). Also shows days-to-expiry - the media expires 30 days after +build (`ExpirationDuration`). + +### 3. PREPARE + +| Operation | What it does | +|---|---| +| `PrepareDoDisking` | copies `\DiskPart\diskpartEFI.txt` to `X:\diskpartEFI.txt`, then `cmd /c DISKPART /S X:\diskpartEFI.txt` | +| `PrepareUnattend` | loads the unattend, substitutes, saves to `X:\Unattend.xml` | + +`PrepareUnattend` in detail: + +```csharp +text = MediaDrive + UnattendFile; // \Deploy\Tools\GE\XML\FlatUnattendW10.xml +if (!File.Exists(text)) text = MediaDrive + UnattendFile2; // \Deploy\FlatUnattendW10.xml +xml.Load(text); +xml.InnerXml = xml.InnerXml.Replace("%serialnumber%", imageinfo.Serialnumber); +xml.InnerXml = xml.InnerXml.Replace("*arch*", arch); +SetPackages(arch, xml, imageinfo); // needs Control\Packages.xml +xml.Save("X:\\Unattend.xml"); +``` + +Two tokens are substituted: `%serialnumber%` and `*arch*`. `SetPackages` merges +in servicing packages from `Control\Packages.xml` and `Control\PackageGroups.xml`. + +The unattend that is USED is the one on the media at `Tools\GE\XML\` if present, +otherwise `Deploy\FlatUnattendW10.xml`. A shopfloor variant only takes effect if +it occupies one of those two paths. + +### 4. COPY + +Four operations. **None of them filters, and none of them reads the unattend.** + +| Operation | Source | Destination | Rule | +|---|---|---|---| +| `CopyTools` | `Z:\Deploy\Tools\GE` | `W:\GE` | whole directory, recursive | +| `CopyPackages` | `Z:\Deploy\Applications` | `W:\Deploy\Applications` | whole directory, recursive | +| `CopyHWPackages` | `Z:\Deploy\HW_Apps` | `W:\Deploy\HW_Apps` | whole directory; **skipped if `IsVirtual` or `HWApps == null`** | +| `CopyDrivers` | one `.zip` chosen by model | `W:\Drivers\` | **unzipped**, not copied | + +`ExecuteInternalCopy` walks `GetDirectoriesRecursive(source, includeSubDirs: +true)` then `Directory.GetFiles(item, "*")`. Every file, every subdirectory. It +clears the read-only attribute on each copy. + +**Consequences worth designing around:** + +- Anything dropped into `Deploy\Applications` on the media lands on the target. + No manifest, no registration, no unattend reference required. +- The copy is FAIL-FAST. One exception on one file returns false and fails the + whole step with `copy_packages_error`. A locked or unreadable file in + `Applications` fails imaging, it does not get skipped. +- Progress is computed from total directory size up front, so bulky additions + visibly lengthen this phase. + +### 5. APPLY + +| Operation | What it does | +|---|---| +| `ApplyImage` | `install.wim` from `OperatingSystem.json`'s `destinationDir`, at `wimindex`, applied to `W:\` | +| `ApplyPackages` | DISM offline session on `W:\`, adds servicing packages; logs to `X:\ApplyOSPatch_.log` | +| `ApplyLanguagePacks` | same pattern, `X:\ApplyLanguagePack_.log` | +| `ApplyUnattend` | DISM offline session on `W:\`, applies `X:\Unattend.xml`; logs to `X:\ApplyUnattend.log` | +| `CreateBCD` | `W:\Windows\System32\bcdboot.exe W:\Windows /l en-US` | +| `CopyWinRE` | `reagentc /setreimage /path T:\Recovery\WindowsRE /target W:\Windows` | + +All DISM work uses `W:\imagetemp` as scratch and deletes it afterwards. + +### 6. FINALIZE + +`CopyLogs`: +- `CopyTagFiles(ToolsDir)` - every `*.tag` from the tools dir to + **`w:\windows\system32\`**. This is how build/media tags reach the OS. +- `CopyLogFiles("X:\\")` and `CopyXMLFile("X:\\")` - the PESetup log and the + generated unattend are preserved. + +## Driver selection, in full + +This is the part most worth understanding, because a miss is nearly silent. + +```csharp +// GatherDataSelection +List list = + JSONHelpers.ReadJSON_Driver(MediaDrive + ControlDir + "\\HardwareDriver.json"); +... +imageinfo.HWDriver = GetDriverByModel(list, imageinfo.Model); +if (imageinfo.HWDriver == null) { + // status = Warning, message "driver for [MODEL] not found" + return operationResult; +} +``` + +```csharp +private static HardwareDriversRootObject? GetDriverByModel(List<...> drivers, string model) +{ + string modelFamily = ""; + if (model.ToUpper().Contains("LATITUDE")) modelFamily = "Latitude"; + if (model.ToUpper().Contains("OPTIPLEX")) modelFamily = "Optiplex"; + if (model.ToUpper().Contains("PRECISION")) modelFamily = "Precision"; + + return drivers.Where(d => { + if (!string.IsNullOrEmpty(modelFamily) && + !d.family.ToLower().Contains(modelFamily.ToLower())) return false; + foreach (string token in d.modelswminame.Split(',')) + if (model.ToLower().Contains(token.ToLower())) return true; // SUBSTRING + return false; + }).ToList()?.FirstOrDefault(); +} +``` + +Then: + +```csharp +sourceFilePath = MediaDrive + HWDriver.destinationDir.Replace("*destinationdir*","") + + "\\" + HWDriver.fileName; +destinationPath = "W:\\Drivers\\" + Path.GetFileNameWithoutExtension(sourceFilePath); +StartUnzipAsync(...) +``` + +### Four traps in that logic + +1. **A miss is a WARNING, not a failure.** Imaging continues and the machine + comes up with no drivers - no NIC, no WiFi, so DNS fails and anything + network-dependent at first boot fails with it. Symptoms appear far from the + cause. +2. **Matching is substring, first match wins.** A token like `7020` matches any + model string containing 7020. Order in the JSON decides ties. +3. **The family filter knows only three Dell lines.** Anything else - MicroPCs, + NUCs, non-Dell - skips the filter and depends entirely on + `modelswminame` substrings being right. +4. **One "virtual platform" entry hijacks the whole catalogue** (see GATHER + DATA step 5) and fails every physical machine. + +## What this means for startnet.cmd + +Three beliefs encoded in `startnet.cmd` do not survive contact with the source: + +- *"The applied volume might not be W:."* PESetup hardcodes W: in nine places + and creates it during disk prep. The volume finder, the diskpart + reassignment and the `W:` wait loop are machinery around a non-problem. +- *"We must copy our payload from the enrollment share after imaging."* Anything + in `Deploy\Applications` on the media is copied to `W:\Deploy\Applications` + by PESetup itself, and is readable at `C:\Deploy\Applications` at first boot. +- *"Display MicroPCs failed because the applied volume was not W:."* Worth + re-testing. A missing `HardwareDriver.json` match produces the same + end-state - no drivers, no network - via a completely different route, and + that route only logs a warning. + +## Files PESetup reads from the media + +``` +Deploy\Control\HardwareDriver.json driver catalogue (NOT hw_drivers.json) +Deploy\Control\hw_applications.json per-model applications +Deploy\Control\OperatingSystem.json OS list, wim path, wimindex +Deploy\Control\LanguagePacks.json language packs +Deploy\Control\packages.json servicing packages +Deploy\Control\Packages.xml unattend package merge +Deploy\Control\PackageGroups.xml package grouping +Deploy\Control\Media.tag media identity +Deploy\Control\skip.json presence = skip package install +Deploy\Control\disableauto.json presence = disable autostart +Deploy\Tools\GE\XML\FlatUnattendW10.xml preferred unattend +Deploy\FlatUnattendW10.xml fallback unattend +```