news 2026/10/29 11:17:17

二进制文件查看与安全修改:HexEdit、偏移表与字节序实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
二进制文件查看与安全修改:HexEdit、偏移表与字节序实战

简介:HexEdit 是一套托管于 GitHub 的开源十六进制编辑器工程源码,面向需要直接查看、搜索和修改二进制文件的开发者、逆向工程与安全分析人员,也适合游戏数值调试、损坏文件修复等字节级编辑场景。压缩包共收录 220 个文件,约 528KB,以 C 与 C++ 源文件及头文件为主体,配套 BMP、ICO、PNG 等界面位图与图标资源,并包含 Visual Studio 解决方案、项目筛选器、资源脚本、批处理构建文件及少量说明文档,便于读者梳理编辑器从界面到核心逻辑的组织方式。已有 204 人学习关注,开源属性也便于审查代码与协作改进。借助这套源码,读者可研究十六进制数据显示、字节定位与编辑、资源组织及多平台构建脚本的实现思路,也可在此基础上进行二次开发与功能扩展,对理解二进制文件分析工具的工作机制具有参考价值。

1. 二进制文件查看不是眼力活,是坐标系活

上周排查一个升级失败的问题,日志里只有一句「校验不通过」。同事把固件拖进编辑器看了两个小时,最后发现包头里那个 4 字节长度字段是小端写的,校验逻辑却按大端读——字节一个没改错,错的是读法。这类活儿最后都会落到同一件事上:二进制文件查看。工具选错,剩下全是眼力活;工具选对,剩下才是方法活。HexEdit 是 macOS 上我长期留在 Dock 里的一个原生十六进制编辑器,打开快、跳转准,几十上百 MB 的文件拖进去不会先卡你三十秒,十六进制区和字符区能对着看,改字节之前也能先把结构摸清楚。它适合经常要拆 .bin、.dat、图片头、打包产物、协议帧的人;只想读文本的没必要上它。下面按我自己的顺序拆:先定位,再建坐标系,然后才是动手改。

2. 从魔数到偏移表:一次查看拆成四步

拿到一个陌生二进制,最忌讳的就是直接打开编辑器从头滚到尾。人的肉眼对十六进制没有尺度感,滚到第 3000 行你早就忘了开头长什么样。我一般把一次查看压成四步:命令行先摸形状,编辑器里建坐标,然后把关键字段抄成表,最后才动结构视图。顺序反了,后面每一步都在返工。

2.1 先看魔数,别急着开图形界面

任何二进制文件的第一件事都是认亲。文件扩展名是给人看的,头几个字节才是它自己说的身份。.bin可能是一段裸固件,也可能压根就是个被改名的 zip;.dat更是万能后缀,什么都往里装。用file先问一句,再用xxd打印前 64 字节,两秒钟就能排除掉一半的误判。

# 1) 让系统按魔数猜类型,别信扩展名 file ./target.bin # 2) 打印前 64 字节,左列是十六进制偏移,中间是字节,右边是 ASCII xxd -l 64 ./target.bin # 3) 记录文件体量,决定后面用不用分块读 ls -lh ./target.bin

-l 64表示只读 64 字节,不是读 64 行,这个参数在打开几百 MB 文件时很关键。xxd的输出左边是偏移量,默认从 0 开始、按十六进制显示,这一点必须先记住,后面所有的偏移讨论都以它为基准。

常见的头部特征值得背下来,至少背到能条件反射:

格式起始魔数关键字段偏移说明
PNG89 50 4E 47 0D 0A 1A 0A宽高在0x10起大端
ZIP50 4B 03 04压缩方法在0x08小端
GZIP1F 8B 08原始大小在尾部 4 字节小端
BMP42 4D文件大小在0x02小端
PE4D 5Ae_lfanew在0x3C小端,指向 PE 头
ELF7F 45 4C 46位宽在0x04,入口在0x18看EI_DATA定字节序

看到4D 5A就知道这不是「随便一个二进制」,而是 Windows 可执行体,接下来该去0x3C读那个 4 字节偏移;看到89 50 4E 47就知道第 9 到第 12 字节是块长度、第 13 到 16 字节是块类型。这套条件反射建立起来之后,你在编辑器里点开文件的头三秒就已经有假设了,而不是盲目地滚动。

