news 2026/9/18 10:59:24

终端十六进制编辑器 hexedit:定长覆盖修复二进制文件

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
终端十六进制编辑器 hexedit:定长覆盖修复二进制文件

服务器上改一个字节,这活儿多半是走投无路才接的。下载中断的压缩包、开机报格式错的固件、从存储卡里捞出来却打不开的照片——文件就在那儿,只差几个字节不对。hexedit就是为这种场景准备的:一个用 ncurses 写的终端十六进制编辑器,装完也就几 KB,全程键盘操作,不改变文件长度,只干一件事——把某个偏移上的字节换成你要的值。没有图形界面、没有 X11 转发、没有鼠标,一个 SSH 会话就能干活。

它小众到什么程度?很多人常年用xxd看二进制,但真到要"动手改"的时候,第一反应还是去装 HxD 或者 010 Editor。问题是这些工具在纯终端环境里根本跑不起来。而 hexedit 的定位非常清楚:它是一个定长的、原地覆盖式的二进制编辑器。这个定位既是它的全部优点,也是它的全部局限。把这一点吃透,你就知道什么时候该用它、什么时候该换别的工具。

下面我按"它是什么 → 屏幕怎么看 → 键怎么按 → 活怎么干 → 坑在哪 → 什么时候别用"的顺序,把 hexedit 拆开讲一遍。里面夹了不少我在实际操作里踩出来的经验,尤其是终端流控和校验和那两个坑,属于文档里不会写、但一定会遇上的。

1. hexedit 的定位:它只干"原地改字节"这一件事

1.1 和 xxd、od、hexdump 的根本分工

先把工具族谱理清楚。xxdodhexdump这三个是查看器,它们把二进制渲染成文本,输出到标准输出,然后你可以grep、可以管道给下一个程序。它们做的事情是"读",不碰原文件。

hexedit编辑器,它接管整个终端,用 ncurses 画出一个全屏界面,光标停在哪个字节上,你就能改哪个字节,改完按保存直接写回原文件。它不产生中间文本,不需要你来回倒腾格式。

提示:区分方法很简单——如果这个工具的输出能被>重定向到文件里,那它基本是查看器;如果它占满整个屏幕、按 q 才退得出来,那它是编辑器。

这个分工决定了两者的配合方式:先用查看器定位,再用编辑器下手。比如xxd -l 64 firmware.bin看开头长什么样,grep -aboP找出魔数的十进制偏移,然后hexedit -o 那个偏移 firmware.bin直接跳过去改。全流程不需要离开终端。

1.2 定长覆盖这个设计,为什么反而是优点

hexedit 最容易被吐槽的一点是:它不能插入字节,也不能删除字节。文件本来是 1024 字节,你改完还是 1024 字节,一个不多一个不少。

新手会觉得这是缺陷,用过一阵子之后你会发现这是特性。原因在于偏移量的语义:一旦允许插删,被插入点之后的所有字节偏移全部平移。而在二进制世界里,偏移是被硬引用着的——ELF 里的段表记录着偏移、ZIP 的中央目录记录着每个条目的偏移、跳转表里存的是地址、校验区间有始有末。你往中间塞一个字节,后面这些引用全部错位,文件直接报废,而且报错信息往往离真正的原因十万八千里。

所以定长覆盖给了一个非常强的保证:改前和改后,同一偏移指向的还是同一个逻辑位置。这对打补丁、修文件头、改单个标志位来说,是最省心的模型。真要插删,那是另一个工具族的事,后面第 5 节会给出落地方案。

1.3 安装:主流发行版一行命令的事

大部分发行版都有现成包,包名就叫hexedit,不要和hexcursebvidhex搞混,那几个是不同作者写的同类工具。

  • Debian / Ubuntu:sudo apt install hexedit
  • Fedora / RHEL 系:sudo dnf install hexedit(老版本用yum,某些发行版要走 EPEL 源)
  • Arch:sudo pacman -S hexedit
  • openSUSE:sudo zypper install hexedit
  • macOS:先试brew install hexedit,如果仓库里没有,就下载源码包./configure && make && sudo make install,编译前确认装了 ncurses 的开发头文件

源码编译这条路值得提前知道,因为有些精简版的服务器镜像里没有对应包,尤其是内网环境。源码包很小,依赖也只有 ncurses,几分钟就能过。

