Unlocking the Motorola Bootloader

In this blog post, we'll explore the Motorola bootloader on recent Qualcomm Snapdragon devices. Our goal will be to unlock the bootloader of a Moto X (2nd Gen), by using the TrustZone kernel code execution vulnerability from the previous blog posts. Note that although we will show the complete unlocking process for this specific device, it should be general enough to work at-least for most modern Motorola devices.

Why Motorola?

After reporting the previous TrustZone kernel privilege escalation to Qualcomm, I was gifted a shiny new Moto X. However... There was one little snag - they accidentally sent me a locked device. This was a completely honest mistake, and they did offer many times to unlock the device - but where's the fun in that? So without further ado, let's dive into the Motorola bootloader and see what it takes to unlock it.

Setting the Stage

Before we start our research, let's begin with a short introduction to the boot process - starting right at the point at which a device is powered on.

First - the PBL (Primary Boot Loader), also known as the "BootROM" is executed. Since the PBL is stored within an internal mask ROM, it cannot be modified or provisioned, and is therefore an intrinsic part of the device. As such, it only serves the very minimal purpose of allowing the device to boot, and authenticating and loading the next part of the boot-chain.

Then, two secondary bootloaders are loaded, SBL1 (Secondary Boot Loader), followed by SBL2. Their main responsibility is to boot up the various processors on the SoC and configure them so that they're ready to operate.

Next up in the boot-chain, the third and last secondary bootloader, SBL3, is loaded. This bootloader, among other tasks, verifies and loads the Android Bootloader - "aboot".

Now this is where we get to the part relevant for our unlocking endeavours; the Android Bootloader is the piece of software whose responsibility is, as its name suggests, to load the Android operating system and trigger its execution.

This is also the piece of boot-chain that OEMs tend to customize the most, mainly because while the first part of the boot-chain is written by Qualcomm and deals with SoC specifics, the Android bootloader can be used to configure the way the Android OS is loaded.

Among the features controlled by aboot is the "bootloader lock" - in other words, aboot is the first piece of the boot-chain which can opt to break the chain of trust (in which each bootloader stage verifies the next) and load an unsigned operating system.

For devices with an unlockable bootloader, the unlocking process is usually performed by rebooting the device into a special ("bootloader") mode, and issuing the relevant fastboot command. However, as we will later see, this interface is also handled by aboot. This means that not only does aboot query the lock status during the regular boot process, but it also houses the code responsible for the actual unlocking process.

As you may know, different OEMs take different stances on this issue. In short, "Nexus" devices always ship with an "unlockable" bootloader. In contrast, Samsung doesn't allow bootloader unlocking for most of its devices. Other OEMs, Motorola included, ship their devices locked, but certain devices deemed "eligible" can be unlocked using a "magic" (signed) token supplied by the OEM (although this also voids the warranty for most devices).

So... it's all very complex, but also irrelevant. That's because we're going to do the whole process manually - if aboot can control the lock status of the device, this means we should probably be able to do so as well, given an elevated enough set of privileges.

Getting Started

Now that we have a general grasp of the components involved and of our goal, the next stage is to analyse the actual aboot code.

Since the binaries for all stages of the boot-chain are contained within the factory firmware image, that would naturally be a good place to start. There are several download links available - here are a few. In case you would like to follow along with me, I'm going to refer to the symbols in the version "ATT_XT1097_4.4.4_KXE21.187-38".

After downloading the firmware image, we are faced with our first challenge - the images are all packed using a proprietary format, in a file called "motoboot.img". However, opening the file up in a hex-editor reveals it has a pretty simple format we can deduce:

As you can see above, the sought-after aboot image is stored within this file, along with the TrustZone image, and various stages of the boot-chain. Good.

After analysing the structure above, I've written a python script which can be used to unpack all the images from a given Motorola bootloader image, you can find it here.

Much ado aboot nothing

We'll start by inspecting the aboot image. Discouragingly, it is 1MB large, so going over it all would be a waste of time. However, as we've mentioned above, when booting the device into the special "bootloader" mode, the actual interaction with the user is provided by aboot itself. This means that we can start by searching for the strings which are displayed when the unlocking process is performed - and continue from there.

