【问题标题】:Methods to hex edit binary files via Powershell通过 Powershell 十六进制编辑二进制文件的方法
【发布时间】:2014-01-22 23:52:03
【问题描述】:

我正在尝试仅使用 powershell 从命令行执行二进制十六进制编辑。使用此剪辑执行十六进制替换已部分成功。当 123456 多次出现并且替换应该只发生在特定位置时,就会出现问题。

注意:剪辑需要在此处找到的 Convert-ByteArrayToHexStringConvert-HexStringToByteArray 函数。

http://www.sans.org/windows-security/2010/02/11/powershell-byte-array-hex-convert

$readin = [System.IO.File]::ReadAllBytes("C:\OldFile.exe");
$hx = Convert-ByteArrayToHexString $readin -width 40 -delimiter "";
$hx = $hx -replace "123456","FFFFFF";
$hx = "0x" + $hx;
$writeout = Convert-HexStringToByteArray $hx;
set-content -value $writeout -encoding byte -path "C:\NewFile.exe";

我们如何在 powershell 中指定一个偏移位置来替换这个粗略的 -replace 命令。

【问题讨论】:

  • 这里有很多好的答案,但很少有人能上门。很高兴看到一个函数需要:(1)一个文件名,(2)一个 hex-string 来搜索,(3)一个偏移量, (4) 一个要替换的十六进制字符串,作为某些 powershell 函数的输入。我想我们将不得不等待......

标签: powershell binary hex powershell-2.0 patch


【解决方案1】:

您已经有一个字节数组,因此您可以简单地修改任何给定偏移量的字节。

$bytes  = [System.IO.File]::ReadAllBytes("C:\OldFile.exe")
$offset = 23

$bytes[$offset]   = 0xFF
$bytes[$offset+1] = 0xFF
$bytes[$offset+2] = 0xFF

[System.IO.File]::WriteAllBytes("C:\NewFile.exe", $bytes)

