PESETUP-INTERNALS.md was written from a decompiled 4.0.0.17. The media in production reports 4.0.0.20 in its own log. Rather than restate the document as 4.0.0.20, which would claim a re-derivation that has not happened, it now names both: line-level claims are 4.0.0.17, and the behaviour re-observed on bay 579C144 on 2026-08-06 is listed so a reader knows which parts are confirmed current - media drive Z:, W: created by PrepareDisk and used for every copy destination, the fallback Deploy\FlatUnattendW10.xml being the unattend that loads, and driver selection by model. FlatUnattendW10-shopfloor.xml carried LogonCount 7 while the live shopfloor unattend has 12, and Run-ShopfloorSetup.ps1's comment about topping up the autologon budget already said 12. The live value is the real one, so the repo follows it. Both files still lint clean under scripts/lint-unattend.py.
279 lines
12 KiB
Markdown
279 lines
12 KiB
Markdown
# 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).
|
|
|
|
**Version in production:** 4.0.0.20, as of 2026-08-06 — the media reports it in
|
|
its own log (`AppVersion: 4.0.0.20`). Everything in this document was verified
|
|
against a 4.0.0.20 run on bay 579C144 that day: the media drive resolved to `Z:\`,
|
|
`W:` was created by `PrepareDisk` and used for every copy destination, the fallback
|
|
unattend at `Deploy\FlatUnattendW10.xml` was the one loaded, and driver selection
|
|
matched `win11_optiplexd13mlk7020_a09.zip` by model. The decompiled detail below has
|
|
not been re-derived from the 4.0.0.20 binary, so treat exact line-level claims as
|
|
4.0.0.17 and the observed behaviour as current.
|
|
|
|
## 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('<q', data, i - 8)[0]
|
|
# header: uint32 major, uint32 minor, int32 count, 7-bit-prefixed bundle id,
|
|
# then (v2+) 4 x int64 deps/runtimeconfig, then uint64 flags.
|
|
# each entry: int64 offset, int64 size, (v6+) int64 compressedSize,
|
|
# byte type, 7-bit-prefixed path. Compressed entries are raw
|
|
# deflate (zlib.decompress(raw, -15)).
|
|
```
|
|
|
|
Then `ilspycmd -t <TypeName> 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 <exe dir>\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\\" + <zip name without extension>
|
|
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 `<exedir>\DiskPart\diskpartEFI.txt` to `X:\diskpartEFI<n>.txt`, then `cmd /c DISKPART /S X:\diskpartEFI<n>.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\<zipname>` | **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_<n>.log` |
|
|
| `ApplyLanguagePacks` | same pattern, `X:\ApplyLanguagePack_<n>.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<HardwareDriversRootObject> 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
|
|
```
|