2.2 命令行先做一次交叉验证

图形界面有个天然缺陷:你看到的是它渲染出来的样子,字体、列宽、分组方式都可能骗你。所以我习惯在打开 HexEdit 之前,先用命令行把「前 N 字节的原始值」固定下来,作为后面比对的基准。

# hexdump 的 -C 是 canonical 模式:偏移 + 16 字节十六进制 + ASCII hexdump -C -n 64 ./target.bin # od 的写法,-A x 表示偏移用十六进制,-t x1z 表示字节 + 可打印字符 od -A x -t x1z -N 64 ./target.bin # 先算一份哈希,改之前改之后都要有 shasum -a 256 ./target.bin

hexdump -C和xxd的输出格式几乎一样,但-C模式下遇到连续重复行会折叠成一个*,这个特性在扫大段填充区时很爽,在核对具体偏移时就是个陷阱——你以为的「第 5 行」可能实际在第 5000 行。所以核对具体字段时我改用od,它不会折叠,一行一行老实打完。

哈希这一步别省。shasum -a 256在 macOS 上是自带的,不用装东西。改之前存一份,改之后立刻再算一次,两次不一致说明写入确实生效了——这比肉眼盯着屏幕确认可靠得多,尤其是你只改了一个字节的时候。

提示:shasum -a 256输出的是十六进制摘要,把它和文件名一起贴进你的排查记录,比截图靠谱。

2.3 在 HexEdit 里建立可复用的坐标系

进入图形界面之后,第一件事不是找字段,是调显示。默认列宽通常是每行 16 字节,这个值不是随便定的:16 是 2 的幂,一行的起始偏移永远是0x?0,心算起来最省力。如果你的字段跨在 16 字节边界上看着难受,可以临时切成 32 字节一行,但别改成 10 这种十进制友好的值,那会让所有偏移计算都变成噩梦。

第二件事是把地址栏的显示进制确认清楚。十六进制编辑器里所有地址都应该是十六进制的,但人的脑子会偷懒:看到0x100,手指在计算器上按出 100,这就已经差了 156 个字节。我的习惯是永远在纸上写带0x前缀的值,写不出前缀就先不写,逼自己养成习惯。

第三件事是把常用的跳转记成动作。看到魔数后,你已经有了第一个假设偏移,直接在跳转框里输入0x3C、0x10、0x18这类值,比用滚动条找快一个数量级。选中一段字节时,注意看状态栏给出的选区长度的单位——有的是字节数,有的是字节数加一,差一个字节在解析长度字段时足以让整个结构错位。

字符区也要用起来。很多协议帧里混着 ASCII 的标识串,比如IHDR、WAVE、ftyp,这些串在字符区一眼就能认出来,比在十六进制区数49 48 44 52快得多。我通常会把字符区宽度调窄一点,让它只在我需要的时候提供线索,不喧宾夺主。

2.4 把偏移抄成表,别记在脑子里

看完一遍之后,人会产生一种「我都记住了」的错觉。等你切去回个消息再切回来,那个0x2A到底是 payload 长度还是 flags,八成已经模糊了。所以我的规矩是:只要一个文件里出现了三个以上字段,就立刻建表。

字段名偏移长度类型期望值实际值
magic0x004bytes89 50 4E 47一致
ihdr_len0x084uint32 大端1313
width0x104uint32 大端19201920
height0x144uint32 大端10801080
bit_depth0x181uint888

这张表的价值不在于记录,而在于「期望值」这一列。填期望值的过程会逼你回去翻规范,而翻规范的过程往往会发现你原来的假设就是错的。我见过太多次这样的情况:表填到一半,发现两个字段的偏移重叠了,那就说明前面某个字段的长度估错了,问题在动手改之前就被拦下来了。

表建好之后,HexEdit 的结构视图才有意义。先有表、后有模板,顺序不能反——模板只是把你脑子里的表用工具能懂的方式写出来,表本身错了,模板只会让你错得更自信。

3. 结构模板与字节序:让工具替你算偏移