【讨论】:

    【解决方案2】:

    PowerShell 最惯用的方式可能是:

    $offset = 0x3C
    [byte[]]$bytes = Get-Content C:\OldFile.exe -Encoding Byte -Raw
    
    $bytes[$offset++] = 0xFF
    $bytes[$offset++] = 0xFF
    $bytes[$offset] = 0xFF
    
    ,$bytes |Set-Content C:\NewFile.exe -Encoding Byte
    

    【讨论】:

      【解决方案3】:

      我们如何在 PowerShell 中指定一个偏移位置来替换这个粗略的-replace 命令。

      Ansgar Wiechers' helpful answer 解决了偏移问题,brianary's helpful answer 显示了一个更符合 PowerShell 惯用的变体。

      也就是说,如果您有一个解决方案只替换 first 出现的搜索字符串,那么您的原始解决方案可能会起作用。


      仅首次出现的字符串替换:

      不幸的是,PowerShell 的 -replace 运算符和 .NET 的 String.Replace() 方法都没有提供将替换限制为一次次(或固定次数)

      但是,有一个解决方法

      $hx = $hx -replace '(?s)123456(.*)', 'FFFFFF$1'
      
      • (?s)是一个内联正则表达式选项,它使正则表达式元字符.也匹配换行符

      • (.*) 捕获捕获组 1 中的所有 剩余 个字符,替换字符串中的 $1 引用它们,这有效地删除了仅 第一个 出现. (有关-replace 和替换操作数语法的更多信息,请参阅this answer。)

      • 一般注意事项:

        • 如果您的 搜索字符串 恰好包含您想要按字面意思理解的正则表达式元字符,\-将它们单独转义,或者更一般地,将整个搜索词传递给 @987654336 @。

        • 如果您的 替换字符串 恰好包含您想要按字面意思理解的 $ 字符,$- 转义它们,或者更一般地说,应用 -replace '\$', '$$$$'(原文如此)给它。

      然而,正如iRon 指出的那样,虽然上述一般解决了仅替换一次的问题,但它不是一个完全稳健的解决方案,因为不能保证搜索字符串将匹配 字节边界;例如,单字节搜索字符串12 将匹配0123 中的中间12,即使输入字符串中没有byte 12,由字节0123.

      为了解决这种歧义,输入“字节字符串”和搜索字符串的构造必须不同:只需用空格分隔构成一个字节的数字,如下所示。


      搜索而不是固定的偏移量替换字节序列:

      这是一个不需要第三方功能的全 PowerShell 解决方案 (PSv4+):

      注意:

      • 与您的尝试一样,一次读取整个文件内容,并执行往返字符串转换; PSv4+ 语法

      • 搜索和替换字符串构造为用空格分隔的十六进制“字节字符串”。从字节数组输入创建的表示,使用与从 input 构造字节字符串相同的方法,如下所示,例如:

        • (0x12, 0x34, 0x56, 0x1).ForEach('ToString', 'X') -join ' ' -> '12 34 56 1'
          • .ForEach('ToString', 'X') 相当于在每个数组元素上调用 .ToString('X') 并收集结果。
        • 如果希望每个字节一致地表示为两个十六进制数字,即使值小于0x10(例如,01 而不是1),请使用'X2',但是,这会增加内存消耗。
          此外,您还必须在搜索字符串中添加0-前缀个位数字节值,例如:
          '12 34 56 01'
      # Read the entire file content as a [byte[]] array.
      # Note: Use PowerShell *Core* syntax. 
      # In *Windows PowerShell*, replace `-AsByteStream` with `-Encoding Byte`
      # `-Raw` ensures that the file is efficiently read as [byte[]] array at once.
      $byteArray = Get-Content C:\OldFile.exe -Raw -AsByteStream
      
      # Convert the byte array to a single-line "byte string", 
      # where the whitespace-separated tokens are the hex. encoding of a single byte.
      # If you want to guaranteed that even byte values < 0x10 are represented as
      # *pairs* of hex digits, use 'X2' instead.
      $byteString = $byteArray.ForEach('ToString', 'X') -join ' '
      
      # Perform the replacement.
      # Note that since the string is guaranteed to be single-line, 
      # inline option `(?s)` isn't needed.
      # Also note how the hex-digit sequences representing bytes are also separated
      # by spaces in the search and replacement strings.
      $byteString = $byteString -replace '\b12 34 56\b(.*)', 'FF FF FF$1'
      
      # Convert the byte string back to a [byte[]] array, and save it to the
      # target file.
      # Note how the array is passed as an *argument*, via parameter -Value, 
      # rather than via the pipeline, because that is much faster.
      # Again, in *Windows PowerShell* use `-Encoding Byte` instead of `-AsByteStream`.
      [byte[]] $newByteArray = -split $byteString -replace '^', '0x'
      Set-Content "C:\NewFile.exe" -AsByteStream -Value $newByteArray
      

      【讨论】:

      • 这里发布的方法,因重复而关闭,速度更快,使用的内存更少。 stackoverflow.com/questions/57336893/…
      • @js2010:我假设您指的是您自己的答案:(a)问题仍然是重复的,并且(b)您的答案显示了如何替换 single 字节值,它在文件中出现的频率(而最初的问题是完全开放式的)。我建议您在此处重新创建答案并对其进行修改以满足此问题的特定要求,并指出处理 decimal 值可以实现更短且更有效的解决方案。如果您还将-Raw 添加到Get-Content 呼叫并修复笨拙的-as 'byte[]',您将获得我的支持。
      • 不是我的答案,是第一个提出问题的人。
      • @js2010:将答案编辑成问题是不合适的。如果你觉得那里有什么值得注意的地方(乍一看,这对我来说并不明显,因为那里有非常具体的基于 CSV 的代码,而且整个文件仍然被读入内存,以及基于管道的低效代码,以及关于内存使用的明确警告),鼓励作者发布答案,最好是here
      【解决方案4】:

      据我所知,没有必要对字节流进行任何十六进制转换来进行替换。您可以在十进制值列表(默认字符串转换)上进行替换,其中值以空格(单词结尾)为界,例如:
      (我正在跳过文件输入/输出,这已经在answer from @mklement0)

      $bInput = [Byte[]](0x69, 0x52, 0x6f, 0x6e, 0x57, 0x61, 0x73, 0x48, 0x65, 0x72, 0x65)
      $bOriginal = [Byte[]](0x57, 0x61, 0x73, 0x48)
      $bSubstitute = [Byte[]](0x20, 0x77, 0x61, 0x73, 0x20, 0x68)
      
      $bOutput = [Byte[]]("$bInput" -Replace "\b$bOriginal\b", "$bSubstitute" -Split '\s+')
      

      如果您喜欢使用十六进制字符串(例如,对于替换参数),您可以将十六进制字符串转换为字节数组,如下所示:[Byte[]]('123456' -Split '(..)' | ? { $_ } | % {[Convert]::toint16($_, 16)})

      请注意,此解决方案支持不同的 $bOriginal$bSubstitute 长度。在这种情况下,如果您想从特定偏移量开始替换,您可能需要使用 Select-Object cmdlet:

      $Offset = 3
      $bArray = $bInput | Select -Skip $Offset
      $bArray = [Byte[]]("$bArray" -Replace "\b$bOriginal\b", "$bSubstitute" -Split '\s+')
      $bOutput = ($bInput | Select -First $Offset) + $bArray
      

      【讨论】:

      • +1 用于巧妙的隐式(十进制)字符串化;它会导致更大的字符串,但这可能无关紧要。但是请注意,-replace 的使用仍然存在无意替换 multiple 事件的风险 - 这是 OP 的最初问题(尽管不是唯一的问题,由于字节边界问题你指出)。另外,我会使用数组切片而不是 Select-Object,因为这会显着提高性能。
      猜你喜欢
      • 2015-04-13
      • 2011-03-14
      • 2014-10-30
      • 2012-05-10
      • 2017-02-01
      • 2015-10-26
      • 1970-01-01
      • 1970-01-01
      • 2016-08-10
      相关资源
      最近更新 更多