记一道折腾了我大半天的题,今天红帽杯Misc的PicPic,最后六点多一点做出来了(比赛六点结束😭血亏323pt)。感觉做这个题的时候把所有关于fft代码实现的博客和加解密相关的论文都翻了个遍,短时间内应该不想再看到它了(
challenge 1 题目里面可以看到是一个challenge 1的文件夹,和next_challenge.rar的加密压缩包,合理猜测是用challenge 1解出压缩包的密码。
很容易就能发现题目给的create.py就是生成r、1.mkv和2.mkv的源码,逻辑也很容易走:
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 import  osimport  cv2import  structimport  numpy as  npdef  mapping (data, down=0 , up=255 , tp=np.uint8 ):    data_max = data.max ()     data_min = data.min ()     interval = data_max - data_min     new_interval = up - down     new_data = (data - data_min) * new_interval / interval + down     new_data = new_data.astype(tp)     return  new_data def  fft (img ):    fft = np.fft.fft2(img)     fft = np.fft.fftshift(fft)     m = np.log(np.abs (fft))     p = np.angle(fft)     return  m, p if  __name__ == '__main__' :    os.mkdir('m' )     os.mkdir('p' )     os.mkdir('frame' )     os.system('ffmpeg -i secret.mp4 frame/%03d.png' )          files = os.listdir('frame' )     r_file = open ('r' , 'wb' )     for  file in  files:         img = cv2.imread(f'frame/{file} ' , cv2.IMREAD_GRAYSCALE)         m, p = fft(img)         r_file.write(struct.pack('!ff' , m.min (), m.max ()))         new_img1 = mapping(m)         new_img2 = mapping(p)         cv2.imwrite(f'm/{file} ' , new_img1)         cv2.imwrite(f'p/{file} ' , new_img2)     r_file.close()     os.system('ffmpeg -i m/%03d.png -r 25 -vcodec png 1.mkv' )     os.system('ffmpeg -i p/%03d.png -r 25 -vcodec png 2.mkv' ) 
好了,逆向手老本行,勤勤恳恳写逆算法.jpg(一些注释写代码里了):
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 import  osimport  cv2import  structimport  numpy as  npos.system('ffmpeg -i 1.mkv -r 25 m/%03d.png' ) os.system('ffmpeg -i 2.mkv -r 25 p/%03d.png' ) def  ifft (m,p,imgsize ):    s1=np.exp(m)     s1_angle=p     s1_real=s1*np.cos(s1_angle)     s1_imag=s1*np.sin(s1_angle)     s2=np.zeros(imgsize,dtype=complex )     s2.real=np.array(s1_real)     s2.imag=np.array(s1_imag)     f2shift=np.fft.ifftshift(s2)     img_back=np.fft.ifft2(f2shift)     img_back=np.abs (img_back)     return  img_back def  rev_mapping (new_data,t=(0 ,up=255  ):    new_data=new_data.astype(np.float64)     if  t==():         data_max=2 *np.pi         data_min=0      else :         data_max=t[1 ]         data_min=t[0 ]     interval=data_max-data_min     new_interval=up-down     data=(new_data-down)*interval/new_interval+data_min     return  data file1=os.listdir('m' ) file2=os.listdir('p' ) unpack=[] with  open ('r' ,'rb' ) as  f:    for  i in  range (200 ):         s=f.read(8 )         unpack.append(struct.unpack('!ff' ,s))  for  i in  range (200 ):    img1=cv2.imread(f'm/{file1[i]} ' , cv2.IMREAD_GRAYSCALE)     img2=cv2.imread(f'p/{file2[i]} ' , cv2.IMREAD_GRAYSCALE)     m=rev_mapping(img1,unpack[i])     p=rev_mapping(img2)     new_img=ifft(m,p,img1.shape)     cv2.imwrite(f'frame/{file1[i]} ' ,new_img) os.system('ffmpeg -i frame/%03d.png -r 25 secret.mp4' ) 
然后能得到一个secret.mp4,得到了压缩包密码。
zs6hmdlq5ohav5l1 
challenge 2 查看hint.txt可以看到:
1 <math > <mrow > <mo > {</mo > <mtable > <mtr > <mtd > <mi > A</mi > <mi > cos</mi > <mo > </mo > <mo > (</mo > <mi > m</mi > <mi > x</mi > <mo > +</mo > <mi > n</mi > <mo > )</mo > </mtd > </mtr > <mtr > <mtd > <mi > B</mi > <mi > cos</mi > <mo > </mo > <mo > (</mo > <mi > p</mi > <mi > x</mi > <mo > +</mo > <mi > q</mi > <mo > )</mo > </mtd > </mtr > </mtable > <mo > </mo > </mrow > <mo > ⟶</mo > <mrow > <mo > {</mo > <mtable > <mtr > <mtd > <mi > A</mi > <mi > cos</mi > <mo > </mo > <mo > (</mo > <mi > p</mi > <mi > x</mi > <mo > +</mo > <mi > q</mi > <mo > )</mo > </mtd > </mtr > <mtr > <mtd > <mi > B</mi > <mi > cos</mi > <mo > </mo > <mo > (</mo > <mi > m</mi > <mi > x</mi > <mo > +</mo > <mi > n</mi > <mo > )</mo > </mtd > </mtr > </mtable > <mo > </mo > </mrow > <math > 
查了一下是Mathematical Markup Language (MathML),在菜鸟教程在线编辑器 跑一下可以看到:
然后写上一关的逆算法的时候看到过两幅图像幅度谱和相位谱替换_陨星落云的博客-CSDN博客 ,联想到这个式子有点幅度谱和相位谱交换的味道,所以直接把文章里的脚本拿来用,秒解:
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 import  numpy as  npimport  cv2from  matplotlib import  pyplot as  plt  def  magnitude_phase_split (img ):         dft = np.fft.fft2(img)     dft_shift = np.fft.fftshift(dft)          magnitude_spectrum = np.abs (dft_shift)          phase_spectrum = np.angle(dft_shift)     return  magnitude_spectrum,phase_spectrum    def  magnitude_phase_combine (img_m,img_p ):         img_mandp = img_m*np.e**(1j *img_p)     img_mandp = np.uint8(np.abs (np.fft.ifft2(img_mandp)))     img_mandp =img_mandp/np.max (img_mandp)*255      return  img_mandp   img1 = cv2.imread("mix1.png" ,0 ) img2= cv2.imread("mix2.png" ,0 )   img1_m,img1_p = magnitude_phase_split(img1) img2_m,img2_p = magnitude_phase_split(img2)   img_1mAnd2p = magnitude_phase_combine(img1_m,img2_p) img_2mAnd1p = magnitude_phase_combine(img2_m,img1_p)   plt.figure(figsize=(10 ,8 )) plt.subplot(221 ) plt.xlabel("1" ) plt.imshow(img1,cmap="gray" ) plt.subplot(222 ) plt.imshow(img2,cmap="gray" ) plt.xlabel("2" ) plt.subplot(223 ) plt.imshow(img_1mAnd2p,cmap="gray" ) plt.xlabel("1m+2p" ) plt.subplot(224 ) plt.imshow(img_2mAnd1p,cmap="gray" ) plt.xlabel("2m+1p" ) plt.show() 
就能看到:
扫一下左下二维码能得到文本:0f88b8529ab6c0dd2b5ceefaa1c5151aa207da114831b371ddcafc74cf8701c1d3318468d50e4b1725179d1bc04b251f
final challenge 
被上个challenge的那一串文本误导了,一直以为是用来解密图片的密钥啥的,结果白白卡了三个多小时(菜鸡落泪
 
中间查了n多资料就不细说了(
总之最后拿相位掩膜+傅立叶变换进行图像加密_isyiming的博客-CSDN博客 的脚本改了一下:
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 I=imread('phase.png' ); A=im2double(I); figure ,imshow(A);title('The original image' );  Y=fftshift(A); figure ,imshow(Y);title('shifted image' );B=fft2(Y); figure ,imshow(B);title('FFT imagea' );M1=0.814  M11=exp (i *2 *pi *M1); M111=B.*M11;  figure ,imshow(M111);title('phase mask' );D=fft2(M111);  figure ,imshow(D);title('FFT image b' );C=abs (D); C1=ifft2(C);  figure ,imshow(C1);title('2-D IFFT b' );C11=C1.*exp (-i *2 *pi *M1);  figure ,imshow(C11);title('remove mask' );C111=ifft2(C11);  figure ,imshow(C111);title('2-D IFFT a' );C1111=ifftshift(C111);  F=abs (C1111);  figure ,imshow(F);title('The decrypted image' );
就能看到其中一张图(phase mask)是:
好像是没分离好(等其他大佬的wp),不过能大概辨认出key是a8bms0v4qer3wgd67ofjhyxku5pi1czl
(其实是用AES解密脚本试的
1 2 3 4 5 6 7 8 9 from  Crypto.Cipher import  AESimport  binasciikey=b"a8bms0v4qer3wgd67ofjhyxku5pi1czl"  aes=AES.new(key,AES.MODE_ECB) cipher=binascii.unhexlify("0f88b8529ab6c0dd2b5ceefaa1c5151aa207da114831b371ddcafc74cf8701c1d3318468d50e4b1725179d1bc04b251f" ) text=aes.decrypt(cipher) print(text) 
就能得到flag:flag{1ba48c8b-4eca-46aa-8216-d164538af310}