Decrypting Android M adopted storage

One of the new features Android M introduces is adoptable storage. This feature allows external storage devices such as SD cards or USB drives to be 'adopted' and used in the same manner as internal storage. What this means in practice is that both apps and their private data can be moved to the adopted storage device. In other words, this is another take on everyone's (except for widget authors...) favorite 2010 feature -- AppsOnSD. There are, of course, a few differences, the major one being that while AppsOnSD (just like app Android 4.1 app encryption) creates per-app encrypted containers, adoptable storage encrypts the whole device. This short post will look at how adoptable storage encryption is implemented, and show how to decrypt and use adopted drives on any Linux machine.

Adopting an USB drive

In order to enable adoptable storage for devices connected via USB you need to execute the following command in the Android shell (presumably, this is not needed if your device has an internal SD card slot; however there are no such devices that run Android M at present):

$ adb shell sm set-force-adoptable true

Now, if you connect a USB drive to the device's micro USB slot (you can also use an USB OTG cable), Android will give you an option to set it up as 'internal' storage, which requires reformatting and encryption. 'Portable' storage is formatted using VFAT, as before.


After the drive is formatted, it shows up under Device storage in the Storage screen of system Settings. You can now migrate media and application data to the newly added drive, but it appears that there is no option in the system UI that allows you to move applications (APKs).


Adopted devices are mounted via Linux's device-mapper under /mnt/expand/ as can be seen below, and can be directly accessed only by system apps.

$ mount
rootfs / rootfs ro,seclabel,relatime 0 0
...
/dev/block/dm-1 /mnt/expand/a16653c3-... ext4 rw,dirsync,seclabel,nosuid,nodev,noatime 0 0
/dev/block/dm-2 /mnt/expand/0fd7f1a0-... ext4 rw,dirsync,seclabel,nosuid,nodev,noatime 0 0

You can safely eject an adopted drive by tapping on it in the Storage screen, and the choosing Eject from the overflow menu. Android will show a persistent notification that prompts you to reinsert the device once it's removed. Alternatively, you also can 'forget' the drive, which removes it from the system, and should presumably delete the associated encryption key (which doesn't seem to be the case in the current preview build).

Inspecting the drive

Once you've ejected the drive, you can connect it to any Linux box in order to inspect it. Somewhat surprisingly, the drive will be automatically mounted on most modern Linux distributions, which suggests that there is at least one readable partition. If you look at the partition table with fdisk or a similar tool, you may see something like this:

# fdisk /dev/sdb
Disk /dev/sdb: 7811 MB, 7811891200 bytes, 15257600 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk label type: gpt


 #        Start          End    Size  Type            Name
 1         2048        34815     16M  unknown         android_meta
 2        34816     15257566    7.3G  unknown         android_expand

As you can see, there is a tiny android_meta partition, but the bulk of the device has been assigned to the android_expand partition. Unfortunately, the full source code of Android M is not available, so we cannot be sure how exactly this partition table is created, or what the contents of each partition is. However, we know that most of Android's storage management functionality is implemented in the vold system daemon, so we check if there is any mention of android_expand inside vold with the following command:

$ strings vold|grep -i expand
--change-name=0:android_expand
%s/expand_%s.key
/mnt/expand/%s

Here expand_%s.key suspiciously looks like a key filename template, and we already know that adopted drives are encrypted, so our next step is to look for any similar files in the device's /data partition (you'll need a custom recovery or root access for this). Unsurprisingly, there is a matching file in /data/misc/vold which looks like this:

# ls /data/misc/vold
bench
expand_8838e738a18746b6e435bb0d04c15ccd.key

# ls -l expand_8838e738a18746b6e435bb0d04c15ccd.key

-rw-------  1 root root 16  expand_8838e738a18746b6e435bb0d04c15ccd.key


# od -t x1 expand_8838e738a18746b6e435bb0d04c15ccd.key
0000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
0000020

Decrypting the drive