1.4 命令行参数:把"打开就定位"变成肌肉记忆

hexedit 的参数不多,但用得对了能省掉每次打开后手动跳转的麻烦:

hexedit [选项] 文件
参数用途典型场景
-o, --offset <偏移>打开后光标立刻定位到指定偏移已知问题字节在 0x3E 处
-s, --search <字符串>打开后立刻执行一次搜索想直接跳到某段魔数
--color开启彩色高亮长时间盯着屏幕时更舒服
--help/--version查看帮助和版本确认按键是否和本文一致
-l, --length <长度>只处理文件前 N 个字节大文件只想看头部时

偏移量支持0x前缀的十六进制,比如-o 0x1000。这一点很重要,因为文档、反汇编器、strings输出基本都是十六进制偏移,直接照抄过去就行,不需要心算转换。

举例,一个下载中断、文件头被覆盖的压缩包:

hexedit --color -s "PK" -o 0x0 broken.zip

第一条命令就同时做了三件事:开彩色、搜PK、把光标放到文件开头。如果某个偏移是你每周都要看的,直接写成 alias:

alias fw='hexedit --color -o 0x1000 /srv/images/firmware.bin'

1.5 运行环境的最低要求

hexedit 依赖 ncurses 和正确的TERM环境变量。如果TERM是空的或者设成了dumb,界面会画得乱七八糟甚至直接报错。SSH 进去之后先echo $TERM确认一下,正常应该是xterm-256color之类。

另外它需要终端至少有一定宽度才能把"偏移 + 十六进制 + ASCII"三栏排开。终端太窄的时候,能看到的内容会变少,甚至布局错乱。用 tmux 或 screen 分屏的时候尤其要注意这一点——我自己就试过在一个窄分栏里开 hexedit,结果每行只能显示 8 个字节,看着特别别扭。

2. 屏幕上的三栏分别代表什么

2.1 偏移量、十六进制区、ASCII 预览

hexedit 的界面横着切三块。

最左边一列是偏移量,用十六进制显示,宽度会随文件大小自动调整——小文件是 8 位,大文件会加宽。这里有个新手最容易犯的错:偏移是从 0 开始数的,第一行的偏移是00000000,不是00000001。也就是说,"第 100 个字节"的偏移是 99(0x63)。这个差一位的问题,在对着协议文档和文件格式规范改数据的时候,会让你改错位置还找不到原因。

中间是十六进制区,每个字节两个十六进制字符,中间用空格或分组隔开。十六进制区是真正决定文件内容的地方,你在这里看到的就是磁盘上的实际数值。

右边是ASCII 渲染区,把每个字节按字符解释一遍。可打印字符原样显示,不可打印的(控制字符、高位字节)统一显示成点号。这一栏的价值在于快速扫字符串——找http://、找文件路径、找版本号,眼睛扫一遍就出来了,比在十六进制里认 ASCII 快十倍。

底部通常有一行状态信息,显示当前文件名、文件大小、当前光标偏移。编辑过程中文件会有"已修改"的标记,提醒你还没落盘。

2.2 Tab 键的真正作用:决定你敲进去的是字符还是字节值

这是 hexedit 里最需要理解的一个机制,也是新手最容易被绕晕的地方。

光标可以停在十六进制区,也可以停在 ASCII 区。你敲进去的东西怎么被解释,完全取决于光标在哪个区

  • 光标在十六进制区,你敲4再敲1,写入的字节是0x41
  • 光标在 ASCII 区,你敲A,写入的字节同样是0x41

两条路走到同一个结果,但输入方式完全不同。Tab键就是在两个区之间切换输入目标。

由此可以推出一个实用结论:想改成某个具体数值,用十六进制区;想改成某个可读字符,用 ASCII 区。反过来会出问题——在十六进制区敲字母A,hexedit 会提示非法字符,因为它只接受 0-9 和 A-F;在 ASCII 区敲41,写入的是两个字节0x340x31,而不是一个字节0x41。这个差别在批量填充的时候会让你多写一倍的数据。

提示:改文件头这类工作,我一般在十六进制区做,因为要对照的规范文档给的就是十六进制值。改字符串常量这类工作,切到 ASCII 区直接打字更快。

2.3 只读还是可写:F2 存不下去的两种原因

按 F2 保存没反应,通常是两个原因之一。