A short search for the "unlock..." string which is printed after starting the unlock process brings us straight to the function (@0xFF4B874) which deals with the unlocking logic:

That was pretty fast!

As you can see, after printing the string to the console, three functions are called consecutively, and if all three of them succeed, the device is considered unlocked.

Going over the last two functions reveals their purpose is to erase the user's data partitions (which is always performed after the bootloader is unlocked, in order to protect the device owner's privacy). In any case, this means they are irrelevant to the unlocking process itself and are simply side-effects.

This leaves us with a single function which, when called, should unlock the bootloader.

So does this mean we're done already? Can we just call this function and unlock the device?

Actually, not yet. Although the TrustZone exploit allows us to achieve code-execution within the TrustZone kernel, this is only done after the operating system is loaded, at which point, executing aboot code directly could cause all sorts of side-effects (since, for example, the code might assume that there is no operating system/the MMU could be disabled, etc.). And even if it were that simple, perhaps there is something interesting to be learned by fully understanding the locking mechanism itself.

Regardless, if we can understand the logic behind the code, we can simply emulate it ourselves, and perform the meaningful parts of it from our TrustZone exploit. Analysing the unlocking function reveals a surprisingly simple high-level logic:

Unfortunately, these two functions wreak havoc within IDA (which fails to even display a meaningful call-graph for them).

Manually analysing the functions reveals that they are in fact quite similar to one another. They both don't contain much logic of their own, but instead they prepare arguments and call the following function:

This is a little surprising - instead of handling the logic itself, this function issues an an SMC (Supervisor Mode Call) in order to invoke a TrustZone system-call from aboot itself! (as we've discussed in previous blog posts). In this case, both functions issue an SMC with the request code 0x3F801. Here is the relevant pseudo-code for each of them:

At this point we've gleaned all the information we need from aboot, now lets switch over to the TrustZone kernel to find out what this SMC call does.

Enter Stage Left, TrustZone

Now that we've established that an SMC call is made with the command-code 0x3F801, we are left with the task of finding this command within the TrustZone kernel.

Going over the TrustZone kernel system calls, we arrive at the following entry:

This is a huge function which performs widely different tasks based on the first argument supplied, which we'll call the "command code" from now on.

It should be noted an additional flag is passed into this system-call indicating whether or not it was called from a "secure" context. This means that if we try invoking it from the Android OS itself, an argument will be passed marking our invocation is insecure, and will prevent us from performing these operations ourselves. Of course, we can get around this limitation using our TrustZone exploit, but we'll go into that later!

As we've seen above, this SMC call is triggered twice, using the command codes #1 and #2 (I've annotated the functions below to improve readability):

In short, we can see both commands are used to read and write (respectively) values from something called a "QFuse".


Much like a real-life fuse, a QFuse is a hardware component which facilitates a "one-time-writeable" piece of memory. Each fuse represents a single bit; fuses which are in-tact represent the bit zero, and "blown" fuses represent the bit one. However, as the name suggests, this operation is irreversible - once a fuse is blown it cannot be "un-blown".

Each SoC has it's own arrangement of QFuses, each with it's own unique purpose. Some fuses are already blown when a device is shipped, but others can be blown depending on the user's actions in order to change the way a specific device feature operates.

Unfortunately, the information regarding the role of each fuse is not public, and we are therefore left with the single option of reversing the various software components to try and deduce their role.

In our case, we call a specific function in order to decide which fuse we are going to read and write:

Since we call this function with the second syscall argument, in our case "4", this means we will operate on the fuse at address 0xFC4B86E8.

Putting it all together

Now that we understand the aboot and the TrustZone logic, we can put them together to get the full flow:

  • First, aboot calls SMC 0x3F801 with command-code #1
    • This causes the TrustZone kernel to read and return the QFuse at address 0xFC4B86E8
  • Then, iff the first bit in the QFuse is disabled, aboot calls SMC 0x3F801 once more, this time with command-code #2
    • This causes the TrustZone kernel to write the value 1 to the LSB of the aforementioned QFuse.
