anecdote@shelter:/logs/log_000
back to /logs
[server] connecting to darknoise://127.0.0.1:6667 ...
[notice] -ChanServ- welcome to ##anecdote-shelter
*** anecdote has joined #log0
[mode] +o anecdote
log_000 // 2026-02-05

my experience on the H61 platform

firmware re uefi x86 uart

hey, glad you found this place. since you're here, i'll try to lay everything out clearly — the goal is that you can actually use this information. recently i got interested in pushing code into the firmware and looking at how it behaves down there. today we're not going to cover the full persistence mechanism, but we will walk through the injection methods i actually used and what happened with each of them.

hardware setup

first things first — you need real hardware. yes, you can use a VM with OVMF (and honestly it's a fine option for initial testing), but for seeing how payloads behave on actual silicon, i prefer physical boards. in this writeup i ended up using two motherboards — i'll explain why later.

the primary board was an ASUS P8H61M LX3 R2.0. not a bad choice — almost everything needed was there. the rest of the rig:

cpuIntel Pentium G2020
dram2 GB DDR3-1333
psu450W (garbage, but it works)
hdd500 GB Toshiba
bios2012 vintage AMI firmware
both boards share the same H61 chipset family. the second board comes in later when the first one turns out to have a missing COM header — more on that below.

method 1 — shellcode injection

technique: byte patching

this one has no real tricks to it. the approach: find a string or byte sequence inside a firmware module that the module doesn't strictly need for correct operation, and overwrite those bytes with your payload. before the injection, write your asm, assemble it to raw bytes, and then you're ready to patch.

to do this, load your firmware image into UEFITool — and make sure it's a dump you pulled from the board itself, not a file downloaded from the vendor site. flashing a modified image built from a downloaded ROM(Read-only memory) has a real chance of producing a brick. dump it yourself.

UEFITool

i targeted the SmiFlash module. the name is self-explanatory — it holds SMM privileges even when it shows up as a DXE driver in the tree. to verify the injection was actually running, i wrote a shellcode that calls the PC speaker in a tight loop. To inject code into the body of an existing function, we need to open the file itself with a binary editor. But to edit, we need to know where to write our payload, so I use ida pro for analysis. In this program, we need to find the function we defined in the file and check if there's free space for code injection in the same section (for example, .rdata). After that, we insert our code into the empty bytes and note the address where it's located.

Next, we need to find the location in the original function from which control will be transferred to our code. Typically, we select the beginning of the function or a section containing suitable instructions. It's important to preserve the original bytes that we'll overwrite so that program execution can continue correctly. Then we replace the selected instructions with a jump (jmp) to our code. This way, when executed, the program will first access our insertion point. The required logic is executed inside it, after which we return back - either to the next instruction after the substitution, or by restoring the saved bytes and switching back to the original execution flow.

UEFITool

after patching the bytes, replace the module back into UEFITool, reflash, and boot. the result: an infinite beep loop. what's interesting is that the beeping was extremely fast — no audible gaps, just a continuous high-pitched tone. that's because SMM handles the code in sync with the processor clock, not in human-scale time. you get ultrasonic beeping instead of the discrete clicks you'd expect.

[+] payload executed. speaker beep confirmed SMM context.

method 2 — building a proper payload

technique: function body replacement + ffs container

by now this feels like "the basics," but the first few times i did this it was genuinely disorienting. the approach here: replace the body of original firmware functions with your own code.

to do that you first need an actual payload. i used EDK II — specifically the UDK2014 branch, for full compatibility with a 2012-era board. newer branches might work too, but i didn't test them. (the board has a 2012 BIOS, so... come on.)

[link] dockerfile for UDK2014 build environment → github

after writing and compiling, you have a .efi file — the driver body. i kept targeting SmiFlash since it doesn't affect system startup. but first attempt: black screen. no POST, nothing. as if there was no BIOS at all. tried a different module — same result.

then i ran an experiment: took a module called AsusFptDxe and replaced its body with the content of SmiFlash. board posted fine. so: swapping a native module's body under a different module's identity bypasses whatever integrity checks exist and the board boots. useful to know.

spent a week trying to get my code running as both DXE and SMM. nothing worked — just a black screen every time. then i remembered that people had modded similar boards to add NVMe support where it wasn't native. looked at that method, and it used a tool called MMTool. it has a bunch of features but i only needed replace and insert.

the key realization: firmware modules aren't bare .efi files — they live inside FFS (Firmware File System) containers. to get your payload to load, it needs to look right from the firmware's perspective. that means building a proper FFS container yourself.

i assembled the container body, used GenSec to create a correct DEPEX (Dependency Expression is a section that describes the dependencies of a UEFI driver and determines under what conditions it should be loaded and executed; usually contains logical conditions related to protocol availability), copied it from an already working driver, brought everything to the desired form, and then pasted it through MMTool as a new driver.

[link] ffs container build script → github

three beeps, logo on screen. code is running.

[+] FFS container accepted. driver loaded. payload executing.

debugging — the COM port problem

technique: uart over pci card

getting the payload to run is one thing. actually seeing what it's doing is another. i looked at debug options and settled on the COM port / serial approach.

problem: the P8H61M LX3 R2.0 doesn't have the COM header populated on the PCB. the SuperIO controller is there, there's even a hidden BIOS option (found via AMIBCE), but the physical pins aren't soldered. so i went with a PCI COM card — two-port, the cheapest one available — plus a serial cable and a USB-to-COM adapter.

in Linux (Arch, manual stty setup) the signal came through fine. in UEFI: nothing useful. the card has a non-standard controller that needs specific initialization before it will do anything.

spent two weeks trying to make it work in UEFI shell. wrote a lot of test code. the core issue: LSR (Line Status Register) during UART init kept returning 0xFF or 0x4D — corrupted data or device busy. manual register writes via MMIO, manual PCI BAR configuration — none of it helped.

two weeks lost. and there was a second problem lurking: even if i'd gotten the serial link working, SMM code has its own initialization requirements that might have silently swallowed the log output during early boot anyway. the timing just doesn't align with when the serial driver is available.

[-] PCI COM card: not viable in UEFI context. LSR = 0xFF / 0x4D. abandoned.

decision: swap the board. got a P8H61M-Pro, which has a native COM header and more interfaces in general. pulled a fresh dump, rewrote the DEPEX for the new board, rebuilt the FFS container, injected, booted.

ttylog
[+] serial output confirmed. log data visible. we're in.

closing thoughts

that was the "small" expedition into firmware injection and hardware debugging. what looked like a straightforward task turned into a multi-week hardware debugging session — two boards, a pile of failed COM configurations, and a healthy disrespect for vendor documentation. the main takeaways:

— always dump the firmware yourself, never flash from a vendor download
— bare .efi won't load without proper FFS wrapping
— cheap PCI serial cards are essentially broken in UEFI without custom drivers
— native COM headers are worth the extra few euros on the board

hope this was useful. more to come when i have something worth writing down.

— anecdote // directly from the hardware hacking world
2026-04-05 // H61 platform series // log_000