Recover media files from iMazing backup

Awhile back I used a piece of software (iMazing) to backup an old iPhone of mine. The raw backup when extracted is a bunch of files without extensions in many subdirs. I needed a way to extract actual images from the backup folder and copy them to an extracted folder giving them the correct file extension and ensuring I kept the date created / modified in tact.

Here’s a PowerShell script that will recursively go through a specified folder and copy any files that are images or videos to an extracted folder location.

# Define the magic numbers for various image and video types
$fileTypes = @{
jpg = @(0xFF, 0xD8, 0xFF)
png = @(0x89, 0x50, 0x4E, 0x47)
gif = @(0x47, 0x49, 0x46, 0x38)
bmp = @(0x42, 0x4D)
mp4 = @(0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6F, 0x6D)
avi = @(0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x41, 0x56, 0x49, 0x20)
mkv = @(0x1A, 0x45, 0xDF, 0xA3)
mov = @(0x00, 0x00, 0x00, 0x14, 0x66, 0x74, 0x79, 0x70, 0x71, 0x74, 0x20, 0x20)
wmv = @(0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11)
}

# Function to get the file header as a byte array
function Get-FileHeader {
param (
[string]$filePath,
[int]$headerLength = 12
)

$fileBytes = [System.IO.File]::ReadAllBytes($filePath)
return $fileBytes[0..($headerLength - 1)]
}

# Function to match the file header with known file types
function Get-FileType {
param (
[byte[]]$fileHeader
)

foreach ($fileType in $fileTypes.Keys) {
$magicNumbers = $fileTypes[$fileType]
$match = $true

for ($i = 0; $i -lt $magicNumbers.Length; $i++) {
if ($fileHeader[$i] -ne $magicNumbers[$i] -and $magicNumbers[$i] -ne 0x00) {
$match = $false
break
}
}

if ($match) {
return $fileType
}
}

return $null
}

# Source and destination directories
$sourceDirectory = "C:\iPhoneBackup"
$destinationDirectory = "D:\Recovery"

# Ensure the destination directory exists
if (-not (Test-Path -Path $destinationDirectory)) {
New-Item -ItemType Directory -Path $destinationDirectory | Out-Null
}

# Get all files in the source directory recursively
$files = Get-ChildItem -Path $sourceDirectory -Recurse -File

foreach ($file in $files) {
$header = Get-FileHeader -filePath $file.FullName
$fileType = Get-FileType -fileHeader $header

if ($fileType) {
# Create the relative path for the destination file
$relativePath = $file.FullName.Substring($sourceDirectory.Length)
$newFileName = [System.IO.Path]::ChangeExtension($relativePath, ".$fileType")
$destinationPath = Join-Path -Path $destinationDirectory -ChildPath $newFileName

# Ensure the destination subdirectory exists
$destinationSubDir = [System.IO.Path]::GetDirectoryName($destinationPath)
if (-not (Test-Path -Path $destinationSubDir)) {
New-Item -ItemType Directory -Path $destinationSubDir | Out-Null
}

# Copy the file to the destination with the new extension
Copy-Item -Path $file.FullName -Destination $destinationPath

# Preserve the original file timestamps
$originalCreationTime = Get-ItemProperty -Path $file.FullName -Name CreationTime
$originalLastWriteTime = Get-ItemProperty -Path $file.FullName -Name LastWriteTime
$originalLastAccessTime = Get-ItemProperty -Path $file.FullName -Name LastAccessTime

Set-ItemProperty -Path $destinationPath -Name CreationTime -Value $originalCreationTime.CreationTime
Set-ItemProperty -Path $destinationPath -Name LastWriteTime -Value $originalLastWriteTime.LastWriteTime
Set-ItemProperty -Path $destinationPath -Name LastAccessTime -Value $originalLastAccessTime.LastAccessTime

Write-Output "Copied $($file.FullName) to $destinationPath"
} else {
Write-Output "$($file.FullName) is not a recognized image or video file"
}
}

Published by

Rich

Just another IT guy.

Leave a Reply

Your email address will not be published. Required fields are marked *