UPDATE: I got a working script to accomplish the task. I needed to batch process a bunch of files so it accepts a csv file formatted as FileName,OriginalHEX,CorrectedHEX. It's very slow even after limiting the search to the first 512 bytes. It could probably be written better and made faster. Thanks for the help.
UPDATE 2: Revised the search method to be faster but it's nowhere near as fast as a dedicated hex editor. Be aware that it's memory intensive. peaks around 32X the size of the file in RAM. 10MB=320MB of RAM. 100MB=3.2GB of RAM. I don't recommend for big files. It also saves to a new file instead of overwriting. Original file renamed as File.ext_original@date-time.
Import-CSV $PSScriptRoot\HEXCorrection.csv | ForEach-Object {
$File = $_.'FileName'
$Find = $_.'OriginalHEX'
$Replace = $_.'CorrectedHEX'
IF (([System.IO.File]::Exists("$PSScriptRoot\$File"))) {
$Target = (Get-ChildItem -Path $PSScriptRoot\$File)
} ELSE {
Write-Host $File "- File Not Found`n" -ForegroundColor 'Red'
RETURN
}
Write-Host "File: "$Target.Name`n"Find: "$Find`n"Replace: "$Replace
$TargetLWT = $Target.LastWriteTime
$TargetCT = $Target.CreationTime
IF ($Target.IsReadOnly) {
Write-Host $Target.Name "- Is Read-Only`n" -ForegroundColor 'Red'
RETURN
}
$FindLen = $Find.Length
$ReplaceLen = $Replace.Length
$TargetLen = (1..$Target.Length)
IF (!($FindLen %2 -eq 0) -OR !($ReplaceLen %2 -eq 0) -OR
[String]::IsNullOrEmpty($FindLen) -OR [String]::IsNullOrEmpty($ReplaceLen)) {
Write-Host "Input hex values are not even or empty" -ForegroundColor 'DarkRed'
RETURN
} ELSEIF (
$FindLen -ne $ReplaceLen) {
Write-Host "Input hex values are different lengths" -ForegroundColor 'DarkYellow'
RETURN
}
$FindAsBytes = New-Object System.Collections.ArrayList
$Find -split '(.{2})' | ? {$_} | % { $FindAsBytes += [Convert]::ToInt64($_,16) }
$ReplaceAsBytes = New-Object System.Collections.ArrayList
$Replace -split '(.{2})' | ? {$_} | % { $ReplaceAsBytes += [Convert]::ToInt64($_,16) }
# ^-- convert to base 10
Write-Host "Starting Search"
$FileBytes = [IO.File]::ReadAllBytes($Target)
FOREACH ($Byte in $FileBytes) {
$ByteCounter++
IF ($Byte -eq [INT64]$FindAsBytes[0]) { TRY {
(1..([INT64]$FindAsBytes.Count-1)) | % {
$Test = ($FileBytes[[INT64]$ByteCounter-1+[INT64]$_] -eq $FindAsBytes[$_])
IF ($Test -ne 'True') {
THROW
}
}
Write-Host "Found at Byte:" $ByteCounter -ForegroundColor 'Green'
(0..($ReplaceAsBytes.Count-1)) | % {
$FileBytes[[INT64]$ByteCounter+[INT64]$_-1] = $ReplaceAsBytes[$_]}
$Found = 'True'
$BytesReplaces = $BytesReplaces + [INT64]$ReplaceAsBytes.Count
}
CATCH {}
}
}
IF ($Found -eq 'True'){
[IO.File]::WriteAllBytes("$Target-temp", $FileBytes)
$OriginalName = $Target.Name+'_Original'+'@'+(Get-Date).ToString('yyMMdd-HHmmss')
Rename-Item -LiteralPath $Target.FullName -NewName $OriginalName
Rename-Item $Target"-temp" -NewName $Target.Name
#Preserve Last Modified Time
$Target.LastWriteTime = $TargetLWT
$Target.CreationTime = $TargetCT
Write-Host $BytesReplaces "Bytes Replaced" -ForegroundColor 'Green'
Write-Host "Original saved as:" $OriginalName
} ELSE {
Write-Host "No Matches" -ForegroundColor 'Red'}
Write-Host "Finished Search`n"
Remove-Variable -Name * -ErrorAction SilentlyContinue
} # end foreach from line 1
PAUSE
Original: This has been asked before but found no solutions to perform a simple and straight up find hex value and replace hex value on large files, 100MB+. Even better would be any recommendations for a hex editor with command line support for this task.