Generic.Utils.DeadDiskRemapping

Calculate a remapping configuration from a dead disk image.

The artifact uses some heuristics to calculate a suitable remapping configuration for a dead disk image:

The following cases are handled:

  • If ImagePath is a directory to a mounted partition then we generate directory remapping. This is suitable for handling images with filesystems that Velociraptor can not yet directly handle.

  • If the ImagePath points to a file which starts with the NTFS signature we assume this is a partition image and not a disk image.

  • If the ImagePath is a full disk image we assume it has a partition table at the front, we then enumerate all the partitions and look for an ntfs partition with a Windows directory at the top level. We assume this is the windows drive and remap it to the C: drive.


name: Generic.Utils.DeadDiskRemapping
description: |
  Calculate a remapping configuration from a dead disk image.

  The artifact uses some heuristics to calculate a suitable remapping
  configuration for a dead disk image:

  The following cases are handled:

  * If ImagePath is a directory to a mounted partition then we
    generate directory remapping. This is suitable for handling images
    with filesystems that Velociraptor can not yet directly handle.

  * If the ImagePath points to a file which starts with the NTFS
    signature we assume this is a partition image and not a disk
    image.

  * If the ImagePath is a full disk image we assume it has a partition
    table at the front, we then enumerate all the partitions and look
    for an ntfs partition with a `Windows` directory at the top
    level. We assume this is the windows drive and remap it to the C:
    drive.

parameters:
  - name: ImagePath
    default: /tmp/image.dd
    description: Path to the image file to inspect.

  - name: Accessor
    description: |
      Accessor to read the image with.

      If not provided guess based on image file extension.

  - name: Hostname
    default: Virtual Host

  - name: Upload
    type: bool
    default: "Y"
    description: If specified we upload the generated YAML

  - name: CommonRemapping
    description: Common clauses for all remapping in YAML
    default: |
      remappings:
      - type: permissions
        permissions:
        - COLLECT_CLIENT
        - FILESYSTEM_READ
        - FILESYSTEM_WRITE
        - READ_RESULTS
        - MACHINE_STATE
        - SERVER_ADMIN
        - COLLECT_SERVER
        - EXECVE
      - type: impersonation
        os: windows
        hostname: {{ .Hostname }}
        env:
        - key: SystemRoot
          value: C:\Windows
        - key: WinDir
          value: C:\Windows
        disabled_functions:
        - amsi
        - lookupSID
        - token
        disabled_plugins:
        - users
        - certificates
        - handles
        - pslist
        - interfaces
        - modules
        - netstat
        - partitions
        - proc_dump
        - proc_yara
        - vad
        - winobj
        - wmi
      - type: shadow
        from:
          accessor: zip
        "on":
          accessor: zip
      - type: shadow
        from:
          accessor: raw_reg
        "on":
          accessor: raw_reg
      - type: shadow
        from:
          accessor: data
        "on":
          accessor: data