That's exactly 16 bytes, enough for a 128-bit key. As we know, Android's FDE implementation uses an AES 128-bit key, so it's a good bet that adoptable storage uses a similar (or identical) implementation. Looking at the start and end of our android_expand partition doesn't reveal any readable info, nor is it similar to Android's crypto footer, or LUKS's header. Therefore, we need to guess the encryption mode and/or any related parameters. Looking once again at Android's FDE implementation (which is based on the dm-crypt target of Linux's device-mapper), we see that the encryption mode used is aes-cbc-essiv:sha256. After consulting dm-crypt's mapping table reference, we see that the remaining parameters we need are the IV offset and the starting offset of encrypted data. Since the IV offset is usually zero, and most probably the entire android_expand partition (offset 0) is encrypted, the command we need to map the encrypted partition becomes the following:

# dmsetup create crypt1 --table "0 `blockdev --getsize /dev/sdb2` crypt \
aes-cbc-essiv:sha256 00010203040506070809010a0b0c0d0e0f 0 /dev/sdb2 0"

It completes with error, so we can now try to mount the mapped device, again guessing that the file system is most probably ext4 (or you can inspect the mapped device and find the superblock first, if you want to be extra diligent).

# mount -t ext4 /dev/mapper/crypt1 /mnt/1/
# cd /mnt/1
# find ./ -type d
./
./lost+found
./app
./user
./media
./local
./local/tmp
./misc
./misc/vold
./misc/vold/bench

This reveals a very familiar Android /data layout, and you should see any media and app data you've moved to the adopted device. If you copy any files to the mounted device, they should be visible when you mount the drive again in Android.

Storage manager commands

Back in Android, you can use the sm command (probably short for 'storage manager') we showed in the first section to list disks and volumes as shown below:

$ sm list-disks
disk:8,16
disk:8,0

$ sm list-volumes
emulated:8,2 unmounted null
private mounted null
private:8,18 mounted 0fd7f1a0-2d27-48f9-8702-a484cb894a92
emulated:8,18 mounted null
emulated unmounted null
private:8,2 mounted a16653c3-6f5e-455c-bb03-70c8a74b109e

If you have root access, you can also partition, format, mount, unmount and forget disks/volumes. The full list of supported commands is shown in the following listing.

$ sm
usage: sm list-disks
       sm list-volumes [public|private|emulated|all]
       sm has-adoptable
       sm get-primary-storage-uuid
       sm set-force-adoptable [true|false]

       sm partition DISK [public|private|mixed] [ratio]
       sm mount VOLUME
       sm unmount VOLUME
       sm format VOLUME

       sm forget [UUID|all]

Most features are also available from the system UI, but sm allows you to customize the ratio of the android_meta and android_expand partitions, as well as to create 'mixed' volumes.

Summary

Android M allows for adoptable storage, which is implemented similarly to internal storage FDE -- using dm-crypt with a per-volume, static 128-bit AES key, stored in /data/misc/vold/. Once the key is extracted from the device, adopted storage can be mounted and read/written on any Linux machine. Adoptable storage encryption is done purely in software (at least in the current preview build), so its performance is likely comparable to encrypted internal storage on devices that don't support hardware-accelerated FDE.

Comments

Lukas said…
Very informative, thanks!

Could you possibly elaborate a bit more on how adoptable storage is exposed to applications? The preview SDK docs are unfortunately not very helpful.

- Is an encrypted card usable for media files without migrating just like an unencrypted card, i.e. is migration decoupled from encryption?
- Where is the primary shared storage mounted, and does that change after migrating application data? What does Context.getExternalFilesDirs() return in each case?
- After migration, is the internal (emulated) shared storage still accessible (via file managers and/or the external storage APIs), or is it completely shadowed by the external shared storage?
- Where are APK expansion files (OBBs) stored, before and after migration?
Nikolay Elenkov said…
This comment has been removed by the author.
Nikolay Elenkov said…
Encryption is separate from migration. You can have multiple encrypted volumes mounted and migrate to only one (or none) of them. External storage is implemented the same way as before, i.e. emulated using FUSE on top of the media/ directory of the 'real' volume. It does look you can only have one 'primary external storage' at a time, but not 100% about this without the code.

You can write a simple app to check how the Contexts methods behave
Unknown said…
Is it Possible to decyrpt and save data from the adopted SD card without being able to extract the key from the phone. For instance broken phone trying to save pictures of the kids. Thank you
Sam said…
So maybe use dmcrypt and then resize the ext4 file system, then close the crypt session and resize the partition, and then add a new vFAT partition as External SD that Google Music can store on?
Sam said…
You had the Holy Grail without realising it.

