|
The iPhone Wiki is no longer updated. Visit this article on The Apple Wiki for current information. |
Difference between revisions of "LwVM"
(→Structure) |
(Ugh) |
||
| (8 intermediate revisions by 3 users not shown) | |||
| Line 1: | Line 1: | ||
'''LwVM''' (Lightweight Volume Manager) - partition table, which is used by iOS since 5.0. |
'''LwVM''' (Lightweight Volume Manager) - partition table, which is used by iOS since 5.0. |
||
| + | '''com.apple.driver.LightweightVolumeManager''' kext wraps the block device (<code>/dev/disk0</code>) where the actual LwVM partition table is stored and publishes <code>/dev/disk0s1</code> with emulated [[Wikipedia:GUID Partition Table|GPT]] which can be edited from user-land using conventional [[Wikipedia:GUID Partition Table|GPT]] editing tools (e.g. [http://sourceforge.net/projects/gptfdisk/ gptfdisk]) [https://code.google.com/p/iphone-dataprotection/wiki/LightweightVolumeManager]. All edits are trapped by the kext and before shutting down (after the volumes are unmounted) it writes the corresponding changes to LwVM partition table on <code>/dev/disk0</code> (implemented in <code>LightweightVolumeManager::_gptLazyRepartition()</code> function from the LwVM kext). After next boot new devices (e.g. <code>/dev/rdisk0s1s3</code>) are published by the kext. This mechanism is used during [[OTA Updates]] to create third partition where the update ramdisk is copied to and booted into by setting nvram <code>boot-command</code> to <code>upgrade</code> [https://twitter.com/iH8sn0w/status/580016602512539648] and probably also <code>boot-partition</code> to <code>2</code> (partitions are indexed from 0, so third partition). |
||
| + | |||
| + | The update partition is set up by <code>com.apple.MobileSoftwareUpdate.CleanupPreparePathService</code> (XPC service accompanying [[softwareupdated]]). It shrinks HFS of the second (data) partition using <code>fsctl</code> as illustrated in [https://github.com/danzatt/hfs_resize hfs_resize]. It then edits [[Wikipedia:GUID Partition Table|GPT]] on <code>/dev/rdisk0s1</code> to reflect the size of shrunk HFS and creates new entry for the update partition in the freed space and creates new HFS on it. To create file-system manually you can use e.g.: <code>newfs_hfs -s -b 8192 -J 8192k -v Data2 /dev/rdisk0s1s4</code>. |
||
== Structure == |
== Structure == |
||
| Line 10: | Line 13: | ||
uint64_t end; //End. |
uint64_t end; //End. |
||
uint64_t attribute; // 0 == unencrypted; 0x1000000000000 == encrypted |
uint64_t attribute; // 0 == unencrypted; 0x1000000000000 == encrypted |
||
| − | + | uint16_t partitionName[0x24]; // UTF-16 encoded |
|
} __attribute__ ((packed)) LwVMPartitionRecord; |
} __attribute__ ((packed)) LwVMPartitionRecord; |
||
| Line 30: | Line 33: | ||
static const char LwVMPartitionTypeHFS[] = { 0x48, 0x46, 0x53, 0x00, 0x00, 0x00, 0x11, 0xAA, 0xAA, 0x11, 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC }; |
static const char LwVMPartitionTypeHFS[] = { 0x48, 0x46, 0x53, 0x00, 0x00, 0x00, 0x11, 0xAA, 0xAA, 0x11, 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC }; |
||
</pre> |
</pre> |
||
| + | |||
| + | == LightweightVolumeManager IOService == |
||
| + | Running 'ioreg | grep "Lightweight"' shows, that there's an IOService with such name. [[User:rzhikharevich|I]] tried fuzzing IOConnectCallStructMethod selectors (very bad idea). The first call which returned KERN_SUCCESS was done with selector 1. Next, I tried this code: |
||
| + | <pre> |
||
| + | <...> |
||
| + | kern_ret = IOConnectCallStructMethod(lwvm_conn, 1, NULL, 0, &info_sz, &o_sz); |
||
| + | if (kern_ret == KERN_SUCCESS) |
||
| + | { |
||
| + | printf("OK.\n"); |
||
| + | void *info = malloc(info_sz); |
||
| + | IOConnectCallStructMethod(lwvm_conn, 1, NULL, 0, info, &info_sz); |
||
| + | <...> |
||
| + | </pre> |
||
| + | All disk read operations started resulting with errors. Then my device rebooted and entered a boot loop. The code possibly messed the partition table up. |
||
== Tools == |
== Tools == |
||
[http://www.github.com/rzhikharevich/lwvmedit lwvmedit] |
[http://www.github.com/rzhikharevich/lwvmedit lwvmedit] |
||
| + | |||
| + | == External links == |
||
| + | [https://code.google.com/p/iphone-dataprotection/wiki/LightweightVolumeManager iPhone dataprotection wiki] |
||
Latest revision as of 16:11, 12 June 2015
LwVM (Lightweight Volume Manager) - partition table, which is used by iOS since 5.0.
com.apple.driver.LightweightVolumeManager kext wraps the block device (/dev/disk0) where the actual LwVM partition table is stored and publishes /dev/disk0s1 with emulated GPT which can be edited from user-land using conventional GPT editing tools (e.g. gptfdisk) [1]. All edits are trapped by the kext and before shutting down (after the volumes are unmounted) it writes the corresponding changes to LwVM partition table on /dev/disk0 (implemented in LightweightVolumeManager::_gptLazyRepartition() function from the LwVM kext). After next boot new devices (e.g. /dev/rdisk0s1s3) are published by the kext. This mechanism is used during OTA Updates to create third partition where the update ramdisk is copied to and booted into by setting nvram boot-command to upgrade [2] and probably also boot-partition to 2 (partitions are indexed from 0, so third partition).
The update partition is set up by com.apple.MobileSoftwareUpdate.CleanupPreparePathService (XPC service accompanying softwareupdated). It shrinks HFS of the second (data) partition using fsctl as illustrated in hfs_resize. It then edits GPT on /dev/rdisk0s1 to reflect the size of shrunk HFS and creates new entry for the update partition in the freed space and creates new HFS on it. To create file-system manually you can use e.g.: newfs_hfs -s -b 8192 -J 8192k -v Data2 /dev/rdisk0s1s4.
Structure
typedef struct _LwVMPartitionRecord {
uint64_t type[2]; //Should be equal to LwVMPartitionTypeHFS (see below) on an unmodified device.
uint64_t guid[2]; //Random.
uint64_t begin; //Partitions begin in bytes.
uint64_t end; //End.
uint64_t attribute; // 0 == unencrypted; 0x1000000000000 == encrypted
uint16_t partitionName[0x24]; // UTF-16 encoded
} __attribute__ ((packed)) LwVMPartitionRecord;
typedef struct _LwVM {
uint64_t type[2]; //Should be LwVMType or LwVMType_noCRC.
uint64_t guid[2]; //Random.
uint64_t mediaSize; //Size in bytes.
uint32_t numPartitions; //Number of partitions.
uint32_t crc32;
uint8_t unkn[464]; //Some unknown bytes, usually nulls.
LwVMPartitionRecord partitions[12];
uint16_t chunks[1024]; // chunks[0] should be 0xF000
} __attribute__ ((packed)) LwVM;
static const char LwVMType[] = { 0x6A, 0x90, 0x88, 0xCF, 0x8A, 0xFD, 0x63, 0x0A, 0xE3, 0x51, 0xE2, 0x48, 0x87, 0xE0, 0xB9, 0x8B };
static const char LwVMType_noCRC[] = { 0xB1, 0x89, 0xA5, 0x19, 0x4F, 0x59, 0x4B, 0x1D, 0xAD, 0x44, 0x1E, 0x12, 0x7A, 0xAF, 0x45, 0x39 };
static const char LwVMPartitionTypeHFS[] = { 0x48, 0x46, 0x53, 0x00, 0x00, 0x00, 0x11, 0xAA, 0xAA, 0x11, 0x00, 0x30, 0x65, 0x43, 0xEC, 0xAC };
LightweightVolumeManager IOService
Running 'ioreg | grep "Lightweight"' shows, that there's an IOService with such name. I tried fuzzing IOConnectCallStructMethod selectors (very bad idea). The first call which returned KERN_SUCCESS was done with selector 1. Next, I tried this code:
<...>
kern_ret = IOConnectCallStructMethod(lwvm_conn, 1, NULL, 0, &info_sz, &o_sz);
if (kern_ret == KERN_SUCCESS)
{
printf("OK.\n");
void *info = malloc(info_sz);
IOConnectCallStructMethod(lwvm_conn, 1, NULL, 0, info, &info_sz);
<...>
All disk read operations started resulting with errors. Then my device rebooted and entered a boot loop. The code possibly messed the partition table up.