es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Transmita solo una parte de un archivo utilizando PowerShell y calcule el hash.

Necesito poder identificar algunos archivos binarios grandes que han sido copiados y renombrados entre servidores seguros. Para hacer esto, me gustaría poder hacer hash de los primeros X bytes y los últimos X bytes de todos los archivos. Necesito hacer esto solo con lo que está disponible en un sistema estándar de Windows 10 sin software adicional instalado, por lo que PowerShell parece ser la opción correcta.

Algunas cosas que no funcionan:

  • No puedo leer todo el archivo y luego extraer las partes del archivo que quiero hashear. El objetivo que estoy tratando de lograr es minimizar la cantidad de archivo que necesito leer, y leer todo el archivo va en contra de ese propósito.
  • Leer porciones moderadamente grandes de un archivo en una variable de PowerShell parece ser bastante lento, por lo que $hash.ComputeHash($moderatelyLargeVariable) no parece ser una solución viable.

Estoy bastante seguro de que necesito hacer $hash.ComputeHash($stream) donde $stream solamente hace streaming de parte del archivo.

Hasta ahora he intentado:

function Get-FileStreamHash {
    param (
        $FilePath,
        $Algorithm
    )

$hash = [Security.Cryptography.HashAlgorithm]::Create($Algorithm)

## METHOD 0: See description below
$stream = ([IO.StreamReader]"${FilePath}").BaseStream
$hashValue = $hash.ComputeHash($stream)
## END of part I need help with

# Convert to a hexadecimal string
$hexHashValue = -join ($hashValue | ForEach-Object { "{0:x2}" -f $_ })
$stream.Close()

# return
$hexHashValue
}

Método 0: Esto funciona, pero está haciendo streaming del archivo completo y, por lo tanto, no resuelve mi problema. Para un archivo de 3GB esto tarda aproximadamente 7 segundos en mi máquina.

Método 1: $hashValue = $hash.ComputeHash((Get-Content -Path $FilePath -Stream "")). Esto también está haciendo streaming del archivo completo y también tarda para siempre. Para el mismo archivo de 3GB toma algo más de 5 minutos (cancelé en ese punto y no sé cuál sería la duración total).

Método 2: $hashValue = $hash.ComputeHash((Get-Content -Path $FilePath -Encoding byte -TotalCount $qtyBytes -Stream "")). Esto es lo mismo que el Método 1, excepto que limita el contenido a $qtyBytes. A 1000000 (1MB) tarda 18 segundos. Creo que eso significa que el Método 1 habría tardado ~15 horas, 7700 veces más lento que el Método 0.

¿Hay alguna manera de hacer algo como el Método 2 (limitar lo que se lee) pero sin la desaceleración? Y si es así, ¿hay una buena forma de hacerlo solo en la parte final del archivo?

¡Gracias!

Tags:  , ,

Answer

  1. Avatar for davy.ai

    Solución

    Para minimizar la cantidad de archivo que necesita leer, puede utilizar una combinación de FileStream y BinaryReader para leer solo los primeros y últimos X bytes del archivo. Aquí hay una función de ejemplo que toma una ruta de archivo, un algoritmo hash y el número de bytes para crear el hash desde el principio y el final del archivo:

    function Get-PartialFileHash {
        param (
            [string]$Path,
            [string]$Algorithm = 'SHA256',
            [int]$StartBytes = 1024 * 1024, # 1 MB
            [int]$EndBytes = 1024 * 1024  # 1 MB
        )
    
        $hash = [Security.Cryptography.HashAlgorithm]::Create($Algorithm)
    
        # Crear un FileStream para el archivo
        $fs = New-Object System.IO.FileStream($Path, 'Open', 'Read')
    
        # Crear un BinaryReader para el principio y el final del archivo
        $br1 = New-Object System.IO.BinaryReader($fs)
        $br2 = New-Object System.IO.BinaryReader($fs)
    
        # Leer los primeros X bytes del archivo
        $buffer1 = $br1.ReadBytes($StartBytes)
    
        # Buscar el final del archivo y leer los últimos X bytes
        $fs.Seek(-$EndBytes, 'End')
        $buffer2 = $br2.ReadBytes($EndBytes)
    
        # Concatenar los dos arreglos de bytes y calcular el hash
        $buffer = $buffer1 + $buffer2
        $hashValue = $hash.ComputeHash($buffer)
    
        # Convertir a cadena hexadecimal
        $hexHashValue = -join ($hashValue | ForEach-Object { "{0:x2}" -f $_ })
    
        # Limpiar
        $br1.Close()
        $br2.Close()
        $fs.Close()
    
        # Devolver el valor hash
        return $hexHashValue
    }
    

    Esta función utiliza un FileStream para abrir el archivo en modo de lectura y crea dos objetos BinaryReader para leer los primeros y últimos X bytes del archivo. El método ReadBytes del BinaryReader se utiliza para leer los bytes de cada parte del archivo, y los dos arreglos de bytes resultantes se concatenan y se calcula el hash utilizando el algoritmo especificado.

    Puede llamar a esta función con la ruta del archivo que desea hashear y, opcionalmente, especificar el algoritmo hash y el número de bytes desde el principio y el final del archivo que desea hashear:

    Get-PartialFileHash -Path 'C:\ruta\al\archivo.bin' -Algorithm 'SHA512' -StartBytes 4096 -EndBytes 4096
    

    Esto calculará el hash SHA512 de los primeros 4 KB y los últimos 4 KB del archivo. Puede ajustar los parámetros StartBytes y EndBytes para hashear más o menos del archivo según sea necesario.

    Este método debería ser más rápido que los que ha intentado hasta ahora, ya que solo lee el número especificado de bytes del archivo en lugar de leer todo el archivo.

Comments are closed.