Backup Android /data partition without any permissions 

Posted on: Aug 22, 2020 9:19 AM UTC

Contents

Around two years ago my Nexus 5 died and I bought a Redmi Note 5 Pro (whyred) for around $200.
Of course it couldn't compete but I liked the big screen and its weight among others.
The first disappointment came when I learnt that Xiaomi wanted to control the unlocking of their devices.
I didn't have the time to fully grasp the implications, including the data deletion when I will unlock.
That never really bothered me because I had no problem with the phone.
The second disappointment was Xiaomi's aggressive background app managing: MiUI kills all background apps, whatever their purpose.
That meant no VPN app, no Automate.
I already had much data and that still wasn't enough to motivate me to root the phone and to get rid of the crazy situation.
Since then, Xiaomi introduced ads in many places (settings, app installations, etc.) in the Chinese ROM but they promised that would never happen in global versions.
And finally as you can guess, they thought it was a good idea to lie and put them in the global ROM.
Well, this was the final straw. And I won't even talk about auto update installation (now fixed?) and the inability to shut off their calendar app among others.
Let's now focus on the goal here: backup all my data, including app data, and never touch a Xiaomi device again.
As mentionned earlier, the official process is:
  • you ask Xiaomi for an unlock code using a proprietary binary on Windows
  • you wait for up to 360 hours, i.e. 15 days
  • if the request is approved, their proprietary binary erases all your data and unlocks the phone
So to access my data, I need to erase it all.
Read my data -----> delete my data
Let's introduce two intermediate steps to find where the chain can be attacked:
Read app data --(2)--> be root in OS --(3)--> unlock bootloader --(1)--> delete app data
So in order to access my data without having to delete it, I need to perform any one of the three following things:
  • find how to read the app data without being root (2)
  • find how to be root without unlocking the bootloader (3)
  • find how to unlock the bootloader without erasing the data (1)
I will explore the three cases in this article.
Don't mind the numbering pattern, it's only the chronological way I will discuss them.

Unlock the bootloader without erasing the data

The dirty and unsafe way

A termination of the Mi Unlock tool and some recovery flashings are reported to allow you to unlock the bootloader without having to suffer the data deletion.
People have reported doing this method with success.
While this simple solution is rather appealing, some people have reported failing to do so.
I can't accept any chance of losing data though so I didn't test this.

The surgical way

Xiaomi's phones can be unlocked but also re-locked.
That means that so the bootloader state isn't permanently written in fuses and there is a place where the value is stored.
This place seems to be in the devinfo partition. I found mentions of this for LG, ZTE and Xiaomi phones.
Some indexes you can supposedly write to in order to unlock the bootloader come from the device_info structure in the lk bootloader.
It may have been used for older devices but the code deletion and my inability to reproduce (I put 0x01 in many places of devinfo) seem to indicate it is not used anymore and the info is stored else where.
There are two ways to determine the place to edit:
  • compare the whole flash before and after an official unlock (+ hope only a bit has changed)
  • find the SBL and ABL to find where they look for these flags
Although these two things are promising, the former needs to have access to the same flash both before and after the unlocking, and the latter would too time consuming for me right now.

Read the app data without being root

Backup the encrypted /userdata

Path (1) being out of the way, let's talk about reading the data without gaining special privileges.
We know the data is here so maybe there is a way to dump it.
After digging through lots of forum pages I indeed found one: Qualcomm's emergency mode, known as QDL, QDLoader, EDL or 9008 mode.
When you boot into this mode, you have direct access to the SoC and can send it commands.
This mode is designed for OEMs but often stays available on shipped products.
There are different way to boot in this mode, sometimes it's accessible with adb reboot edl or fastboot reboot-edl but it seems these commands have been deleted from the tools.
For the some phones, including the Note 5 Pro, you can connect two pads on the motherboard before booting up and this mode will be enabled.
The screen will stay black so the only way to be sure it works is using lsusb:
Bus 001 Device 009: ID 05c6:9008 Qualcomm, Inc. Gobi Wireless Modem (QDL mode)
Continuing still needs some work: the SoC is waiting but it doesn't have any capabilities yet.
For this you need to provide a device-specific program that will actually do the work.
Fortunately many have leaked on the web so if you search with the right terms, chances are you'll find the one you're looking for.
They are found in two formats, MBN and ELF, and contain the codename of the Qualcomm chip of your device.
In my case, I found the file named prog_emmc_firehose_sdm660_ddr.mbn.
Once this is done you can start to talk to the SoC using publicly available tools.
The most used one is QPST (Qualcomm Product Support Tools), which is a supposedly leaked Qualcomm software.
I never found any real evidence of that so I don't know how much you can trust the shady binaries on shady websites that have these names.
I nevertheless tried using several versions of it on a Windows 10 virtual machine but it never worked. It could have been bad rules on the Linux side, bad Virtual Box configuration or anything else.
Anyway I finally chose to use an open-source program on my Linux setting, it worked like a charm.
It allows you to do tons of things but the only one I wanted is "./edl.py rf flash.bin" -> to dump whole flash for device with emmc.
Just don't forget the loader and you're good to go.
After waiting a bit, I finally had my 58.2GiB all.img
Three interesting things to note here:
  • using edl.py you can read but also write partitions
  • you have the whole partition table (gdisk -l filename)
  • the /userdata partition is encrypted, as expected
