Inside the InsydeH2O BIOS

Disclaimer: Don't blindly follow this "guide", bricking your BIOS is easy, recovering it is not.

Introduction

A quick Google query will return tons of results for modded BIOSes for laptops that unlock additional advanced and dangerous options like "Advanced" and "Power" tabs.

But how do they do it - and more importantly - how can we do it ourselves?

The problem with these modded BIOS ROMs is that you have to flash them, oftentimes with an external programmer due to the BIOS image region being locked or the manufacturer updaters having a checksum which doesn't allow flashing custom images.

Note: Most of this article assumes the use of Linux

Dumping the BIOS ROM

On my Dell Inspiron 3537, flashrom detects the following:

/tmp/BIOS # flashrom -p internal
[OMITTED FOR LENGTH]
Found chipset "Intel Lynx Point LP Premium".
[OMITTED FOR LENGTH]
Enabling flash write... Warning: BIOS region SMM protection is enabled!
Warning: Setting Bios Control at 0xdc from 0x22 to 0x01 failed.
New value is 0x22.
SPI Configuration is locked down.
FREG0: Flash Descriptor region (0x00000000-0x00000fff) is read-only.
FREG2: Management Engine region (0x00001000-0x003fffff) is locked.
Not all flash regions are freely accessible by flashrom. This is most likely
due to an active ME. Please see https://flashrom.org/ME for details.
PR0: Warning: 0x00700000-0x007fffff is read-only.
PR1: Warning: 0x00402000-0x006a1fff is read-only.
At least some flash regions are read protected. You have to use a flash
layout and include only accessible regions. For write operations, you'll
additionally need the --noverify-all switch. See manpage for more details.
PROBLEMS, continuing anyway
Found Winbond flash chip "W25Q64.V" (8192 kB, SPI) mapped at physical address 0x00000000ff800000.
No operations were specified.

As we can see, there is an internal programmer but due to the Intel Management Engine being active, we can't dump the whole ROM.

/tmp/BIOS # flashrom -p internal -r bios.bin
[OMITTED FOR LENGTH]
At least some flash regions are read protected. You have to use a flash
layout and include only accessible regions. For write operations, you'll
additionally need the --noverify-all switch. See manpage for more details.
PROBLEMS, continuing anyway
Found Winbond flash chip "W25Q64.V" (8192 kB, SPI) mapped at physical address 0x00000000ff800000.
Reading flash... Transaction error!
Read operation failed!
FAILED.

It turns out that 0x00000000-0x00000fff is the Flash Descriptor and 0x00001000-0x003fffff is the Intel ME region, flashrom attemps to dump the whole ROM but due to the locked (read-protected) region of the Intel ME this is not possible.

To dump the other regions we could use a ROM layout description file:

/tmp/BIOS # cat rom.layout
00000000:00000fff fdr
00001000:003fffff me
0x00700000:0x007fffff pr0
0x00402000:0x006a1fff pr1

Which allows us to specify the exact region flashrom should operate on:

/tmp/BIOS # flashrom -p internal -l rom.layout --image pr0 -r bios.bin
[OMITTED FOR LENGTH]
PROBLEMS, continuing anyway
Found Winbond flash chip "W25Q64.V" (8192 kB, SPI) mapped at physical address 0x00000000ff800000.
Reading flash... done.

We can use this to dump pr0 and pr1 which is usually most of the BIOS (without Intel ME).

Sadly, this can't be used to generate a full backup (yet...) but is still nice to have.

Extracting a full ROM from BIOS upgrades

I have only tried this for my specific BIOS, so the steps for yours might differ. Consider the following section Dell-only.

Download the BIOS upgrade of your choice (in this case Inspiron_3537_A09.exe).

We will use the script from here to extract the different sections.

This allows us to split the executable into sections:

/tmp/BIOS/ $ ./splitter.py -f Inspiron_3537_A09.exe

Which leaves us with this:

/tmp/BIOS/Inspiron_3537_A09 $ ls -lh
total 13M
-rw-r--r-- 1 rafael rafael 1.5M Feb 13 18:39 five
-rw-r--r-- 1 rafael rafael  65K Feb 13 18:39 four
-rw-r--r-- 1 rafael rafael 2.5M Feb 13 18:39 one
-rw-r--r-- 1 rafael rafael 8.1M Feb 13 18:39 three
-rw-r--r-- 1 rafael rafael 125K Feb 13 18:39 two
PartDescription
oneMS-DOS Executable, CWSDPMI, bundles a flash tool that communicates to the DPMI host
twoMS-DOS Executable, has references to FlashSecureBIOSOverride
threeIntel serial flash for PCH ROM
fourConfiguration file (.ini)
fiveIntel GbE image(?)

Hidden BIOS settings

So how do we access the hidden menus and whatnot? We would first need to reverse engineer the whole BIOS update procedure to disable the BIOS Write Lock, and only then can we experiment with our own modified BIOS.

But why bother with any of that.

UEFI Variable Storage

If you ever wondered how userspace tools change the UEFI bootloader settings - like the boot order - then efivars is the answer. UEFI stores most of the settings in its NVRAM storage - which can be mounted at runtime - and looks like this:

/sys/firmware/efi/efivars # ls
[OMITTED FOR LENGTH]
Boot0000-8be4df61-93ca-11d2-aa0d-00e098032b8c
Boot0001-8be4df61-93ca-11d2-aa0d-00e098032b8c
Boot0002-8be4df61-93ca-11d2-aa0d-00e098032b8c
[OMITTED FOR LENGTH]
db-d719b2cb-3d3a-4596-a3bc-dad00e67656f
dbDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c
KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c
PK-8be4df61-93ca-11d2-aa0d-00e098032b8c
PKDefault-8be4df61-93ca-11d2-aa0d-00e098032b8c
[OMITTED FOR LENGTH]
Setup-a04a27f4-df00-4d42-b552-39511302113d

Random descriptions:

NameDescription
Boot*Boot entry
dbSignature Database
KEKKey Exchange Key Signature Database.
PKpublic Platform Key
PKDefaultOEM's default public Platform Key

You might have noticed a rather interestingly named variable called Setup that isn't included in the table. Note its UUID as we will come to it later.

Peeking inside the BIOS image