Many people have been stuck with adopted internal storage, because they then have no SD card to which Google Play Music can store the music; and as Google Play Music won't move to the adopted SD card, all music is stored in the internal memory. For me, I have more music than Apps, so this just makes things worse.

However your reference to "sm" and "mixed" gave me something to work with.

$ adb shell sm list-disks adoptable
disk:179_64
$ sm partition disk:179_64 mixed 75
$


gives me 75% as portable storage.

However I advise a reboot after setting the new music storage location this as Google Music may get the wrong idea about much space is available.

http://blog.sam.liddicott.com/2016/02/android-6-semi-adopted-storage.html
Unknown said…
Will it be possible to use this command to add system storage to let's say the new Galaxy s7 or the G5?
PK Mehta said…
My adopted memory card stopped working. So I pressed 'Forget the card'.
Now I want to use the card as portable in my phone.
Can't seem to format it either as portable card or internal card.
Will this technique help? If yes, then great I'm gonna try it and let you know.
If no please guide me through the way which may help me.
Sam said…
It sounds to me like your adopted card stopped working because the card is damaged, and this is why it also doesn't work as portable storage.

I would say that you probably need a new card.
Sam said…
It sounds to me like your adopted card stopped working because the card is damaged, and this is why it also doesn't work as portable storage.

I would say that you probably need a new card.
Tomas Mendez said…
Hi, I configured my micro sd as internal storage in my moto g3 and I foolishly reseted the phone. So, now I can't access to all my photos because android tells me that I have to format the sd. The problem is that I really need to recover all my information. Is there any way in which I could unlocked the sd?
I'll apreciate your help since I am really desperate and nobody can give me any solution.
Thanks.
Maitreya Hegde said…
Bought a new Samsung 16GB SD Card. When i try to set-it up and format it as internal memory, settings app crashes and later when i open, it says SD card Corrupted. Im running on CM13 CONDOR NIGHTLY. Thanks in advance.
Bladder Runner said…
Thank you sir!!! You saved my life. Android wouldn't mount my microsd card anymore due to some corrupt directories. (I used adb shell and ran dmesg to see what's going on). I was able to backup over 18GB of precious family pictures by decrypting it from my ubuntu desktop. You are awesome!
Emanuel said…
This comment has been removed by the author.
Mirco said…
Doesn't work for me. I got this on my ubunut 14.04:

command:
dmsetup create mmc --table "0 62299103 crypt aes-cbc-essiv_sha256 1hereismykey1 0 /dev/mmcblk0p2 0"

the result is

"device-mapper: reload ioctl on mmc failed: Invalid argument
Command failed"

dmesg says:
[119671.798439] device-mapper: table: 252:3: crypt: Invalid IV mode
[119671.798447] device-mapper: ioctl: error adding target to table


Any suggestions?
thx


Nikolay Elenkov said…
Should be 'aes-cbc-essiv:sha256'. Also I very much doubt that Ubuntu has a `mmcblk0p2` device. Check dmesg for the correct device name.
Mirco said…
Thanks a lot for pointing that out...what a silly typing error.

just for the record:
the filesystem on the encrypted card was "f2fs" in my case.
I am using Marshmellow 6.0.1
Unknown said…
Hi,
Trying to decrypt my SD card so I can expand a partition here, but when I run:

dmsetup create crypt1 --table "0 `blockdev --getsize /dev/mmcblk0p2` crypt \aes-cbc-essiv:sha256 0...f 0 /dev/mmcblk0p2 0"

I get the error:

device-mapper: reload ioctl on crypt1 failed: No such file or directory

and I'm not quite sure what to make of it. Could you point me in the right direction?

Cheers.
Raz said…
This comment has been removed by the author.
hugo1997rm said…
hello I have a problem with my sd card, I had configured in android marshmallow like internal storage but I had to reinstall the rom and when I put the micro sd card I get as damaged and asks me to format but have important files , some form of they rescue these files or lost forever
Manuel Nájera said…
Did you find any solution? I'm stuck on the same problem here
Chad said…
Hi thanks for your contribution. I would like to consult you something because in my case I have a little more complicated. It has happened the same case as you, but additionally I did a hard reset the cell phone, which, I think I do not have that .key file. Without the key file there is no possibility that can decrypt the file system?
Hertz! said…
Amazing article, very informative. would you know if the implementation has changed in Android N ? Is still possible to extract keys ?

