CRC

本文最后更新于:2023年10月31日 凌晨

本文主要介绍CRC的定义,以及CRC的相关变种。

CRC

循环冗余校验(英语:Cyclic redundancy check,通称“CRC”)是一种根据网络数据包或电脑文件等数据产生简短固定位数校验码的一种散列函数,主要用来检测或校验数据传输或者保存后可能出现的错误。

CRC的一个重要性质是仿射函数。确切的说,CRC(xy)=CRC(x)CRC(y)c\mathrm{CRC}(x\oplus y)=\mathrm{CRC}(x)\oplus\mathrm{CRC}(y)\oplus ccc 取决于 x,yx,y 的长度。也有另一种表达方式,对于等长的 x,y,zx,y,z,有 CRC(xyz)=CRC(x)CRC(y)CRC(z)\mathrm{CRC}(x\oplus y\oplus z)=\mathrm{CRC}(x)\oplus\mathrm{CRC}(y)\oplus\mathrm{CRC}(z)

CRC 求逆

参考:hackergame2022-writeups/official/不可加密的异世界 at db7b525937fb68e0ba3941ce0ec8d532eb159af8 · SuperSodaSea/hackergame2022-writeups (github.com)

现在如果知道 CRC(x)\mathrm{CRC}(x),要怎么求 xx 呢。首先定义 _CRC(x)=CRC(x)CRC(0l)\_\mathrm{CRC}(x)=\mathrm{CRC}(x)\oplus \mathrm{CRC}(0^l) ,其中 0l0^l 代表与 xx 等长的 b'\x00 字节流。那么对于等长的输入 a,ba,b 有:

_CRC(Δa)_CRC(Δb)=_CRC(ab)Generally xi,n1n_CRC(xi)=_CRC(1nxi)\begin{aligned}\_\mathrm{CRC}(\Delta\oplus a)\oplus\_\mathrm{CRC}(\Delta\oplus b)&=\_\mathrm{CRC}(a\oplus b)\\ \text{Generally }\Longrightarrow\forall x_i,n\quad\oplus_1^n\_\mathrm{CRC}(x_i)&=\_\mathrm{CRC}(\oplus_1^nx_i)\end{aligned}

_CRC(x)\_\mathrm{CRC}(x) 可以理解为一个同态。

有了这个性质,我们只要选取一个基底 viv_i,例如 vi=(0,,1,,0)i1,2,3,,128v_i=(0,\ldots,1,\ldots,0)\quad i\in1,2,3,\ldots,128 ,记这一组标准正交基的 _CRC\_\mathrm{CRC} 哈希值的对应的向量为 Vi=_CRC(vi)V_i=\_\mathrm{CRC}(v_i) ,用 ViV_i 构建一个在 GF(2)GF(2) 上的矩阵:

letMT=[V1V2V128],x=(x1,,xn)GF(2)128\text{let}\quad M^T=\begin{bmatrix}V_1 \\V_2 \\ \vdots\\V_{128}\end{bmatrix},\forall\vec{x}=(x_1,\ldots,x_n)\in GF(2)^{128} \\

x=i=1128xivix=\sum\limits_{i=1}^{128} x_iv_i ,从而:

xMT=i=0128xiVi=_CRC(i=0128xivi)=_CRC(x)=CRC(x)+CRC(0128)GF(2)128xM^T = \sum\limits_{i=0}^{128} x_iV_i = \_\mathrm{CRC}(\sum\limits_{i=0}^{128} x_iv_i) = \_\mathrm{CRC}(x) = \mathrm{CRC}(x) + \mathrm{CRC}(0^{128}) \in GF(2)^{128}

进而可以求出 xT=M1(CRC(x)T+C)where C=CRC(0128)Tx^T=M^{-1}*(\mathrm{CRC}(x)^T+C) \quad \textrm{where} \ C = \mathrm{CRC}(0^{128})^T

CRC128

ACTF2023出过一道CRC128的题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from base64 import *

def crc128(data, poly = 0x883ddfe55bba9af41f47bd6e0b0d8f8f):
crc = (1 << 128) - 1
for b in data:
crc ^= b
for _ in range(8):
crc = (crc >> 1) ^ (poly & -(crc & 1))
return crc ^ ((1 << 128) - 1)

