# Patching UEFI/BIOS firmware without fighting the GUI

If you've used [UEFITool](https://github.com/LongSoft/UEFITool) to poke around
inside a BIOS image, you've probably noticed that the current build (the "New
Engine," tagged like `A75`) only ships `UEFIExtract` and `UEFIFind` as
standalone CLI tools. There's no `UEFIReplace` or `UEFIPatch` anymore — if you
actually want to *change* something (swap a boot logo, edit a string, patch a
module), the only path the current release offers is the GUI: open the image,
find the file in the Structure tree, right-click → Replace body, save.

That's fine for one change. It falls apart the moment you want to script it,
batch it, or verify it byte-for-byte — and on some setups, driving the GUI
tree via accessibility APIs is genuinely unreliable (Qt's custom-painted tree
widgets don't always respond to synthetic clicks the way native controls do).

**The fix: go back one generation.** Before the New Engine rewrite, LongSoft's
UEFITool releases were tagged with plain semver (`0.28.0`, etc.) instead of
the `A##` alpha tags, and that "legacy engine" line still ships `UEFIReplace`
and `UEFIPatch` as real standalone CLI binaries. They work perfectly for
one-shot programmatic edits — no GUI, no automation, no accessibility
permissions needed at all.

Grab it from the legacy release tag, e.g.:
```
https://github.com/LongSoft/UEFITool/releases/download/0.28.0/UEFIReplace_0.28.0_mac.zip
```
(swap `_mac` for your platform's build). These are old, Intel-only binaries —
on Apple Silicon you'll need Rosetta once (`softwareupdate --install-rosetta
--agree-to-license`), then run everything via `arch -x86_64`.

## Usage

```
UEFIReplace <image_file> <GUID> <section_type_hex> <content_file> [-o <output>] [-all] [-asis]
```

- `<GUID>` — the FFS **File** GUID that owns the section you're replacing
  (find it with `UEFIExtract <image> report`, or `UEFIFind` for a text/byte
  search if you don't already know it).
- `<section_type_hex>` — a hex byte, not a name. The common ones:

  | Hex | Section type |
  |-----|--------------|
  | `10` | PE32 image (a compiled driver/module) |
  | `19` | Raw (images, arbitrary binary blobs) |
  | `01` | Compression |
  | `02` | GUID-defined |

- Default mode (no `-asis`) is `REPLACE_MODE_BODY` — functionally identical
  to the GUI's "Replace body..." action. It re-does compression/repacking
  correctly rather than blindly splicing bytes in, so this is almost always
  what you want.
- `-all` replaces every file matching that GUID, not just the first found —
  worth knowing if your image has a duplicate/recovery volume (common on
  boards with CrashFree-BIOS-style redundancy).

## The verification recipe

Don't trust "the tool said File replaced" — verify it actually landed:

1. **Byte-diff the whole image** before/after. Expect the target region to
   change and *nothing* in unrelated regions (flash descriptor, ME region,
   other firmware volumes) to move at all.
2. **Extract the modified section back out** (`UEFIExtract <new_image>
   <GUID> -o check.bin -m body`) and compare it directly — MD5 or byte-diff
   — against the exact file you fed in as `<content_file>`. This is the only
   way to be sure you didn't just get "some bytes changed" but the *wrong*
   bytes changed.
3. **Confirm the file count/GUID listing is unchanged** (`UEFIExtract
   <image> report`, count `^ File` lines, diff the GUID list against the
   original). Nothing should be added or removed.

## A gotcha worth knowing: the "cascade" diff

If the file you're replacing sits inside a **Compressed** section (most
modules do), a size change — even a few bytes, from something as small as
a string edit — changes that section's recompressed size. Since firmware
volumes are tightly packed, every file *after* yours in the same volume then
shifts to a new absolute address. This shows up as a byte-diff across a much
larger range than you actually touched (tens or hundreds of KB isn't
unusual) — but it's just position shift, not corruption. To confirm: extract
the *other* files in that volume and check their **content** hasn't changed
(only their address has). If step 3 above still shows the same file count
and GUIDs, and step 2 confirms your actual target file is correct, a big
raw diff number by itself is not a red flag.