第一种是权限问题。hexedit 打开文件时会检查写权限,没有写权限就以只读模式打开,此时编辑动作可能被允许但保存一定失败。先ls -l看一眼 owner 和权限位,需要提权就用sudo hexedit。但要特别提醒:提权改系统文件是很危险的动作,/etc下很多东西是有校验或者会被包管理器覆盖的,改之前一定想清楚是不是真的应该这么干。

第二种是文件本身的状态变了。如果你在 hexedit 打开某个文件的同时,另一个进程把这个文件删了、重命名了或者整体重写了,保存就可能失败或者写到已经不存在的 inode 上。这种问题在服务器上很常见,比如你改的是某个服务正在写的日志或者数据文件。稳妥做法是先把服务停下来,或者改一份副本。

3. 按键操作的全链路梳理

3.1 定位:Ctrl-G 与偏移量的进制换算

最常用的定位方式是Ctrl-G,会弹出一个输入框,你填偏移量,回车就跳过去。输入支持0x前缀的十六进制,也支持纯十进制。

这里的关键是偏移量从哪来。常见的三个来源:

# 来源一:十六进制偏移,strings 命令输出 strings -t x firmware.bin | grep -i "version" # 来源二:十进制字节偏移,grep 的 -b 参数 grep -aboP "MAGIC" firmware.bin # 来源三:反汇编器或者文档里直接给的地址

strings -t x给的是十六进制偏移,可以直接丢进Ctrl-Ggrep -ab给的是十进制偏移,也能直接填,因为 hexedit 认十进制。但要注意 GNU grep 的-b算的是从文件开头算起的字节偏移,如果前面加了-o只输出匹配部分,偏移就正好是匹配串的起始位置,非常好用。

还有一个细节:很多格式规范里的偏移是相对某个段或者某个结构体起始位置的,换算成文件绝对偏移需要加上段基址。这一步别偷懒,算错了改的位置就全错。

3.2 搜索与替换:Ctrl-S 与 Ctrl-R

Ctrl-S是向前搜索,会弹出输入框。输入框里同样可以用 Tab 切换十六进制模式和 ASCII 模式,这一点很重要,因为有些字节根本没法用 ASCII 输入——比如0x00(NUL)、0x0A(换行)、0x89(PNG 的第一个字节,超出 ASCII 范围)。这种情况下必须切到十六进制模式,输入FF D8 FF这样的字节序列。

Ctrl-S找到第一个匹配后会停下来,继续按可以往下找。如果搜到文件末尾还没有,通常会提示没找到。

Ctrl-R是替换,会依次问你"找什么"和"换成什么"。这里有个必须强调的点:替换是覆盖面很广的操作。如果这个字节序列在文件里出现了几千次,而你只想改其中一处,一次替换就可能把文件改得面目全非。我的做法是:永远不要用替换做单点修改——单点修改就用光标定位加手动输入,慢一点但是安全。替换只用在"全文件范围同意图批量修改"的场景,而且改之前必须备份。

注意:搜索和替换的输入内容都要区分十六进制与 ASCII 模式。搜50 4B和搜字符串PK在很多情况下结果一样,但在大小端或者含控制字符的序列上就完全不同了。养成"按字节搜就用十六进制"的习惯,最不容易出错。

3.3 改写、备份与落盘

编辑动作本身很简单:把光标移到目标字节,敲入新值,光标自动前进。如果你要连续改一段,就一路敲下去。改过的字节通常会高亮显示,一眼能看出哪些位置动过。

落盘有三个出口:F2只保存不退出,Ctrl-X保存并退出,Ctrl-C直接退出。Ctrl-C是最危险的那个——因为你在 shell 里已经形成肌肉记忆,很多人下意识就按了Ctrl-C想"取消当前操作",结果直接退出了编辑器。所以我自己的用法是:编辑期间完全不碰 Ctrl-C,改完统一用 Ctrl-X 保存退出

保存之前,务必要做一次备份。这不是客套话:

cp firmware.bin firmware.bin.bak

一个字节的改动就可能让设备变砖,而这个备份只花半秒。我在第一次给设备打补丁的时候没备份,把校验区的一个字节改错,结果设备连恢复模式都进不去,最后只能拆机接调试口重刷。从那以后一条铁律:hexedit 之前先 cp,改完先 diff

3.4 撤销能力的实际边界

