由一道CTF题目引入今天的主题。

[ACTF新生赛2020]crypto-des

本题给了一个压缩包,需要我们解密码才能打开。

1
2
3
4
5
6
7
8
72143238992041641000000.000000,
77135357178006504000000000000000.000000,
1125868345616435400000000.000000,
67378029765916820000000.000000,
75553486092184703000000000000.000000,
4397611913739958700000.000000,
76209378028621039000000000000000.000000

这是我们需要解的密码,提示为

1
To solve the key, Maybe you know some interesting data format about C language?

我们引入数据转字节的方式。因为我们输入的数据,在内存中是存放在对应地址,每个地址代表一个字节空间。

比如int a = 5;就是代表a所在的地址空间,以及向下,一共连续4个地址,都会存放5这个数,其实a就像数组名,代表了数组首地址。

为什么是连续4个地址,因为int型变量占4个字节。

比如我们输入字节

1
b'abc'

计算机会如何存储这个字节呢?既然已经是字节了,就是找到对应的数目,直接存储到计算机中。它会将其转换为字节型数据,先取ascii值,分别为97, 98, 99, 然后将其看成256进制数,对应计算出

1
97*(256^2) + 98*(256) + 99

这种顺序的运算,我们称为大端序,因为得到256进制数,一个256进制数可以写成8位二进制数。所以上面就是3个字节,24位二进制数。

会把从最大位的字节数,也就是97对应的8位二进制数,存到最小的地址,然后把最小的8位,即99,放到最高位地址。即为大端序。

同理,小端序也就知道了。

注:网络传输中,采用大端序。

上述运算的处理结果就是平时我们可能会看到的

1
bytes_to_long()函数或者libnum.s2n()或者binascii.unhexlify()等。

我们如何处理这个题目的数据,采用python的struct库

在python中,如果要把一个普通数转换为字节,

1
2
3
4
5
6
7
8
>>> n = 10240099
>>> b1 = (n & 0xff000000) >> 24
>>> b2 = (n & 0xff0000) >> 16
>>> b3 = (n & 0xff00) >> 8
>>> b4 = n & 0xff
>>> bs = bytes([b1, b2, b3, b4])
>>> bs
b'\x00\x9c@c'

我们可以分析一下与运算,就是分别取出每个8位的数字,也就是取出每个字节的数字,对于16进制来说,两个16进制字符代表一个字节。所以取出高8位用

1
b1 = (n & 0xff000000) >> 24

但是我们可以直接用struct库

1
2
3
>>> import struct
>>> struct.pack('>I', 10240099)
b'\x00\x9c@c'

>表示字节顺序是big-endian,也就是网络序,I表示4字节无符号整数。

unpackbytes变成相应的数据类型

1
2
>>> struct.unpack('>IH', b'\xf0\xf0\xf0\xf0\x80\x80')
(4042322160, 32896)

根据>IH的说明,后面的bytes依次变为I:4字节无符号整数和H:2字节无符号整数。

所以,尽管Python不适合编写底层操作字节流的代码,但在对性能要求不高的地方,利用struct就方便多了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from libnum import*
import struct
import binascii

s = [72143238992041641000000.000000,77135357178006504000000000000000.000000,1125868345616435400000000.000000,67378029765916820000000.000000,75553486092184703000000000000.000000,4397611913739958700000.000000,76209378028621039000000000000000.000000]
a = ''
b = ''
for i in s:
i = float(i)
a += struct.pack('<f',i).hex() #小端序
print(a)

for j in s:
j = float(i)
b += struct.pack('>f',j).hex() #大端序
print(b)

a = 0x496e74657265737472696e67204964656120746f20656e6372797074
b = 0x74707972747079727470797274707972747079727470797274707972
print(n2s(a))
print(n2s(b))

就可以得出两种端序下的结果。