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

时间:2014-01-05 15:28:21

标签: powershell binary hex powershell-v2.0 patch

尝试仅使用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命令。

4 个答案:

答案 0 :(得分:15)

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

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

答案 1 :(得分:1)

  

我们如何在PowerShell中指定偏移位置以替换此粗略的-replace命令。

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

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


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

不幸的是, PowerShell的-replace运算符和.NET的String.Replace()方法都不提供将替换限制为一个事件(或固定数目)。 / p>

但是,有一个解决方法

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

  • (.*)捕获捕获组1中的所有剩余字符,替换字符串中的$1引用它们,这实际上只删除了 first < / em>发生。 (有关-replace和替换操作数的语法的更多信息,请参见this answer。)

  • 一般警告:

    • 如果您的搜索字符串恰好包含您要原义使用的正则表达式元字符,请\分别进行转义,或更普遍地,将整个搜索词传递给[regex]::Escape()

    • 如果您的替换字符串恰好包含您要原义使用的$个字符,请$进行转义,或更普遍地,应用{{ 1}}(原文如此)。

但是,正如iRon所指出的那样,尽管上述方法通常可以解决一次性替换的问题,但由于无法保证, 不是一个完全可靠的解决方案搜索字符串将在字节边界 处匹配;例如,即使输入字符串中没有 byte -replace '\$', '$$$$',单字节搜索字符串12也会与12中的中间0123相匹配,由字节1201组成。

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


通过 search 而不是固定偏移量替换字节序列:

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

注意:

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

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

    • 23-> (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'

答案 2 :(得分:1)

据我所知,不需要对字节流进行任何十六进制转换来进行替换。您可以只在十进制值列表(默认字符串转换)上进行替换,其中值以空格(字尾)为界,例如:
(我正在跳过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 -as [Byte[]])
$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

答案 3 :(得分:0)

可能是PowerShell最常用的方式:

$offset = 0x3C
[byte[]]$bytes = Get-Content C:\OldFile.exe -Encoding Byte

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

$bytes |Set-Content C:\NewFile.exe -Encoding Byte