hexedit 有撤销功能,Ctrl-Z可以回退上一步操作。但千万别把它当成安全网。撤销的层数是有限的,而且一旦保存落盘,撤销就只能撤销"保存之后"的改动,已经写进文件的内容不会因为按了 Ctrl-Z 就自动还原。

所以正确的心理预期是:撤销只用来修手滑,不用来兜底。真正的兜底是备份文件加上cmp差异对比。改完之后跑一遍:

cmp -l firmware.bin firmware.bin.bak | head -20

cmp -l会逐字节比出差异,输出三个字段:字节位置(十进制,从 1 开始)、第一个文件里的字节值(八进制)、第二个文件里的字节值(八进制)。注意这里输出的是八进制,别当成十六进制读,否则你会以为改错了地方。只改了一处的话,这里应该只输出一行。

4. 实战里最常见的几类活儿

4.1 修文件头:把"打不开"的文件救回来

这是 hexedit 最经典的用途。场景通常是:从损坏的存储介质、下载工具、或者某个不靠谱的传输链路里捞出来的文件,双击打不开,系统说"文件格式不支持"。

第一步永远是确认现场,用filexxd两条命令:

file broken.jpg xxd -l 32 broken.jpg

如果file报的是data,说明它连文件类型都认不出来,基本可以确定文件头的魔数(magic number)坏了。这时候对照标准魔数表,把正确的字节写回去。

我把工作中最常遇到的一组魔数列在这里,不用每次都去查:

文件类型起始字节(十六进制)ASCII 表现
PNG89 50 4E 47 0D 0A 1A 0A.PNG....
JPEGFF D8 FFÿØÿ
GIF47 49 46 38GIF8
ZIP50 4B 03 04PK..
RAR52 61 72 21Rar!
7z37 7A BC AF 27 1C7z..'
gzip1F 8B.‹
PDF25 50 44 46%PDF
ELF 可执行7F 45 4C 46.ELF
Windows PE4D 5AMZ
Mach-OFE ED FA CECF FA ED FE视字节序
SQLite 数据库53 51 4C 69 74 65 20 66 6F 72 6D 61 74 20 33 00SQLite format 3.
Java classCA FE BA BE不可打印
WebAssembly00 61 73 6D.asm
BMP42 4DBM

操作流程是这样的:hexedit broken.jpgCtrl-G跳 0,在十六进制区依次输入FF D8 FFF2保存,退出,再跑一次file broken.jpg验证。

这里必须说清楚 hexedit 的能力边界:它能做的只是"把已经被覆盖的头部字节改回正确值"。如果原始文件头是被剪切走了(比如文件整体前移了 16 个字节,FF D8 FF现在躺在偏移 0x10 上),那 hexedit 帮不了你——因为你没法把后面的数据整体往前搬。这种"数据整体位移"的修复,得靠dd把从 0x10 开始的所有内容复制到新文件的开头,属于另一个套路。判断方法是:把偏移 0x00 到 0x40 的内容用xxd全看一遍,如果正确的魔数出现在后面而不是被覆盖成别的值,那就是位移不是覆盖。

4.2 二进制补丁:改固件、改可执行文件里的一个字节

这一类活儿的难度不在于操作,而在于改完之后文件还能不能用

先说最简单的:改可执行文件里的一段提示字符串。比如某个程序里硬编码了/etc/app/config,你想让它读/etc/app/confia——这种做法本身很粗糙,但作为例子很直观。步骤是:strings -t x app | grep "/etc/app"拿到偏移,Ctrl-G跳过去,Tab 切到 ASCII 区,把最后一个字符改掉。

这里有个硬约束必须记住:不能改超出原字符串的长度。原始字符串后面紧跟着一个0x00作为结束符,如果你把字符串写得更长,就会覆盖掉后面那个0x00,然后程序读这个字符串的时候会一路读下去,读到后面别的数据,行为完全不可预测。所以字符串修改的规则是:等长替换,或者短了用0x00补齐

再说改指令。这是逆向里很经典的手法,x86 架构下有些指令长度相同,可以一字节互换。举个实际例子,某段校验逻辑是:

00001234: 75 1A jne +0x1A

75是条件跳转 JNE 的机器码。如果你想让它无条件跳转,把75改成EB就行,两条指令都是两字节,后面的相对偏移1A不用动:

00001234: EB 1A jmp +0x1A