手工数偏移这件事,做十个字段还行,做五十个字段必错。人脑不擅长累加,尤其不擅长在十六进制里累加。所以真正稳定的做法是:用代码或者模板把结构描述出来,让机器去算偏移,你只负责核对结果。这一章的核心就是把「人肉数格子」换成「声明式描述」。

3.1 人肉累加为什么会错,错在哪

先看一个典型错误。假设头部结构是这样:4 字节魔数、2 字节版本、2 字节 flags、4 字节 payload 长度、8 字节保留位。你在编辑器里从头往下数,数到 payload 长度时应该是0x08。但如果中间那个 flags 你记成了 4 字节,后面所有字段全部前移 2 字节,而且因为十六进制看起来都差不多,你未必能立刻发现。

更隐蔽的错误是「偏移 vs 序号」。第 9 个字节的偏移是0x08,不是0x09。这个差一错误在数组下标里天天发生,在二进制解析里同样天天发生。当你在编辑器里跳到0x09去读一个应该在0x08的字段,读出来的值可能依然「看起来合理」——因为二进制里到处都是00,错位读到的往往是另一个字段的高位字节。

第三种错误来自对齐。很多格式要求字段按 4 字节对齐,中间会插入填充字节。规范里写的是「保留 8 字节」,你可能理解成「8 个有意义的字节」,实际那 8 个字节全是00,纯粹为了对齐。数偏移时把填充算进去还是不算进去,结果差得很远。

结论很简单:只要字段超过五个,就别用眼睛数。要么用下面的脚本生成偏移表,要么用编辑器的结构描述功能。

3.2 用一段声明式描述替代手工数格子

我一般会先在 Python 里写一份字段清单,用struct.calcsize算出每个字段的长度和偏移,打印成表,再拿这张表去和编辑器里的实际字节对。这样做的好处是:字段定义只写一次,偏移自动累加,改了字段顺序表会自动更新。

import struct # 字段清单:(名字, struct 格式, 备注) # 格式串里的 '<' 表示小端,'4s' 表示 4 字节定长字节串 FIELDS = [ ("magic", "4s", "固定标识"), ("version", "<I", "协议版本"), ("flags", "<H", "标志位"), ("payload_len", "<I", "负载长度,不含头部"), ("reserved", "8s", "保留区,全 0"), ] offset = 0 total = 0 for name, fmt, note in FIELDS: size = struct.calcsize(fmt) print(f"{name:<12} offset=0x{offset:04X} size={size:<3} {note}") offset += size total = offset print(f"{'HEADER':<12} total={total} bytes (0x{total:02X})")

这段代码跑出来是一张干净的偏移表,offset从 0 开始,每个字段的起始位置和长度都列得清清楚楚。struct.calcsize("4s")返回 4,calcsize("<I")返回 4,calcsize("<H")返回 2,calcsize("8s")返回 8——注意8s和<8s在长度上没有区别,字节串不吃端序。

参数上只有两个地方需要注意。第一,格式串里的端序字符只对数字类型生效,4s这种字节串永远按原样切,不受<或>影响。第二,calcsize在默认模式下可能会做对齐填充,如果你混用了I和H且不加端序前缀,得到的长度可能比字段实际占用更大。加上显式的<或>之后,struct会按标准大小紧凑排布,不会再插填充。

拿到这张表,再回到编辑器里逐行核对:跳到0x00应该看到magic,跳到0x04应该看到版本号。对不上就说明字段清单本身有问题,回去改清单,别改偏移。

3.3 同一段字节的两种读法:字节序必须显式声明

字节序是二进制查看里最容易「看起来没错」的坑。01 00 00 00这段字节,按小端读是 1,按大端读是 16777216。两个值都是合法整数,都不会报错,你只能靠外部信息判断哪个对。

import struct raw = bytes.fromhex("01000000") little = struct.unpack("<I", raw)[0] # 按小端解释 big = struct.unpack(">I", raw)[0] # 按大端解释 print(f"little=0x{little:08X} ({little})") print(f"big =0x{big:08X} ({big})")

输出会是little=0x00000001 (1)和big=0x01000000 (16777216)。这段代码本身没什么技术含量,它的作用是逼你把「我按什么端序读的」这件事写出来。写不出来,说明你还没确定;确定了再读,出错概率下降一大截。

