Monday, June 29, 2015

On the Forensic Trail - Guid Partition Table (GPT)

[This is the 3rd in a multi-part series titled "On the Forensic Trail".  My goal with this series is to introduce my PowerShell Forensics module called PowerForensics, and the forensic artifacts that it parses.  This post covers the Guid Partition Table (GPT), an alternative hard disk partitioning scheme to the Master Boot Record, and how to use PowerForensics to inspect it.]

For help with downloading/installing PowerForensics please visit the Installing PowerForensics article.

As United Extensible Firmware Interface (UEFI) begins to replace legacy BIOS firmware interfaces, we will see the rise of the Guid Partition Table (GPT) as a replacement for the Master Boot Record.  The UEFI Specification requires that all compliant firmware supports the GPT partitioning scheme, so GPT will begin to become more common as UEFI is implemented by vendors.  That being said, the GPT partitioning scheme is not unique to UEFI, as it is also supported by many modern BIOS systems.

This post explores the Guid Partition Table data structure, and focuses on how PowerForensics can be used with it.

Advantages of GPT
The GUID Partition Table provides a number of advantages over the legacy Master Boot Record disk layout.  Three of the main advantages of the GPT disk layout are listed below:

1)  Logical Block Addresses (LBAs) are 64 bits (rather than 32 bits) increasing the maximum partition size from 2 TiB to 8 ZiB. 
2)  GPT supports many partitions (rather than just four primary partitions). 
3)  Provides both a primary and backup partition table for redundancy.  Two GPT Header structures are stored on the device: the primary and the backup. The primary GPT Header must be located in LBA 1 (i.e., the second logical block), and the backup GPT Header must be located in the last LBA of the device. 

GPT Data Structure
Protective MBR
When a disk is formatted with a GPT disk layout, a Protective MBR is located at Logical Block Address (LBA) 0.  The Protective MBR is used to provide compatibility with legacy tools that do not understand the GPT format.  The Protective MBR is functionally equivalent to a normal or "Legacy" MBR, but only has one partition.  The first/only partition will be of type 0xEE (EFI_GPT_DISK) and reserves the entire disk starting at LBA 1 for the formal Guid Partition Table structure.  

