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!
davy.ai
Solución
Para minimizar la cantidad de archivo que necesita leer, puede utilizar una combinación de
FileStream
yBinaryReader
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:Esta función utiliza un
FileStream
para abrir el archivo en modo de lectura y crea dos objetosBinaryReader
para leer los primeros y últimos X bytes del archivo. El métodoReadBytes
delBinaryReader
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:
Esto calculará el hash SHA512 de los primeros 4 KB y los últimos 4 KB del archivo. Puede ajustar los parámetros
StartBytes
yEndBytes
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.