判断端序的依据通常有三类。一是格式规范,PNG、JPEG 这类网络格式几乎都是大端,x86 平台上的私有结构体基本都是小端。二是数值合理性,比如长度字段读出来是 16777216 而文件总共才 3 MB,那一定是端序反了。三是魔数本身,有些格式会在头部放一个固定的校验值,用两种端序各读一遍,哪个等于规范里的常量就是哪个。

在编辑器里看的时候,还有一个辅助手段:把同一段字节分别按 1、2、4、8 字节宽度切换显示,看哪个宽度的值「像个正常的数」。这招不严谨,但在没有规范文档的时候能快速收敛假设。

3.4 结构描述落到编辑器里,只做一件事

HexEdit 这类工具支持把结构描述出来,在十六进制视图旁边叠一层字段名和解析值。我自己的用法很克制:只用它做「可视化核对」,不用它做「自动生成结论」。

具体做法是,把上一节算出来的偏移表按顺序写成结构描述,字段名和脚本里的名字保持完全一致,长度和端序也保持一致。然后在编辑器里加载这个描述,看它解析出来的值和 Python 解析出来的值是否一一对应。对应上了,说明两边理解一致;对应不上,先怀疑结构描述写错了,再怀疑文件本身有问题。

这里有个容易忽略的点:结构描述里的字段顺序必须和文件里的物理顺序一致,不能按逻辑顺序写。有些格式的逻辑顺序和物理顺序是打乱的,比如头部在文件末尾、索引在文件开头。这种时候结构描述反而会误导你,老老实实用偏移表加跳转更稳。

另一个点是嵌套。结构里套结构的时候,偏移是相对的还是绝对的,各家工具的处理不一样。我的习惯是全部拍平成绝对偏移,宁可多写几行,也不去赌工具的嵌套语义。拍平之后,任何一行出错都能单独定位,不用顺藤摸瓜。

4. 大文件与写入安全:动手改字节前的三道闸

看到这里都还是「读」。真正危险的是「写」。十六进制编辑器里一次误操作,可能把整个文件的结构毁掉,而且很多编辑器不提供可靠的撤销。我给自己定了三道闸:备份加哈希、副本策略、回读校验。三道都过,才允许在主文件上落笔。

4.1 第一道闸:备份与哈希,先给自己留后悔药

备份这件事听起来废话,但我见过太多「就改一个字节,不用备份」的现场。改一个字节确实可能没事,但改一个字节之前你得先确认这个字节在哪,而确认的过程本身就可能误触。

#!/usr/bin/env bash set -euo pipefail SRC="firmware.bin" STAMP="$(date +%Y%m%d%H%M%S)" BAK="${SRC}.${STAMP}.bak" # -p 保留时间戳和权限,某些固件工具会检查 mtime cp -p "$SRC" "$BAK" # 原始文件、备份文件各算一次,两次必须一致 echo "--- 原始 ---" shasum -a 256 "$SRC" echo "--- 备份 ---" shasum -a 256 "$BAK"

set -euo pipefail这三件套是必须的:-e让任何一步失败就停,-u让未定义变量直接报错(防止$SRC拼错变成空串把根目录搞了),-o pipefail让管道里任何一环失败都能被捕获。cp -p的-p保留权限和时间戳,有些工具链会拿 mtime 做增量判断,时间戳变了会触发不必要的全量重建。

两次哈希输出必须逐字符一致。如果不一致,别急着往下走,先看看是不是复制过程中源文件被别的进程写了。

提示:备份文件不要放在同一个会被批量清理的临时目录里。我一般放在同级的backup/下,命名带时间戳,一个项目最多留五份,多了就删最老的。

4.2 第二道闸:只在副本上改,主文件最后才碰

我的实际流程是:主文件只读,所有修改先在副本上做,验证通过后再用副本覆盖主文件。这样即使副本改废了,主文件还是干净的,重来一次的成本只是复制一份。

在编辑器里打开文件时,如果工具提供了只读模式,先勾上。只读模式下你能随便跳、随便选、随便看结构,就是按不了保存。等确认了要改的偏移和期望值,再关掉只读。这一步听起来多余,但它把「探索」和「修改」两个阶段物理隔开了,误触概率下降非常明显。