Turns out to be very simple after all - we just need to set a single bit in a single QFuse, and the bootloader will be considered unlocked.

But how can QFuses be written?

DIY QFuses

Luckily the TrustZone kernel exposes a pair of system-call which allow us to read and write a restricted set of QFuses - tzbsp_qfprom_read_row and tzbsp_qfprom_write_row, respectively. If we can lift those restrictions using our TrustZone exploit, we should be able to use this API in order to blow the wanted QFuse.

Lets take a look at these restrictions within the tzbsp_qfprom_write_row system-call:

So first, there's a DWORD at 0xFE823D5C which must be set to zero in order for the function's logic to continue. Normally this flag is in fact set to one, thus preventing the usage of the QFuse calls, but we can easily enough overwrite the flag using the TrustZone exploit.

Then, there's an additional function called, which is used to make sure that the ranges of fuses being written are "allowed":

As we can see, this function goes over a static list of pairs, each denoting the start and end address of the allowed QFuses. This means that in order to pass this check, we can overwrite this static list to include all QFuses (setting the start address to zero and the end address to the maximal QFuse relative address - 0xFFFF).

Trying it out

Now that we have everything figured out, it's time to try it out ourselves! I've written some code which does the following:
  • Achieves code-execution within TrustZone
  • Disables the QFuse protections
  • Writes the LSB QFuse in QFuse 0xFC4B86E8
I encourage you to check out the code here: https://github.com/laginimaineb/Alohamora

Have fun!

Final Thoughts

In this blog post we went over the flow controlled by a single QFuse. But, as you can probably guess, there are many different interesting QFuses out there, waiting to be discovered.

On the one hand, blowing a fuse is really "dangerous" - making one small mistake can permanently brick you device. On the other hand, some fuses might facilitate a special set of features that we would like to enable.

One such example is the "engineering" fuse; this fuse is mentioned throughout the aboot image, and can be used to enable an amazing range of capabilities such as skipping secure boot, loading unsigned peripheral images, having an unsigned GPT, and much more.

