3minutesOf: a bit of X-Ways and RAID


Some days ago I was working on four images coming from a QNAP storage: so, four disk whose partitions were used to build up RAID volumes. "No problem" I said to myself, knowing that QNAP are *nix based and that XWF (X-Ways Forensics) is so powerful that I'll not need to switch on Linux.

Which RAID?


That's true, but you need to instruct XWF about which type and parameters the RAID is using. Easy again, let's find the configuration raidtab file. Here is it: 

 raiddev /dev/md0
    raid-level               0
    nr-raid-disks            4
    nr-spare-disks           0
    chunk-size               4
    persistent-superblock    1
    device                   /dev/sda3
    raid-disk                0
    device                   /dev/sdb3
    raid-disk                1
    device                   /dev/sdc3
    raid-disk                2
    device                   /dev/sdd3
    raid-disk                3


The third partition of each disk is used inside a level-0 RAID (striping) with a block size of 4KB (the chunk size is expressed in kilobytes, as man says), so 8 sectors (assuming 512... bla bla bla).

chunk-size size
Sets the stripe size to size kilobytes. Has to be a power of 2 and has a compilation-time maximum of 4M. (MAX_CHUNK_SIZE in the kernel driver) typical values are anything from 4k to 128k, the best value should be determined by experimenting on a given array, alot depends on the SCSI and disk configuration.
 

Moreover from the first disk (the only one showing up to own a file system structure) I got an EXT4 volume. Ok, let XWF rebuild the RAID and inspect the volume.

But... at this point XWF showed up many errors about wrong inodes... hum, something weird there... The first doubt is usually about the stripe size, under the assumption that the RAID type is correct. I got that info from the only configuration file available, so where is the issue?

persistent-superblock


Following the maccO razor (the worst and complicated idea) I thought to explore the ext4 fs structure to see where there is a jump from the first disk volume to the second inside the Group Descriptors: but, luckily since easier, I opened again the raidtab file and I spot the persistent block configuration value. From the man:

persistent-superblock 0/1
newly created RAID arrays should use a persistent superblock. A persistent superblock is a small disk area allocated at the end of each RAID device, this helps the kernel to safely detect RAID devices even if disks have been moved between SCSI controllers. It can be used for RAID0/LINEAR arrays too, to protect against accidental disk mixups. (the kernel will either correctly reorder disks, or will refuse to start up an array if something has happened to any member disk. Of course for the 'fail-safe' RAID variants (RAID1/RAID5) spares are activated if any disk fails.) Every member disk/partition/device has a superblock, which carries all information necessary to start up the whole array. (for autodetection to work all the 'member' RAID partitions should be marked type 0xfd via fdisk) The superblock is not visible in the final RAID array and cannot be destroyed accidentally through usage of the md device files, all RAID data content is available for filesystem use.

That's interesting: first because if images (or disks) were wrong labeled, you can reorder the disks. Moreover you get a lot of information to verify the RAID type, the RAID UUID and... the chunk-size. Where this data is memorized? It's intuitive, but from the linux raid source code (thanks God is OSS!) and from RAID_superblock_formats, I got all the information needed to explore the RAID superblock.


let's template it


Exploring the hexadecimal is great but if you can create something that displays the data... and XWF has templates! So based on the raid source code I made a little template to get the following output:




 

You know where to apply the template ("The superblock is 4K long and is written into a 64K aligned block that starts at least 64K and less than 128K from the end of the device") and you know the magic value. So, in my case, the stripe size (chunk size) is 128 sectors... and XWF was able to complete its work.

Here is it the template: cut and paste inside a tpl file and use it with XWF. Note that only version 0.90 is "supported".

template "RAID superblock version 0.90"

// Template by Francesco "fpi" Picasso
// Tweet me @dfirfpi

description "To be applied to RAID superblock"
applies_to disk
sector-aligned
requires 0x0 FC4E2BA9
requires 0x4 00000000
requires 0x8 5A000000

begin
    hexadecimal uint32 "Signature: 0xA92B4EFC"
    uint32    "major version (want 0!)"
    uint32    "minor version (want 90!)"

    section "Generic Information"
    uint32     "patch level"
    uint32    "section words len"
    hexadecimal uint32    "Raid UIDD0 (1)"
    time_t    "Creation time"
    uint32    "RAID level"
    uint32    "Size of individual disk"
    uint32    "Number of disks"
    uint32    "Fully functional disks"
    uint32    "Preferred min MD device"
    uint32    "Persistent superblock"
    hexadecimal uint32    "Raid UUID1 (2)"
    hexadecimal uint32    "Raid UUID2 (3)"
    hexadecimal uint32    "Raid UUID3 (4)"
    move 64
    endsection

    section "Generic state information"
    time_t     "Superblock update time"
    hexadecimal uint32    "State bitmask"
    uint32    "Active disks"
    uint32    "Working disks"
    uint32    "Failed disks"
    uint32    "Spare disks"
    hexadecimal uint32 "Superblock checksum"
    int64    "Superblock update count"
    int64    "Checkpoint update count"
    uint32    "Recovery sector count"
    int64    "(v>90) reshape position"
    uint32    "(v>90) new level"
    uint32    "(v>90) delta disks"
    uint32    "(v>90) new layout"
    uint32    "(v>90) new chunk size bytes"
    move 56
    endsection

    section "Personality Information"
    uint32    "Array physical layout"
    uint32    "Chunk size (bytes)"
    uint32    "LV root PV"
    uint32    "LV root block"
    move 240
    endsection

    section "RAID disks descriptors (first 6 of 27)"
    {
        uint32 "Disk~ number"
        uint32 "Disk~ major"
        uint32 "Disk~ minor"
        uint32 "Disk~ raid disk"
        hexadecimal uint32 "Disk~ state"
        move 108
    }[6]
    move 2688
    endsection

    section "Disk descriptor"
    uint32    "Number"
    uint32    "Major"
    uint32    "Minor"
    uint32    "Raid disk"
    hexadecimal uint32 "State"
    move 108
    endsection

end

that's all!



Comments

Popular posts from this blog

A first look at Android 14 forensics

Huawei backup decryptor

Dissecting the Android WiFiConfigStore.xml for forensic analysis