Created
January 11, 2019 16:43
-
-
Save TestItOnlyOnce/e18b61e520293a17388cba17994bbf20 to your computer and use it in GitHub Desktop.
Binary PowerShell patcher (fc /b format); Бинарный патчер [codepage: win-1251]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.КАК ИСПОЛЬЗОВАТЬ | |
0. Разрешите локальные скрипты вбив set-executionpolicy remotesigned в консоли PowerShell ISE или | |
запустив скрипт как powershell -executionpolicy bypass -File "patcher.ps1". | |
1. Скопируйте .fix файл в папку с пропатчиваемой программой. | |
2. Запустите этот скрипт. | |
3. Укажите путь к .fix файлу. | |
Всё... | |
Патчер подходит только для относительно небольших файлов, | |
т.к. в процессе полностью копирует их содержимое в память. | |
.ФОРМАТ .fix ФАЙЛА | |
Первая строка: описание патча одной строкой | |
названиефайла1.эхэ | |
патч1: регэксп(.{3}).по(и)ска1 строка$1\x20замены$2\x01 | |
патч2: регэксп(.{3}).по(и)ска2 строка$1\x20замены$2\x02 | |
адрес1: старыйбайт1 новыйбайт1 | |
адрес2: старыйбайт2 новыйбайт2 | |
.\подкаталог\названиефайла2.эхэ | |
патч1: регэксп(.{3}).по(и)ска1 строка$1\x20замены$2\x01 | |
патч2: регэксп(.{3}).по(и)ска2 строка$1\x20замены$2\x02 | |
адрес1: старыйбайт1 новыйбайт1 | |
адрес2: старыйбайт2 новыйбайт2 | |
Пробелы в регэкспах заменяйте на '\s', т.к. ' ' тут как разделитель между ними. | |
#> | |
param([string]$Fix); | |
#Формат патча -> название: регэксп_поиска строка_замены | |
function patchString | |
{ | |
param ([ref]$StringData, [ref]$PatchLine, [ref]$CounterRef) | |
if ($PatchLine.value[0] -eq "#") { return } | |
if (-Not ($PatchLine.value -match [regex]"([^:]+): ([^\s]+) ([^\s]+)")) { return } | |
$PatchName = $matches[1].Trim() | |
$PatchRegExp = $matches[2].Trim() | |
$Replacer = $matches[3].Trim() | |
Try | |
{ | |
$BytePatch = [Convert]::ToInt32($matches[2], 16) | |
$ByteReplacer = [Convert]::ToInt32($matches[3] , 16) | |
$Address = [Convert]::ToInt64($matches[1], 16) | |
return | |
} | |
Catch | |
{} | |
$PatchRegExp = [Regex] $PatchRegExp | |
$Counter = 0 | |
$matchEvaluator = | |
[System.Text.RegularExpressions.MatchEvaluator] { | |
param($m) | |
$Counter++ | |
write-output ([regex]::Unescape([regex]::Replace($m.value, $PatchRegExp, $Replacer))) | |
} | |
Write-Host ('Патч "' + $PatchLine.value) -nonewline | |
$StringData.value = [regex]::Replace($StringData.value, $PatchRegExp, $matchEvaluator); | |
if ($Counter -eq 0) | |
{ | |
Write-Host '" не применён.' | |
} | |
else | |
{ | |
Write-Host '" применён.' | |
} | |
$CounterRef.value += $Counter | |
} | |
#Формат патча -> адрес: старыйбайт новыйбайт | |
function patchByte | |
{ | |
param ([ref]$ByteArray, [ref]$PatchLine, [ref]$CounterRef) | |
if ($PatchLine.value[0] -eq "#") { return } | |
if (-Not ($PatchLine.value -match [regex]"([^:]+):\s+([a-fA-F0-9]+)\s+([a-fA-F0-9]+)")) { return } | |
Try | |
{ | |
$BytePatch = [Convert]::ToInt32($matches[2], 16) | |
$ByteReplacer = [Convert]::ToInt32($matches[3], 16) | |
$Address = [Convert]::ToInt64($matches[1].Trim(), 16) | |
if (($ByteReplacer -gt 255) -or ($BytePatch -gt 255)) | |
{ | |
Write-Host "Неверный формат патча:"$PatchLine.value | |
return | |
} | |
Write-Host ('Патч "' + $PatchLine.value) -nonewline | |
if ($ByteArray.value[$Address] -eq $BytePatch) | |
{ | |
$CounterRef.value++ | |
$ByteArray.value[$Address] = $ByteReplacer | |
Write-Host '" применён.' | |
} | |
else | |
{ | |
Write-Host '" не применён.' | |
} | |
} | |
Catch | |
{} | |
} | |
function patchFile | |
{ | |
param([ref]$Patch, [int]$index) | |
$patcheeName = ($Patch.value[$index]).Trim() | |
$localPath = "" | |
Write-Host "`nПрименяем патч к"$patcheeName"." | |
$BinaryString = "" | |
Try | |
{ | |
$localPath = [string](Resolve-Path (".\" + $patcheeName)) | |
$BinaryString = [System.IO.File]::ReadAllBytes($localPath); | |
} | |
Catch | |
{ | |
Write-Host "Не найден файл" $patcheeName "для патча." | |
for ($i=$index+1; $i -le $Patch.value.length-1; $i++) | |
{ | |
$Line = $Patch.value[$i].Trim() | |
if ([string]::IsNullOrEmpty($Line)) { continue } | |
if ($Line -match [regex]"^([^#:/$]+)$") | |
{ | |
patchFile $Patch $i | |
return | |
} | |
} | |
return | |
} | |
$Counter = 0 | |
$nextFileIndex = 0 | |
for ($i=$index+1; $i -le $Patch.value.length-1; $i++) | |
{ | |
$Line = $Patch.value[$i].Trim() | |
if ([string]::IsNullOrEmpty($Line)) { continue } | |
if ($Line -match [regex]"^([^#:/$]+)$") { break } | |
patchByte ([ref]$BinaryString) ([ref]$Line) ([ref]$Counter) | |
} | |
$BinaryString = [Text.Encoding]::GetEncoding(28591).GetString($BinaryString); | |
for ($i=$index+1; $i -le $Patch.value.length-1; $i++) | |
{ | |
$Line = $Patch.value[$i].Trim() | |
if ([string]::IsNullOrEmpty($Line)) { continue } | |
if ($Line -match [regex]"^([^#:/$]+)$") | |
{ | |
$nextFileIndex = $i | |
break | |
} | |
patchString ([ref]$BinaryString) ([ref]$Line) ([ref]$Counter) | |
} | |
if ($Counter -gt 0) | |
{ | |
Try | |
{ | |
Rename-Item -Path $localPath -NewName $localPath".bak" | |
} | |
Catch | |
{} | |
[System.IO.File]::WriteAllBytes($localPath, [Text.Encoding]::GetEncoding(28591).GetBytes($BinaryString)); | |
Write-Host "Патч выполнен и бекап сохранён как"$patcheeName".bak." | |
} | |
else | |
{ | |
Write-Host "Файл" $patcheeName "не изменён, проверьте соответствие версий патча и файла." | |
} | |
if ($nextFileIndex -gt 0) | |
{ | |
patchFile $Patch $nextFileIndex | |
} | |
} | |
#Окно выбора файла | |
function openFileDialog | |
{ | |
param([string]$WindowTitle) | |
Add-Type -AssemblyName System.Windows.Forms | |
$openFileDialog = New-Object System.Windows.Forms.OpenFileDialog | |
$openFileDialog.initialDirectory = $MyInvocation.MyCommand.Path | |
$openFileDialog.Title = $WindowTitle | |
$openFileDialog.filter = "Patch files (*.fix)| *.fix" | |
$openFileDialog.ShowDialog() > $null | |
return $openFileDialog.Filename | |
} | |
function doPatch | |
{ | |
param([string]$filePath) | |
Set-Location (Split-Path -Path $filePath) | |
$patchArray = Get-Content $filePath | |
Write-Host "`n[Патч]"$patchArray[0] | |
for ($i=1; $i -le $patchArray.length-1; $i++) | |
{ | |
$patchLine = $patchArray[$i].Trim() | |
if ([string]::IsNullOrEmpty($patchLine)) { continue } | |
patchFile ([ref]$patchArray) $i | |
break | |
} | |
} | |
Write-Host "`nПараметры командной строки (консоль):`n`tpatcher.ps1 [-fix <путь к .fix файлу>]" | |
if ($Fix) | |
{ | |
doPatch -filePath $Fix | |
} | |
else | |
{ | |
Add-Type -AssemblyName System.Windows.Forms | |
$App = New-Object System.Windows.Forms.Form -Property @{ | |
AutoSize = $false | |
MinimizeBox = $false | |
MaximizeBox = $false | |
StartPosition = "CenterScreen" | |
Size = New-Object System.Drawing.Size(180,125) | |
Text = "PowerShell патчер" | |
FormBorderStyle = "FixedDialog" | |
} | |
$Button = New-Object System.Windows.Forms.Button -Property @{ | |
Location = New-Object System.Drawing.Point -Property @{ | |
X = 12 | |
Y = 10 | |
} | |
Size = New-Object System.Drawing.Size(150, 35) | |
Text = "Патчить" | |
} | |
$ButtonExit = New-Object System.Windows.Forms.Button -Property @{ | |
Location = New-Object System.Drawing.Point -Property @{ | |
X = 12 | |
Y = 50 | |
} | |
Size = New-Object System.Drawing.Size(150, 35) | |
Text = "Выход" | |
} | |
$Button.Add_Click({ | |
$Button.Enabled = $false | |
$FixPath = openFileDialog -WindowTitle "Выберите файл патча" | |
if ([string]::IsNullOrEmpty($FixPath)) | |
{ | |
Write-Host "Не выбран файл патча." | |
$Button.Enabled = $true | |
return | |
} | |
doPatch -filePath $FixPath | |
$Button.Enabled = $true | |
}) | |
$ButtonExit.Add_Click({ | |
$App.Close() | |
}) | |
$App.Controls.Add($Button) | |
$App.Controls.Add($ButtonExit) | |
$App.ShowDialog() > $null | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment