#Requires -Version 2.0 # Batch apply hex edits # The edits to apply come from a CSV file in the same path # Reference: https://stackoverflow.com/questions/20935356/ # Handle HTTPS [Net.ServicePointManager]::SecurityProtocol = "Tls12, Tls11, Tls, Ssl3" # More cock-smoking boilerplate [System.Reflection.Assembly]::LoadWithPartialName('System.Web.Extensions') # There is [Environment]::Is64BitProcess since PowerShell 3.0 function Get-Architecture { param($FilePath) [int32]$MACHINE_OFFSET = 4 [int32]$PE_POINTER_OFFSET = 60 [byte[]]$data = New-Object -TypeName System.Byte[] -ArgumentList 4096 $stream = New-Object -TypeName System.IO.FileStream -ArgumentList ($FilePath, 'Open', 'Read') $stream.Read($data, 0, 4096) | Out-Null [int32]$PE_HEADER_ADDR = [System.BitConverter]::ToInt32($data, $PE_POINTER_OFFSET) [int32]$machineUint = [System.BitConverter]::ToUInt16($data, $PE_HEADER_ADDR + $MACHINE_OFFSET) $result = "" switch ($machineUint) { 0 { $result = "Native" } 0x014c { $result = "x86" } 0x0200 { $result = "Itanium" } 0x8664 { $result = "x64" } } $result } # TODO Add warning about executing code from the Internet function Get-Data { param ( [Parameter(Mandatory = $True)] [String] $pathstring ) $abspath = ConvertTo-AbsoluteUri($pathstring) $url = [System.Uri]::New($abspath) $domain = $url.Host If ($domain -match '^(github\.com|gist\.github|(cdn\.)?rawgit\.com)') { $user = $url.Segments[1].Replace('/', '') $id = $url.Segments[2].Replace('/', '') $client = New-Object System.Net.WebClient # Encourage your mom. It's fucking mandatory. # Otherwise you get protocol violation error $client.Headers.Add('Accept', 'application/vnd.github.v3+json') # User-Agent also fucking mandatory. Suck cock, GitHub, gimme the fucking data $client.Headers.Add('User-Agent', 'PowerShell') $json = $client.DownloadString('https://api.github.com/gists/' + $id) $ser = New-Object System.Web.Script.Serialization.JavaScriptSerializer $obj = $ser.DeserializeObject($json) # TODO WTF $dataurl = $obj.files.Keys | Where-Object { $obj.files.Item($_).language -eq 'CSV' } | Select -First 1 | % { $obj.files.Item($_).raw_url } Return $dataurl } Else { # TODO Prompt user Return $Null } } # Take care of short URL redirection function ConvertTo-AbsoluteUri { param ( [String] $pathstring ) Return [System.Net.HttpWebRequest]::Create($pathstring).GetResponse().ResponseUri.AbsoluteUri } # Work in TEMP directory Set-Location $env:TEMP # Get URL from invocation and build CSV URL $RemotePath = Get-Data($_) # Store in user Temp folder with a random filename to avoid clashes $LocalPath = $env:TEMP + '\' + [System.IO.Path]::GetRandomFileName() # Request the file from the Gist (New-Object System.Net.WebClient).DownloadFile($RemotePath, $LocalPath) # Read the data from the CSV file $entries = Import-Csv $LocalPath | Where-Object {$_.Platform -eq "Windows"} # Get the program to work on $target = $entries.Filename | Select-Object -First 1 # Try to find the target program on PATH $program = Get-Command -ErrorAction SilentlyContinue $target | Get-Item # If not ask user to input the path manually while ($program -eq $null) { $userinput = Read-Host "Enter path for executable to patch" If (Test-Path $userinput -Include *.exe) { $program = Get-Item -Path $userinput } else { Write-Error "Invalid file path, re-enter." $program = $null } } $name = $program.VersionInfo.ProductName $build = $program.VersionInfo.ProductVersion $arch = Get-Architecture $program # Get the edits to apply for your build version and architecture $edits = $entries | Where-Object {$_.Build -eq $build -and $_.Architecture -eq $arch} If ($edits) { # Kill if running Start-Process -FilePath tskill -ArgumentList $program.BaseName ` -NoNewWindow -PassThru -Wait -RedirectStandardError nul | Out-Null # Backup try { $program.CopyTo($([IO.Path]::ChangeExtension($program, '.bak')), $false) | Out-Null } catch { Write-Output $error[0].Exception.InnerException } finally { Write-Output "Original file backed up" } $bytes = [System.IO.File]::ReadAllBytes($program) Write-Output "Patching $name build $build (Windows $arch)" For ($i=0; $i -lt $edits.length; $i++) { Write-Output "[$($i+1) of $($edits.length)] $($edits[$i].Offset) $($edits[$i].Original) > $($edits[$i].Modified)" # Patch $bytes[$edits[$i].Offset] = $edits[$i].Modified } # Write new bytes to file # Will fail if you have it selected in Explorer! Just try again. [System.IO.File]::WriteAllBytes($program, $bytes) # Extra: Output new program hash $hasher = [System.Security.Cryptography.HashAlgorithm]::Create('SHA256') $hash = $hasher.ComputeHash([System.IO.File]::ReadAllBytes($program)) $hashString = [System.BitConverter]::ToString($hash).Replace('-', '') Write-Output "Patched!" "New hash: $hashString" } Else { Write-Error "Could not locate patches." }