Qualcomm abl (Android bootloader) packing/signing

Search This thread

Renate

Recognized Contributor / Inactive Recognized Dev
The Qualcomm XBL (SBL1) and Firehose loader images are packed somewhat reasonably.
They are ELF images (32 or 64 bit) with no sections but 3 or more programs:
Code:
E:\>elfview xbl /p
 #  Type     Flags     Size  Offset  Address
--  -------  -----  -------  ------  --------
 0  null                960  000000  00000000 // this is the standard ELF header
 1  null               6952  001000  9fdb6000 // this is the signing
 2  load     RX      350012  003000  14015000 // these are various things that actually get loaded
 3  load     RWZ          0  058740  14077000
 4  load     RW       31844  058740  1407a000
 5  load     RWZ          0  0603B0  14084800
 6  load     RWZ          0  0603B0  85e00000
 7  load     RX       11824  0603B0  146ae000
 8  load     RW        2916  0631E0  146b1000
 9  load     RWX     107032  063D50  14098000
10  load     RWZ          0  07DF70  146b2000
11  load     RWX    1792000  07DF70  9fc00000
12  load     RX       78208  233770  14699000
13  load     RX      171536  2468F0  85e35000
14  load     RW        6409  270700  85ea8000
15  load     RWZ          0  272010  85e97000
That is to say:
Code:
32 bit ELF file
   Program table
   Signing
      Header
      Hashes // one for each program, the 2nd is zeroes as you can't hash the hash!*
      Signature
      Certificate chain
   Payload // multiple programs
So the XBL loads the next one, usually abl which is the Android bootloader which also implements the fastboot protocol.
Now we get a bit deep in the gumbo:
Code:
E:\>elfview abl /p
 #  Type     Flags     Size  Offset  Address
--  -------  -----  -------  ------  --------
 0  null                148  000000  00000000 // this is the standard ELF header
 1  null               6536  001000  9fa22000 // this is the signing
 2  load     RWX     139264  003000  9fa00000
Code:
32 bit ELF file
   Program table
   Signing
      Header
      Hashes
      Signature
      Certificate chain
   LZMA archive
      MZ Windows executable
         PE Portable executable
            64 bit ARM code
This is all (U)EFI compatibility so it has sworn fealty to Intel/Microsoft.
So, accepting all the idiocy of this, my question remains:
7-Zip can extract the structure of what is the #2 (i.e. the third) program:
Code:
C:\>7zip ablefi
Type = UEFIf
ERRORS:
Headers Error
Physical Size = 139264
Method = LZMA

   Date      Time    Attr         Size   Compressed  Name
------------------- ----- ------------ ------------  ------------------------
                    D....                            9E21FD93
                    D....                            9E21FD93\EE4E5898
                    .....            0               9E21FD93\EE4E5898\0.raw
                    D....                            9E21FD93\EE4E5898\VOLUME
                    .....           20               9E21FD93\EE4E5898\VOLUME\FFFFFFFF
                    .....       376832               9E21FD93\EE4E5898\VOLUME\LinuxLoader.efi
------------------- ----- ------------ ------------  ------------------------
                                376852       139264  3 files, 3 folders
Errors: 1
The "program" itself starts with 16 bytes 0x00.
If I remove these 16 bytes then 7-Zip can't decypher the file.

The ultimate question is that while I can trivially reverse engineer the actual abl and modify it so that "orange state" doesn't wait 30 seconds when rebooting, how can I LZMA-ish pack the modified results so that it's acceptable?
LZMA normally has a 13 byte header. Why does this all start with nulls?

*Please note that although you can't hash the hash, you can Can the Can
 
  • Like
Reactions: Appreciative

Renate

Recognized Contributor / Inactive Recognized Dev
@Appreciative
Sorry, I don't have anything technical documents on this at all except for Qualcomm stuff at the level of a PowerPoint.
All that stuff from booting onward is non-disclosure aggreement restricted.

I think that SecureBoot is in a OTP (one time programmable) fuse, but I don't know.
I can monitor the hardware console while booting the Firehose loader:
Code:
Format: Log Type - Time(microsec) - Message - Optional Info
Log Type: B - Since Boot(Power On Reset),  D - Delta,  S - Statistic
S - QC_IMAGE_VERSION_STRING=BOOT.XF.1.4-00246-S660LZB-1
S - IMAGE_VARIANT_STRING=Sdm660LA
S - OEM_IMAGE_VERSION_STRING=cibuild
S - Boot Interface: Unknown
S - Secure Boot: Off
S - Boot Config @ 0x00786070 = 0x000001c1
S - JTAG ID @ 0x00786130 = 0x000cc0e1
S - OEM ID @ 0x00786138 = 0x00000000
S - Serial Number @ 0x12345678 = 0x12345678
S - OEM Config Row 0 @ 0x00784188 = 0x0000000000000000
S - OEM Config Row 1 @ 0x00784190 = 0x0000000000000000
S - Feature Config Row 0 @ 0x007841a0 = 0x007030000b580100
S - Feature Config Row 1 @ 0x007841a8 = 0x00000000000000c0
S - Core 0 Frequency, 3715 MHz
S - PBL Patch Ver: 5
S - I-cache: On
S - D-cache: On
B -         0 - PBL, Start
B -      7024 - bootable_media_detect_entry, Start
B -    141363 - bootable_media_detect_success, Start
B -    141369 - elf_loader_entry, Start
B -  19372540 - auth_hash_seg_entry, Start
B -  19372841 - auth_hash_seg_exit, Start
B -  19481121 - elf_segs_hash_verify_entry, Start
B -  19534026 - elf_segs_hash_verify_exit, Start
B -  19534831 - auth_xbl_sec_hash_seg_entry, Start
B -  19563960 - auth_xbl_sec_hash_seg_exit, Start
B -  19563963 - xbl_sec_segs_hash_verify_entry, Start
B -  19570674 - xbl_sec_segs_hash_verify_exit, Start
B -  19570719 - PBL, End
B -         0 - SBL1, Start
I've also pieced together some addresses from logs and dumping the DTB:
Code:
00780000 msm-core
00780350 secure_boot
00784138 serial_number
00784188 oem_config0
00784190 oem_config1
007841a0 feature_config0
007841a8 feature_config1
00786040 jtagfuse
00786070 boot_config
00786130 jtag_id
00786138 oem_id
 
  • Like
Reactions: Appreciative

Yahoo Mike

Senior Member
Apr 3, 2011
429
186
Tamworth
Nice work. This saved me days of hard work.

The ultimate question is that while I can trivially reverse engineer the actual abl and modify it so that "orange state" doesn't wait 30 seconds when rebooting, how can I LZMA-ish pack the modified results so that it's acceptable?
LZMA normally has a 13 byte header. Why does this all start with nulls?
Did you ever find a solution to this re-pack problem? I'd like to do something similar for the bootloader on my tablet.
 

Yahoo Mike

Senior Member
Apr 3, 2011
429
186
Tamworth
...
The ultimate question is that while I can trivially reverse engineer the actual abl and modify it so that "orange state" doesn't wait 30 seconds when rebooting, how can I LZMA-ish pack the modified results so that it's acceptable?
LZMA normally has a 13 byte header. Why does this all start with nulls?
After a bit of research, I can answer that question.

It starts with nulls because it's not an LZMA header. It's a UEFI Firmware Volume (FV) header, in which the first 16 bytes are always zero.

I looked at a few UEFI FV editing tools, but none of them do quite what we want. So I've started on my own. I'll keep you posted on progress.