with open('./flag.txt','r') as f:
flag = f.readline()

YourInput = input().encode()
YourDecode = b64decode(YourInput, validate=True)

print(len(YourDecode))

assert len(YourDecode) <= 127 and YourDecode.startswith(b'Dear guest, welcome to CRCRC Magic House, If you input ') and YourDecode.endswith(b", you will get 0x9c6a11fbc0e97b1fff5844fa88b1ee2d")

YourCRC = crc128(YourInput)
print(hex(YourCRC))

if(YourCRC) == 0x9c6a11fbc0e97b1fff5844fa88b1ee2d:
print(flag)

大概就是找到一个输入 xx 使得 xx 在 pad 之后进行crc128等于一个定值,即0x9c6a11fbc0e97b1fff5844fa88b1ee2d。并且 xx 是可见字符,长度不能超过30.

我的做法是对于 xx 的每个字节,固定其前两个比特为 0,1,这样极大可能得到A-Za-z。然后算出基本解系,再搜索进行组合,可以找出符合条件的解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
var_bytes = 26
ans_bytes = 16
start = len(b'RGVhciBndWVzdCwgd2VsY29tZSB0byBDUkNSQyBNYWdpYyBIb3VzZSwgSWYgeW91IGlucHV0IC') * 8
s = b'RGVhciBndWVzdCwgd2VsY29tZSB0byBDUkNSQyBNYWdpYyBIb3VzZSwgSWYgeW91IGlucHV0IC' + b'\x40'*var_bytes + b'LCB5b3Ugd2lsbCBnZXQgMHg5YzZhMTFmYmMwZTk3YjFmZmY1ODQ0ZmE4OGIxZWUyZA=='
target_bytes = len(s)
D = crc128(s) ## 作为常数向量
zero_crc = crc128(target_bytes*b"\x00")
target_bits = 8 * target_bytes
var_bits = 8 * var_bytes
ans_bits = 8 * ans_bytes
v2n = lambda v: int(''.join(map(str, v)), 2)
n2v = lambda n: vector(GF(2), bin(n)[2:].zfill(ans_bits))
crc_value = 0x9c6a11fbc0e97b1fff5844fa88b1ee2d

Affine_Matrix = []
for i in range(var_bits):
if i % 8 in [0, 1]:
continue
v = [0] * target_bits
v[start+i] = 1
v = vector(GF(2), v)
value = crc128(long_to_bytes(v2n(v),target_bytes)) ^ zero_crc
Affine_Matrix.append(n2v(value))
# crc affine function: crc_128(x) = M*x+ C
M, D = matrix(GF(2),Affine_Matrix).transpose(), n2v(D)
# crc affine function: crc_128(x) = M*x+ C
v2n = lambda v: int(''.join(map(str, v)), 2)
n2v = lambda n: vector(GF(2), bin(n)[2:].zfill(128))
res = M.solve_right(n2v(crc_value)+D)
dv = vector([i % 8 == 1 for i in range(var_bits)])
res_ = [0] * var_bits
for i in range(len(res) // 6):
# res_[8*i] = 0
# res_[8*i+1] = 1
for j in range(6):
res_[8*i+j+2] = res[6*i+j]

res_ = vector(GF(2), res_)
print(long_to_bytes(v2n(res_+dv), var_bytes))
basis = M.right_kernel().basis()


import itertools


def convert(res, var_bits):
res_ = [0] * var_bits
for i in range(len(res) // 6):
# res_[8*i] = 0
# res_[8*i+1] = 1
for j in range(6):
res_[8*i+j+2] = res[6*i+j]

res_ = vector(GF(2), res_)
return res_ + dv

for num in range(len(basis)):
for i in itertools.combinations(basis,num+1):
tmp = res
for j in i:
tmp += j
s = long_to_bytes(v2n(convert(tmp, var_bits)),var_bytes)
if re.fullmatch(b'[A-Za-z0-9+/]*', s):
print('success')
print(f'i:{i}')
print('s:', s)
break

CRC
https://algellar.github.io/2023/10/30/Math/CRC/
作者
algellar
发布于
2023年10月30日
许可协议