However, this fuse is blown in all consumer devices, marking the device as a "non-engineer" device, and disabling these features. But who knows, maybe there are other fuses which are just as important, which have not yet been discovered...


  1. Lol, love the warning message. Injury to users!?

    1. Those allergic to excessive warning messages, maybe?

    2. Does this work for Motorola Droid maxx/Ultra/Mini

    3. For those of you asking about the4 noble truths Droid Ultra/Maxx/Mini, this guy used a different exploit to unlock his bootloader: http://theroot.ninja/disclosures/TRUSTNONE_1.0-11282015.pdf. Unfortunately, he has not released his code, and someone would have to write their own code in order to take advantage of the exploit he used. Additionally, as far as I know, the latest firmware for these devices (SU6-7.7) cannot be rooted (not even with Dirty COW, unless someone can devise a way around SELinux), and you would need to be rooted in order to take advantage of these exploits first.

  2. Hold Up! Did you really just do this

  3. Hi, I'm wondering how did you did the trustzone exploit on this device. As I can remember your trustzone exploit needed a modified kernel (or can I load it as a kernel module?) And if the device is bootloader locked how did you get the custom kernel to run?
    Thank you for the research :D If this is double on by loading a kernel module. I'm going to try adjust this to my Fire Phone(It'll probably brick :P But I'm going to do it anyway)

    1. This wouldnt apply to the firephone, iirc the boot unlock mechanism on that is a signed blob, not a qfuse.

    2. Hi Madushan,

      I read your question and got a little curious, so I downloaded the Fire Phone aboot and had a look at it.

      As Justin said, the bootloader lock there is facilitated by using a signed blob. Here is the unlocking code: http://imgur.com/OZeTqNC

      That said, it might still be possible to craft a blob that'll cause the verification to pass, ultimately depends on how the verification is done (let me know if you take a look at it!).

      Anyway, as for the unlocking code I provided - you're right, this version of the code depends on my modified kernel. I also have another version, written in C, which uses a kernel exploit to directly execute code in the kernel and issue SMCs from there. I'll publish that as well (just need to clean it up a little).


    3. UPDATE: Dug a little bit deeper; seems like a 2048-bit RSA signature. I carved out the certificate: http://imgur.com/1a2TY0P

      So unless there's some kind of bug in the verification itself or an alternative unlocking flow, seems like a no-go.

    4. UPDATE2: So the code actually calls RSA_public_decrypt (with PKCS1 padding) on the given token, then makes sure that the content in it is 0x[SOME_WORD][SOME_DWORD][zero_pad_to_length_256]. I'm still thinking about this a little... I don't know what these DWORDs are (could try and find out), but if they can be changed, then you could modify them to fit any given signed token (for any other phone).

    5. UPDATE3: Okay - a lot of the code there is borrowed from LK (https://www.codeaurora.org/cgit/external/gigabyte/qrd-gb-dsds-7225/plain/bootable/bootloader/lk/platform/msm_shared/mmc.c) which makes following the flow easier.

      Anyway, these DWORDs are read in from the MMC - in the version of aboot that I analysed (http://forum.xda-developers.com/attachment.php?attachmentid=3437011&d=1439413035), they are fetched in from: (byte)0xF967AA4+0x4A4, (DWORD)0xF967AA4+0x4B8.

      If you want to play around with the TrustZone exploit and read those addresses, we can try and figure out what they are. In any case, we can always call the MMC flashing code to overwrite them, and then supply *any* signed token to unlock.

      Just so you know, though, this is quite dangerous - if anything else depends on these values we may brick the device.

    6. Wow, There I was waiting for a reply in this comment and I've forgotten to turn on the email notifications! Sorry for the *very* late reply. I just saw this.
      A while back I started this thread (http://forum.xda-developers.com/fire-phone/general/dev-bootloader-unlock-development-t3183330) on XDA to discuss about probable fire phone bootloader unlocks (I've meantioned some blog posts here in it :) ).

      So the version of the aboot you inspected is from fireos 3.x.x version. It has your trustzone exploit. But in the newer version of the fireos (4.6.3 which sadly I have in my phone :/ ) seems to have fixed the bug. I'm not sure though. I'm not very good with reversing stuff. I can upload the newest version of the aboot to somewhere if you are interested.

      Anyway this means people with older fireos versions will be able to run your exploit without a kernel modification right? (kernel module is ok too. I can load unsigned kernel modules without a problem). I can try this too on my phone if you release the code.

      What you are proposing is something like overwriting the public key on the MMC right?

      Thank you for taking the time to look at my device, which amazon has forgotten. :)

    7. BTW, there was some person(or two) over XDA who was willing to donate a firephone to whoever is trying to unlock it. Checkout this thread (http://forum.xda-developers.com/fire-phone/general/bounty-pledge-to-unlock-fire-phone-t3204176)

    8. Hi Madushan,

      I didn't forget my promise, I will definitely release the C version of my exploit (once I get a little bit of free time). Bear in mind that I'm releasing a whole new TrustZone exploit chain, which I'm pretty sure is relevant for the FirePhone as well - so that should work for you.

      If you're willing to experiment, feel free to use it to follow the instructions in the blog posts. I'll do my best to help you out, but I'm pretty busy so I can't promise anything.


    9. Hi Gal,
      Am I correcting assuming the exploit you mention here is the FuzzZone explicit that was fixed after 30.10.14? If that so, New releases of the fire os had it fixed. :/ Older ones would work though.
      I'm looking forward to you release to start experimenting. Thank you for all the work. :)

    10. For the C version, I was referring to the previous exploit, yes. I'll do my best to release the new ones as soon as time permits.

  4. This is impressive :) I'm tempted to puck up a cheap Moto E LTE just to try and see if it works there too!

    1. Where are you finding a Moto E using MSM8974/SD810?


  5. Thanks for the interesting articles!

    Got a small question - what do you use to get comments with ARM opcodes description?

    1. Thanks for reading!

      The comments are a builtin feature in IDA (Options->General->Disassembly->Comments).

      P.S - I usually don't use this (as I find the clutter a little annoying), but for the purpose of the blog posts I enable it so that it'll be easier for people without an ARM background to read.

  6. Why would your C exploit need cleaned up, I can do it in 4 lines of code :P Nice writeup, as always.


    1. Right now I have a C file with all my TZ experiments and *lots* of irrelevant code, I wouldn't wish it on anyone to try and figure out what's going on there ;)

  7. Is there an easy way to remap ram over the qfuse range for experimentation purposes?
    Or alternately a central place to patch a read-qfuse function?
    To make it less dangerous to explore the different fuse settings...

    1. You can overwrite the read-QFuse function by setting the DACR and overwriting the TrustZone function I detailed above, such as tzbsp_qfprom_read_row (see the TrustZone exploit post for more info), but this won't be of much help...

      Since a lot of these QFuses are checked during the boot process by components which are loaded way before the HLOS is executed (such as aboot, SBL and PBL), hooking this function won't let you observe the behaviours which would be exhibited by those components.

      That said, there may be an option to overwrite the function and then attempt to jump directly into the SBL3 or aboot - I've never tried to do this, but in practice there should be some support for a "warm boot", which means this might work. Of course, in practice this is probably a lot harder, since SBL3/aboot may depend a lot on the current state which should be set by previous stages of the boot-chain, and directly handing over control to them might have unpredictable results.

  8. So what are the exact steps do i have to carried out to unlock the bootloader for mine xt1254...I cant afford the sunshine since dont have visa card...Will i be able to unlock it for free like other devices do...

  9. i wonder if this can be done on the BlackBerry Priv that is now says that is the most secure device of Android device...

    all files there and no need is spacial unpacking just unzip.

  10. is there any chance you could give us a tutorial on how to use your unpack script as i am having trouble getting it to point to motoboot.img

  11. AMAZING!!! Is there any possibility to port it to MOTO MAXX XT 1225, trust me... lots of guys here in brazil will be your slave after this work's includind ME!!!!

  12. How to use it and where is the files..Can we use it on stock xt907 183.46.15 locked bootloader

  13. I'm getting a syntax error on line 45 of exploit.py. Anybody know what I'm missing?

    1. File "exploit.py", line 45
      print current_dword.encode("hex")
      SyntaxError: invalid syntax

      I'm using python 3.5, do I need to use python 2?

    2. Yeah the exploit script was written in pythons older format so you will get Syntax errors or you can replace the errors with the update syntax format in Python 3.5 which is what I did and it's pretty straight forward:)

    3. The scripts are for python 2.x

  14. I'm confused how to use the codes to unlock the bootloader. I'm a little familiar with Python codes and yada yada but never actually payed attention how to execute them. Please give me a little tutorial on this.

  15. can somebody help me?, i don't know how to do it

  16. How did you get FuzzZone onto the locked device in the first place? Did you use an existing Android Exploit to patch kernel memory?

    In order to use FuzzZone you would have needed to edit the kernel?

    1. I actually used a native version that injects code into the kernel using a kernel exploit. I cleaned the original code up and posted it on my github, here: https://github.com/laginimaineb/standalone_msm8974

  17. Amazing work.

    Would the same symbols in your provided exploit work for another device(2nd gen 4g Moto E in my case)?

    If not would you mind telling me if there is an easy way to get the correct addresses without redoing a TrustZone exploit ground up?

    Thank you.

    1. In order to use the exploit you'll have to find the symbols for your version. However, this isn't really all that hard - you can just download the firmware image for the device I used, and match the symbols up with your own firmware.

  18. This comment has been removed by the author.

  19. Hello and great work can you guide me to the right direction if possible and how to unlock bootloader of the lg gflex 2 wich uses snapdragon 810? If its not possible right now do you have plans for future

    1. Sorry, haven't had a look at that bootloader. No plans to do so right now, but perhaps I'll take a look at an LG bootloader in the future.

  20. Who ever this poster is he clearly copied all your work created from this site and repost on this site. No credit given to you. https://rstforums.com/forum/topic/100469-unlocking-the-motorola-bootloader/

    1. Ah, that's too bad... Thanks for letting me know.

  21. How did you reverse the bootloader to get such a clear idea of what and where is the unlock function?
    I'm trying to reverse the g4 bootloader and I can't fugire that out...

    1. Just work your way backwards from the relevant strings, I think that's easiest. (For example, I started by looking for the string "Unlock").

    2. This comment has been removed by the author.

    3. Pretty sure the magic happens here.. but.. I think I messed up the reversing.. (I hope I messed it up)


  22. how did you read the aboot image? I have IDA Pro but don't know how to use it :(

  23. can you write one for unlocking the moto droid maxx 2 verizon

  24. What is your view on exploiting TrustZone on droid turbo (xt1254 , SnapDragon805 ) running latest Marshmallow MCG24.251-5 in order to achieve BL unlock?. I'm looking into it but really need help from more experienced. After SU4TL (Lollipop) , exploit has been obviously patched, but my noob logic tells me the principle should be simillar with finding functions that trigger qfuse which is responsible for BL unlock? Or "unlock" itself has been completely patched ,and it doesn't "exist" anymore at all in aboot? Would really appreciate if somebody could shed some light in here..Checking with IDA , aboot looks the same to me..

  25. Work your magic for the SM-G935V I am sure thousands would consider you aneed android God if you were able to be successful.

  26. Looks like you're not the first to look into this: http://blog.azimuthsecurity.com/2013/04/unlocking-motorola-bootloader.html

  27. Have you had a look at the Maxx 2? It's my understanding that the TrustZone exploit was patched. Do you know of any bootloader exploits for the more recent Motorola Devices?

  28. Think you ?can you help me unlock MSM8937 ?

  29. There seems to be an error when running the python scripts:
    line 17, in execute_register_scm
    return int(re.search("^IOCTL RES: (\d+)", resp_str, re.MULTILINE).group(1))
    AttributeError: 'NoneType' object has no attribute 'group'

    Any fix?

  30. Hi, i find these commands in samsung aboot
    C:\Users\Home\Documents\Strings\sblapp.bin: init frp lock flag 0x%x and OEM unlo
    ck flag 0x%x
    C:\Users\Home\Documents\Strings\sblapp.bin: Can't lock and unlock in the same de
    C:\Users\Home\Documents\Strings\sblapp.bin: Device unlocked: %s
    C:\Users\Home\Documents\Strings\sblapp.bin: Device critical unlocked: %s
    C:\Users\Home\Documents\Strings\sblapp.bin: get_unlock_ability: %d
    C:\Users\Home\Documents\Strings\sblapp.bin: Device is unlocked! Skipping verific
    C:\Users\Home\Documents\Strings\sblapp.bin: unlocked!
    C:\Users\Home\Documents\Strings\sblapp.bin: oem unlock is not allowed
    C:\Users\Home\Documents\Strings\sblapp.bin: Need wipe userdata. Do 'fastboot oem
    C:\Users\Home\Documents\Strings\sblapp.bin: frp-unlock
    C:\Users\Home\Documents\Strings\sblapp.bin: use_signed_kernel=%d, is_unlocked=%d
    , is_tampered=%d.
    C:\Users\Home\Documents\Strings\sblapp.bin: oem unlock
    C:\Users\Home\Documents\Strings\sblapp.bin: flashing unlock
    C:\Users\Home\Documents\Strings\sblapp.bin: flashing unlock_critical
    C:\Users\Home\Documents\Strings\sblapp.bin: flashing get_unlock_ability
    C:\Users\Home\Documents\Strings\sblapp.bin: SM5703_REG_PARAM_CTRL || SM5703_FG_P
    C:\Users\Home\Documents\Strings\sblapp.bin: fuel_gauge_unlock

  31. Very interesting & fabulous post, I really enjoy the article that you share, Thank you very much.
    Know How to Remove Ytmp3 virus?