I knew this wasn't the end of the road but it is the step 1: I can theoretically flash the image again and have my data back on the phone.
A kind a poor man's backup.
Number  Start (sector)    End (sector)  Size       Code  Name
   1              40              55   8.0 KiB     8300  switch
   2              56              71   8.0 KiB     FFFF  dpo
   3              72              87   8.0 KiB     FFFF  fsc
   4              88             151   32.0 KiB    FFFF  limits
   5             152             215   32.0 KiB    FFFF  ssd
   6             216             255   20.0 KiB    FFFF  sec
   7             256             383   64.0 KiB    FFFF  vbmeta
   8             384             511   64.0 KiB    0700  vbmetabak
   9             512             767   128.0 KiB   FFFF  storsec
  10             768            1023   128.0 KiB   0700  storsecbak
  11            1024            1535   256.0 KiB   FFFF  apdp
  12            1536            2047   256.0 KiB   FFFF  msadp
  13            2048            3071   512.0 KiB   FFFF  keystore
  14            3072            4095   512.0 KiB   FFFF  frp
  15            4096            5119   512.0 KiB   FFFF  rpm
  16            5120            6143   512.0 KiB   0700  rpmbak
  17            6144            7167   512.0 KiB   FFFF  hyp
  18            7168            8191   512.0 KiB   0700  hypbak
  19            8192            9215   512.0 KiB   FFFF  pmic
  20            9216           10239   512.0 KiB   0700  pmicbak
  21           10240           12287   1024.0 KiB  FFFF  cmnlib
  22           12288           14335   1024.0 KiB  0700  cmnlibbak
  23           14336           16383   1024.0 KiB  FFFF  cmnlib64
  24           16384           18431   1024.0 KiB  0700  cmnlib64bak
  25           18432           20479   1024.0 KiB  FFFF  abl
  26           20480           22527   1024.0 KiB  0700  ablbak
  27           22528           24575   1024.0 KiB  FFFF  dip
  28           24576           26623   1024.0 KiB  FFFF  ddr
  29           26624           28671   1024.0 KiB  FFFF  toolsfv
  30           28672           30719   1024.0 KiB  0700  devcfgbak
  31           30720           32767   1024.0 KiB  FFFF  devcfg
  32           32768           40959   4.0 MiB     FFFF  tz
  33           40960           49151   4.0 MiB     0700  tzbak
  34           49152           57343   4.0 MiB     FFFF  mdtpsecapp
  35           57344           65535   4.0 MiB     0700  mdtpsecappbak
  36           65536           67583   1024.0 KiB  FFFF  keymaster
  37           67584           69631   1024.0 KiB  0700  keymasterbak
  38           69632           71679   1024.0 KiB  FFFF  bluetooth
  39           71680           75775   2.0 MiB     FFFF  sti
  40           75776           82943   3.5 MiB     FFFF  xbl
  41           82944           90111   3.5 MiB     0700  xblbak
  42           90112           98303   4.0 MiB     FFFF  misc
  43           98304          114687   8.0 MiB     FFFF  devinfo
  44          114688          131071   8.0 MiB     FFFF  logfs
  45          131072          147455   8.0 MiB     FFFF  fsg
  46          147456          163839   8.0 MiB     FFFF  modemst1
  47          163840          180223   8.0 MiB     FFFF  modemst2
  48          180224          212991   16.0 MiB    FFFF  dsp
  49          212992          262143   24.0 MiB    8300  bk1
  50          262144          327679   32.0 MiB    FFFF  mdtp
  51          327680          393215   32.0 MiB    8300  bk2
  52          393216          524287   64.0 MiB    FFFF  splash
  53          524288          589823   32.0 MiB    FFFF  persist
  54          589824          655359   32.0 MiB    0700  persistbak
  55          655360          786431   64.0 MiB    FFFF  logdump
  56          786432         1179647   192.0 MiB   0700  modem
  57         1179648         1441791   128.0 MiB   FFFF  rawdump
  58         1441792         1572863   64.0 MiB    FFFF  boot
  59         1572864         1703935   64.0 MiB    FFFF  recovery
  60         1703936         2228223   256.0 MiB   FFFF  cache
  61         2228224         8519679   3.0 GiB     FFFF  system
  62         8519680        12713983   2.0 GiB     FFFF  vendor
  63        12713984        14417919   832.0 MiB   FFFF  cust
  64        14417920       122142686   51.4 GiB    FFFF  userdata
What can I do now to have access to my raw decrypted data?
Easy: read the code and decrypt it.

Decrypting: the legit way

So now I have a userdata.img 1 that contains my data in an encrypted state.
The Android encryption scheme is known (as being open-source) so it shouldn't be too difficult to decrypt my data.
Looking at cryptfs.c sure looks promising.
Before Android 7, the scheme was the following:
  • generate a random DEK (data encrypting key)
  • encrypt /userdata with DEK
  • user passphrase used to create the KEK (key encrypting key)
  • KEK is used to encrypt the DEK
