elhacker.net cabecera Bienvenido(a), Visitante. Por favor Ingresar o Registrarse
¿Perdiste tu email de activación?.


Tema destacado: Entrar al Canal Oficial Telegram de elhacker.net


+  Foro de elhacker.net
|-+  Programación
| |-+  Scripting
| | |-+  [APORTE] [PowerShell] Modificar aleatoriamente el checksum de un archivo ejecutable
0 Usuarios y 1 Visitante están viendo este tema.
Páginas: [1] Ir Abajo Respuesta Imprimir
Autor Tema: [APORTE] [PowerShell] Modificar aleatoriamente el checksum de un archivo ejecutable  (Leído 2,471 veces)
Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.966



Ver Perfil
[APORTE] [PowerShell] Modificar aleatoriamente el checksum de un archivo ejecutable
« en: 21 Septiembre 2025, 15:07 pm »

El siguiente script, desarrollado en PowerShell, sirve para modificar de forma aleatoria el checksum actual de un archivo ejecutable (formato PE).

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á.

24 de septiembre de 2025: Script actualizado para identificar archivos PE que contienen una tabla de certificados y abortar la operación de inmediato, evitando así la posible corrupción del ejecutable.

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
  1. <#PSScriptInfo
  2. .VERSION 1.1
  3. .GUID A1B2C3D4-E5F6-7890-ABCD-EF1234567890
  4. .AUTHOR ElektroStudios
  5. .COMPANYNAME ElektroStudios
  6. .COPYRIGHT ElektroStudios © 2025
  7. #>
  8.  
  9. <#
  10. ===========================================================================================
  11. |                                                                                         |
  12. |                                       User Fields                                       |
  13. |                                                                                         |
  14. ===========================================================================================
  15. #>
  16.  
  17. # Path to the executable file to randomize its header and file checksums.
  18. $exePath = ".\MyExecutable.exe"
  19.  
  20. <#
  21. ===========================================================================================
  22. |                                                                                         |
  23. |                                        .NET Code                                        |
  24. |                                                                                         |
  25. ===========================================================================================
  26. #>
  27.  
  28. Add-Type @"
  29. using System;
  30. using System.Runtime.InteropServices;
  31.  
  32. public static class NativeMethods {
  33.    [DllImport("imagehlp.dll", SetLastError = true)]
  34.    public static extern uint MapFileAndCheckSum(string filename, out uint headerSum, out uint checkSum);
  35. }
  36. "@
  37.  
  38. Add-Type -TypeDefinition @"
  39. using System;
  40.  
  41. public class CRC32
  42. {
  43.    private static readonly uint[] Table = new uint[256];
  44.    private uint crc;
  45.  
  46.    static CRC32()
  47.    {
  48.        uint poly = 0xEDB88320;
  49.        for (uint i = 0; i < 256; i++)
  50.        {
  51.            uint temp = i;
  52.            for (int j = 0; j < 8; j++)
  53.            {
  54.                if ((temp & 1) == 1)
  55.                    temp = (temp >> 1) ^ poly;
  56.                else
  57.                    temp >>= 1;
  58.            }
  59.            Table[i] = temp;
  60.        }
  61.    }
  62.  
  63.    public CRC32()
  64.    {
  65.        crc = 0xFFFFFFFF;
  66.    }
  67.  
  68.    public void Update(byte[] buffer, int offset, int count)
  69.    {
  70.        for (int i = offset; i < offset + count; i++)
  71.        {
  72.            byte index = (byte)((crc ^ buffer[i]) & 0xFF);
  73.            crc = (crc >> 8) ^ Table[index];
  74.        }
  75.    }
  76.  
  77.    public uint Compute(byte[] buffer)
  78.    {
  79.        Update(buffer, 0, buffer.Length);
  80.        return crc ^ 0xFFFFFFFF;
  81.    }
  82.  
  83.    public static uint ComputeChecksum(byte[] buffer)
  84.    {
  85.        CRC32 crc32 = new CRC32();
  86.        return crc32.Compute(buffer);
  87.    }
  88. }
  89. "@
  90.  
  91. <#
  92. ===========================================================================================
  93. |                                                                                         |
  94. |                                    Functions                                            |
  95. |                                                                                         |
  96. ===========================================================================================
  97. #>
  98.  
  99. function Show-WelcomeScreen {
  100.    Clear-Host
  101.    Write-Host ""
  102.    Write-Host " $($host.ui.RawUI.WindowTitle)"
  103.    Write-Host " +================================================================+"
  104.    Write-Host " |                                                                |"
  105.    Write-Host " | This script modifies the file checksum of the specified        |"
  106.    Write-Host " | executable file (PE format) by injecting a new random header   |"
  107.    Write-Host " | checksum field and appending random padding bytes to alter the |"
  108.    Write-Host " | file checksum without affecting the executable's functionality.|"
  109.    Write-Host " |                                                                |"
  110.    Write-Host " | This process is useful to bypass simple file integrity checks  |"
  111.    Write-Host " | or signature verifications that rely on fixed checksum values, |"
  112.    Write-Host " | such as those implemented by anti-cheat videogaming systems    |"
  113.    Write-Host " | and similar software protection agents.                        |"
  114.    Write-Host " +================================================================+"
  115.    Write-Host ""
  116.    Write-Host " Script Settings " -ForegroundColor DarkGray
  117.    Write-Host " ================" -ForegroundColor DarkGray
  118.    Write-Host " Executable Path: $([System.IO.Path]::GetFullPath($exePath))" -ForegroundColor DarkGray
  119.    Write-Host ""
  120. }
  121.  
  122. function Confirm-Continue {
  123.    Write-Host "Press 'Y' key to continue or 'N' to exit."
  124.    Write-Host ""
  125.    Write-Host "-Continue? (Y/N)"
  126.    do {
  127.        $key = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
  128.        $char = $key.Character.ToString().ToUpper()
  129.        if ($char -ne "Y" -and $char -ne "N") {
  130.            [console]::beep(1500, 500)
  131.        }
  132.    } while ($char -ne "Y" -and $char -ne "N")
  133.    if ($char -eq "N") {Exit(0)} else {Clear-Host}
  134. }
  135.  
  136. function Read-UInt16($bytes, $offset) {
  137.    if ($offset -lt 0 -or $offset + 2 -gt $bytes.Length) {
  138.        throw "Offset $offset out of range for Read-UInt16"
  139.    }
  140.    return [BitConverter]::ToUInt16($bytes, $offset)
  141. }
  142.  
  143. function Read-UInt32($bytes, $offset) {
  144.    if ($offset -lt 0 -or $offset + 4 -gt $bytes.Length) {
  145.        throw "Offset $offset out of range for Read-UInt32"
  146.    }
  147.    return [BitConverter]::ToUInt32($bytes, $offset)
  148. }
  149.  
  150. function Write-UInt32([byte[]]$bytes, $offset, [uint32]$value) {
  151.    if ($offset -lt 0 -or $offset + 4 -gt $bytes.Length) {
  152.        throw "Offset $offset out of range for Write-UInt32"
  153.    }
  154.    $valBytes = [BitConverter]::GetBytes($value)
  155.    [Array]::Copy($valBytes, 0, $bytes, $offset, 4)
  156. }
  157.  
  158. function Randomize-ExecutableChecksum{
  159.    param(
  160.        [string]$exePath
  161.    )
  162.  
  163.    # Read the raw file bytes
  164.    if (-Not (Test-Path $exePath)) {
  165.        Show-GoodbyeScreen "File path '$([System.IO.Path]::GetFullPath($exePath))' does not exist." ([System.ConsoleColor]::Red)
  166.    }
  167.    [byte[]]$bytes = [System.IO.File]::ReadAllBytes($exePath)
  168.  
  169.    # Locate e_lfanew (pointer to PE header)
  170.    $e_lfanew = Read-UInt32 $bytes 0x3C
  171.    # Check that e_lfanew is within valid range (at least 4 bytes from the end)
  172.    if ($e_lfanew -ge $bytes.Length - 4) {
  173.        Show-GoodbyeScreen "Invalid e_lfanew or corrupt file." ([System.ConsoleColor]::Red)
  174.    }
  175.  
  176.    # Read and validate the PE signature (should be "PE\0\0") at the offset pointed by e_lfanew
  177.    $peSignature = [System.Text.Encoding]::ASCII.GetString($bytes, $e_lfanew, 4)
  178.    if ($peSignature -ne "PE`0`0") {
  179.        Show-GoodbyeScreen "Not a valid PE file (PE signature not found)." ([System.ConsoleColor]::Red)
  180.    }
  181.  
  182.    # Calculate Optional Header offset (PE signature + File Header)
  183.    $optionalHeaderOffset = $e_lfanew + 4 + 20
  184.  
  185.    # Read Magic field in the Optional Header that determines PE32/PE32+
  186.    $magic = Read-UInt16 $bytes $optionalHeaderOffset
  187.    if ($magic -eq 0x10b) {
  188.        # PE32 (32-bit)
  189.        $checksumOffset = $optionalHeaderOffset + 64
  190.        $dataDirectoryOffset = $optionalHeaderOffset + 96
  191.    } elseif ($magic -eq 0x20b) {
  192.        # PE32+ (64-bit)
  193.        $checksumOffset = $optionalHeaderOffset + 64
  194.        $dataDirectoryOffset = $optionalHeaderOffset + 112
  195.    } else {
  196.        Show-GoodbyeScreen ("Unknown or unsupported PE format. Magic = 0x{0:X}" -f $magic) ([System.ConsoleColor]::Red)
  197.    }
  198.  
  199.    # Read Certificate Table directory (DataDirectory[4])
  200.    $certSize = Read-UInt32 $bytes ($dataDirectoryOffset + 4*8 + 4) # IMAGE_DATA_DIRECTORY::Size
  201.    if ($certSize -gt 0) {
  202.        Show-GoodbyeScreen "This PE file has a certificate table. Operation aborted to avoid breaking PE signature." ([System.ConsoleColor]::Red)
  203.    }
  204.  
  205.    # Calculate the actual CRC32 file checksum
  206.    $currentCRC32 = '{0:x8}' -f ([CRC32]::ComputeChecksum($bytes))
  207.  
  208.    # Calculate the actual header and file checksums using Windows API
  209.    [uint32]$currentHeaderSum = 0
  210.    [uint32]$currentFileSum = 0
  211.    [NativeMethods]::MapFileAndCheckSum($exePath, [ref]$currentHeaderSum, [ref]$currentFileSum) | Out-Null
  212.  
  213.    # Generate a new random header checksum value
  214.    $randomBytes = New-Object byte[] 4
  215.    [System.Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($randomBytes)
  216.    $newHeaderSum = [BitConverter]::ToUInt32($randomBytes, 0)
  217.  
  218.    # Write the new random header checksum value into the file bytes
  219.    Write-UInt32 $bytes $checksumOffset $newHeaderSum
  220.  
  221.    # Add useless padding bytes at the end (overlay) of the file bytes to generate a new file checksum
  222.    $paddingLength = Get-Random -Minimum 4 -Maximum 16
  223.    $padding = New-Object byte[] $paddingLength
  224.    [Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($padding)
  225.    $newBytes = New-Object byte[] ($bytes.Length + $paddingLength)
  226.    [Array]::Copy($bytes, 0, $newBytes, 0, $bytes.Length)
  227.    [Array]::Copy($padding, 0, $newBytes, $bytes.Length, $paddingLength)
  228.  
  229.    # Calculate the new CRC32 file checksum
  230.    $newCRC32 = '{0:x8}' -f ([CRC32]::ComputeChecksum($newBytes))
  231.  
  232.    Write-Host "========== CHECKSUM INFORMATION ==========" -ForegroundColor Cyan
  233.    Write-Host "File: $exePath" -ForegroundColor Yellow
  234.    Write-Host ""
  235.    Write-Host "Header checksum field" -ForegroundColor Cyan
  236.    Write-Host "=====================" -ForegroundColor Gray
  237.    Write-Host "Current    : $currentHeaderSum" -ForegroundColor White
  238.    Write-Host "Replacement: $newHeaderSum" -ForegroundColor White
  239.    Write-Host ""
  240.    Write-Host "Calculated CRC-32 file checksum" -ForegroundColor Cyan
  241.    Write-Host "===============================" -ForegroundColor Gray
  242.    Write-Host "Current    : 0x$($currentCRC32.ToUpper())" -ForegroundColor White
  243.    Write-Host "Replacement: 0x$($newCRC32.ToUpper())" -ForegroundColor White
  244.    Write-Host ""
  245.    Write-Host "Calculated file checksum via 'MapFileAndCheckSum' API" -ForegroundColor Cyan
  246.    Write-Host "=====================================================" -ForegroundColor Gray
  247.    Write-Host "Current    : $currentFileSum" -ForegroundColor White
  248.    Write-Host "Replacement: Can't be calculated until the" -ForegroundColor White
  249.    Write-Host "             new header checksum field is" -ForegroundColor White
  250.    Write-Host "             written to the actual file." -ForegroundColor White
  251.    Write-Host ""
  252.    Write-Host "-----------------------------------------------------" -ForegroundColor Gray
  253.    Write-Host ""
  254.  
  255.    Confirm-Continue
  256.  
  257.    try {
  258.        # Replace the source file by writing the changed bytes, containing the new header checksum and the padding bytes.
  259.        [System.IO.File]::WriteAllBytes($exePath, $newBytes)
  260.    } catch {
  261.        Show-GoodbyeScreen "Failed to write the modified bytes to the actual file. Please check file permissions and path." ([System.ConsoleColor]::Red)
  262.    }
  263.  
  264.    # Calculate the new file checksum using Windows API
  265.    [uint32]$newFileSum = 0
  266.    [NativeMethods]::MapFileAndCheckSum($exePath, [ref]0, [ref]$newFileSum) | Out-Null
  267.  
  268.    Write-Host "========== CHECKSUM INFORMATION ==========" -ForegroundColor Cyan
  269.    Write-Host "File: $exePath" -ForegroundColor Yellow
  270.    Write-Host ""
  271.    Write-Host "Header checksum field" -ForegroundColor Cyan
  272.    Write-Host "=====================" -ForegroundColor Gray
  273.    Write-Host "Previous: $currentHeaderSum" -ForegroundColor White
  274.    Write-Host "Current : $newHeaderSum" -ForegroundColor White
  275.    Write-Host ""
  276.    Write-Host "Calculated CRC-32 file checksum" -ForegroundColor Cyan
  277.    Write-Host "===============================" -ForegroundColor Gray
  278.    Write-Host "Previous: 0x$($currentCRC32.ToUpper())" -ForegroundColor White
  279.    Write-Host "Current : 0x$($newCRC32.ToUpper())" -ForegroundColor White
  280.    Write-Host ""
  281.    Write-Host "Calculated file checksum via 'MapFileAndCheckSum' API" -ForegroundColor Cyan
  282.    Write-Host "=====================================================" -ForegroundColor Gray
  283.    Write-Host "Previous: $currentFileSum" -ForegroundColor White
  284.    Write-Host "Current : $newFileSum" -ForegroundColor White
  285.    Write-Host ""
  286.    Write-Host "-----------------------------------------------------" -ForegroundColor Gray
  287.    Write-Host ""
  288. }
  289.  
  290. function Show-GoodbyeScreen{
  291.    param(
  292.        [string]$msg,
  293.        [string]$forecolor = "White"
  294.    )
  295.  
  296.    Write-Host $msg -BackgroundColor Black -ForegroundColor $forecolor
  297.    Write-Host ""
  298.    Write-Host "Press any key to exit..."
  299.    $key = $Host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown")
  300.    Exit(0)
  301. }
  302.  
  303. <#
  304. ===========================================================================================
  305. |                                                                                         |
  306. |                                         Main                                            |
  307. |                                                                                         |
  308. ===========================================================================================
  309. #>
  310.  
  311. [System.Console]::Title = "Randomize executable checksum - by Elektro"
  312. #[System.Console]::SetWindowSize(150, 45)
  313. [CultureInfo]::CurrentUICulture = "en-US"
  314.  
  315. if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
  316.    Write-Host "Please run this script as Administrator!" -ForegroundColor Red
  317.    Pause
  318.    exit
  319. }
  320.  
  321. try { Set-ExecutionPolicy -ExecutionPolicy "Unrestricted" -Scope "Process" } catch { }
  322.  
  323. Show-WelcomeScreen
  324. Confirm-Continue
  325. Randomize-ExecutableChecksum $exePath
  326. 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
  1. <#
  2. ===========================================================================================
  3. |                                                                                         |
  4. |                                       User Fields                                       |
  5. |                                                                                         |
  6. ===========================================================================================
  7. #>
  8.  
  9. # Path to the executable file to randomize its header and file checksums.
  10. $exePath = ".\MyExecutable.exe"

Atentamente,
Elektro. 👋


« Última modificación: 24 Septiembre 2025, 06:49 am por Eleкtro » En línea



W17CH3R

Desconectado Desconectado

Mensajes: 125



Ver Perfil
Re: [APORTE] [PowerShell] Modificar aleatoriamente el checksum de un archivo ejecutable
« Respuesta #1 en: 23 Septiembre 2025, 20:01 pm »

Gracias por el aporte, y también por el aviso de archivos PE que tengan un certificado digital puedan corromperse, lo testeare en una máquina virtual con Windows.

De nuevo, excelente aporte. :)


