首页 > 解决方案 > 如何使用 PowerShell 截断二进制文件的结尾超过已知地址?

问题描述

我为这篇冗长的帖子先道歉,但我正在尝试包含我迄今为止使用和测试过的脚本。我对使用二进制文件也很陌生,而且PowerShell- 我在这里拔头发。我有一个文件,我必须将数据从已知地址删除到文件末尾。我在这里引用了多篇关于 SO 的文章,但似乎让我最接近我想要完成的内容的是这里,它链接到我在这里找到的一篇文章

我觉得我真的很接近了,但我不确定我是否正确使用了该函数,因为我在找出正则表达式以查找 0 或更多“。*”的十六进制等效项时遇到了一些麻烦匹配删除已知地址到文件末尾的剩余数据。也许我想得太复杂了?

我的已知地址总是005A08B0,之后再也没有可重复的模式,所以我不能简单地使用类似\xF0\x00\x01或类似的模式来搜索。

这部分脚本没有改变 - 我假设的功能仍然是相同的,并且在松散的层面上,我理解它在做什么 - 流式传输指定的文件并转到文件末尾以查找匹配的正则表达式的数量模式:

function ConvertTo-BinaryString {
    # converts the bytes of a file to a string that has a
    # 1-to-1 mapping back to the file's original bytes. 
    # Useful for performing binary regular expressions.
    [OutputType([String])]
    Param (
        [Parameter(Mandatory = $True, ValueFromPipeline = $True, Position = 0)]
        [ValidateScript( { Test-Path $_ -PathType Leaf } )]
        [String]$Path
    )

    $Stream = New-Object System.IO.FileStream -ArgumentList $Path, 'Open', 'Read'

    # Note: Codepage 28591 returns a 1-to-1 char to byte mapping
    $Encoding     = [Text.Encoding]::GetEncoding(28591)
    $StreamReader = New-Object System.IO.StreamReader -ArgumentList $Stream, $Encoding
    $BinaryText   = $StreamReader.ReadToEnd()

    $StreamReader.Close()
    $Stream.Close()

    return $BinaryText
}

我的输入文件的这一部分非常易于理解:

$inputFile  = 'C:\StartFile.dat'
$outputFile = 'C:\EndFile_test.dat'
$fileBytes  = [System.IO.File]::ReadAllBytes($inputFile)
$binString  = ConvertTo-BinaryString -Path $inputFile

这是事情分崩离析的地方,我认为这将是我必须真正修改的唯一部分:

# This is the portion I am having a problem with - what do I need to do for this regex???
$re = [Regex]'[\x5A08B0]{30}*'

这部分似乎我不需要修改太多,因为位置会自然地在文件中移动并在每次找到匹配后自行偏移?

# use a MemoryStream object to store the result
$ms  = New-Object System.IO.MemoryStream
$pos = $replacements = 0

$re.Matches($binString) | ForEach-Object {
    # write the part of the byte array before the match to the MemoryStream
    $ms.Write($fileBytes, $pos, $_.Index)
    # update the 'cursor' position for the next match
    $pos += ($_.Index + $_.Length)
    # and count the number of replacements done
    $replacements++
}

# write the remainder of the bytes to the stream
$ms.Write($fileBytes, $pos, $fileBytes.Count - $pos)

# save the updated bytes to a new file (will overwrite existing file)
[System.IO.File]::WriteAllBytes($outputFile, $ms.ToArray())
$ms.Dispose()

if ($replacements) {
    Write-Host "$replacements replacement(s) made."
}
else {
    Write-Host "Byte sequence not found. No replacements made."
}

此外,我还尝试了以下方法,至少看看我是否可以确定在已知文件上引用了适当的地址,这似乎是一个不同的好的开始:

#Decimal Equivalent of the Hex Address:
$offset = 5900464

$bytes = [System.IO.File]::ReadAllBytes("C:TestFile.dat");
Echo $bytes[$offset]

当我运行上面的较小脚本时,我至少得到了已知文件的正确字符——它在文件中产生了与 Ascii 字符等效的十进制。

我可以使用十六进制编辑器手动执行此操作,但这必须通过脚本来实现。. . 感谢我能得到的所有帮助。一些披露 - 它必须使用 Windows 7/Windows 10 的本机程序来完成 - 无法下载任何单独的可执行文件,并且 SysInternals 也是不行的。最初是在看批处理文件的想法,但是我可以PowerShell轻松地将命令移植到批处理文件中。

标签: powershellbinary

解决方案


要简单地截断文件,即删除超出给定字节偏移量的任何内容,您可以使用System.IO.File的静态OpenWrite()方法获取System.IO.FileStream实例并调用其.SetLength()方法:

$inputFile  = 'C:\StartFile.dat'
$outputFile = 'C:\EndFile_test.dat'

# First, copy the input file to the output file.
Copy-Item -LiteralPath $inputFile -Destination $outputFile

# Open the output file for writing.
$fs = [System.IO.File]::OpenWrite($outputFile)

# Set the file length based on the desired byte offset
# in order to truncate it (assuming it is larger).
$fs.SetLength(0x5A08B0)

$fs.Close()

注意:如果给定的偏移量增加了文件的大小,则似乎额外的空间被 NUL ( 0x0) 字节填充,正如 macOS 和 Windows 上的快速测试所建议的那样;但是,从文档来看,似乎不能保证这种行为:.SetLength()

如果流被扩展,则流的新旧长度之间的内容是未定义的。


推荐阅读