This way when you change your password only the KEK and the encrypted DEK are re-written.
This was good news because knowing the algorithm and the key you can decrypt your data.
This is why starting with Android 7, Google introduced a new algorithm involving hardware chips.
Dealing with this is much less fun and possibly proprietary code comes in the process.
Following the function calls has been too much of a hurdle and I'd need to actually talk to the hardware in the end anyway, so I would have to execute unauthorized code to do this.
Note that running unauthorized code is already something that would make me achieve my goal (path 3: how to be root without unlocking the bootloader) so no need to continue this.

Decrypting: the tricky way

There is another thing worth mentioning though.
Instead of decrypting using the hardware keys, we may be able to trick the system to downgrade the encryption scheme to an older version where they are not used.
In crypt.c you can see these blobs of code:
c
// Upgrade if we're not using the latest KDF. use_keymaster = keymaster_check_compatibility(); if (crypt_ftr->kdf_type == KDF_SCRYPT_KEYMASTER) { // Don't allow downgrade } else if (use_keymaster == 1 && crypt_ftr->kdf_type != KDF_SCRYPT_KEYMASTER) { crypt_ftr->kdf_type = KDF_SCRYPT_KEYMASTER; upgrade = 1; } else if (use_keymaster == 0 && crypt_ftr->kdf_type != KDF_SCRYPT) { crypt_ftr->kdf_type = KDF_SCRYPT; upgrade = 1; } if (upgrade) { rc = encrypt_master_key(passwd, crypt_ftr->salt, saved_master_key, crypt_ftr->master_key, crypt_ftr); if (!rc) { rc = put_crypt_ftr_and_key(crypt_ftr); } ...
c
int cryptfs_changepw(int crypt_type, const char *newpw){ ... #ifdef CONFIG_HW_DISK_ENCRYPTION if (!strcmp((char *)crypt_ftr.crypto_type_name, "aes-xts")) { if (crypt_type == CRYPT_TYPE_DEFAULT) { int rc = update_hw_device_encryption_key(DEFAULT_PASSWORD, (char*) crypt_ftr.crypto_type_name); SLOGD("Update hardware encryption key to default for crypt_type: %d. rc = %d", crypt_type, rc); if (!rc) return -1; } else { int rc = update_hw_device_encryption_key(newpw, (char*) crypt_ftr.crypto_type_name); SLOGD("Update hardware encryption key for crypt_type: %d. rc = %d", crypt_type, rc); if (!rc) return -1; } } #endif ... }
As you can see, it wouldn't be too difficult to either perform a scheme downgrade or even to retrieve the master key itself.
But again we have a problem because there are two choices:
  • modify system.img or boot.img to change the code, but to do this you need to unlock the bootloader so we are back to path (1)
  • or patch the kernel once booted and then change the password to trigger a new encryption, but that would mean running unauthorized code, which is path (3)
It's again a dead end, path (2) doesn't seem possible.

Be root without unlocking the bootloader

This chapter will be short because there is another article on this subject with many explanations that didn't fit in this one.
There are two ways to have root access without unlocking the bootloader: either you modify a partition to have the su binary in your booted system or you exploit a vulnerability to gain root access.

Partition editing

Modifying partitions is widely known in the Android world as it is what is done by issuing commands like fastboot flash boot boot.img or by flashing from inside a custom recovery.
Depending on the expected result it targets a specific partition.
In the boot chain I previously mentioned, it is clear you can't do what you want: if your bootloader is locked, the SBL and the ABL will have to trust the next link and will check it is signed with a key they know.
'Destroyed' actually means 'tampered with'
Flashing the original partitions back 'undestroys' the system
You don't have the Xiaomi signing key so you can't produce a valid signature if you change boot.img, system.img or vendor.img, so the ABL will reject them and show you an error.
You don't have the Xiaomi signing key so you can't produce a valid signature if you change the ABL, so the SBL/XBL will reject it and show you an error.
You don't have the Xiaomi signing key so you can't produce a valid signature if you change the SBL/XBL, so the PBL will reject it and show you an error.
Final answer is: if you want to edit the partitions, you have to attack the PBL.
I didn't have the time to play with this but I'll try to make a full article on this later.

Exploit a vulnerability to gain root access

During my research I stumbled upon a vulnerability used to root Pixel 2 XL's.
I didn't think much of it at first because all the previous paths were more or less simultaneously being tried.
Finally this became the most promising way to claim my data back.
This is what I finally used and this is what I finally got.
More on this in this article: Exploiting CVE-2019-2215 to gain root access on the Redmi Note 5 Pro (whyred)
\o/
This vulnerability has been patched in the Android security patch of october 2019 that is included in MIUI 11.
Fortunately even though I didn't take the time to root my phone, I always thought it would be better to stay on the lowest possible version if one day I attempt the rooting.

Conclusion

Sadly the easiest way to do that is using an Android vulnerability.
Also I will never buy a Xiaomi product ever again.

Notes

1: dd if=all.img of=userdata.img skip=14417920 count=$((122142686-14417920+1))



Comments (0)
No comments yet