Este procedimiento podría ser útil, por ejemplo, para evitar comprobaciones simples de integridad de archivos o verificaciones de firma que dependen de valores de checksum fijos, como los que pudieran estar implementados por sistemas anti-trampas en videojuegos y otro software de protección.
Para cumplir con este objetivo, el script cubre dos técnicas combinadas. La primera consiste en inyectar un valor aleatorio en el campo opcional de checksum del encabezado PE. Esta técnica es necesaria para alterar el resultado de la validación de la API de Windows 'MapFileAndCheckSum', ya que no calcula el checksum de forma corriente sobre todo el archivo, sino que utiliza el valor presente en ese campo del PE.
La segunda técnica consiste en agregar una cantidad pequeña de bytes de relleno o padding (entre 4 y 16 bytes) al final del archivo para alterar el tamaño del mismo, y por consiguiente el cálculo del checksum del archivo, sin afectar a la funcionalidad del ejecutable.



El mismo archivo "reabierto" tras la modificación:

⚠️ Importante: no utilizar el script con archivos PE que tengan un certificado digital, ya que el archivo se corromperá.
Aunque considero haber probado el código lo suficiente, no me hago responsable de posibles daños causados al intentar modificar un archivo. Hagan siempre una copia de seguridad antes de modificar un archivo. 👍
Randomize executable checksum.ps1
El código fuente
Código
<#PSScriptInfo .VERSION 1.0 .GUID A1B2C3D4-E5F6-7890-ABCD-EF1234567890 .AUTHOR ElektroStudios .COMPANYNAME ElektroStudios .COPYRIGHT ElektroStudios © 2025 #> <# =========================================================================================== | | | User Fields | | | =========================================================================================== #> # Path to the executable file to randomize its header and file checksums. $exePath = ".\MyExecutable.exe" <# =========================================================================================== | | | .NET Code | | | =========================================================================================== #> Add-Type @" using System; using System.Runtime.InteropServices; public static class NativeMethods { [DllImport("imagehlp.dll", SetLastError = true)] public static extern uint MapFileAndCheckSum(string Filename, out uint HeaderSum, out uint CheckSum); } "@ Add-Type -TypeDefinition @" using System; public class CRC32 { private static readonly uint[] Table = new uint[256]; private uint crc; static CRC32() { uint poly = 0xEDB88320; for (uint i = 0; i < 256; i++) { uint temp = i; for (int j = 0; j < 8; j++) { if ((temp & 1) == 1) temp = (temp >> 1) ^ poly; else temp >>= 1; } Table[i] = temp; } } public CRC32() { crc = 0xFFFFFFFF; } public void Update(byte[] buffer, int offset, int count) { for (int i = offset; i < offset + count; i++) { byte index = (byte)((crc ^ buffer[i]) & 0xFF); crc = (crc >> 8) ^ Table[index]; } } public uint Compute(byte[] buffer) { Update(buffer, 0, buffer.Length); return crc ^ 0xFFFFFFFF; } public static uint ComputeChecksum(byte[] buffer) { CRC32 crc32 = new CRC32(); return crc32.Compute(buffer); } } "@ <# =========================================================================================== | | | Functions | | | =========================================================================================== #> function Show-WelcomeScreen { Clear-Host Write-Host "" Write-Host " $($host.ui.RawUI.WindowTitle)" Write-Host " +================================================================+" Write-Host " | |" Write-Host " | This script modifies the file checksum of the specified |" Write-Host " | executable file (PE format) by injecting a new random header |" Write-Host " | checksum field and appending random padding bytes to alter the |" Write-Host " | file checksum without affecting the executable's functionality.|" Write-Host " | |" Write-Host " | This process is useful to bypass simple file integrity checks |" Write-Host " | or signature verifications that rely on fixed checksum values, |" Write-Host " | such as those implemented by anti-cheat videogaming systems |" Write-Host " | and similar software protection agents. |" Write-Host " +================================================================+" Write-Host "" Write-Host " Script Settings " -ForegroundColor DarkGray Write-Host " ================" -ForegroundColor DarkGray Write-Host " Executable Path: $([System.IO.Path]::GetFullPath($exePath))" -ForegroundColor DarkGray Write-Host "" } function Confirm-Continue { Write-Host "Press 'Y' key to continue or 'N' to exit." Write-Host "" Write-Host "-Continue? (Y/N)" do { $key = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown") $char = $key.Character.ToString().ToUpper() if ($char -ne "Y" -and $char -ne "N") { [console]::beep(1500, 500) } } while ($char -ne "Y" -and $char -ne "N") if ($char -eq "N") {Exit(0)} else {Clear-Host} } function Read-UInt16($bytes, $offset) { if ($offset -lt 0 -or $offset + 2 -gt $bytes.Length) { throw "Offset $offset out of range for Read-UInt16" } } function Read-UInt32($bytes, $offset) { if ($offset -lt 0 -or $offset + 4 -gt $bytes.Length) { throw "Offset $offset out of range for Read-UInt32" } } function Write-UInt32([byte[]]$bytes, $offset, [uint32]$value) { if ($offset -lt 0 -or $offset + 4 -gt $bytes.Length) { throw "Offset $offset out of range for Write-UInt32" } $valBytes = [BitConverter]::GetBytes($value) [Array]::Copy($valBytes, 0, $bytes, $offset, 4) } function Randomize-ExecutableChecksum{ param( [string]$exePath ) # Read the raw file bytes if (-Not (Test-Path $exePath)) { Show-GoodbyeScreen "File path '$([System.IO.Path]::GetFullPath($exePath))' does not exist." ([System.ConsoleColor]::Red) } [byte[]]$bytes = [System.IO.File]::ReadAllBytes($exePath) # Calculate the actual CRC32 file checksum $currentCRC32 = '{0:x8}' -f ([CRC32]::ComputeChecksum($bytes)) # Calculate the actual header and file checksums using Windows API [uint32]$currentHeaderSum = 0 [uint32]$currentFileSum = 0 # Generate a new random header checksum value $randomBytes = New-Object byte[] 4 [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($randomBytes) $newHeaderSum = [BitConverter]::ToUInt32($randomBytes, 0) # Locate e_lfanew (pointer to PE header) $e_lfanew = Read-UInt32 $bytes 0x3C # Check that e_lfanew is within valid range (at least 4 bytes from the end) if ($e_lfanew -ge $bytes.Length - 4) { Show-GoodbyeScreen "Invalid e_lfanew or corrupt file." ([System.ConsoleColor]::Red) } # Read and validate the PE signature (should be "PE\0\0") at the offset pointed by e_lfanew $peSignature = [System.Text.Encoding]::ASCII.GetString($bytes, $e_lfanew, 4) if ($peSignature -ne "PE`0`0") { Show-GoodbyeScreen "Not a valid PE file (PE signature not found)." ([System.ConsoleColor]::Red) } # Calculate Optional Header offset (PE signature + File Header) $optionalHeaderOffset = $e_lfanew + 4 + 20 # Read the Magic field in the Optional Header to determine PE32 (32-bit) or PE32+ (64-bit) $magic = Read-UInt16 $bytes $optionalHeaderOffset if ($magic -eq 0x10b -or $magic -eq 0x20b) { $checksumOffset = $optionalHeaderOffset + 64 } else { Show-GoodbyeScreen ("Unknown or unsupported PE format. Magic = 0x{0:X}" -f $magic) ([System.ConsoleColor]::Red) } # Write the new random header checksum value into the file bytes Write-UInt32 $bytes $checksumOffset $newHeaderSum # Add useless padding bytes at the end (overlay) of the file bytes to generate a new file checksum $paddingLength = Get-Random -Minimum 4 -Maximum 16 $padding = New-Object byte[] $paddingLength [Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($padding) $newBytes = New-Object byte[] ($bytes.Length + $paddingLength) [Array]::Copy($bytes, 0, $newBytes, 0, $bytes.Length) [Array]::Copy($padding, 0, $newBytes, $bytes.Length, $paddingLength) # Calculate the new CRC32 file checksum $newCRC32 = '{0:x8}' -f ([CRC32]::ComputeChecksum($newBytes)) Write-Host "========== CHECKSUM INFORMATION ==========" -ForegroundColor Cyan Write-Host "File: $exePath" -ForegroundColor Yellow Write-Host "" Write-Host "Header checksum field" -ForegroundColor Cyan Write-Host "=====================" -ForegroundColor Gray Write-Host "Current : $currentHeaderSum" -ForegroundColor White Write-Host "Replacement: $newHeaderSum" -ForegroundColor White Write-Host "" Write-Host "Calculated CRC-32 file checksum" -ForegroundColor Cyan Write-Host "===============================" -ForegroundColor Gray Write-Host "Current : 0x$($currentCRC32.ToUpper())" -ForegroundColor White Write-Host "Replacement: 0x$($newCRC32.ToUpper())" -ForegroundColor White Write-Host "" Write-Host "Calculated file checksum via 'MapFileAndCheckSum' API" -ForegroundColor Cyan Write-Host "=====================================================" -ForegroundColor Gray Write-Host "Current : $currentFileSum" -ForegroundColor White Write-Host "Replacement: Can't be calculated until the" -ForegroundColor White Write-Host " new header checksum field is" -ForegroundColor White Write-Host " written to the actual file." -ForegroundColor White Write-Host "" Write-Host "-----------------------------------------------------" -ForegroundColor Gray Write-Host "" Confirm-Continue try { # Replace the source file by writing the changed bytes, containing the new header checksum and the padding bytes. [System.IO.File]::WriteAllBytes($exePath, $newBytes) } catch { Show-GoodbyeScreen "Failed to write the modified bytes to the actual file. Please check file permissions and path." ([System.ConsoleColor]::Red) } # Calculate the new file checksum using Windows API [uint32]$newFileSum = 0 Write-Host "========== CHECKSUM INFORMATION ==========" -ForegroundColor Cyan Write-Host "File: $exePath" -ForegroundColor Yellow Write-Host "" Write-Host "Header checksum field" -ForegroundColor Cyan Write-Host "=====================" -ForegroundColor Gray Write-Host "Previous: $currentHeaderSum" -ForegroundColor White Write-Host "Current : $newHeaderSum" -ForegroundColor White Write-Host "" Write-Host "Calculated CRC-32 file checksum" -ForegroundColor Cyan Write-Host "===============================" -ForegroundColor Gray Write-Host "Previous: 0x$($currentCRC32.ToUpper())" -ForegroundColor White Write-Host "Current : 0x$($newCRC32.ToUpper())" -ForegroundColor White Write-Host "" Write-Host "Calculated file checksum via 'MapFileAndCheckSum' API" -ForegroundColor Cyan Write-Host "=====================================================" -ForegroundColor Gray Write-Host "Previous: $currentFileSum" -ForegroundColor White Write-Host "Current : $newFileSum" -ForegroundColor White Write-Host "" Write-Host "-----------------------------------------------------" -ForegroundColor Gray Write-Host "" } function Show-GoodbyeScreen{ param( [string]$msg, [string]$forecolor = "White" ) Write-Host $msg -BackgroundColor Black -ForegroundColor $forecolor Write-Host "" Write-Host "Press any key to exit..." $key = $Host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown") Exit(0) } <# =========================================================================================== | | | Main | | | =========================================================================================== #> [System.Console]::Title = "Randomize executable checksum - by Elektro" #[System.Console]::SetWindowSize(150, 45) [CultureInfo]::CurrentUICulture = "en-US" if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Host "Please run this script as Administrator!" -ForegroundColor Red Pause } try { Set-ExecutionPolicy -ExecutionPolicy "Unrestricted" -Scope "Process" } catch { } Show-WelcomeScreen Confirm-Continue Randomize-ExecutableChecksum $exePath Show-GoodbyeScreen "Operation Completed!" ([System.ConsoleColor]::Green)
Aspectos a tener en cuenta antes de usar
La configuración del script está definida directamente en el código y no admite parámetros por línea de comandos.
Citar
Código
<# =========================================================================================== | | | User Fields | | | =========================================================================================== #> # Path to the executable file to randomize its header and file checksums. $exePath = ".\MyExecutable.exe"
Atentamente,
Elektro. 👋