副本命名也要有规矩。我一般用xxx.work作为工作副本,改完验证通过后mv成正式文件名。这样从文件名就能看出哪个是原始、哪个是半成品、哪个是成品,不用靠记忆。

4.3 第三道闸:带期望值的写入,写完立刻回读

真正落笔的时候,我不用编辑器的直接编辑功能,而是写脚本。原因很简单:脚本可以在写入前检查「这个位置现在是不是我以为什么」,不匹配就中止。

import hashlib import os import shutil from pathlib import Path def sha256(path: Path) -> str: h = hashlib.sha256() with open(path, "rb") as f: for chunk in iter(lambda: f.read(1 << 20), b""): h.update(chunk) return h.hexdigest() def patch(path: str, offset: int, expect: bytes, new: bytes) -> None: p = Path(path) bak = p.with_name(p.name + ".bak") if not bak.exists(): shutil.copy2(p, bak) with open(p, "r+b") as f: f.seek(offset) cur = f.read(len(expect)) if cur != expect: raise SystemExit( f"偏移 0x{offset:X} 实际为 {cur.hex()}," f"预期 {expect.hex()},已中止" ) f.seek(offset) f.write(new) f.flush() os.fsync(f.fileno()) # 强制落盘,别信缓冲 # 回读校验:重新打开文件读一遍,确认真的写进去了 with open(p, "rb") as f: f.seek(offset) back = f.read(len(new)) if back != new: raise SystemExit(f"回读不一致:{back.hex()} != {new.hex()}") print(f"OK offset=0x{offset:X} {expect.hex()} -> {new.hex()}") print(f"sha256={sha256(p)}") if __name__ == "__main__": patch("firmware.work", 0x1F4, b"\x00\x00\x00\x01", b"\x00\x00\x00\x02")

几个参数值得说明。expect是写入前必须匹配的原始字节,长度必须和new一致,不一致会导致后续偏移全部错位。os.fsync在 macOS 上会把内核缓冲刷到磁盘,不加这一句,脚本正常退出但断电后文件可能是旧的。回读用的是新打开的文件句柄,不复用写入时的句柄,避免读到缓存。

shutil.copy2和第二道闸里的cp -p效果类似,保留元数据。备份只在不存在时创建,避免重复运行脚本时把已经改过的版本覆盖成备份。

4.4 大文件不要整读进内存,分块扫就够了

几十 MB 的文件用read_bytes()无所谓,上 GB 的时候就会开始吃内存甚至被系统杀掉。做统计类操作时我改成流式读取。

from collections import Counter def scan_freq(path: str, block: int = 4 << 20) -> None: freq = Counter() total = 0 with open(path, "rb") as f: for chunk in iter(lambda: f.read(block), b""): freq.update(chunk) total += len(chunk) print(f"total={total} bytes") for byte, cnt in freq.most_common(6): print(f"0x{byte:02X} count={cnt:<12} ratio={cnt / total:.2%}") if __name__ == "__main__": scan_freq("firmware.bin")

block设成 4 MB 是个经验值:太小了系统调用次数多,太大了单次内存占用高。iter(lambda: f.read(block), b"")是标准的流式读法,读到空字节串就停,不会漏掉最后一块不完整的块。Counter.update直接吃 bytes 对象,会按字节逐个计数,比自己写循环快。

这个统计能回答不少问题:如果 0x00 占比超过 60%,说明文件里有大量填充或未使用区,真正有效的数据可能集中在某几段;如果 0xFF 占比异常高,可能是擦除态没写过的闪存镜像。这些判断不精确,但能帮你快速缩小要重点看的范围。

5. 排查:二进制文件查看里最容易翻车的五个瞬间

下面这些都是我自己或者身边人真实翻过的车,按「现象 → 原因 → 解决」写,能对上号最好,对不上也当个检查清单。

5.1 写入与备份环节的三次翻车

现象一:改完保存,工具打开报格式错误,撤销也回不去。

原因是直接在主文件上编辑,而且编辑器的撤销栈只覆盖当前会话,一旦保存并关闭,历史就没了。有些编辑器会生成一个临时文件,但那个文件在正常退出时会被清理。解决办法只有一个:动手前先复制一份带时间戳的备份,用cp -p而不是cp,保留原始 mtime,避免某些构建系统因为时间戳变化触发全量重编。我现在已经养成条件反射,手指还没碰到键盘,备份命令已经在终端里跑完了。