这种改动之所以安全,是因为指令总长度没变,后续所有指令的地址全部保持不变。如果换成一条长度不同的指令,后面所有代码的地址都会平移,除非你同步改掉所有引用,否则程序直接崩。这就是为什么"定长编辑"在逆向补丁里是刚需。

改固件比改可执行文件麻烦的地方在于校验和。很多设备的固件镜像末尾或者头部带一段 CRC32、MD5 或者厂商自定义的校验值。你改了内容但没重算校验,设备加载固件时校验失败,轻则拒绝启动新固件,重则进恢复模式。所以动手之前必须查清楚:这个格式有没有校验字段,校验算法是什么,校验范围覆盖哪些字节。如果覆盖了你改的那个字节,就必须改完之后重新计算并写回;如果算法不明或者算不出来,那就别改。

4.3 逆向、取证与 CTF 场景里的定位套路

这类场景的重点是"快速找到该改哪儿",hexedit 的作用是最后一步的落地执行。

找魔数用grep

grep -aboP "\x89PNG" disk_image.bin

-a把文件当文本处理,-b输出字节偏移,-o只输出匹配内容,-P启用 PCRE 才认得了\x89这种转义。输出形如1234567:PNG,前面的数字就是十进制偏移,直接丢给Ctrl-G

找字符串用strings

strings -t x -n 6 disk_image.bin | grep -i "flag\|key\|password"

-t x让偏移以十六进制显示,-n 6表示只输出长度不小于 6 的字符串,可以过滤掉大量噪声。

做文件拼接(file carving)的时候,hexedit 用来确认边界。比如你怀疑一段数据是嵌入的 PNG,跳到疑似起始位置看是不是89 50 4E 47,再往后找到49 45 4E 44 AE 42 60 82(PNG 的 IEND 块)确认结束位置,然后把这两点之间的数据切出来。

取证场景里还有一种用法:把疑似被改过的字节和原始版本对比。这时候cmp -lxxd配合使用效率最高,先用cmp定位差异位置,再用 hexedit 跳到那个位置看上下文,判断这处改动是有意义的还是噪声。

4.4 只有终端可用时的应急修复

这个场景特别真实:内网的跳板机、生产环境的容器、没有图形栈的嵌入式设备。你连复制文件到本地都不方便,更别说装 GUI 工具。这时候 hexedit 是唯一的选择。

配合 tmux 或者 screen 使用会更稳:

tmux new -s fix hexedit /data/corrupted.db

一旦网络抖动断开,重连之后tmux attach -t fix就能接着干,编辑状态还在。用普通 SSH 会话直接编辑,断线时未保存的修改全丢,运气不好的话还可能留下一个半写状态的文件。

5. 那些文档里不写、但一定会踩的坑

5.1 Ctrl-S 把终端卡死:软件流控在捣鬼

这是 hexedit 使用中最高频的"灵异事件"。你按Ctrl-S想搜索,结果整个终端像死了一样,什么键都没反应。

原因不在 hexedit,在终端本身。终端默认开启了 IXON 软件流控Ctrl-S在这个协议里是 XOFF,含义是"暂停输出",Ctrl-Q是 XON,"恢复输出"。所以 hexedit 想用Ctrl-S当搜索快捷键,终端却把它拦截成了暂停信号。

两个层面的解法:

第一个是临时救急,按Ctrl-Q就能恢复输出。

第二个是根治,在~/.bashrc里加上一行:

stty -ixon

这行关闭软件流控,之后Ctrl-S就正常传给前台程序了。我建议所有经常在终端里干活的人都加上这一行,因为不只是 hexedit,lessvimscreen都有类似问题。

提示:如果你在stty -ixon之前已经卡住了,Ctrl-Q恢复之后不要急着继续操作,先确认刚才的编辑有没有生效,因为卡死期间按键可能已经被吞掉了。

5.2 保存了但没生效:缓存、挂载与权限的三重陷阱

"我明明改了,怎么还是老样子",这个问题有至少三种成因。

第一种是文件被别的进程缓存着。Linux 的页缓存不会因为你改了磁盘上的文件就自动失效,如果某个进程已经把这块数据读进内存并一直持有,你改完之后它读到的还是旧值。典型场景是数据库文件、正在运行的服务配置文件、被 loop 设备挂载的镜像文件。解决办法是让持有方重新读——最保险的是停服务、umount、再改。

