0%

Medicalimage-CTF

来源信息

  • 巅峰极客2021
  • 题目名称:Medicalimage
  • 类型:Crypto
  • 难度:★★★☆☆
  • 题目内容:小明对医疗影像资料进行了加密,但是源代码不小心损坏,请尝试恢复出原始的影像资料

解题过程

原始信息

加密之后的图片如下:

fig1.png

给出的部分源代码如下所示:

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
68
69
70
71
72
73
74
75
76
77
78
from PIL import Image
from decimal import *
import numpy as np
import random
getcontext().prec = 20

def f1(x):
# It is based on logistic map in chaotic systems
# The parameter r takes the largest legal value
# assert(x>=0)s
# assert(x<=1)

def f2(x):
# same as f1
...

def f3(x):
# same as f1
...

def encryptImage(path):
im = Image.open(path)
size = im.size
pic = np.array(im)
im.close()
r1 = Decimal('0.478706063089473894123')
r2 = Decimal('0.613494245341234672318')
r3 = Decimal('0.946365754637812381837')
w,h = size
for i in range(200):
r1 = f1(r1)
r2 = f2(r2)
r3 = f3(r3)
const = 10**14

for x in range(w):
for y in range(h):
x1 = int(round(const*r1))%w
y1 = int(round(const*r2))%h
r1 = f1(r1)
r2 = f2(r2)
tmp = pic[y,x]
pic[y,x] = pic[y1,x1]
pic[y1,x1] = tmp
p0 = random.randint(100,104)
c0 = random.randint(200,204)
config = (p0,c0)

for x in range(w):
for y in range(h):
k = int(round(const*r3))%256
k = bin(k)[2:].ljust(8,'0')
k = int(k[p0%8:]+k[:p0%8],2)
r3 = f3(r3)
p0 = pic[y,x]
c0 = k^((k+p0)%256)^c0
pic[y,x] = c0
return pic,size,config

def decryptImage(path,config):
...

def outputImage(path,pic,size):
im = Image.new('P', size,'white')
pixels = im.load()
for i in range(im.size[0]):
for j in range(im.size[1]):
pixels[i,j] = (int(pic[j][i]))

im.save(path)



enc_img = 'flag.bmp'
out_im = 'flag_enc.bmp'

pic,size,_ = encryptImage(enc_img)
outputImage(out_im,pic,size)

解题

本题是通过加密函数,反推出解密函数,然后还原出原始的图片。通过代码可以发现整个加密过程如下:

  • 将图片读入并转换成矩阵
  • 按行操作:对像素点的值进行交换
  • 按行操作:c0 = k^((k+p0)%256)^c0对每一个像素点进行如代码方法的操作
  • 将加密后的图片写入到新图片中

这里需要注意的是,在两次操作中,都会用到f1,f2,f3三个函数,根据注释可以发现,该函数是混沌系统中的逻辑斯蒂映射,根据公式:
$x_{n+1} = \mu x_n(1-x_n)$

所以这里f1,f2,f3函数为:

1
2
3
4
def f1(x):
mu = 4
x = mu * x * (1 - x)
return x

解密的过程是加密的逆过程,如下:

  • 按行操作:执行c0 = k^((k+p0)%256)^c0的逆操作c0 = (((k^c1^c0)%256)-k)%256
    这里需要注意的是在加密的过程中,k与p0有关,而上一步的c0会参与下一步的加密。因此在解密的过程需要找到对应的k与c0.
1
2
3
4
5
6
7
8
9
10
11
for x in range(w):
for y in range(h):
k = int(round(const*r3))%256
k = bin(k)[2:].ljust(8,'0')
k = int(k[p0%8:]+k[:p0%8],2)
r3 = f3(r3)
c1 = pic[y,x]
c0 = (((k^c1^c0)%256)-k)%256
pic[y,x] = c0
p0 = c0
c0 = c1
  • 还原交换之前的矩阵,首先我们重新执行一遍加密的过程,将整个交换过程记录下来,然后逆一下这个过程即可实现解密。

加密过程呢p0和c0是随机的,但是由于不是很大,所以我们可以遍历出来。

整个代码如下:

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
from PIL import Image
from decimal import *
import numpy as np
import random
getcontext().prec = 20

def f1(x):
# It is based on logistic map in chaotic systems
# The parameter r takes the largest legal value
mu = 4
x = mu * x * (1 - x)
return x

def f2(x):
# same as f1
mu = 4
x = mu * x * (1 - x)
return x

def f3(x):
# same as f1
mu = 4
x = mu * x * (1 - x)
return x

def encryptImage(path):
im = Image.open(path)
size = im.size
pic = np.array(im)
im.close()
r1 = Decimal('0.478706063089473894123')
r2 = Decimal('0.613494245341234672318')
r3 = Decimal('0.946365754637812381837')
w,h = size
for i in range(200):
r1 = f1(r1)
r2 = f2(r2)
r3 = f3(r3)
const = 10**14

replace_list = list()
for x in range(w):
for y in range(h):
x1 = int(round(const*r1))%w
y1 = int(round(const*r2))%h
r1 = f1(r1)
r2 = f2(r2)
tmp = pic[y,x]
pic[y,x] = pic[y1,x1]
pic[y1,x1] = tmp
replace_list.append([(y,x),(y1,x1)])
f = open('test.txt','a')
f.write(str(replace_list))

p0 = random.randint(100,104)
c0 = random.randint(200,204)

config = (p0,c0)

for x in range(w):
for y in range(h):
k = int(round(const*r3))%256
k = bin(k)[2:].ljust(8,'0')
k = int(k[p0%8:]+k[:p0%8],2)
r3 = f3(r3)
p0 = pic[y,x]
c0 = k^((k+p0)%256)^c0
pic[y,x] = c0
return pic,size,config


def decryptImage(path,config):
im = Image.open(path)
size = im.size
pic = np.array(im)
im.close()
r1 = Decimal('0.478706063089473894123')
r2 = Decimal('0.613494245341234672318')
r3 = Decimal('0.946365754637812381837')
w, h = size
for i in range(200):
r1 = f1(r1)
r2 = f2(r2)
r3 = f3(r3)

const = 10 ** 14
p0 = config[0]
c0 = config[1]
for x in range(w):
for y in range(h):
k = int(round(const*r3))%256
k = bin(k)[2:].ljust(8,'0')
k = int(k[p0%8:]+k[:p0%8],2)
r3 = f3(r3)
c1 = pic[y,x]
c0 = (((k^c1^c0)%256)-k)%256
pic[y,x] = c0
p0 = c0
c0 = c1
print(pic)

f = open('test.txt','r')
tihuan = f.read()
tihuan_list = eval(tihuan)
new_pic = pic

for i in tihuan_list[::-1]:
t1 = i[0]
t2 = i[1]
tmp = new_pic[t1[0],t1[1]]
new_pic[t1[0], t1[1]] = new_pic[t2[0],t2[1]]
new_pic[t2[0], t2[1]] = tmp

return new_pic,size,config



def outputImage(path,pic,size):
im = Image.new('P', size,'white')
pixels = im.load()
for i in range(im.size[0]):
for j in range(im.size[1]):
pixels[i,j] = (int(pic[j][i]))

im.save(path)


enc_img = 'flag_enc.bmp'

pic,size,_ = encryptImage(enc_img)


def decmain():

config_list = list()
for p0 in range(100,105):
for c0 in range(200,205):
config = (p0,c0)
config_list.append(config)


for config in config_list:
config_index = config_list.index(config)
enc_img = 'flag_enc.bmp'
dec_img = 'flag_' + str(config_index) + '.bmp'
pic, size, config = decryptImage(enc_img,config)
outputImage(dec_img,pic,size)

decmain()

最后得到的25张图片其实都是一样的,成功拿到flag。

flag{31576107-c243-4962-a4f8-7330d4ad9a76}

fig2.png