现象二:脚本写入成功,但设备读出来还是旧值。

原因是写入走了页缓存,脚本退出时数据还在内核缓冲里,没真正落盘。表现是本地xxd看是新值,换个工具或者重启后再看又变回去了。解决是在write之后调用flush()加os.fsync(fileno()),两个都要,只flush只是把 Python 层的缓冲推到操作系统,fsync才推到磁盘。如果是往块设备或者挂载的镜像里写,还要考虑sync命令。

现象三:备份文件被当成正式文件用了。

原因是备份命名和主文件太像,比如firmware.bin.bak和firmware.bin,在文件管理器里排序后挨在一起,拖错了自己都没发现。表现是改了半天没效果,其实是改在备份上。解决是备份放到独立子目录,或者命名里带明显的时间戳前缀,让它在排序上远离主文件。我现在的习惯是backup/20240101-120000_firmware.bin,一眼就能看出这不是当前工作文件。

5.2 判断与读值环节的两次误判

现象四:字段值读出来是个巨大的数,明显不合理。

原因是字节序搞反。01 00 00 00按小端是 1,按大端是 16777216,如果你的文件只有几 MB,那 16777216 这个值本身就是个报警信号。解决是用两种端序各读一遍,拿物理约束去筛:长度字段不能大于文件大小,宽高不能是几万,版本号通常是个位数或两位小数。还有一种情况是偏移差了一位,读到的其实是相邻字段的高位字节,这种时候值往往「看起来还行」,最难发现,所以我在读任何关键字段前都会先打印它前后各 8 字节的原始内容做对照。

现象五:hexdump -C的输出和编辑器里对不上。

原因是hexdump -C会把连续重复的行折叠成一个*,你以为的第 20 行实际在第 2000 行。表现是照着命令行的偏移去编辑器里跳,跳过去看到的内容完全无关。解决办法是核对具体偏移时改用od -A x -t x1z,它不做折叠;或者给hexdump加上-v参数关掉折叠。这个小坑我踩过不止一次,现在只要涉及「按行号定位」,一律不用hexdump。

5.3 一份可以贴在显示器边上的自查清单

每次动手前花三十秒过一遍:备份做了没有,哈希存了没有,改的是副本还是主文件,期望值写进脚本了没有,端序显式声明了没有,改完回读验证了没有。这六条里任何一条没做到,就先别按保存。

还有一条不在清单里但同样重要:把「我为什么认为这个偏移是对的」写下来。写不出理由的假设,八成是猜的。猜出来的偏移,改之前多验证一次,成本远低于改完之后回滚。

6. 进阶:把一次查看沉淀成可复用的解析脚本

单次排查做完,我的习惯是把偏移表、校验逻辑、写入函数合成一个脚本,参数化之后留在项目仓库里。下一次遇到同类文件,--info看结构、--check做校验、--patch做修改,三分钟就能复现上一次的全部分析。

具体做法是拆成三个子命令:info打印头部字段和偏移表,check做完整性校验(比如块级 CRC),patch执行带期望值比对的写入。下面这段是 PNG 场景的示意,其他格式换掉字段定义和校验函数就行。

import argparse, struct, zlib from pathlib import Path def read_chunks(path): raw = Path(path).read_bytes() assert raw[:8] == b"\x89PNG\r\n\x1a\n", "签名不符" off, out = 8, [] while off < len(raw): length, = struct.unpack_from(">I", raw, off) ctype = raw[off + 4:off + 8].decode("ascii", "replace") data = raw[off + 8:off + 8 + length] crc, = struct.unpack_from(">I", raw, off + 8 + length) out.append((off, ctype, data, crc)) off += 12 + length if ctype == "IEND": break return out def cmd_info(args): for off, ctype, data, crc in read_chunks(args.path): calc = zlib.crc32(ctype.encode() + data) & 0xFFFFFFFF flag = "OK" if calc == crc else f"BAD(calc=0x{calc:08X})" print(f"0x{off:06X} {ctype:<6} len={len(data):<8} crc={flag}") parser = argparse.ArgumentParser() sub = parser.add_subparsers(required=True) p_info = sub.add_parser("info") p_info.add_argument("path") p_info.set_defaults(func=cmd_info) args = parser.parse_args() args.func(args)