Thanks!
Satheesh Kumar said…
Hello all,
I have recently upgraded my phone to Android 6.0
Everything worked fine until i formatted my SD card to Internal.
After the conversion, it was working well. I was pretty much satisfied. With that confidence, i moved all my apps from Internal to SD card.
After some minutes my phone got struck, so i restarted by removing and placing the battery in the back.

When it booted up it didn't detect my SD card and kept showing an error that, i need to Forget my SD card.
i googled the condition and found a post explaining the issue that it is Android 6.0's bug, and it's highly recommended to not move apps from Internal to SD card without a reboot, after converting the SD card to Internal.

The post also explained about wiping the SD card, as the only option left, and instructed to fastboot the device.

For some unknown reasons, the default "hold press Power button and press Volume Down" didn't work in my phone and it took me to a meta mode, where nothing is displayed apart from saying that it is a meta mode.

Then i took a wrong turn by repeating the same by pressing the Power and Volume Up button. It took me to factory mode, where i was displayed with 6 options in Chinese.

I can't read Chinese, and one of the option had an English word "eMMC", i thought this was the option to wipe out the SD card and selected it. It restored my phone to factory settings by erasing the Internal memory instead of the SD card.

Now, my phone detects my SD card, but shows it as "Unsupported" under Storage and USB option. And it asks me to Set Up the SD card by formatting it.
There are two options, "Use as portable storage" and "Use as Internal Storage".
If i select the first option, it says SD card is ready. But it doesn't format or fix the errors . And if i go for the 2nd option, it doesn't format and it displays the following error as a Toast.

Couldn't erase SD card
command '46 volume partition disk: 179,128 private' failed with '400 46 Command failed'

This is a Strontium 32GB class 10 SD Card. Kindly help me to format the SD Card and fix it's errors so i can use it again.
Wolf Kürschner said…
Hi, my adobted card stopped working. Is is possible to reveal the encrypting key on my phone? It is not rooted. For rooting it needs to have an opened bootloader, but if I want to open it the phone is completely factory resettled - and the key deleted?
Unknown said…
Hi
Some months ago, I had to do a hard reset on my phone, since then I have my 32gb sd card encrypted, it was originally set as adopted storage.
After a lot of search, I'm still trying to recover such important files that I have in this sd card, after I fone The hard reset on my phone, is there any way to save the data on The sd? If I lost the encryption key, is there any chance to recover my sd data?
Piramida Sveta said…
Can you tell me, please.
Is it possible to recover data from the old card, if I clicked "forget" the disk and replaced it with a new one. Whether the result of this operation, a new encryption key is created and with it it is impossible to do?
Piramida Sveta said…
This comment has been removed by the author.
Hinduraja said…
When i m trying command sm string vold ] grep ~i expand i m gettin error string not found
When set adoptable is done to true afyer has adoptable command it again shows false
Unable to set it to true
Please help hard earned money sdcard waste
Tried cmd prompt and all formatters
Adb shell method also fail
efebo_abel said…
is there a way to get the SD data/files after a factory reset and not having the .key file ?
Unknown said…
Hello runner, can you please tell me how you were able to decrypt and backup data from SD card. I am stuck in same kind of situation and I have pressed forget button as I wasn't able to access any other SD card on my phone. Now it is recognizing my SD card which was adopted as internal storage but asks to reformat it to be used as portable media or internal storage again. Please replyme on rakesh19900406@gmail.com
Error: java.lang.IllegalStateException: command '34 volume partition disk:179_64 public' failed with 'null'
When trying
1|root@ms013g:/ # sm partition disk:179_64 public 50
mounika mouni said…
your blog look nice and informative which I found on internet.thank you for your post.
Ernan John said…
Can someone create a video tutorial? detailing the step-by-step process on how to decrypt adoptable storage? Thanks.
Jeffrey Lui said…
Wonderful, and very informative! It helps me understand the internal storage file system and encryption, however my problem is that my phone is destroyed so I'm unable to retrieve my encryption key. Is there a way to find out how the encryption key is derived, or if there is another way to retrieve it?

Thanks!

Popular posts from this blog

Password storage in Android M

Unpacking Android backups