С помощью ChatGPT со второй попытки сделал получил почти готовый к работе скрипт, который решает задачу из заголовка темы.
Вот финальный код:
spoiler
# Main script
param (
[string]$filePath,
[string]$searchPattern,
[string]$replacePattern
)
if (-not (Test-Path $filePath)) {
Write-Error "File not found: $filePath"
exit 1
}
# Function to convert hex string to byte array
function Convert-HexStringToByteArray {
param (
[string]$hexString
)
$hexString = $hexString -replace ' ', ''
if ($hexString.Length % 2 -ne 0) {
throw "Invalid hex string length."
}
[byte[]]$byteArray = @()
for ($i = 0; $i -lt $hexString.Length; $i += 2) {
$byteArray += [Convert]::ToByte($hexString.Substring($i, 2), 16)
}
return $byteArray
}
# Function to search and replace hex patterns in a binary file
function SearchAndReplace-HexPatternInBinaryFile {
param (
[string]$filePath,
[string]$searchPattern,
[string]$replacePattern
)
$searchBytes = Convert-HexStringToByteArray -hexString $searchPattern
$replaceBytes = Convert-HexStringToByteArray -hexString $replacePattern
if ($searchBytes.Length -ne $replaceBytes.Length) {
throw "Search and replace patterns must be of the same length."
}
[byte[]]$fileBytes = [System.IO.File]::ReadAllBytes($filePath)
[int]$searchLength = $searchBytes.Length
[int]$index = 0
while ($index -lt $fileBytes.Length) {
$foundIndex = [Array]::IndexOf($fileBytes, $searchBytes[0], $index)
if ($foundIndex -eq -1) {
break
}
$match = $true
for ($i = 1; $i -lt $searchLength; $i++) {
if ($fileBytes[$foundIndex + $i] -ne $searchBytes[$i]) {
$match = $false
break
}
}
if ($match) {
[Array]::Copy($replaceBytes, 0, $fileBytes, $foundIndex, $searchLength)
$index = $foundIndex + $searchLength
} else {
$index = $foundIndex + 1
}
}
[System.IO.File]::WriteAllBytes($filePath, $fileBytes)
}
try {
SearchAndReplace-HexPatternInBinaryFile -filePath $filePath -searchPattern $searchPattern -replacePattern $replacePattern
Write-Output "Hex pattern replaced successfully in $filePath"
} catch {
Write-Error $_.Exception.Message
exit 1
}
Сохраняем его в файл с расширением .ps1, потом запускаем Powershell и изменяем политику выполнения (иначе получим ошибку при попытке выполнить скрипт)
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
И потом выполняем скрипт примерно так:
.\test.ps1 -filePath "D:\TEMP\file.exe" -searchPattern "E8961A00000FB6D8488D4C2440FF1548" -replacePattern "11111111111111111111111111111111"
Проверил на .exe размером ~250 МБ и эта операция поиска и замены выполнилась примерно за 3-4 секунды. Не так быстро как хотелось бы, за то без сторонних утилит.
=====
UPDATE:
Я немного разобрался, как работает этот код, и немного его улучшил. Теперь он поддерживает передачу нескольких шаблонов, показывает время выполнения поиска и замены, а также выводит более подробную информацию после работы.
Это все еще не дотягивает до функциональности утилит perl и sed в macOS и Linux.
Вы не сможете использовать регулярные выражения типа "[\x00-\xFF]{17}\xEB\x6F\xD3\x01\x18\x00{3}\xFF{3}\xFE" и вы не сможете ограничить количество совпадений для поиска шаблонов.
Конечно, скрипт можно улучшить и добавить эту функциональность, но текущая реализация покрывает мои потребности.
Надеюсь, кто-нибудь улучшит этот код (функциональность и скорость выполнения)
https://gist.github.com/Drovosek01/9d47068365ea0bc...