Misc-盲相阵列
解题思路(必须包含文字说明+截图)
1. 附件分析
压缩包内包含 array_iq.csv 和 rx_note.txt。提示给出采样率 48000 Hz、符号率 1200 baud、残余频偏约 +1700 Hz,并明确数据链路为 PN mask -> 16-lane column DMA -> H(7,4) nibbles -> BP1 frame。
2. 信号解调
由 48000 / 1200 = 40 可知每个符号有 40 个采样点。对复数 I/Q 样本做 -1700 Hz 频偏校正,枚举 0..39 的符号边界后,最佳符号偏移为 17。
对每 40 个采样求平均得到符号,再用前 80 个符号估计公共相位,完成 BPSK 判决。前 64 bit 是 1010… 训练序列,后 16 bit 是同步字 0xE5A2。
3. 去 PN、DMA 复原与纠错
训练块后的 560 bit 是有效载荷,正好对应 80 个 Hamming(7,4) 码字。对 PN 参数、DMA 顺序和 Hamming 解码方式进行搜索,以 BP1 帧头、低纠错距离和 CRC32 作为判据。
最终命中的参数为:PN8,seed=0xA5,mask=0x1D,右移,输出 MSB。
DMA 为 16-lane column-major 转回 row-major,即 depn.reshape(16, 35).T.reshape(-1)。
Hamming(7,4) 采用标准布局 [p1, p2, d1, p4, d2, d3, d4],总共修正 5 bit。
4. BP1 帧与 CRC 校验
恢复出的帧十六进制为:
42503120495343437b523721715f5a40346d5e54393f702456256b26322a6e7e4c23787da60a993c
其中 42 50 31 为字符串 BP1,长度字节为 0x20,表示后续 payload 长度 32,帧尾 a6 0a 99 3c 为大端 CRC32。
对 payload ISCC{R7!q_Z@4m^T9?p$V%k&2*n~L#x} 计算 CRC32,结果为 0xA60A993C,与帧尾一致,校验通过。
5. 最终结果
Flag: ISCC{R7!q_Z@4m^T9?p$V%k&2*n~L#x}
Exp(如有,请粘贴完整代码,不允许截图!)
from pathlib import Path
import zlib
import numpy as np
CSV_PATH = Path(r"blind-phase\bundle\array_iq.csv")
def pn8_mask(length: int, seed: int = 0xA5, mask: int = 0x1D) -> np.ndarray:
state = seed & 0xFF
out = np.empty(length, dtype=np.uint8)
for i in range(length):
out[i] = (state >> 7) & 1
fb = (state & mask).bit_count() & 1
state = ((state >> 1) | (fb << 7)) & 0xFF
return out
def hamming74_decode(bits: np.ndarray) -> tuple[list[int], int]:
codebook = []
for nib in range(16):
d1 = (nib >> 3) & 1
d2 = (nib >> 2) & 1
d3 = (nib >> 1) & 1
d4 = nib & 1
codebook.append((
np.array([d1 ^ d2 ^ d4, d1 ^ d3 ^ d4, d1, d2 ^ d3 ^ d4, d2, d3, d4], dtype=np.uint8),
nib,
))
nibbles = []
corrections = 0
for i in range(0, len(bits), 7):
cw = bits[i:i + 7]
dist, nib = min((int(np.count_nonzero(cw != code)), nib) for code, nib in codebook)
corrections += dist
nibbles.append(nib)
return nibbles, corrections
def main() -> None:
samples = np.loadtxt(CSV_PATH, delimiter=",", skiprows=1, usecols=(1, 2))
x = samples[:, 0] + 1j * samples[:, 1]
fs = 48_000
sps = 40
freq_offset = 1_700
symbol_offset = 17
n = np.arange(len(x))
corrected = x * np.exp(-1j * 2 * np.pi * freq_offset * n / fs)
symbols = corrected[symbol_offset:symbol_offset + ((len(corrected) - symbol_offset) // sps) * sps]
symbols = symbols.reshape(-1, sps).mean(axis=1)
phase = 0.5 * np.angle(np.sum(symbols[:80] ** 2))
symbols *= np.exp(-1j * phase)
bits = (symbols.real < 0).astype(np.uint8)
payload = bits[80:640]
depn = payload ^ pn8_mask(len(payload))
16-lane column DMA: received column-major stream back to row-major order.
code_bits = depn.reshape(16, 35).T.reshape(-1)
nibbles, corrections = hamming74_decode(code_bits)
frame = bytes((nibbles[i] << 4) | nibbles[i + 1] for i in range(0, len(nibbles), 2))
length = frame[3]
flag = frame[4:4 + length]
crc_expected = int.from_bytes(frame[-4:], “big”)
crc_actual = zlib.crc32(flag) & 0xFFFFFFFF
print(f"frame={frame.hex()}")
print(f"corrections={corrections}")
print(f"crc_ok={crc_actual == crc_expected}")
print(flag.decode())
if name == “main”:
main()
评论