关键在于把 CRC 校验放进info里一起输出。很多时候文件「能打开」但某一块 CRC 对不上,肉眼完全看不出来,只有算一遍才知道。zlib.crc32返回的是无符号值,和文件里存的 4 字节大端整数比较前要& 0xFFFFFFFF,否则在某些版本上会得到负数。

一个具体的技巧:把这份脚本和备份命令写进同一个Makefile,make backup和make info FILE=xxx各占一行。人 memorizing 命令的能力远不如记忆动词,make info这种短动作能坚持下来,长命令坚持不下来。

从那以后,我每次打开一个陌生二进制之前,都会强制走一遍「file → xxd 前 64 字节 → 哈希备份 → 偏移表」这四步,哪怕只是一个 4 KB 的配置文件。血泪经验是,省掉任何一步省下来的两分钟,往往会在后面以两小时的形式还回来。希望帮到你。

本文还有配套的精品资源,点击获取

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

FileZilla Server 0.9.39汉化绿色版部署指南:从解压到外网访问

简介&#xff1a;这份资源是 FileZilla Server 0.9.39 的汉化绿色版&#xff0c;面向需要在 Windows 环境下快速搭建 FTP 服务的运维人员、开发者与小型团队。它免安装、解压即用&#xff0c;省去配置系统服务的繁琐步骤&#xff0c;适合临时文件共享、内网传输或本地测试等场景…

作者头像 李华
网站建设 2026/10/29 11:15:18

Windows 上开发 Expo iOS,用 KXBuild 本地生成工程、真机调试与打包 ipa

这篇从新建一个 Expo 工程开始&#xff0c;在 Windows 本机生成 iOS 工程&#xff0c;装到 iPhone 上跑开发版&#xff0c;最后打出能上传 App Store Connect 的 ipa。你需要 Apple 开发者账号、一台 iPhone 和数据线&#xff0c;并且已经安装 KXBuild。第一次走完大约一小时&a…

作者头像 李华
网站建设 2026/10/29 11:15:10

远程电脑屏幕监控源码:采集、传输与查看端低延迟闭环

简介&#xff1a;这份面向Java初学者与远程控制技术爱好者的远程电脑屏幕监控示例工程&#xff0c;围绕网络实时查看与远端屏幕控制场景&#xff0c;提供一套可导入Eclipse分析学习的轻量源码。压缩包为rar格式&#xff0c;共14个文件、约12KB&#xff0c;包含5个java源文件、6…

作者头像 李华
网站建设 2026/10/29 11:14:38

计算机组成原理习题解析:CPU、Cache与总线复习地图

简介&#xff1a;《计算机组成原理第六版习题解析》PDF面向计算机专业本科生、考研复习者及讲授该课程的教师&#xff0c;配套教材第六版使用&#xff0c;用于课后练习核对、章节知识梳理与考前查漏补缺。资源包仅含1个PDF文件&#xff0c;大小约3.93MB&#xff0c;单文件结构便…

作者头像 李华
网站建设 2026/10/29 11:12:27

基于 STM32 的智慧大棚环境监测与自动灌溉系统:传感器选型、接线详解与完整代码实现

基于 STM32 的智慧大棚环境监测与自动灌溉系统:传感器选型、接线详解与完整代码实现 大棚里最怕两件事:缺水把苗烤死,和多浇把根沤烂。这篇文章用一套 STM32 + 多传感器 + 自动灌溉 的方案,把"人不在棚里也能把地伺候好"这件事彻底做实,并给出从选型、接线到 Fr…

作者头像 李华
网站建设 2026/10/29 11:12:01

Claude Code mods:TypeScript驱动的AI行为操作系统

1. 这不是插件&#xff0c;是 Claude Code 的“行为操作系统”——mods 功能到底在解决什么问题&#xff1f;Claude Code 推出 mods 功能&#xff0c;可用 TypeScript 定制行为与 UI。这句话乍看像一句产品更新公告&#xff0c;但作为连续三年深度参与 AI 编程工具链落地的开发…

作者头像 李华