通过Powershell编辑二进制文件的方法

通过Powershell编辑二进制文件的方法,powershell,binary,hex,powershell-2.0,patch,Powershell,Binary,Hex,Powershell 2.0,Patch,我试图仅使用powershell从命令行执行二进制十六进制编辑。已经部分成功地用这个剪子替换了一个十六进制。当123456多次出现时,问题就会出现,而更换只应在特定位置发生 注意:snip需要此处提供的Convert ByteArrayToHexString和Convert HexStringToByteArray函数 我们如何在powershell中指定偏移位置以替换此sketchy-replace命令。您已经有了一个字节数组,因此您可以简单地修改任意给定偏移处的字节 $bytes = [

我试图仅使用powershell从命令行执行二进制十六进制编辑。已经部分成功地用这个剪子替换了一个十六进制。当123456多次出现时,问题就会出现,而更换只应在特定位置发生

注意:snip需要此处提供的
Convert ByteArrayToHexString
Convert HexStringToByteArray
函数


我们如何在powershell中指定偏移位置以替换此sketchy-replace命令。

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

$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)

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
如何在PowerShell中指定偏移位置以替换此粗略的
-replace
命令

解决了偏移量问题,并展示了一个更具PowerShell风格的变体

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


仅第一次出现的字符串替换: 不幸的是,PowerShell的
-replace
运算符和.NET的
String.replace()
方法都没有将替换限制为一次(或固定次数)

但是,有一个解决办法:

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

  • (.*)
    捕获捕获组1中的所有剩余字符,替换字符串中的
    $1
    引用这些字符,这实际上只删除了第一次出现的字符。(有关
    -replace
    和替换操作数语法的更多信息,请参阅。)

  • 一般注意事项:

    • 如果您的搜索字符串恰好包含您希望按字面理解的正则表达式元字符,
      \
      -将它们单独转义,或者更一般地,将整个搜索项传递给
      [regex]::escape()

    • 如果替换字符串恰好包含希望按字面理解的
      $
      字符,
      $
      -对其进行转义,或者更一般地,对其应用
      -替换“\$”、“$$”
      (原文如此)

然而,正如所指出的,虽然上述方法一般解决了“只替换一次”问题,但它并不是一个完全健壮的解决方案,因为不能保证搜索字符串将在字节边界匹配;e、 例如,单字节搜索字符串
12
将匹配
0123
中的中间
12
,即使输入字符串中没有字节
12
,由字节
01
23
组成

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


通过搜索而不是固定偏移量替换字节序列: 以下是一个不需要第三方功能的全PowerShell解决方案(PSv4+):

注:

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

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

    • (0x12,0x34,0x56,0x1)。ForEach('ToString','X')-连接“”
      ->
      '12 34 56 1'
      • .ForEach('ToString','X')
        相当于对每个数组元素调用
        .ToString('X')
        ,并收集结果
    • 如果希望每个字节一致地表示为两个十六进制数字,即使对于小于
      0x10
      (例如
      01
      而不是
      1
      )的值,也可以使用
      'X2'
      ,这会增加内存消耗。
      此外,您还必须
      0
      -在搜索字符串中为单个数字字节值添加前缀,例如:
      '12345601'
#以[byte[]数组的形式读取整个文件内容。
#注意:使用PowerShell*核心*语法。
#在*Windows PowerShell*中,将`-AsByTestStream`替换为`-编码字节`
#`-Raw`确保文件一次被有效地读取为[byte[]]数组。
$byteArray=获取内容C:\OldFile.exe-Raw-AsByteStream
#将字节数组转换为单行“字节字符串”,
#其中以空格分隔的标记是十六进制。单个字节的编码。
#如果您想保证即使小于0x10的字节值也表示为
#*对*的十六进制数字,请改用“X2”。
$byteString=$byteArray.ForEach('ToString','X')-连接“”
#进行更换。
#请注意,由于字符串保证为单行,
#不需要内联选项“(?s)”。
#还要注意表示字节的十六进制数字序列也是如何分开的
#按搜索和替换字符串中的空格。
$byteString=$byteString-替换“\b12 34 56\b(.*)”,“FF$1”
#将字节字符串转换回[byte[]]数组,并将其保存到
#目标文件。
#注意数组是如何通过parameter-Value作为*参数*传递的,
#而不是通过管道,因为这要快得多。
#同样,在*Windows PowerShell*中,使用`-Encoding Byte`而不是`-AsByteStream`。
[byte[]$newByteArray=-split$byteString-replace'^',0x'
设置内容“C:\NewFile.exe”-AsByteStream-值$newByteArray

就我所能监督的任务而言,没有必要对字节流进行任何十六进制转换来进行替换。您只需在十进制值列表(默认字符串转换)上进行替换,其中值以空格(单词结尾)为界,例如:
(我正在跳过文件输入/输出,它是alrea
$hx = $hx -replace '(?s)123456(.*)', 'FFFFFF$1'
# 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
$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+')
$Offset = 3
$bArray = $bInput | Select -Skip $Offset
$bArray = [Byte[]]("$bArray" -Replace "\b$bOriginal\b", "$bSubstitute" -Split '\s+')
$bOutput = ($bInput | Select -First $Offset) + $bArray