En línea

La importancia de los Datos es la importancia de los Recuerdos.

Mi perfil de Github: https://github.com/W17CHeR
Eleкtro
Ex-Staff
*
Desconectado Desconectado

Mensajes: 9.966



Ver Perfil
Re: [APORTE] [PowerShell] Modificar aleatoriamente el checksum de un archivo ejecutable
« Respuesta #2 en: 24 Septiembre 2025, 06:53 am »

Gracias por el aporte, y también por el aviso de archivos PE que tengan un certificado digital puedan corromperse

Gracias a ti por el comentario.

He actualizado el script para identificar archivos PE que contienen una tabla de certificados y abortar la operación de inmediato, evitando así la posible corrupción del ejecutable.

El problema radica en que el certificado digital de un archivo PE se ubica (o debería ubicarse) al final del flujo de datos del archivo. En un PE firmado, la tabla de certificados tiene un tamaño fijo que se define dentro del propio PE, y la firma puede incluir un hash del archivo (llamémosle digest o 'image hash'): https://learn.microsoft.com/es-es/windows/win32/debug/pe-format#certificate-data - que se calcula tomando en cuenta las partes del PE excepto la zona ocupada por el certificado.

Por ese motivo, cualquier byte de relleno que se agregue al final del archivo se considerará parte de la zona excluida del certificado, y esto provoca que el digest (image hash) calculado del PE ya no coincida con el almacenado en la firma, lo que hace que Windows muestre un error genérico al intentar ejecutar el archivo: "No se puede ejecutar esta aplicación en el equipo."



