在地址处写入字节(使用命令行进行十六进制编辑/修改二进制文件)。

74
有没有一种简单的方法可以在命令行中修改二进制文件?假设我知道我的二进制文件包含1234ABCD,我想将它更改为12FFABCD或FFFFABCD,甚至可能是FF34FFABC0(你明白我的意思):)如何在不使用任何特殊工具(如Swiss File Knife或类似工具)的情况下实现这一点?最好能够只使用标准Linux工具从命令行执行。或者,直接在偏移量0x10000处写入FF,在偏移量0x100001处写入12等,而不是搜索要替换的十六进制字符串。它应该可编写脚本,并直接从命令行运行。我正在寻找像“包含在发行版中的二进制文件--write AB --at-offset 100000 --file thebinary.bin”这样的东西。我相当确定可以使用dd完成,但我无法理解man page
8个回答

129
printf '\x31\xc0\xc3' | dd of=test_blob bs=1 seek=100 count=3 conv=notrunc

dd的参数:

  • of | 要打补丁的文件
  • bs | 每次处理1个字节
  • seek | 跳转到第100个位置(十进制)
  • conv=notrunc | 在编辑后不要截断输出(这是dd默认情况下会执行的操作)

一个Josh在为另一个人提供帮助 :)


4
优美而优雅的解决方案!是否可以使用此命令插入字节?或将修补后的文件输出到不同的文件名?或将寻址位置基于像文件大小这样的变量? - ben
1
这是一个很好的解决方案,但遗憾的是它不适用于BusyBox。BusyBox的dd实现不支持conv=notrunc(或conv=任何内容)。 - Adam Haun
1
请注意,seek可能不是以字节为单位,而是以obs的大小为单位。 - markzz
1
@AdamHaun BusyBox的dd支持一些conv=选项,包括conv=notrunc,并且我已经验证了这个解决方案在BusyBox的默认配置下是可行的(BusyBox非常可配置,这个功能可以在自定义构建中禁用)。 - Marcel
这会覆盖数据。如果要在特定位置附加数据,可以使用此答案的第二个解决方案:https://askubuntu.com/questions/1276339/how-do-i-append-data-to-a-binary-file-with-an-hex-editor/1276363#1276363 - baptx
很棒的解决方案!但是,如何使用管道修补发送到另一个程序的数据?例如,在 echo "char* x = \"xxx\";" | gcc -xc - -c -std=c11 -pedantic -Wall -Wextra 中,如何将 xxx 修补为 x<null>x - pmor

13

"printf + dd"的解决方案似乎不能用于写出零。以下是Python 3的通用解决方案(包含在所有现代发行版中),应该适用于所有字节值...

#!/usr/bin/env python3
#file: set-byte

import sys

fileName = sys.argv[1]
offset = int(sys.argv[2], 0)
byte = int(sys.argv[3], 0)

with open(fileName, "r+b") as fh:
    fh.seek(offset)
    fh.write(bytes([byte]))

使用方法...

set-byte eeprom_bad.bin 0x7D00 0
set-byte eeprom_bad.bin 1000 0xff

注意:此代码可以处理十六进制(以0x为前缀)和十进制(无前缀)输入数字。


2
重要的是在Python3中运行此代码(而不是Python2),否则你最终会写出一个列表的字面字符串表示:[byte] - Eugene K
1
查询是针对Shell脚本,而不是Python脚本的。 - s3n0
2
当写出零时,printf+dd可以工作,因为必须使用"conv=notrunc"选项。 - King Sumo
2
printf '\x00\x00\x00\x00' | dd of=blah bs=1 seek=24 count=4 conv=notrunc 可以正常工作(Linux Mint 20)。 - Agguro
printf 中的十六进制支持取决于 printf 的实现。Bash 内置的 printf 支持它,GNU printf(在许多 Linux 中作为 /usr/bin/printf 找到)也支持它。但是某些 sh 变体(如 dash)中的内置 printf 不支持它。八进制应该由所有变体支持。有关 POSIX 解释,请参见 https://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html。因此,另一种选择是使用其他东西,例如 xxd 或 perl,将流输出到 "poke" with dd。 - Pedro Gimeno
这个命令可以在Ubuntu 20中直接使用:printf '\x00\x00\x00\x00' | dd of=somefile bs=1 seek=4 count=4 conv=notrunc - BuvinJ

10

这是一个Bash函数replaceByte,它接受以下参数:

  • 文件名,
  • 要重写的文件中字节的偏移量,以及
  • 字节的新值(一个数字)。
#!/bin/bash

# param 1: file
# param 2: offset
# param 3: value
function replaceByte() {
    printf "$(printf '\\x%02X' $3)" | dd of="$1" bs=1 seek=$2 count=1 conv=notrunc &> /dev/null
}

# Usage:
# replaceByte 'thefile' $offset 95

4

xxd 工具,与 Vim 一同提供(因此很可能可用),可以将二进制文件进行十六进制转储,并从修改后的十六进制转储构建新的二进制文件。


但是从命令行也不会起作用(请参见我在丹的回复上面的评论),对吗? - Josh
应该可以工作:xxd -ps ... | sed -s "s/.../.../g/" | xxd -r ... - SK-logic
1
针对您的情况:echo "010000: FF" | xxd -r - binary.x - SK-logic
我喜欢这个工具,因为它还允许我在不知道确切位置时对十六进制数据进行简单的搜索和替换。 - Archimedes Trajano

2
用一行代码在同一个文件的两个不同位置写入相同的字节。
printf '\x00'| tee >(dd of=filename bs=1 count=1 seek=692 conv=notrunc status=none) \
    >(dd of=filename bs=1 count=1 seek=624 conv=notrunc status=none)

status=none非常有用,当您不想在dd命令执行后得到任何统计数据时使用。


2

1

如果您不需要它可编程化,您可以尝试使用hexedit实用程序。它在许多Linux发行版中都可用(如果不是默认安装,则通常可以在发行版的软件包存储库中找到)。

如果您的发行版没有它,您可以从源代码构建和安装它。


它应该是可脚本化的,并可以直接从命令行运行。我正在寻找类似于“包含在发行版中的二进制文件--write AB --at-offset 100000 --file thebinary.bin”的东西。我相当确定这可以使用“dd”完成,但我无法理解手册。 - Josh

0
关于Josh的回答:如果您想针对特定地址执行此操作-
hexdump -C {file location}

如果你尝试添加0x来表示十六进制值,可能会失败:

dd: warning: ‘0x’ is a zero multiplier; use ‘00x’ if that is intended

你可以通过将其封装在 $(()) 中来实现,这样终端就会将其转换为整数值:

mybinary={file location}
printf '\x31\xc0\xc3' | dd of=$mybinary bs=1 seek=$((0x100)) count=3 conv=notrunc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接