|
#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 "sublime.csv" |
|
} |
|
} |
|
|
|
# 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($_) |
|
$RemotePath = Get-Data("sublime.csv") # DEBUG |
|
# 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 |
|
# Will say it's in use by another program if not |
|
Start-Sleep 2 |
|
} |
|
|
|
# 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." |
|
} |
3190?