Here's a summary of my research:
Code:
 The layout of a UEFI Firmware Volume
 ====================================
 see: UEFI Platform Initialization Specification, Vol. 3 (https://uefi.org/specifications) 
 see also: https://sudonull.com/post/125061-UEFI-BIOS-file-device-part-two-UEFI-Firmware-Volume-and-its-contents

  +----------------+----------------+
  |                |   FV Header    |
  |                |================|--------------------+-------------------+
  |                |                |                    |     FF Header     |
  |                |                |                    |===================|---------------+
  |                |                |                    |                   |   FS Header   |
  |                |                |                    |  FF Section (FS)  |===============|
  |                |                |                    |        #1         |      data     |
  |                |                | Firmware File (FF) |                   |               |
  |                |                |        #1          |-------------------|---------------+
  |                |                |                    |       . . .       |     . . . 
  |                |                |                    |-------------------|
  |                |                |                    |  FF Section (FS)  |
  |  UEFI          |  Firmware      |                    |        #N         |
  |  Firmware      |  File          |--------------------|-------------------+
  |  Volume (FV)   |  System (FFS)  |                    |       . . .
  |                |                |        . . .       |
  |                |                |                    |
  |                |                |--------------------|
  |                |                |                    |
  |                |                | Firmware File (FF) |
  |                |                |        #N          |
  |                |                |                    |
  +----------------+----------------+--------------------+

To implement this, an object model might look like this:
 * a FirmwareVolume object has 1    FirmwareFileSystem
 * a FirmwareFileSystem    has 0..n FirmwareFile objects
 * a FirmwareFile object   has 0..n FirmwareFileSection objects

Just to make life interesting, one type of FF Section is the EFI_SECTION_FIRMWARE_VOLUME_IMAGE.
It has a UEFI Firmware Volume as its data.  So there can be FVs nested in other FVs ad infinitum.

FF Sections can also be encrypted, compressed or encoded by an OEM.
Once decrypted/decompressed/decoded, these sections contain further FF sections.

And in accordance with Murphy's Law, the file we want (LinuxLoader.efi) is in one of those nested firmware volumes. ...And you guessed it - the nested FV is compressed using LZMA. Presumably that's deliberate - so you can't directly edit the bootloader in the abl.elf.

So I plan for my tool to do two things:
  • extract the LinuxLoader.efi file from the abl.elf file
  • repack a modified LinuxLoader.efi back into the ELF file.
I don't know if a repack will work. There are some checksums in the EFi headers. And the whole ABL.ELF might be signed by a private RSA key and the SoC will refuse to load it without the correct signature. We'll see..
 
  • Like
Reactions: Renate

Renate

Recognized Contributor / Inactive Recognized Dev
After a bit of research, I can answer that question.

It starts with nulls because it's not an LZMA header. It's a UEFI Firmware Volume (FV) header, in which the first 16 bytes are always zero.
Well, that's very clever of them. Most standards use "magic" values in the header.
These guys have taken a bold stand: "When you see 16 0x00 it's an unambiguous sign that you've found our UEFI FV!"

<rant>
I've dealt with kernels that were uncompressed, compressed with gzip or compressed and a decompressor stub.
So, imagine my surprise when I ungzipped a kernel from a boot image recently and found that it starts with "MZ".
"OMG, did Bill Gates buy Android? Is this the MS-DOS compatibilty layer?"
No, it's just the MS-DOS header stuck on a Windows PE header stuck on a kernel.
Code:
00000000 t _head
00000000 T _text
00000040 t pe_header
00000044 t coff_header
00000058 t optional_header
00000070 t extra_header_fields
000000f8 t section_table
00001000 T do_undefinstr
00001000 t efi_header_end
00001000 T _stext
</rant>
 
  • Like
Reactions: kopsu

Yahoo Mike

Senior Member
Apr 3, 2011
429
186
Tamworth
Well, that's very clever of them. Most standards use "magic" values in the header.
These guys have taken a bold stand: "When you see 16 0x00 it's an unambiguous sign that you've found our UEFI FV!"

<rant>
I've dealt with kernels that were uncompressed, compressed with gzip or compressed and a decompressor stub.
So, imagine my surprise when I ungzipped a kernel from a boot image recently and found that it starts with "MZ".
"OMG, did Bill Gates buy Android? Is this the MS-DOS compatibilty layer?"
No, it's just the MS-DOS header stuck on a Windows PE header stuck on a kernel.
Code:
00000000 t _head
00000000 T _text
00000040 t pe_header
00000044 t coff_header
00000058 t optional_header
00000070 t extra_header_fields
000000f8 t section_table
00001000 T do_undefinstr
00001000 t efi_header_end
00001000 T _stext
</rant>
Yes, very clever. But there is method in their madness. According to the spec: "The first 16 bytes are reserved to allow for the reset vector of processors whose reset vector is at address 0."

There is a bit of magic at byte 0x28 in the FV header. It's always "_FVH". That's how you identify a UEFI FV. But I guess the first 16 nulls are always a big hint.

...and it's great to see that the ghost of MS-DOS lives on. It reminds me of what we used to say in my early programming days: "DOS is boss".
 
  • Like
Reactions: Renate

Renate

Recognized Contributor / Inactive Recognized Dev
I've been looking into this hashing business.
I know that I'm basically on the right track.
I can take a Firehose loader (basically a custom xbl) make a trivial spelling change somewhere and it will refuse to load.
I can recalculate the SHA256, replace it and it will load correctly.

I have a WIP that checks the hashes on xbl/abl/firehose loaders (all basically the same).
I see tons of files that check out 100%.
Code:
C:\>qcomview /h poke3.bin
64 bit ELF, SHA256
 0  00000000  00000318  a117dbc5 e643e404 361bfe30 45fbda01 4c153842 59a4cbe8 09b7da55 a2dd413e  OK
 1  00001000  00001ac8
 2  00003000  0005709c  7b833734 f2763b9e 35f3310c f6fb22a9 a514eac0 3eddbe46 b5ff339b 3c7b045c  OK
 3  0005a0a0  00000000
 4  0005a0a0  00009f00  6296c006 31852f79 b99691c3 e8d598f2 9d323e9a ba0358aa b742901f 506709d5  OK
 5  00063fa0  00009908  41176495 3e07ad84 8923398e ce854131 91066dca 43f253fa c027c4f4 a3c21483  OK
 6  0006d8b0  00000000
 7  0006d8b0  00001e7c  fe77c473 b02e4a71 d3f287e4 cf85ccbe b5a43326 53930bd8 d68e4e40 6e71a0b8  OK
 8  0006f730  00000000
 9  0006f730  000188d8  1bfef74c ed467a22 8616419d e71ab1ea 22a717e5 4874c704 541793ed f5d5c5e5  OK
10  00088010  00000000
11  00088010  00000000
12  00088010  00012dc0  b72cb77e 81026632 446c3462 cc6c83fc d7904333 cb8807cc 27d6e4c9 189c7ca4  OK

I see a bunch of files that don't check out at all.
I can SHA256 a program chunk and the SHA256 appears nowhere in the entire file.
Are they salting the SHA256 or using a different hash?

Edit: Oh, it looks like SHA512. :( Er, maybe SHA3 512?


Edit: Ok, big deal, so I can't count. It's SHA(2)-384. WIP can be found http://www.temblast.com/qcomview.htm
The SHA256 displays the calculated and verifies.
The SHA384 displays the file content and does not verify (yet). calculated and verifies.
There is also an option to dump an ASN1 listing of the certificate chain.
 
Last edited:
  • Like
Reactions: HemanthJabalpuri

Yahoo Mike

Senior Member
Apr 3, 2011
429
186
Tamworth
qcomview works great. Thanks.

With @Renate 's invaluable guidance, I put together a tool to extract the LinuxLoader from the abl.elf file, and to repack a modified LinuxLoader back into the abl.elf file. But it comes with a warning: don't try to run a modified abl.elf on a device with SecureBoot on - if you do, you will probably brick your device. Of course, if SecureBoot is off, this tool should work for you.

If you want to try it, the tool source is available at: github.com/Yahoo-Mike/lltool. It's still in beta. It was only tested on a limited number of ELF files and it might need some fine-tuning for different OEMs. So feel free to report issues or send pull requests. If it doesn't work on your ELF file, send me a link to the file and I'll see what I can do.
 

Renate

Recognized Contributor / Inactive Recognized Dev
If you want to try it, the tool source is available at...
Nice work! (And a bunch of it.)

I just got around to modding my abl. I just did it manually.
You just split your abl at 0x3078. The high piece is just the LZMA. You expand/mod/compress and stick it back.
Then you have to pad it nicely, change the size in the program table, fix the signing on program #0 and #2.
And just like that you're done!

It made a world of difference. I got rid of the 30 second delay for being Orange. It seems I waste half a day with that 30 seconds.

Have you got a SecureBoot=off device?
I'm sure many people are getting sick of all the restrictions.
 

Yahoo Mike

Senior Member
Apr 3, 2011
429
186
Tamworth
You just split your abl at 0x3078. The high piece is just the LZMA. You expand/mod/compress and stick it back.
Then you have to pad it nicely, change the size in the program table, fix the signing on program #0 and #2.
And just like that you're done!
Spot on!

Have you got a SecureBoot=off device?
No, unfortunately. That's why I'm still hunting around for other options. I'm currently thinking about self-signing the modded abl.elf. Maybe I'll get lucky and the OEM allows that on my device...

BTW, I don't suppose you know the answer to this question: How to find "hw_soc_version" for a QCom SOC?
 

Renate

Recognized Contributor / Inactive Recognized Dev
Usually the SHA256 of the root CA corresponds to the "Hash" burned into the chip.
I see that's true for about 80% of the loaders in the repository.
There may be a newish wrinkle where there is an encryption step or something.
But if you're SecureBoot, everything has to match all the way down.
 

Renate

Recognized Contributor / Inactive Recognized Dev
Some trivia from our favorite loader repository:
Code:
Older style, not an ELF       [ 89]

32 bit ELF, Version 3, SHA256 [460]
32 bit ELF, Version 3, SHA384 [  8]
64 bit ELF, Version 3, SHA256 [ 31]

64 bit ELF, Version 5, SHA256 [ 65]

32 bit ELF, Version 6, SHA384 [  4]
64 bit ELF, Version 6, SHA384 [ 54]
Of course a bunch of these are duplicated and put in different directories.
 

Renate

Recognized Contributor / Inactive Recognized Dev
I just got around to modding my abl. I just did it manually.
You just split your abl at 0x3078. The high piece is just the LZMA. You expand/mod/compress and stick it back.
Then you have to pad it nicely, change the size in the program table, fix the signing on program #0 and #2.
And just like that you're done!
I've got it down to 100% automatic now. It uses a makefile and a few of my "glue" utilities.
Code:
hexcalc size(ablmod)-3000 > hexcalc.tmp
 

Top Liked Posts

  • There are no posts matching your filters.
  • 1
    The Qualcomm XBL (SBL1) and Firehose loader images are packed somewhat reasonably.
    They are ELF images (32 or 64 bit) with no sections but 3 or more programs:
    Code:
    E:\>elfview xbl /p
     #  Type     Flags     Size  Offset  Address
    --  -------  -----  -------  ------  --------
     0  null                960  000000  00000000 // this is the standard ELF header
     1  null               6952  001000  9fdb6000 // this is the signing
     2  load     RX      350012  003000  14015000 // these are various things that actually get loaded
     3  load     RWZ          0  058740  14077000
     4  load     RW       31844  058740  1407a000
     5  load     RWZ          0  0603B0  14084800
     6  load     RWZ          0  0603B0  85e00000
     7  load     RX       11824  0603B0  146ae000
     8  load     RW        2916  0631E0  146b1000
     9  load     RWX     107032  063D50  14098000
    10  load     RWZ          0  07DF70  146b2000
    11  load     RWX    1792000  07DF70  9fc00000
    12  load     RX       78208  233770  14699000
    13  load     RX      171536  2468F0  85e35000
    14  load     RW        6409  270700  85ea8000
    15  load     RWZ          0  272010  85e97000
    That is to say:
    Code:
    32 bit ELF file
       Program table
       Signing
          Header
          Hashes // one for each program, the 2nd is zeroes as you can't hash the hash!*
          Signature
          Certificate chain
       Payload // multiple programs
    So the XBL loads the next one, usually abl which is the Android bootloader which also implements the fastboot protocol.
    Now we get a bit deep in the gumbo:
    Code:
    E:\>elfview abl /p
     #  Type     Flags     Size  Offset  Address
    --  -------  -----  -------  ------  --------
     0  null                148  000000  00000000 // this is the standard ELF header
     1  null               6536  001000  9fa22000 // this is the signing
     2  load     RWX     139264  003000  9fa00000
    Code:
    32 bit ELF file
       Program table
       Signing
          Header
          Hashes
          Signature
          Certificate chain
       LZMA archive
          MZ Windows executable
             PE Portable executable
                64 bit ARM code
    This is all (U)EFI compatibility so it has sworn fealty to Intel/Microsoft.
    So, accepting all the idiocy of this, my question remains:
    7-Zip can extract the structure of what is the #2 (i.e. the third) program:
    Code:
    C:\>7zip ablefi
    Type = UEFIf
    ERRORS:
    Headers Error
    Physical Size = 139264
    Method = LZMA
    
       Date      Time    Attr         Size   Compressed  Name
    ------------------- ----- ------------ ------------  ------------------------
                        D....                            9E21FD93
                        D....                            9E21FD93\EE4E5898
                        .....            0               9E21FD93\EE4E5898\0.raw
                        D....                            9E21FD93\EE4E5898\VOLUME
                        .....           20               9E21FD93\EE4E5898\VOLUME\FFFFFFFF
                        .....       376832               9E21FD93\EE4E5898\VOLUME\LinuxLoader.efi
    ------------------- ----- ------------ ------------  ------------------------
                                    376852       139264  3 files, 3 folders
    Errors: 1
    The "program" itself starts with 16 bytes 0x00.
    If I remove these 16 bytes then 7-Zip can't decypher the file.

    The ultimate question is that while I can trivially reverse engineer the actual abl and modify it so that "orange state" doesn't wait 30 seconds when rebooting, how can I LZMA-ish pack the modified results so that it's acceptable?
    LZMA normally has a 13 byte header. Why does this all start with nulls?

    *Please note that although you can't hash the hash, you can Can the Can
    1
    @Appreciative
    Sorry, I don't have anything technical documents on this at all except for Qualcomm stuff at the level of a PowerPoint.
    All that stuff from booting onward is non-disclosure aggreement restricted.

    I think that SecureBoot is in a OTP (one time programmable) fuse, but I don't know.
    I can monitor the hardware console while booting the Firehose loader:
    Code:
    Format: Log Type - Time(microsec) - Message - Optional Info
    Log Type: B - Since Boot(Power On Reset),  D - Delta,  S - Statistic
    S - QC_IMAGE_VERSION_STRING=BOOT.XF.1.4-00246-S660LZB-1
    S - IMAGE_VARIANT_STRING=Sdm660LA
    S - OEM_IMAGE_VERSION_STRING=cibuild
    S - Boot Interface: Unknown
    S - Secure Boot: Off
    S - Boot Config @ 0x00786070 = 0x000001c1
    S - JTAG ID @ 0x00786130 = 0x000cc0e1
    S - OEM ID @ 0x00786138 = 0x00000000
    S - Serial Number @ 0x12345678 = 0x12345678
    S - OEM Config Row 0 @ 0x00784188 = 0x0000000000000000
    S - OEM Config Row 1 @ 0x00784190 = 0x0000000000000000
    S - Feature Config Row 0 @ 0x007841a0 = 0x007030000b580100
    S - Feature Config Row 1 @ 0x007841a8 = 0x00000000000000c0
    S - Core 0 Frequency, 3715 MHz
    S - PBL Patch Ver: 5
    S - I-cache: On
    S - D-cache: On
    B -         0 - PBL, Start
    B -      7024 - bootable_media_detect_entry, Start
    B -    141363 - bootable_media_detect_success, Start
    B -    141369 - elf_loader_entry, Start
    B -  19372540 - auth_hash_seg_entry, Start
    B -  19372841 - auth_hash_seg_exit, Start
    B -  19481121 - elf_segs_hash_verify_entry, Start
    B -  19534026 - elf_segs_hash_verify_exit, Start
    B -  19534831 - auth_xbl_sec_hash_seg_entry, Start
    B -  19563960 - auth_xbl_sec_hash_seg_exit, Start
    B -  19563963 - xbl_sec_segs_hash_verify_entry, Start
    B -  19570674 - xbl_sec_segs_hash_verify_exit, Start
    B -  19570719 - PBL, End
    B -         0 - SBL1, Start
    I've also pieced together some addresses from logs and dumping the DTB:
    Code:
    00780000 msm-core
    00780350 secure_boot
    00784138 serial_number
    00784188 oem_config0
    00784190 oem_config1
    007841a0 feature_config0
    007841a8 feature_config1
    00786040 jtagfuse
    00786070 boot_config
    00786130 jtag_id
    00786138 oem_id
    1
    Did you ever find a solution to this re-pack problem? I'd like to do something similar for the bootloader on my tablet.
    Nope, I havn't gotten back to this yet.
    I guess it's just a question of biting the bullet and diving into this swamp of stupid packing.
    1
    ...
    The ultimate question is that while I can trivially reverse engineer the actual abl and modify it so that "orange state" doesn't wait 30 seconds when rebooting, how can I LZMA-ish pack the modified results so that it's acceptable?
    LZMA normally has a 13 byte header. Why does this all start with nulls?
    After a bit of research, I can answer that question.

    It starts with nulls because it's not an LZMA header. It's a UEFI Firmware Volume (FV) header, in which the first 16 bytes are always zero.

    I looked at a few UEFI FV editing tools, but none of them do quite what we want. So I've started on my own. I'll keep you posted on progress.

    Here's a summary of my research:
    Code:
     The layout of a UEFI Firmware Volume
     ====================================
     see: UEFI Platform Initialization Specification, Vol. 3 (https://uefi.org/specifications) 
     see also: https://sudonull.com/post/125061-UEFI-BIOS-file-device-part-two-UEFI-Firmware-Volume-and-its-contents
    
      +----------------+----------------+
      |                |   FV Header    |
      |                |================|--------------------+-------------------+
      |                |                |                    |     FF Header     |
      |                |                |                    |===================|---------------+
      |                |                |                    |                   |   FS Header   |
      |                |                |                    |  FF Section (FS)  |===============|
      |                |                |                    |        #1         |      data     |
      |                |                | Firmware File (FF) |                   |               |
      |                |                |        #1          |-------------------|---------------+
      |                |                |                    |       . . .       |     . . . 
      |                |                |                    |-------------------|
      |                |                |                    |  FF Section (FS)  |
      |  UEFI          |  Firmware      |                    |        #N         |
      |  Firmware      |  File          |--------------------|-------------------+
      |  Volume (FV)   |  System (FFS)  |                    |       . . .
      |                |                |        . . .       |
      |                |                |                    |
      |                |                |--------------------|
      |                |                |                    |
      |                |                | Firmware File (FF) |
      |                |                |        #N          |
      |                |                |                    |
      +----------------+----------------+--------------------+
    
    To implement this, an object model might look like this:
     * a FirmwareVolume object has 1    FirmwareFileSystem
     * a FirmwareFileSystem    has 0..n FirmwareFile objects
     * a FirmwareFile object   has 0..n FirmwareFileSection objects
    
    Just to make life interesting, one type of FF Section is the EFI_SECTION_FIRMWARE_VOLUME_IMAGE.
    It has a UEFI Firmware Volume as its data.  So there can be FVs nested in other FVs ad infinitum.
    
    FF Sections can also be encrypted, compressed or encoded by an OEM.
    Once decrypted/decompressed/decoded, these sections contain further FF sections.

    And in accordance with Murphy's Law, the file we want (LinuxLoader.efi) is in one of those nested firmware volumes. ...And you guessed it - the nested FV is compressed using LZMA. Presumably that's deliberate - so you can't directly edit the bootloader in the abl.elf.

    So I plan for my tool to do two things:
    • extract the LinuxLoader.efi file from the abl.elf file
    • repack a modified LinuxLoader.efi back into the ELF file.
    I don't know if a repack will work. There are some checksums in the EFi headers. And the whole ABL.ELF might be signed by a private RSA key and the SoC will refuse to load it without the correct signature. We'll see..
    1
    After a bit of research, I can answer that question.

    It starts with nulls because it's not an LZMA header. It's a UEFI Firmware Volume (FV) header, in which the first 16 bytes are always zero.
    Well, that's very clever of them. Most standards use "magic" values in the header.
    These guys have taken a bold stand: "When you see 16 0x00 it's an unambiguous sign that you've found our UEFI FV!"

    <rant>
    I've dealt with kernels that were uncompressed, compressed with gzip or compressed and a decompressor stub.
    So, imagine my surprise when I ungzipped a kernel from a boot image recently and found that it starts with "MZ".
    "OMG, did Bill Gates buy Android? Is this the MS-DOS compatibilty layer?"
    No, it's just the MS-DOS header stuck on a Windows PE header stuck on a kernel.
    Code:
    00000000 t _head
    00000000 T _text
    00000040 t pe_header
    00000044 t coff_header
    00000058 t optional_header
    00000070 t extra_header_fields
    000000f8 t section_table
    00001000 T do_undefinstr
    00001000 t efi_header_end
    00001000 T _stext
    </rant>