export: |
   -- Searches for a partition with a Windows directory, Unless this
   -- is a partition image.
   LET _FindWindowsPartition(ImagePath, Accessor) = SELECT *
     FROM switch(
       a={
       SELECT 0 AS StartOffset, Accessor, ImagePath AS PartitionPath
       FROM stat(filename=ImagePath)
       WHERE IsDir
     },
       b={
       SELECT 0 AS StartOffset, Accessor, ImagePath AS PartitionPath
       FROM scope()
       WHERE read_file(accessor=Accessor, filename=ImagePath, length=4, offset=3) = "NTFS"
        AND log(message="Detected NTFS signature at offset 0 - " +
               "assuming this is a Windows partition image")
     },
       c={
       SELECT StartOffset, Accessor, _PartitionPath AS PartitionPath
       FROM Artifact.Windows.Forensics.PartitionTable(
           ImagePath=ImagePath,
           Accessor=GuessAccessor(ImagePath=ImagePath))
       WHERE log(level="DEBUG", dedup=-1,
                 message="Searching for Windows directory: %#x-%#x (%v) %v - Magic %v",
                 args=[StartOffset, EndOffset, Size, name, Magic])
         AND TopLevelDirectory =~ "Windows"
         AND log(message="<green>Found Windows Partition</> at offset %#x with top level directory %v",
                 args=[StartOffset, TopLevelDirectory])
       LIMIT 1
     })

   -- Guess the correct accessor based on the file extension. This
   -- allows us to handle several image formats.
   LET GuessAccessor(ImagePath) = Accessor ||
     if(condition=ImagePath =~ 'vmdk$', then='vmdk') ||
     if(condition=ImagePath =~ 'vhdx$', then='vhdx') ||
     if(condition=ImagePath =~ 'e01$', then='ewf')

   LET _MapHiveToKey(Hive, Key, Name, ImagePath) = log(dedup=-1,
      message="<green>Adding hive %v</>", args=Hive) &&
   dict(type="mount",
    `description`=Name,
    `from`=dict(accessor="raw_reg",
      path_type="registry",
      prefix=pathspec(
        Path="/",
        DelegateAccessor="raw_ntfs",
        Delegate=ImagePath + Hive)),
    on=dict(accessor="registry", prefix=Key, path_type="registry"))

   LET _MapDirHiveToKey(Hive, Key, Name) = log(dedup=-1,
      message="<green>Adding hive %v</>", args=Hive) &&
   dict(type="mount",
    `description`=Name,
    `from`=dict(accessor="raw_reg",
      path_type="registry",
      prefix=pathspec(
        Path="/",
        DelegateAccessor="file",
        DelegatePath=Hive)),
    on=dict(accessor="registry", prefix=Key, path_type="registry"))

    -- Look for user hives and map them in HKEY_USERS
    LET _FindUserHives(ImagePath) = SELECT _MapHiveToKey(
           Name="Map User hive for " + OSPath[-2],
           Hive=OSPath,
           Key="HKEY_USERS\\" + OSPath[-2],
           ImagePath=ImagePath
        ) AS Map
      FROM glob(globs='/Users/*/NTUser.DAT',
                accessor="raw_ntfs",
                root=ImagePath)
      WHERE log(dedup=-1, message="<green>Found User Hive at %v</>", args=OSPath.Path)

    LET _FindDirUserHives(ImagePath) = SELECT _MapDirHiveToKey(
           Name="Map User hive for " + OSPath[-2],
           Hive=OSPath,
           Key="HKEY_USERS\\" + OSPath[-2]) AS Map
      FROM glob(globs='/Users/*/NTUser.DAT',
                root=ImagePath)
      WHERE log(dedup=-1, message="<green>Found User Hive at %v</>", args=OSPath.Path)

    LET CalculateWindowsMappings(ImagePath) = Remappings.remappings + (
       dict(type="mount",
            `from`=dict(accessor="raw_ntfs", prefix=ImagePath),
            on=dict(accessor="ntfs", prefix="\\\\.\\C:", path_type="ntfs")
      ),
       dict(type="mount",
            `from`=dict(accessor="raw_ntfs", prefix=ImagePath),
            on=dict(accessor="file", prefix="C:", path_type="windows")
      ),
       dict(type="mount",
            `from`=dict(accessor="raw_ntfs", prefix=ImagePath),
            on=dict(accessor="auto", prefix="C:", path_type="windows")
      ),
      _MapHiveToKey(Name="Map Software Hive",
                    ImagePath=ImagePath,
                    Hive="/Windows/System32/Config/SOFTWARE",
                    Key="HKEY_LOCAL_MACHINE/Software"),
      _MapHiveToKey(Name="Map Security Hive",
                    ImagePath=ImagePath,
                    Hive="/Windows/System32/Config/Security",
                    Key="HKEY_LOCAL_MACHINE/Security"),
      _MapHiveToKey(Name="Map System Hive",
                    ImagePath=ImagePath,
                    Hive="/Windows/System32/Config/System",
                    Key="HKEY_LOCAL_MACHINE/System"),
      _MapHiveToKey(Name="Map SAM Hive",
                    ImagePath=ImagePath,
                    Hive="/Windows/System32/Config/SAM",
                    Key="SAM"),
      _MapHiveToKey(Name="Map Amcache Hive",
                    ImagePath=ImagePath,
                    Hive="/Windows/appcompat/Programs/Amcache.hve",
                    Key="Amcache")
    ) + _FindUserHives(ImagePath=WindowsPartition.PartitionPath).Map

    LET CalculateWindowsDirMappings(ImagePath) = Remappings.remappings + (
       dict(type="mount",
            description="Mount Directory " + ImagePath + " on C: drive",
            `from`=dict(accessor="file", prefix=ImagePath),
            on=dict(accessor="ntfs", prefix="\\\\.\\C:", path_type="ntfs")
      ),
      dict(type="mount",
            `from`=dict(accessor="file", prefix=ImagePath),
            on=dict(accessor="file", prefix="C:", path_type="windows")
      ),
       dict(type="mount",
            `from`=dict(accessor="file", prefix=ImagePath),
            on=dict(accessor="auto", prefix="C:", path_type="windows")
      ),
      _MapDirHiveToKey(Name="Map Software Hive",
                       Hive="/Windows/System32/Config/SOFTWARE",
                       Key="HKEY_LOCAL_MACHINE/Software"),
      _MapDirHiveToKey(Name="Map Security Hive",
                       Hive="/Windows/System32/Config/Security",
                       Key="HKEY_LOCAL_MACHINE/Security"),
      _MapDirHiveToKey(Name="Map System Hive",
                       Hive="/Windows/System32/Config/System",
                       Key="HKEY_LOCAL_MACHINE/System"),
      _MapDirHiveToKey(Name="Map SAM Hive",
                       Hive="/Windows/System32/Config/SAM",
                       Key="SAM"),
      _MapDirHiveToKey(Name="Map Amcache Hive",
                       Hive="/Windows/appcompat/Programs/Amcache.hve",
                       Key="Amcache")
    ) + _FindDirUserHives(ImagePath=ImagePath).Map

sources:
- query: |
    LET WindowsPartition <=
      _FindWindowsPartition(ImagePath=ImagePath, accessor=Accessor)[0]

    LET Remappings <= parse_yaml(
      filename=template(template=CommonRemapping,
                        expansion=dict(Hostname=Hostname)),
      accessor="data")

    -- Select the type of mapping to calculate depending on what ImagePath is.
    LET CalculateMappings =
       ( stat(filename=ImagePath).IsDir &&
         CalculateWindowsDirMappings(ImagePath=ImagePath) ) ||
       ( WindowsPartition.PartitionPath &&
         CalculateWindowsMappings(ImagePath=WindowsPartition.PartitionPath) ) ||
         log(message="<red>No suitable mapping found</>")

    LET YamlText = serialize(format="yaml",
         item=dict(remappings=CalculateMappings))

    SELECT if(condition=Upload,
              then=upload(accessor="data", file=YamlText, name="remapping.yaml"),
              else=YamlText) AS Remapping
    FROM scope()

column_types:
- name: Remapping
  type: upload_preview