曼彻斯特编码与差分曼彻斯特
考点概念
曼彻斯特编码(相位编码)是数字通信的线路编码:每个比特用一个码片表示,码片中间有电平跳变,用上升沿/下降沿的位置区分 0 和 1(常见约定:01 表示 0,10 表示 1,或相反)。它自带时钟信息,无需同步信号,用于以太网等。
差分曼彻斯特:码片内仍有跳变,但信息由"码片起始处是否有跳变"携带(约定:位开始翻转=0,不翻转=1,或相反)。
曼彻斯特以 hex 串或 01 序列形式给出,常伴随"波形、线路编码、Manchester"提示。
解题即把波形/二进制串按码片长度切分,查约定(上升沿/下降沿对应)还原比特流,再按 ASCII 分组。
曼彻斯特运算规则
每个比特占一个码片,码片时长为一个位周期,码片中点必发生一次电平跳变,跳变方向携带信息。按托马斯约定 0 编为 01(前半低电平后半高电平),1 编为 10(前半高后半低);以太网 IEEE 802.3 约定与此相反,解题时两种字典都要试。解码把密文位流两位一组切分,01 译 0、10 译 1,出现 00 或 11 即不是合法曼彻斯特码。
差分曼彻斯特运算规则
每码片中点仍有跳变用作同步,信息不在码片内而在码片起始处,判据是该码片起始电平相对前一码片结束电平是否发生跳变。按本节约定位开始翻转记 0、不翻转记 1(反约定同样存在)。编码端维护上一码片的结束电平,逐位决定本码片起始电平,随后强制中点翻转;解码端逐片比较首电平与前片尾电平直接读出比特。
以比特串 1011 完整演算两种编码。
曼彻斯特(取 0 对应 01、1 对应 10):
第一比特 1 得码片 10,第二比特 0 得码片 01,第三比特 1 得码片 10,第四比特 1 得码片 10;
波形串接为 10 01 10 10,即位串 10011010。解码回代:切成 10、01、10、10,查字典得 1、0、1、1,还原 1011。
差分曼彻斯特(取位开始翻转记 0,设前导电平为 0):
第一比特 1 不翻转,起始电平 0,中点跳到 1,码片为 01,结束电平 1;
第二比特 0 翻转,起始电平从 1 跳到 0,中点再跳到 1,码片为 01,结束电平 1;
第三比特 1 不翻转,起始电平 1,中点跳到 0,码片为 10,结束电平 0;
第四比特 1 不翻转,起始电平 0,中点跳到 1,码片为 01,结束电平 1;
波形串接为 01 01 10 01,即位串 01011001。解码回代:各码片首电平相对前片尾电平依次为相同、不同、相同、相同,读出 1、0、1、1,还原 1011。
两种编码的合法码片中点全有跳变,绝不会出现连续两个相同电平的码片
识别特征
序列特征:01/10 交替出现、无连续 00 或 11 的码串(曼彻斯特码片内必有跳变)。 文件形式:txt 中一段 hex(如 5555555595555A... 之类),或波形图(Audacity 打开规律方波)。 提示语:题名/描述出现"曼联、曼彻斯特、传感器、以太网、相位"。 其它线路编码对照:NRZ 高=1 低=0;NRZI 电平翻转=1 不变=0;均无码片内跳变约束。
靶场题目
传感器(BUUCTF) 传感器 1(攻防世界) 传感器 2(攻防世界) robomunication(BUUCTF)
Writeup
题目:传感器(BUUCTF) 识别:提示"曼联"即曼彻斯特联足球俱乐部,索引到曼彻斯特编码;密文 hex 只含 5、6、9、A 四种字符,对应 0101、0110、1001、1010 四种 4 位模式,每种都是两个合法码片的组合,符合曼彻斯特码流按半字节记录的特征。
解题:每个 hex 字符转 4 位二进制并补齐前导零,按 0101 折 11、0110 折 10、1010 折 00、1001 折 01 的规则压缩为半长位流;题目另声明八位倒序传输协议,把每 8 位逆序后拼字节;得到完整报文 FFFFFED31F645055F9
脚本:
s = "5555555595555A65556AA696AA6666666955" FOLD = {"0101": "11", "0110": "10", "1010": "00", "1001": "01"} s1, msg = "", "" for c in s: s1 += FOLD[format(int(c, 16), "04b")] if len(s1) == 8: msg += format(int(s1[::-1], 2), "02X") s1 = "" print("flag{%s}" % msg) assert msg == "FFFFFED31F645055F9"题目:传感器 1(攻防世界) 识别:传感器报文族再加已知 ID 可比对校验,本组为差分曼彻斯特编码:整串 hex 转位流后按码片切分,比对首电平与前片尾电平即可解码,密文含 3、E 等非 569A 字符是差分形式
解题:两条报文都做差分曼彻斯特解码(前导电平取 0,逐片比较),第一条得字节流 B0 02 4D 88 93 CA 58 41 81,即同步头 B0 后的报文结构 0024D 前缀、ID 8893CA58、类型 41、校验位 81,与题给 ID 对上;第二条解码得 B0 02 4D 88 45 AB F3 41 19,读出 ID 为 8845ABF3
脚本:
def diff_manchester_decode(hexs: str) -> str: bits = format(int(hexs, 16), "0%db" % (len(hexs) * 4)) out, prev_end = "", "0" for i in range(0, len(bits), 2): chip = bits[i:i + 2] out += "1" if chip[0] == prev_end else "0" prev_end = chip[1] return out for name, hx in [("known", "3EAAAAA56A69AA55A95995A569AA95565556"), ("target", "3EAAAAA56A69AA556A965A5999596AA95656")]: b = diff_manchester_decode(hx) print(name, "".join("%02X" % int(b[i:i + 8], 2) for i in range(0, len(b), 8)))题目:传感器 2(攻防世界) 识别:解码现有报文后结构为 0024D、ID、41、校验位,校验位恒 8 位,推测是 CRC 类校验
解题:先把两条已知报文差分解码得到样本对:02 4D 8893CA58 41 对应校验 81,02 4D 8845ABF3 41 对应校验 19。再以两组样本对全参数暴力匹配 CRC8(多项式 0 到 255,初值两档,输入输出反射各两档),唯一命中标准 CRC-8:多项式 0x07,初值 0x00,输入输出均不反转,输入为去掉同步字节 B0 与校验字节的报文体。
按结构重组 02 4D DEADBEEF 41 与 02 4D BAADA555 41 分别计算,校验位分别为 B5 与 15
脚本:
def crc8(data: bytes, poly: int, init: int, refin: bool, refout: bool) -> int: crc = init for b in data: if refin: b = int(format(b, "08b")[::-1], 2) crc ^= b for _ in range(8): crc = ((crc << 1) ^ poly) & 0xFF if crc & 0x80 else (crc << 1) & 0xFF if refout: crc = int(format(crc, "08b")[::-1], 2) return crc samples = [(bytes.fromhex("024D8893CA58 41".replace(" ", "")), 0x81), (bytes.fromhex("024D8845ABF3 41".replace(" ", "")), 0x19)] hit = None for poly in range(256): for init in (0x00, 0xFF): for refin in (False, True): for refout in (False, True): if all(crc8(d, poly, init, refin, refout) == c for d, c in samples): hit = (poly, init, refin, refout) if hit is None: raise SystemExit("no CRC8 match") poly, init, refin, refout = hit print("匹配参数: poly=%s init=%s refin=%s refout=%s" % (hex(poly), hex(init), refin, refout)) for nid in ("DEADBEEF", "BAADA555"): msg = bytes.fromhex("024D" + nid + "41") print(nid, "->", format(crc8(msg, poly, init, refin, refout), "02X"))题目:robomunication(BUUCTF) 识别:播放或看波形,整段只有两种时长规律的滴答声(长短分别记 bi 与 bu),是时域摩斯电码的典型节奏;题名机器人通信也指向机器编码,与曼彻斯特同属线路编码大类,实际考点为音频摩斯。
解题:Audacity 打开逐段听写,长的 bi 记为划、短的 bu 记为点,整理出摩斯串;解码得整句 hellowhatisthekeyitisboopbeep,句尾明示 key 就在句内
脚本:
MORSE = { ".-": "A", "-...": "B", "-.-.": "C", "-..": "D", ".": "E", "..-.": "F", "--.": "G", "....": "H", "..": "I", ".---": "J", "-.-": "K", ".-..": "L", "--": "M", "-.": "N", "---": "O", ".--.": "P", "--.-": "Q", ".-.": "R", "...": "S", "-": "T", "..-": "U", "...-": "V", ".--": "W", "-..-": "X", "-.--": "Y", "--..": "Z", } morse = (".... . .-.. .-.. --- .-- .... .- - .. ... - .... . -.- . -.-- " ".. - .. ... -... --- --- .--. -... . . .--.") plain = "".join(MORSE[t] for t in morse.split()) print(plain, "->", "flag{%s}" % plain[-8:]) assert plain == "HELLOWHATISTHEKEYITISBOOPBEEP" assert plain[-8:] == "BOOPBEEP"工具:Audacity | Free, Open Source Audio Editor and Recorder
脚本
def manchester_encode(bits: str) -> str: return "".join("10" if b == "1" else "01" for b in bits) def manchester_decode(wave: str) -> str: out = [] for i in range(0, len(wave), 2): pair = wave[i:i + 2] if pair == "10": out.append("1") elif pair == "01": out.append("0") else: raise ValueError("非法码片: %r" % pair) return "".join(out) def diff_manchester_encode(bits: str) -> str: wave = []; cur = "0" for b in bits: if b == "0": cur = "1" if cur == "0" else "0" wave.append(cur) wave.append("1" if cur == "0" else "0") return "".join(wave) def diff_manchester_decode(wave: str) -> str: bits = []; prev = "0" for i in range(0, len(wave), 2): start = wave[i] bits.append("0" if start != prev else "1") prev = start return "".join(bits) text = b"flag{manchester_ok}" bits = "".join(format(b, "08b") for b in text) cipher_hex = hex(int(manchester_encode(bits), 2))[2:] wave = bin(int(cipher_hex, 16))[2:].zfill(len(bits) * 2) flag = bytes(int(manchester_decode(wave)[i:i + 8], 2) for i in range(0, len(bits), 8)) print("曼彻斯特解码: %s" % flag.decode()) assert flag == text assert diff_manchester_decode(diff_manchester_encode(bits)) == bits print("差分曼彻斯特自洽验证通过") print("SUCCESS: flag{manchester_ok}")