Grab yourself a build of UEFITool (you won't need UEFIExtract and UEFIFind).

Now let's try loading the BIOS image (the one named three of the extracted ones) into it:

UEFITool successfully loads the extracted BIOS image

UEFITool successfully loads the extracted BIOS image

As you can see from the screenshot above, we can now look at what's inside the BIOS image.

If you poke around you will eventually see the DXE sections containing the EFI executables for lots of functions, including the ones handling all the keys like KEK and PK.

But what is of our interest is the Setup efivar, so let's find out: go to Action -> Search -> GUID and search for it's UUID, in this case a04a27f4-df00-4d42-b552-39511302113d (taken from it's filename).

Among the many matches, UEFITool found one inside a DXE called "SetupUtility". Well it turns out this DXE is essentially the whole BIOS UI.

So if it references that UUID from efivars, maybe it stores some settings in the efivars as well? Let's take a look.

Luckily for us UEFITool can extract sections - right click on the PE32 image section (selected in the above image) and choose Extract body.

This should produce the file named Section_PE32_image_DriverSampleDxe_SetupUtility_body.efi, which we will take a closer look at.

Finding the menu entries

According to the documentation from EDK2, UEFI has a thing called Visual Forms Representation (VFR) language which compiles down to Internal Forms Representation (IFR).

Let's confirm SetupUtility is indeed the BIOS UI.

To extract the IFR we can use something like Universal-IFR-Extractor:

/tmp/BIOS/ $ ifrextract Section_PE32_image_DriverSampleDxe_SetupUtility_body.efi out.txt
Input: Section_PE32_image_DriverSampleDxe_SetupUtility_body.efi
Output: out.txt
Protocol: UEFI

... which successfully extracts the IFR:

/tmp/BIOS/ $ cat out.txt
                                UEFI Protocol Detected
--------------------------------------------------------------------------------


                                String Packages
--------------------------------------------------------------------------------
Offset:		Language:
--------------------------------------------------------------------------------
0x23F24		en-US (0x0)
[OMITTED FOR LENGTH]

                                   Form Sets
--------------------------------------------------------------------------------
Offset:		Title:
--------------------------------------------------------------------------------
0x98A44		Advance (0x2B from string package 0x0)
0x994E4		Wireless (0x82 from string package 0x0)
0x99824		Boot (0x13D from string package 0x0)
0x9A9C4		Security (0x118 from string package 0x0)
0x9CA54		Main (0xE8 from string package 0x0)
0x9D054		Power (0x3E7 from string package 0x0)
0xA0604		Advanced (0x1C6 from string package 0x0)
0xAE894		Main (0x3 from string package 0x0)
0xAEB84		Exit (0x171 from string package 0x0)
[OMITTED FOR LENGTH]

As you can probably tell, the extracted IFR contains multiple forms currently not accessible in the BIOS.

Note that the file is more than 10 thousand lines of text, so I will do my best at presenting only relevant sections.

Seriously, the shortest form is the Main form, and it's already pretty cluttered:

0x9CA58 Form Set: Main [A04A27F4-DF00-4D42-B552-39511302113D], ClassGuid0 [93039971-8545-4B04-B45E-32EB8326040E] {0E A7 F4 27 4A A0 00 DF 42 4D B5 52 39 51 13 02 11 3D E8 00 E9 00 01 71 99 03 93 45 85 04 4B B4 5E 32 EB 83 26 04 0E}
0x9CA7F 	Guid: [0F0B1735-87A0-4193-B266-538C38AF48CE] {5F 15 35 17 0B 0F A0 87 93 41 B2 66 53 8C 38 AF 48 CE 03 01 00}
0x9CA94 	Guid: [0F0B1735-87A0-4193-B266-538C38AF48CE] {5F 15 35 17 0B 0F A0 87 93 41 B2 66 53 8C 38 AF 48 CE 04 00 00}
0x9CAA9 	Default Store: , DefaultId: 0x0 {5C 06 00 00 00 00}
0x9CAAF 	VarStore: VarStoreId: 0x1234 [A04A27F4-DF00-4D42-B552-39511302113D], Size: 0x4AE, Name: SystemConfig {24 23 F4 27 4A A0 00 DF 42 4D B5 52 39 51 13 02 11 3D 34 12 AE 04 53 79 73 74 65 6D 43 6F 6E 66 69 67 00}
0x9CAD2 	Form: Main, FormId: 0x1 {01 86 01 00 E8 00}
0x9CAD8 		Gray Out If {19 82}
0x9CADA 			64 Bit Unsigned Int: 0x1 {45 8A 01 00 00 00 00 00 00 00}
0x9CAE4 				64 Bit Unsigned Int: 0x1 {45 0A 01 00 00 00 00 00 00 00}
0x9CAEE 				Equal {2F 02}
0x9CAF0 			End {29 02}
0x9CAF2 			Text: Statement.Prompt: InsydeH2O Version, Statement.Help:  , TextTwo: Fake Data {03 08 EA 00 E9 00 E7 00}
0x9CAFA 		End If {29 02}
...
0x9CCDB 		Suppress If {0A 82}
0x9CCDD 			True {46 02}
0x9CCDF 			Numeric: , VarStoreInfo (VarOffset/VarName): 0x80, VarStore: 0x1234, QuestionId: 0x5, Size: 1, Min: 0x0, Max 0x0, Step: 0x0 {07 A6 00 00 00 00 05 00 34 12 80 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00}
0x9CD05 			End {29 02}
0x9CD07 		End If {29 02}
0x9CD09 	End Form {29 02}
0x9CD0B End Form Set {29 02}

You might have noticed VarStore: VarStoreId: 0x1234 [A04A27F4-DF00-4D42-B552-39511302113D] - the UUID is the same as the one from efivars and SetupUtility we found previously.

This confirms that the BIOS saves its settings into the EFI variable storage - which we can edit.

Edit time

To test if we can indeed modify the 'invisible' settings, I decided to pick an innocent-looking setting which hints (the OS?) the storage type of a SATA device, and since I recently swapped my Optical Drive for a SSD - this seemed fitting:

One Of: SATA Device Type, Help: "Identify Solid state Drive or Hard Disk Drive", VarStoreInfo (VarOffset/VarName): 0x2D5, VarStore: 0x1234, QuestionId: 0x27, Size: 1, Min: 0x0, Max 0x1, Step: 0x0 {05 A6 33 02 34 02 27 00 34 12 D5 02 00 10 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00}
	One Of Option: Hard Disk Drive, Value (8 bit): 0x0 (default) {09 0E 35 02 10 00 00 00 00 00 00 00 00 00}
	One Of Option: Solid State Drive, Value (8 bit): 0x1 {09 0E 36 02 00 00 01 00 00 00 00 00 00 00}
End One Of {29 02}

Note the format - VarStoreInfo (VarOffset/VarName): 0x2D5, VarStore: 0x1234
0x1234 is the efivar storage and 0x2D5 is the variable offset. In this case, 0x1234 correlates to the Setup-* efivar.

Note: To minimize chance of accidentally modifying the efivars, the following commands are run on a copy created with dd if=/sys/firmware/efi/efivars/Setup-* of=Setupvar

Let's test if we can retrieve the default value (in this case 0x0) from the efivar storage.

Note: this uses a dd hack to convert to LE encoding

/tmp/BIOS/ $ dd conv=swab status=none if=Setupvar ibs=1 skip=$((0x2D5)) count=1|xxd -pl
01

Huh, that doesn't seem right. If you move about a few bytes you will eventually find the value at +4 bytes from the offset in the IFR - this is because the efivar file has a 4 byte header.

We can add a +4 byte offset to the dd command:

/tmp/BIOS/ $ dd conv=swab status=none if=Setupvar ibs=1 skip=$((0x2D5+4)) count=1|xxd -pl
00

Just to be sure this isn't a coincidence, we can find a 16-bit value and check that:

Core Extra Turbo Voltage at 0x3EA with the default of 0x4B0 (16-bit) should do the job:

/tmp/BIOS/ $ dd conv=swab status=none if=Setupvar ibs=1 skip=$((0x3EA+4)) count=2|xxd -pl
04b0

To actually edit the efivar settings, we need a modified EFI GRUB Shell with a patch that allows us to edit the efivars with a command.

Once we boot it we can start messing with efivars by issuing the setup_var command:

Note: No need to adjust for the 4 byte header here - it's only present in the efivars file.

setup_var 0x2D5 will return the current value of 0x0

setup_var 0x2D5 0x1 will change the value to 0x1

Always look at the surrounding values to make sure you have the right offset. Just to be sure you are modifying the right variable, check the values of variables around the current one - in this case - at 0x2D5+1 and 0x2D5-1.

After a reboot we can confirm the value changed:

/tmp/BIOS/ $ dd conv=swab status=none if=Setupvar ibs=1 skip=$((0x2D5+4)) count=1|xxd -pl
01

More worthy/interesting settings:

OffsetDescriptionDefault ValueEdited value
0x1B1Package Power Limit MSR Lock0x1 / Locked0x0 / Unlocked
0x315Low Power Mode0x0 / Disabled0x3 / As requested by OS
0x2BDAggressive Link Power Management0x0 / Disabled0x1 / Enabled
0x362Passive Thermal Trip Point0x47 / 71C0x49 / 73C
0x1EEMe Fw Image Re-Flash function0x0 / Disabled0x1 / Enabled

It turns out this particular InsydeH2O BIOS allows unlocking the Intel ME region for firmware updates.

After issuing setup_var 0x1EE 0x1 and rebooting, we can confirm the Intel ME region is indeed unlocked:

/tmp/BIOS # flashrom -p internal -r bios.full.rom
[OMITTED FOR LENGTH]
Found chipset "Intel Lynx Point LP Premium".
[OMITTED FOR LENGTH]
Enabling flash write... Warning: BIOS region SMM protection is enabled!
Warning: Setting Bios Control at 0xdc from 0x22 to 0x01 failed.
New value is 0x22.
SPI Configuration is locked down.
FREG0: Flash Descriptor region (0x00000000-0x00000fff) is read-only.
Not all flash regions are freely accessible by flashrom. This is most likely
due to an active ME. Please see https://flashrom.org/ME for details.
PR0: Warning: 0x00700000-0x007fffff is read-only.
PR1: Warning: 0x00402000-0x006a1fff is read-only.
At least some flash regions are write protected. For write operations,
you should use a flash layout and include only writable regions. See
manpage for more details.
PROBLEMS, continuing anyway
Found Winbond flash chip "W25Q64.V" (8192 kB, SPI) mapped at physical address 0x00000000ff800000.
Reading flash... done.

Even though PR0 and PR1 are still read-only, the Management Engine region is no longer locked - meaning we can at least create a full backup of the ROM.