It is worth noting that the MBR Boot Code (the first 440 bytes) is not executed by UEFI Firmware. 

    In the example below, the Get-MBR cmdlet is used to display the MBR Partition Table of a GPT formatted disk.  The output shows that there is one EFI_GPT_DISK partition that starts at LBA 1 (the sector immediately following the Protective MBR) and ends at LBA 4294967295 (or 0xFFFFFFFF which represents the entire disk).

    Guid Partition Table
    The formal Guid Partition Table begins at LBA 1 where the GPT Header is found.

    GPT Header
    The GPT Header describes the logical layout of the disk as a whole.  The location of the GPT Header  is described by the MyLBA value and should always be 1, while the AlternateLBA value points to the backup GPT and will always be the last sector on disk.  It contains the DiskGUID, which is used to uniquely identify the disk, and CRC32 values which are used by firmware to detect corruption of the GPT itself.  If the GPT is corrupted, the original GPT will be replaced with the backup GPT.  The FirstUsableLBA and LastUsableLBA values define the portion of the disk that is not reserved by the GPT and therefore can be used by the partitions.  Lastly, the header provides us with details about the Partition Array itself.  The PartitionEntryLBA value points to the start of the array, and its overall size can be derived from the NumberOfPartitionEntries and SizeOfPartitionEntry value.

    Partition Array
    The GPT Header points to the Partition Array via the PartitionEntryLBA value.  The size and number of partitions are defined in the GPT Header (The number of partitions value may not correspond with the number of actual partitions, but rather the space reserved for partition entries).  

    Each partition contains two GUIDs, one representing the type of partition (see this table to interpret) and the second is used to uniquely identify the partition.  The StartingLBA and EndingLBA values to describe the location and size of the partition.  Lastly, 64 bits are reserved for attribute flags, and 72 bytes are reserved for a null terminated name string.

    PowerForensics Cmdlets
    PowerForensics currently has three cmdlets that deal specifically with the GPT (Get-GPT, Get-BootSector, and Get-PartitionTable).  This portion of the post will explain the cmdlets and go through a few example use cases.

    Get-GPT is a cmdlet that parses the GUID Partition Table data structure contained within the first few sector of the device specified.  Get-GPT requires the use of the -Path parameter which takes the Win32 Device Namespace (ex. \\.\PHYSICALDRIVE1) for the device from which the GPT should be parsed.

    In this example, Get-GPT is executed against \\.\PHYSICALDRIVE1 and returns a GuidPartitionTable object.
    If Get-GPT is run against a disk formatted with a Master Boot Record, it will throw an error prompting you to use Get-MBR instead.
    InvokeIR.PowerForensics.GuidPartitionTable Object
    Get-GPT outputs an InvokeIR.PowerForensics.GuidPartitionTable object.  This object contains 11 readonly properties (property descriptions provided by the UEFI Specification):

    1)  Revision -  The revision number for this header.
    2)  HeaderSize - Size in bytes of the GPT Header. Must be greater than or equal to 92 and must be less than or equal to the logical block size.
    3)  MyLBA - The LBA that contains this data structure.
    4)  AlternateLBA -  LBA address of the alternate (backup) GPT Header. 
    5)  FirstUsableLBA - The first usable logical block that may be used by a partition described by a GUID Partition Entry.
    6)  LastUsableLBA - The last usable logical block that may be used by a partition described by a GUID Partition Entry.
    7)  DiskGUID - GUID that can be used to uniquely identify the disk.
    8)  PartitionEntryLBA - The starting LBA of the GUID Partition Array.
    9)  NumberOfPartitionEntries - The number of Partition Entries in the GUID Partition Array.
    10)  SizeOfPartitionEntry - The size, in bytes, of each the GUID Partition Entry structures in the GUID Partition Array.
    11)  PartitionTable - An array of InvokeIR.PowerForensics.GuidPartitionEntry objects.

    The Get-BootSector cmdlet provides an alternative to the Get-MBR and Get-GPT cmdlets.  Get-BootSector reviews the hard drive's first sector and determines if the disk is formatted using the Master Boot Record or Guid Partition Table partitioning scheme.  Once the partitioning scheme is determined, Get-BootSector acts just as Get-MBR or Get-GPT would respectively.

    NOTE: Since making Get-BootSector, I rarely use the Get-MBR or Get-GPT cmdlets, but have kept them in the module in case anyone finds a reason for them.

    Here Get-BootSector running against a disk formatted using the GPT partitioning scheme.  The cmdlet returns an InvokeIR.PowerForensics.GuidPartitionTable object.
    When Get-BootSector targets an MBR formatted disk it returns an InvokeIR.PowerForensics.MasterBootRecord object.
    Like Get-BootSector, Get-PartitionTable determines the type of boot sector (Master Boot Record or Guid PartitionTable) and returns the correct partition object (InvokeIR.PowerForensics.PartitionEntry or InvokeIR.PowerForensics.GuidPartitionTableEntry). 
    As a reminder, this is an example of Get-PartitionTable being run against an MBR formatted disk and returning an PartitionEntry object.
    In this example, Get-PartitionTable is run against a GPT formatted hard drive disk (\\.\PHYSICALDISK1).  The cmdlet returns an array of GuidPartitionTableEntry Objects.

    InvokeIR.PowerForensics.GuidPartitionTableEntry Object
    When Get-PartitionTable is run against a GPT formatted disk, the cmdlet outputs an InvokeIR.PowerForensics.GuidPartitionTableEntry object.  The GuidPartitionTableEntry object is made up of 6 readonly properties (property descriptions provided by the UEFI Specification):

    1)  PartitionTypeGUID - Unique ID that defines the purpose and type of this Partition. A value of zero defines that this partition entry is not being used.
    2)  UniquePartitionGUID -  GUID that is unique for every partition entry.
    3)  StartingLBA - Starting LBA of the partition defined by this entry.
    4)  EndingLBA - Ending LBA of the partition defined by this entry.
    5)  Attributes - Flag value representing the partition's attributes.
    6)  PartitionName - Null-terminated string containing a human-readable name of the partition. 


    Friday, May 1, 2015

    On the Forensic Trail - Master Boot Record (MBR)

    [This is the 2nd in a multi-part series titled "On the Forensic Trail".  My goal with this series is to introduce my PowerShell Forensics module called PowerForensics, and the forensic artifacts that it parses.  This post covers the Master Boot Record, a disk structure that describes the logical layout of the disk in the form of partitions, and how to use PowerForensics to inspect it.]

    For help with downloading/installing PowerForensics please visit the Installing PowerForensics article.

    Master Boot Record is the name given to the first sector (typically 512 bytes) of a physical disk. Technet states that the MBR is "the most important data structure on the disk".  The MBR itself contains boot code, a disk signature, the partition table, and an end of sector marker which is always 0x55AA.

    For a detailed breakdown of the MBR please see the poster above.

    Windows Boot Order
    Before diving into the MBR's boot code, it is important to review the boot order for Windows Operating Systems.

    1.  The BIOS and CPU initiate the Power-on self-test (POST)
    2.  The BIOS searches for a boot device (HDD, Floppy, CD, etc.)
    3.  The BIOS reads the first sector of the device (Master Boot Record) into memory, and transfers CPU execution to that memory address.

    Now that we have covered the boot process, lets look at what happens once the CPU transfers execution to the MBR.

    Boot Code
    The goal of the Master Boot Record's boot code is to find the bootable partition and transfer execution to it.  To find the bootable partition the following checks must occur:

    1.  Search for an active partition (where the partition status is 0x80 or "Bootable")
    2.  Checks that no other partitions are marked as bootable
    3.  If partition is the only bootable partition, then the boot code will read the partition's Volume Boot Record (first sector, 512 bytes, of the partition) and transfer CPU execution to that memory address.
    4.  The VBR is checked for the proper end signature (0xAA55).

    If steps 2 - 4 fail then an execution is halted and an error message is displayed.  Below are the error messages for each step.

    Step 2: If another partition is marked as bootable, the message "Invalid partition table" is displayed.
    Step 3: If there is a problem loading the VBR, the message "Error loading operating system" is displayed.
    Step 4: If the end signature is not present, the message "Missing operating system" is displayed.

    Now that the we have covered how the boot code interacts with the partition table, lets talk about the structure of the partition table and its entries.

    Partition Table
    The partition table is a 64 byte structure made up of 4 0x10 (16) byte partition entries. The fixed size of the partition table limits the OS to 4 partitions, but modern OS have built in the capability for many more partitions through a concept called extended partitions.

    Below I will cover the important values contained within a partition entry.

    Partition Status
    The 1st byte in every partition entry represents the Partition Status (whether the partition is bootable or not).  The Partition Status field has two acceptable values, these values are 0x00 (Non-Bootable) and 0x80 (Bootable).

    It is important to note that only one partition can be marked as bootable per disk.  If this condition is not met, the "Invalid partition table" error will be thrown by the boot code.

    Partition Types

    The 5th byte of the partition table represents the type of file system the partition is using.  A list of partition type ids can be found here.  This value helps the operating system determine which file system device driver to load on startup.

    It is worth noting that NTFS partitions will have an id of 0x07.

    Cylinder Head Sector (CHS) Addressing
    The operating system uses Cylinder Head Sector (CHS) Addressing to determine the location of the volume.  Bytes 2 - 4 and 6 - 8 represent the volume start address and end address respectively. The three bytes representing the CHS address are broken up such that the cylinder value is 10 bits, the head value is 8 bits, and the sector value is 6 bits.  Since disks are formatted with 512 byte sectors this addressing scheme only allows for a maximum addressable size of 7.8 GB.  As hard drive sizes increased, Logical Block Addressing (LBA) was introduced allowing much larger address ranges.

    Partition Location
    Most modern disks use the "Relative Starting Sector" and "Total Sectors" values to determine the location of volumes.  Each of these values are 4 bytes in size, and this allows for a maximum addressable size of right around 2 TB.

    This limit is only exceeded by the introduction of the Guid Partition Table (GPT), which I will write about at a future date.


    MBR boot code executes before the Operating System is loaded, and thus executes in Ring 0 without the normal Operating System protections.  This fact makes the MBR a viable location for malicious code in the form of bootkits.  I have begun research to identify a hash value for the MBR code section for a number of Windows operating systems and popular unix boot loader LILO and GRUB.  These hash values/signatures should help identify when the code section of the MBR has been tampered with, which could lead to bootkit detection.

    NOTE: I admit this is not a full featured solution as a bootkit could easily detect disk IO and return false data, but at this time there are only a few publicly release bootkits that have been found to do this. 

    PowerForensics Cmdlets
    PowerForensics currently has two cmdlets that deal specifically with the MBR.  This portion of the post will explain the cmdlets and go through a few example use cases.

    Get-MBR is a cmdlet that parses the Master Boot Record data structure contained within the first sector of the device specified.  Get-MBR requires the use of the -Path parameter which takes the Win32 Device Namespace (ex. \\.\PHYSICALDRIVE0) for the device from which the MBR should be parsed.

    InvokeIR.PowerForensics.MasterBootRecord Object
    The output of Get-MBR is a InvokeIR.PowerForensics.MasterBootRecord object.  The MasterBootRecord object has four readonly properties:

    1)  DiskSignature - the 4 byte hex value that identifies the disk to the operating system
    2)  BootCode - a byte array of the MBR boot code
    3)  MBRSignature - a string representing the signature associated with the BootCode byte array (The 4)  MBRCodeArea is hashed and compared to a list of known MBR code signatures, benign and malicious. The string identifies which, if any, signature the BootCode matches).
    5)  PartitionTable - an array of InvokeIR.PowerForensics.PartitionEntry objects

    Below is an example of Get-MBR being run against \\.\PHYSICALDRIVE0 and being asked to return all properties (by default Get-MBR only returns MBRSignature and DiskSignature to console).

    InvokeIR.PowerForensics.PartitionEntry Object
    The InvokeIR.PowerForensics.PartitionEntry object represents an entry in the MBR partition table.  Each PartitionEntry object has four readonly properties:

    1)  Bootable - a bool indicating whether the partition is bootable
    2)  StartSector - the offset from the beginning of the disk to the beginning of the volume counting by sectors
    3)  EndSector - the offset from the beginning of the disk to the end of the volume counting by sectors
    4)  SystemID - a string defining the volume type (NTFS, FAT, etc.)

    Here I am using the Select-Object cmdlet to expand the PartitionTable property of the MasterBootRecord object.

    Introducing the -AsBytes Switch Parameter
    While developing PowerForensics I wanted to make each cmdlet as flexible as possible.  I realized that returning the raw bytes for a data structure may be just as appealing as returning an object representing the data structure.  With this in mind, I have added a switch parameter to each cmdlet that parses a data structure called -AsBytes.  When the -AsBytes parameter is used, the cmdlet returns the raw bytes representing the structure instead of the expected object.

    Below is an example of Get-MBR being used with the -AsBytes switch parameter.  In this example, I passed the output to Lee Holmes' Format-Hex cmdlet.

    This example shows the -AsBytes switch parameter to save the MBR data structure's raw bytes to a file.

    The Get-PartitionTable cmdlet is functionally the same as Get-MBR, but instead of returning an MBR object it returns an array of non-empty PartitionEntry objects.

    Future Work
    Currently PowerForensics does not have support for extended partitions or the UEFI Guid Partition Table (GPT).  I plan on adding support for these structures in the very near future.

    My next post will break down the GUID Partition Table (GPT), which is a newer alternative to the Master Boot Record.


    Friday, April 24, 2015

    On the Forensic Trail - Preparing for our Journey

    About a month ago, I posted about a PowerShell module I wrote called PowerForensics.  At the time, the module was nothing more than a proof of concept, but I wanted to show the DFIR community that PowerShell is a viable option for scalable deep dive disk analysis.

    Over the past month I have updated PowerForensics from a single DLL to a proper PowerShell module.  The module now includes a module manifest; default object, formats, and types; and XML-based help for each cmdlet.

    Developing PowerForensics has led to research and understanding of file system structures (mainly NTFS at this point) and file formats for forensic artifacts.  I believe this research has made me a better analyst/hunter, and hope that the DFIR community can benefit from this data being centralized.

    This is the first part of a multi-part series called "On the Forensic Trail".  The purpose of the series is to introduce you to the capabilities of PowerForensics, but also to help build forensic literacy by breaking down each artifact.

    This post will help you get PowerForensics up, running, and prepared to follow future posts.

    Installing PowerForensics
    Please refer to my new post regarding PowerForensics Installation

    Loading the Module into the PowerShell Session
    Open PowerShell or PowerShell_ISE as administrator and import the module into the current session.

    NOTE: Many of PowerForensics’ cmdlets require administrator privilege to run.

    The syntax for importing the PowerForensics module is shown below.

    If the above cmdlet gives you an error, verify that you followed the instructions for installing a PowerShell module.

    Use the Get-Command cmdlet with the –Module parameter to list the cmdlets that come with the module.

    Cmdlet Help
    Each PowerForensics cmdlet has help associated with it, which can be retrieved with the Get-Help cmdlet.  Help includes a description of the cmdlet’s function, syntax, output objects, and usage examples.

    You should now be ready to use PowerForensics.  Stay tuned, in my next post I will dive into the Master Boot Record and the applicable PowerForensics cmdlets.

    Saturday, March 7, 2015

    Parse the MFT in PowerShell

    [This post has been deprecated.  During the development of PowerForensics, I have decided to move away from the Sleuth Kit naming convention in favor of the artifact's true names (ex. Get-IStat has become Get-MFTRecord and Get-ICat has become Get-ContentRaw.  For a better explanation of PowerForensic's capabilities please follow my "On the Forensics Trail" series.]

    Over the past year or so I have been thinking about the best way to implement a Digital Forensic framework from within PowerShell.  Recently, I wrote a PowerShell cmdlet in C# to parse the Windows Prefetch file for useful forensic artifacts, but I quickly realized that accessing files directly is not forensically sound.  

    During my search for solutions, I came across clymb3r's blog post about his Invoke-Ninjacopy cmdlet and thought that I could use this methodology for my Forensic framework.  One of the components of Invoke-Ninjacopy is a DLL that parses the NTFS file system, and upon reviewing the DLL project I decided that porting it to a C# assembly library would be a very logical starting point for PowerForensics.  With the help of Brian Carrier's File System Forensics book, for context, I was able to reproduce the NTFS parsing code in C#.  

    PowerForensics works by opening a read handle to the logical volume (such as the C Drive), and parsing the NTFS structures within the volume's raw bytes.

    NOTE: PowerShell must be run as administrator to access the Volume's file handle.

    The rest of this post describes some of the initial capabilities presented by PowerForensics.

    While writing the code for PowerForensics, I realized that I was starting to reproduce some of the functionality inherent in The Sleuth Kit, so I decided to maintain a similar naming convention to those tools (ex. Get-FSStat, Get-IStat, Get-ICat) for continuity purposes.

    Get-IStat is a cmdlet that can be used to return the MFT Entry for a specified file.  The file can be specified by its Path or via its Index Number (what record it is in the MFT).  Additionally, the investigator can specify what Logical Volume they want to investigate through the VolumeName parameter (Remember that the volume must be using NTFS as its file system).

    FileRecord Object

    Get-IStat returns a custom FileRecord object.  This object is built into PowerForensics, and represents a File Record in the Master File Table.  In the context of this post the important properties of the FileRecord object are the RecordNumber, the record's index into the MFT, and the Attribute Array, the records attribute objects. 

    Upon drilling down into the attribute array, we can see the STANDARD_INFORMATION and FILE_NAME attributes (there are more that didn't make the screenshot).  Each attribute contains information that proves invaluable during a forensic investigation.  For example, an investigator can use the timestamps from the STANDARD_INFORMATION and FILE_NAME attributes for timeline analysis or comparison for evidence of timestomping.


    Great! Now we have the ability to read the MFT by parsing the raw bytes of the volume.  What if we were able to make a copy of the contents of the file via the raw disk?

    PowerForensics includes the Get-ICat cmdlet, which parses the DATA attribute in the file's MFT Record and outputs the contents of the file in the form of a byte array.  This byte array can be saved to a variable, and used as input to the Add-Content cmdlet to be output to a file (NOTE: Add-Content must be used with the Encoding parameter set to "byte").

    NOTE: Get-ICat has not yet implemented all of the functionality of TSK's icat command.

    Once we copy cmd.exe's raw bytes to a new file we can use a MD5 hashing function (shown below) to ensure our dumped file is the same as the original.

    Below you can see that both C:\Windows\System32\cmd.exe (the original) and C:\Users\Public\Desktop\cmd (our copy using PowerForensics) have the same MD5 hash:

    What about files that are locked by the Operating System like the registry hives?  Because PowerForensics is accessing the raw bytes on the HDD and parsing the Master File Table itself we are able to export these locked files while the OS is using them.

    Below you will see that I am unable to use the System.IO.File ReadAllBytes method to read the SAM registry hive "because it is being used by another process".

    Here we use Get-IStat to view the FileRecord object belonging to the SAM hive.  Notice that DATA attribute's NonResident property is set to True.  This means that the file's contents are too large for the MFT Record, which is 1024 bytes, and is stored elsewhere on the disk.  Conveniently, that other location is contained within the StartCluster and EndCluster Arrays.  We are able to multiply the StartCluster and EndCluster values by the size of cluster (typically 4096 bytes), to find the actual bytes on disk containing the file contents.  These properties are used by Get-ICat to output a byte array containing the file's contents.

    Below we use Get-ICat and Add-Content to output the SAM file to our Desktop.

    Although we cannot hash the original SAM file (because of the error discussed above), we can throw the outputted SAM file into a Hex Editor and see the registry hive file header.  Imagine when we add registry parsing to PowerForensics...

    You can download the source code to PowerForensics on my github.  To use PowerForensics within PowerShell download the dll in the repo and use the Import-Module cmdlet within PowerShell (Ex. Import-Module <pathToDLL>).

    I'm excited to hear your feedback and suggestions for further development.

    - Invoke-IR - By Jared Atkinson -