Para hacerlo correctamente con archivos PE firmados habría que añadir una cantidad de padding al final de la tabla de certificados (al final del archivo) pero manteniendo una alineación de 8 bytes, es decir, añadir entre 1 a 8 bytes hasta alinear la tabla, y asignar un nuevo tamaño de la tabla de certificados dentro del PE para incluir este padding y que así los bytes de relleno se excluyan del cálculo del digest o image hash y Windows no lance un error al intentar ejecutar el archivo.

Eso me resultaría tedioso implementarlo con PowerShell. Sin embargo, y por si te sirve de algo a ti o alguna otra persona interesada, compartí un código en VB.NET que precisamente sirve para hacer todo esto, permitiendo adjuntar cualquier cosa (como bytes de relleno) al final de la tabla de certificado de un archivo PE.

El código es este: https://foro.elhacker.net/net_c_vbnet_asp/libreria_de_snippets_para_vbnet_compartan_aqui_sus_snippets-t378770.0.html;msg2285007#msg2285007
(el código completo está repartido en tres posts a partir de ese primer post.)

Con ese código en VB.NET se puede alterar el cálculo del file checksum de un archivo PE firmado digitalmente, sin corromperlo. Se usaría de la siguiente manera para añadir, por ejemplo, dos bytes de relleno a la tabla de certificados (sin contar los bytes de relleno que se añadirán automáticamente para mantener la tabla de certificados alineada):

Código
  1. Dim inputFilePath As String = ".\executable.exe"
  2. Dim outputFilePath As String = ".\executable_patched.exe"
  3. Dim padding As Byte() = {&H0, &H0}
  4.  
  5. AppendBlobToPECertificateTable(inputFilePath, outputFilePath, padding)

Sin embargo, en algunos casos hay que seguir teniendo especial cuidado:

me he topado con software comercial de terceros que parecen hacer sus propias comprobaciones de integridad del archivo ejecutable, por lo que al modificar el PE, dan un error genérico al intentar iniciar el programa. En conclusión, hay que verificar que el archivo ejecutable modificado funcione correctamente, sin asumir nada.

Un saludo.
« Última modificación: 24 Septiembre 2025, 07:21 am por Eleкtro » En línea



Páginas: [1] Ir Arriba Respuesta Imprimir 

Ir a:  

WAP2 - Aviso Legal - Powered by SMF 1.1.21 | SMF © 2006-2008, Simple Machines