第二种是挂在 loop 设备上的镜像。你直接对镜像文件改字节,改动会写进文件,但如果这个镜像正被作为块设备挂载着,挂载点的视图不会变,而且下一次 umount 的时候,内核可能把内存里的脏页回写,覆盖掉你的修改。所以改镜像文件之前,先umount

第三种是权限。这个前面提过,但有一种隐蔽情况:文件所有者是你,但文件所在的文件系统被以只读方式挂载了。这时候ls -l看不出问题,写操作却会失败。用mount | grep 那个分区确认挂载选项里没有ro

改完之后一定要重新读一遍验证filexxd -l 32sha256sum,随便哪个都行,反正别相信"保存成功了"这个提示。

5.3 校验和与长度字段:改一个字节牵一发动全身

这是二进制编辑最容易忽略的系统性问题。很多文件格式除了内容本身,还维护着一堆派生数据:长度字段、偏移表、CRC、哈希、数值校验位。

举个具体的例子,改一个 GIF 文件里的注释字符串,看上去无害,但 GIF 的每一个数据块前面都有一个长度字节。你把字符串改长了,块的长度字段就错了,解码器读到这里就崩。改短了同理,会残留垃圾字节。

再比如 ZIP。每个本地文件头里有 CRC32、压缩后大小、未压缩大小三个字段。你改了压缩数据里的任何一个字节,CRC32 必然对不上,解压工具会报"CRC 校验失败"。这时候要么重算 CRC 写回去,要么老实地解压、修改、重新打包。

所以 hexedit 的操作规范里必须加一条:动手之前,先搞清楚这个文件格式有没有派生字段,以及派生字段覆盖的范围。这个判断成本不高,查一次格式规范就有答案,但漏掉它的代价可能是改完之后文件变成砖头。

5.4 大文件与内存占用的实际表现

hexedit 的设计是围绕内存缓冲来做文件访问的。小文件(几百 KB 到几十 MB)完全无感,打开即用。但到 GB 级别,问题就出来了:打开会明显变慢,内存占用直线上升,翻页的时候偶尔会卡顿,极端情况下直接打不开。

如果你的目标是在一个 4 GB 的磁盘镜像里改几个字节,更合适的组合是xxd + dd

# 先看目标位置 xxd -s 0x1000 -l 64 disk.img # 直接写一个字节进去 printf '\xFF' | dd of=disk.img bs=1 seek=4096 conv=notrunc status=none

seek是偏移(字节为单位),bs=1表示一次处理一个字节,conv=notrunc是关键——没有这个参数,dd会把文件截断到写入位置,你的镜像后半部分就没了。status=none只是让输出干净点。

这套写法还有个额外好处:可脚本化。如果你要处理一批文件,写成循环比手动开编辑器快得多。

for f in *.bin; do printf '\x01' | dd of="$f" bs=1 seek=$((0x20)) conv=notrunc status=none done

5.5 版本差异与按键漂移

hexedit 有多个版本的源码分支,不同发行版打包的版本号不一样,个别按键可能有出入。最权威的按键清单永远是F1打开的内置帮助页,它显示的就是你手上这个版本的实际情况。

养成习惯:新环境里第一次用,先按F1扫一眼,花十秒钟确认Ctrl-SCtrl-RCtrl-G是不是我描述的那几个功能,以及有没有版本特有的快捷键。这比照着某篇几年前的教程硬试要靠谱得多。

另外,终端类型也会影响显示效果。TERM=xtermTERM=xterm-256color下颜色表现不同,--color参数在某些终端里可能显示不出来。宽字符(中文、日文)在 ASCII 区一律显示成点号,这是正常的,因为那些字节确实不是单字节字符。

6. 什么情况下该换个工具

6.1 需要插入或删除字节

这是 hexedit 硬性做不了的事。要在偏移 0x100 处插入 4 个字节,用 shell 就能搞定:

head -c 256 original.bin > new.bin printf '\x00\x00\x00\x00' >> new.bin tail -c +257 original.bin >> new.bin

head -c 256取前 256 字节(偏移 0 到 255),tail -c +257从第 257 个字节开始取(也就是偏移 256)。中间插入 4 个零字节。逻辑很直白。

用 Python 写更清楚,也不容易在 1-based / 0-based 上出错:

data = open('original.bin', 'rb').read() open('new.bin', 'wb').write(data[:0x100] + b'\x00' * 4 + data[0x100:])

切片的含义一目了然,推荐处理复杂插删时用这个。

6.2 需要按结构理解二进制

hexedit 给你的是裸字节,它不知道什么是 ELF 段表、什么是 PNG 块、什么是 ZIP 中央目录。要在这些结构里做定位,你得自己查规范、自己算偏移。

如果你的工作大量涉及"理解二进制结构",那应该上带模板(pattern)功能的工具。这类工具可以加载一份结构描述文件,把裸字节直接渲染成带字段名和字段类型的树,找字段、改字段都是点一下的事,比手工算偏移快一个数量级。hexedit 在这种场景下只适合当"最后一公里"的执行工具——定位靠结构解析工具,改字节靠 hexedit。

6.3 需要可重复、可审计的批量修改

一次性手改一个字节,hexedit 是最舒服的。但当你要改一万个文件,或者这个修改需要写进部署脚本、需要在 CI 里跑、需要留审计记录,那交互式编辑器就完全不合适了。

这种场景必须是脚本。前面给的dd写法就是最小可用的方案。如果逻辑更复杂(比如先读某个字段判断再决定改不改),用 Python 处理:

import sys path = sys.argv[1] with open(path, 'r+b') as f: f.seek(0x20) magic = f.read(4) if magic == b'ABCD': f.seek(0x24) f.write(b'\x01') print(f'{path}: patched') else: print(f'{path}: skipped')

注意'r+b'模式——读写二进制,不截断文件。这个模式是原地覆盖的正确打开方式。

6.4 同类工具横向对照

把常用工具放在一起比一下,选型的时候心里有数:

工具形态最擅长明显短板
hexedit终端 TUI轻量、纯键盘、定长改字节不能插删、无结构解析
bvi终端 TUIvi 键位、支持插删上手门槛在 vi 本身
dhex终端 TUI自带差异对比视图界面风格偏老
hexyl命令行彩色输出、现代感强只读,不能编辑
xxd命令行双向转换、可脚本不交互,改字节要配 dd
结构解析型工具GUI / TUI模板化理解二进制体积大,纯终端环境受限
反汇编器命令行反汇编与改字节一体学习曲线陡

选择逻辑很清晰:定长改字节,终端环境,要快 —— 用 hexedit;要在文件中间插删 —— 换bvi或者脚本;要理解结构 —— 上模板工具;要批量自动化 —— 写脚本。

我在日常里最常用的组合是:xxd看,grep/strings定位,hexedit改,cmp验证。这四步串起来,从发现问题到验证完成,通常在五分钟以内。

最后分享一个我自己的小习惯:只要是动过二进制的活儿,我都会在处理目录里留一个xxx.bin.bak和一份changes.txt,记下"改了哪个偏移、从什么改到什么、为什么改"。这个习惯救过我两次——一次是三个月后设备又出问题,翻记录发现是当初那处补丁和新固件冲突;另一次是同事接手我的工作,直接照着记录就能复现修改过程,完全不用猜。改二进制这种不可逆的操作,把过程记下来,比记住结论更重要。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 10:58:29

Rust与C内存管理本质对比:所有权系统如何重塑系统编程

1. 这个问题背后&#xff0c;藏着程序员十年来最痛的伤口Rust 是不是就相当于新时代的 C 语言&#xff1f;——这句话在 Rust 社区里被反复提起&#xff0c;也常被 C 老兵嗤之以鼻。但真正值得深挖的&#xff0c;不是“是不是”&#xff0c;而是为什么会有这么多人下意识地用 C…

作者头像 李华
网站建设 2026/9/18 10:58:21

Git 版本回退与误删恢复:从工作区、暂存区到 reflog 急救

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 10:57:28

vnpy+Tushare Pro:A股历史日线数据导入与后复权回测数据搭建

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 10:54:10

Windows 安装 Redis 全流程:配置、服务、客户端与排错

Redis 装到 Windows 上这件事&#xff0c;说简单也简单&#xff0c;解压一个压缩包、双击一个 exe 就能跑起来&#xff1b;说麻烦也麻烦&#xff0c;因为官方从很多年前就不发布 Windows 原生版本了&#xff0c;微软当年自己维护的那个分支也停在了 3.2.100&#xff0c;导致很多…

作者头像 李华
网站建设 2026/9/18 10:53:28

OpenClaw 装完接模型渠道,Base URL 填 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华