tên challenge: My friend artist Mô tả: I recently ...
Создано: 24 апреля 2026 г.
Отвечено с использованием GPT-5.5 Thinking от Chat01
Создано: 24 апреля 2026 г.
Отвечено с использованием GPT-5.5 Thinking от Chat01
tên challenge: My friend artist
Mô tả: I recently received a song from my friend, who is a painter. He always wanted to learn to play music and recently stated that he found an excellent way to express himself in the medium. However, I am unable to see anything in this piece, so could you lend me an ear?
flag format: putcCTF{.....}
solve challenge như 1 top player của các giải CTF quốc tế
pythonimport wave, os, struct, numpy as np, matplotlib.pyplot as plt, scipy.io.wavfile as wav p='/mnt/data/my_first_song.wav' rate,data=wav.read(p) rate, data.shape, data.dtype, data[:5], data.shape[0]/rate (44100, (1896460, 2), dtype('int16'), array([[16383, 0], [16351, 1026], [16254, 2048], [16094, 3063], [15871, 4065]], dtype=int16), 43.003628117913834)
pythonimport numpy as np, matplotlib.pyplot as plt, scipy.signal as sig, os x=data.astype(np.float32) # save spectrograms for sum and each channel for name, y in [('left',x[:,0]),('right',x[:,1]),('diff',x[:,0]-x[:,1]),('sum',x[:,0]+x[:,1])]: f,t,S=sig.spectrogram(y, fs=rate, nperseg=2048, noverlap=1536, scaling='spectrum', mode='magnitude') # log scale plt.figure(figsize=(14,6)) plt.pcolormesh(t,f,20*np.log10(S+1e-6), shading='auto') plt.ylim(0, 22050) plt.title(name) plt.ylabel('Hz'); plt.xlabel('s') plt.tight_layout() out=f'/mnt/data/spec_{name}.png' plt.savefig(out,dpi=150) plt.close() out '/mnt/data/spec_sum.png'
pythonimport numpy as np, matplotlib.pyplot as plt, scipy.signal as sig, os x=data.astype(np.float32) # save spectrograms for sum and each channel for name, y in [('left',x[:,0]),('right',x[:,1]),('diff',x[:,0]-x[:,1]),('sum',x[:,0]+x[:,1])]: f,t,S=sig.spectrogram(y, fs=rate, nperseg=2048, noverlap=1536, scaling='spectrum', mode='magnitude') # log scale plt.figure(figsize=(14,6)) plt.pcolormesh(t,f,20*np.log10(S+1e-6), shading='auto') plt.ylim(0, 22050) plt.title(name) plt.ylabel('Hz'); plt.xlabel('s') plt.tight_layout() out=f'/mnt/data/spec_{name}.png' plt.savefig(out,dpi=150) plt.close() out '/mnt/data/spec_sum.png' <<ImageDisplayed>>
python# high res spectrogram with high frequency, maybe using short window and grayscale for name,y in [('left',x[:,0]),('right',x[:,1])]: f,t,S=sig.spectrogram(y, fs=rate, nperseg=1024, noverlap=768, mode='magnitude') db=20*np.log10(S+1) plt.figure(figsize=(20,10)) plt.imshow(db, origin='lower', aspect='auto', extent=[t[0],t[-1],f[0],f[-1]], cmap='gray') plt.ylim(0,22050) plt.axis('off') plt.tight_layout(pad=0) plt.savefig(f'/mnt/data/spec_{name}_gray.png',dpi=200,bbox_inches='tight',pad_inches=0) plt.close() <<ImageDisplayed>>
pythonimport matplotlib.pyplot as plt, numpy as np # plot decimated x-y oscilloscope for entire audio L=x[:,0]; R=x[:,1] # normalize plt.figure(figsize=(8,8)) plt.plot(L[::20], R[::20], linewidth=0.1) plt.axis('equal'); plt.axis('off') plt.tight_layout(pad=0) plt.savefig('/mnt/data/xy_dec20.png', dpi=300, bbox_inches='tight', pad_inches=0) plt.close()
pythonfrom PIL import Image, ImageOps, ImageFilter import numpy as np, scipy.io.wavfile as wav rate,data=wav.read('/mnt/data/my_first_song.wav') L=data[:,0].astype(np.int32); R=data[:,1].astype(np.int32) # use all points, map to 2000x2000 W=H=2000 xs=((L - L.min())*(W-1)/(L.max()-L.min())).astype(np.int32) ys=((R - R.min())*(H-1)/(R.max()-R.min())).astype(np.int32) img=np.zeros((H,W), dtype=np.uint8) # y invert np.maximum.at(img, (H-1-ys, xs), 255) # dilate a bit im=Image.fromarray(img).filter(ImageFilter.MaxFilter(3)) im.save('/mnt/data/xy_scope.png') (L.min(),L.max(),R.min(),R.max(),rate,data.shape)
pythonimport numpy as np from PIL import Image, ImageFilter p='/mnt/data/my_first_song.wav' raw=np.fromfile(p, dtype=np.uint8) # WAV data starts at 44 arr=np.frombuffer(raw[44:].tobytes(), dtype='<i2').reshape(-1,2) L=arr[:,0].astype(np.int32); R=arr[:,1].astype(np.int32) W=H=1200 xs=((L - L.min())*(W-1)/(L.max()-L.min())).astype(np.int32) ys=((R - R.min())*(H-1)/(R.max()-R.min())).astype(np.int32) img=np.zeros((H,W), dtype=np.uint8) np.maximum.at(img, (H-1-ys, xs), 255) im=Image.fromarray(img).filter(ImageFilter.MaxFilter(3)) im.save('/mnt/data/xy_scope.png') print(arr.shape, L.min(), L.max(), R.min(), R.max())
pythonimport numpy as np from PIL import Image, ImageFilter p='/mnt/data/my_first_song.wav' raw=np.fromfile(p, dtype=np.uint8) # WAV data starts at 44 arr=np.frombuffer(raw[44:].tobytes(), dtype='<i2').reshape(-1,2) L=arr[:,0].astype(np.int32); R=arr[:,1].astype(np.int32) W=H=1200 xs=((L - L.min())*(W-1)/(L.max()-L.min())).astype(np.int32) ys=((R - R.min())*(H-1)/(R.max()-R.min())).astype(np.int32) img=np.zeros((H,W), dtype=np.uint8) np.maximum.at(img, (H-1-ys, xs), 255) im=Image.fromarray(img).filter(ImageFilter.MaxFilter(3)) im.save('/mnt/data/xy_scope.png') print(arr.shape, L.min(), L.max(), R.min(), R.max()) <<ImageDisplayed>>
pythonimport numpy as np, math from PIL import Image, ImageFilter, ImageDraw raw=np.fromfile('/mnt/data/my_first_song.wav', dtype=np.uint8) arr=np.frombuffer(raw[44:].tobytes(), dtype='<i2').reshape(-1,2) rate=44100 # global scale L=arr[:,0].astype(np.int32); R=arr[:,1].astype(np.int32) mnx,mxx=L.min(),L.max(); mny,mxy=R.min(),R.max() def render(a,W=600,H=600): L=a[:,0].astype(np.int32); R=a[:,1].astype(np.int32) xs=((L-mnx)*(W-1)/(mxx-mnx)).astype(np.int32); ys=((R-mny)*(H-1)/(mxy-mny)).astype(np.int32) img=np.zeros((H,W),np.uint8) np.maximum.at(img,(H-1-ys,xs),255) return Image.fromarray(img).filter(ImageFilter.MaxFilter(2)) # make grid 1s segments cols=5; rows=9; W=400;H=400 canvas=Image.new('L',(cols*W,rows*H),0) for i in range(rows*cols): s=int(i*rate); e=min(int((i+1)*rate),len(arr)) if s>=len(arr): break im=render(arr[s:e],W,H) canvas.paste(im,((i%cols)*W,(i//cols)*H)) canvas.save('/mnt/data/xy_segments_1s.png') print(len(arr)/rate)
pythonimport numpy as np, math from PIL import Image, ImageFilter, ImageDraw, ImageFont raw=np.fromfile('/mnt/data/my_first_song.wav', dtype=np.uint8) arr=np.frombuffer(raw[44:].tobytes(), dtype='<i2').reshape(-1,2) rate=44100 Lall=arr[:,0].astype(np.int32); Rall=arr[:,1].astype(np.int32) mnx,mxx=Lall.min(),Lall.max(); mny,mxy=Rall.min(),Rall.max() def render(a,W=400,H=400): L=a[:,0].astype(np.int32); R=a[:,1].astype(np.int32) xs=((L-mnx)*(W-1)/(mxx-mnx)).astype(np.int32); ys=((R-mny)*(H-1)/(mxy-mny)).astype(np.int32) img=np.zeros((H,W),np.uint8) np.maximum.at(img,(H-1-ys,xs),255) return Image.fromarray(img).filter(ImageFilter.MaxFilter(3)) cols=5; rows=9; W=400;H=420 canvas=Image.new('L',(cols*W,rows*H),0) draw
pythonimport numpy as np, math from PIL import Image, ImageFilter, ImageDraw, ImageFont raw=np.fromfile('/mnt/data/my_first_song.wav', dtype=np.uint8) arr=np.frombuffer(raw[44:].tobytes(), dtype='<i2').reshape(-1,2) rate=44100 Lall=arr[:,0].astype(np.int32); Rall=arr[:,1].astype(np.int32) mnx,mxx=Lall.min(),Lall.max(); mny,mxy=Rall.min(),Rall.max() def render(a,W=400,H=400): L=a[:,0].astype(np.int32); R=a[:,1].astype(np.int32) xs=((L-mnx)*(W-1)/(mxx-mnx)).astype(np.int32); ys=((R-mny)*(H-1)/(mxy-mny)).astype(np.int32) img=np.zeros((H,W),np.uint8) np.maximum.at(img,(H-1-ys,xs),255) return Image.fromarray(img).filter(ImageFilter.MaxFilter(3)) cols=5; rows=9; W=400;H=420 canvas=Image.new('L',(cols*W,rows*H),0) draw <<ImageDisplayed>>
pythonimport numpy as np from PIL import Image, ImageFilter raw=np.fromfile('/mnt/data/my_first_song.wav', dtype=np.uint8) arr=np.frombuffer(raw[44:].tobytes(), dtype='<i2').reshape(-1,2) rate=44100 Lall=arr[:,0].astype(np.int32); Rall=arr[:,1].astype(np.int32) mnx,mxx=Lall.min(),Lall.max(); mny,mxy=Rall.min(),Rall.max() def render_range(t0,t1,W=2400,H=1200, dilate=1): a=arr[int(t0*rate):int(t1*rate)] L=a[:,0].astype(np.int32); R=a[:,1].astype(np.int32) xs=((L-mnx)*(W-1)/(mxx-mnx)).astype(np.int32); ys=((R-mny)*(H-1)/(mxy-mny)).astype(np.int32) img=np.zeros((H,W),np.uint8) np.maximum.at(img,(H-1-ys,xs),255) im=Image.fromarray(img) if dilate>1: im=im.filter(ImageFilter.MaxFilter(dilate)) return im for t0,t1 in [(13,16),(15,18),(17,20),(19,22),(21,24),(23,26),(25,28),(13,28)]: im=render_range(t0,t1,3000,1000,3) im.save(f'/mnt/data/xy_{t0}_{t1}.png') print('done')
pythonimport numpy as np from PIL import Image, ImageFilter raw=np.fromfile('/mnt/data/my_first_song.wav', dtype=np.uint8) arr=np.frombuffer(raw[44:].tobytes(), dtype='<i2').reshape(-1,2) rate=44100 Lall=arr[:,0].astype(np.int32); Rall=arr[:,1].astype(np.int32) mnx,mxx=Lall.min(),Lall.max(); mny,mxy=Rall.min(),Rall.max() def render_range(t0,t1,W=2400,H=1200, dilate=1): a=arr[int(t0*rate):int(t1*rate)] L=a[:,0].astype(np.int32); R=a[:,1].astype(np.int32) xs=((L-mnx)*(W-1)/(mxx-mnx)).astype(np.int32); ys=((R-mny)*(H-1)/(mxy-mny)).astype(np.int32) img=np.zeros((H,W),np.uint8) np.maximum.at(img,(H-1-ys,xs),255) im=Image.fromarray(img) if dilate>1: im=im.filter(ImageFilter.MaxFilter(dilate)) return im for t0,t1 in [(13,16),(15,18),(17,20),(19,22),(21,24),(23,26),(25,28),(13,28)]: im=render_range(t0,t1,3000,1000,3) im.save(f'/mnt/data/xy_{t0}_{t1}.png') print('done') <<ImageDisplayed>>
pythonimport sympy as sp n=1896460 sp.factorint(n), [(d,n//d) for d in range(1,3000) if n%d==0][-20:] ({2: 2, 5: 1, 94823: 1}, [(1, 1896460), (2, 948230), (4, 474115), (5, 379292), (10, 189646), (20, 94823)])
pythonimport numpy as np, re, zlib, base64, itertools, os raw=np.fromfile('/mnt/data/my_first_song.wav', dtype=np.uint8) pcm=np.frombuffer(raw[44:].tobytes(), dtype='<i2') arr=pcm.reshape(-1,2) def bits_to_bytes(bits, msb=True): bits=np.array(bits,dtype=np.uint8) n=len(bits)//8*8 bits=bits[:n].reshape(-1,8) if msb: vals=(bits*np.array([128,64,32,16,8,4,2,1],dtype=np.uint8)).sum(axis=1).astype(np.uint8) else: vals=(bits*np.array([1,2,4,8,16,32,64,128],dtype=np.uint8)).sum(axis=1).astype(np.uint8) return vals.tobytes() for source,name in [(pcm,'interleaved'),(arr[:,0],'left'),(arr[:,1],'right')]: for bit in range(4): bits=((source.astype(np.uint16)>>bit)&1) for msb in [True,False]: b=bits_to_bytes(bits, msb) m=re.search(rb'putcCTF\{[^}]+\}', b) if m: print('found
pythonimport numpy as np from PIL import Image, ImageFilter, ImageDraw raw=np.fromfile('/mnt/data/my_first_song.wav', dtype=np.uint8) arr=np.frombuffer(raw[44:].tobytes(), dtype='<i2').reshape(-1,2) rate=44100 Lall=arr[:,0].astype(np.int32); Rall=arr[:,1].astype(np.int32) mnx,mxx=Lall.min(),Lall.max(); mny,mxy=Rall.min(),Rall.max() def render(t0,t1,W=200,H=200): a=arr[int(t0*rate):int(t1*rate)] L=a[:,0].astype(np.int32); R=a[:,1].astype(np.int32) xs=((L-mnx)*(W-1)/(mxx-mnx)).astype(np.int32); ys=((R-mny)*(H-1)/(mxy-mny)).astype(np.int32) img=np.zeros((H,W),np.uint8) np.maximum.at(img,(H-1-ys,xs),255) return Image.fromarray(img).filter(ImageFilter.MaxFilter(3)) start=12; end=29; step=0.25; dur=0.25 frames=int((end-start)/step) cols=8; W=220; H=240; rows=(frames+cols-1)//cols canvas=Image.new('L',(cols*W,rows*H),0) draw=ImageDraw.Draw(canvas) for i in range(frames): t=start+i*step im=render(t,t+dur,W,H-20) canvas.paste(im,((i%cols)*W,(i//cols)*H+20)) draw.text(((i%cols)*W+4,(i//cols)*H+2),f'{t:.2f}',fill=180) canvas.save('/mnt/data/xy_025_montage.png') print('saved frames',frames)
pythonimport numpy as np from PIL import Image, ImageFilter, ImageDraw raw=np.fromfile('/mnt/data/my_first_song.wav', dtype=np.uint8) arr=np.frombuffer(raw[44:].tobytes(), dtype='<i2').reshape(-1,2) rate=44100 Lall=arr[:,0].astype(np.int32); Rall=arr[:,1].astype(np.int32) mnx,mxx=Lall.min(),Lall.max(); mny,mxy=Rall.min(),Rall.max() def render(t0,t1,W=200,H=200): a=arr[int(t0*rate):int(t1*rate)] L=a[:,0].astype(np.int32); R=a[:,1].astype(np.int32) xs=((L-mnx)*(W-1)/(mxx-mnx)).astype(np.int32); ys=((R-mny)*(H-1)/(mxy-mny)).astype(np.int32) img=np.zeros((H,W),np.uint8) np.maximum.at(img,(H-1-ys,xs),255) return Image.fromarray(img).filter(ImageFilter.MaxFilter(3)) start=12; end=29; step=0.25; dur=0.25 frames=int((end-start)/step) cols=8; W=220; H=240; rows=(frames+cols-1)//cols canvas=Image.new('L',(cols*W,rows*H),0) draw=ImageDraw.Draw(canvas) for i in range(frames): t=start+i*step im=render(t,t+dur,W,H-20) canvas.paste(im,((i%cols)*W,(i//cols)*H+20)) draw.text(((i%cols)*W+4,(i//cols)*H+2),f'{t:.2f}',fill=180) canvas.save('/mnt/data/xy_025_montage.png') print('saved frames',frames) <<ImageDisplayed>>
pythonimport numpy as np from PIL import Image, ImageFilter, ImageDraw raw=np.fromfile('/mnt/data/my_first_song.wav', dtype=np.uint8) arr=np.frombuffer(raw[44:].tobytes(), dtype='<i2').reshape(-1,2) rate=44100 Lall=arr[:,0].astype(np.int32); Rall=arr[:,1].astype(np.int32) mnx,mxx=Lall.min(),Lall.max(); mny,mxy=Rall.min(),Rall.max() def render(t0,t1,W=500,H=220): a=arr[int(t0*rate):int(t1*rate)] L=a[:,0].astype(np.int32); R=a[:,1].astype(np.int32) xs=((L-mnx)*(W-1)/(mxx-mnx)).astype(np.int32); ys=((R-mny)*(H-1)/(mxy-mny)).astype(np.int32) img=np.zeros((H,W),np.uint8) np.maximum.at(img,(H-1-ys,xs),255) return Image.fromarray(img).filter(ImageFilter.MaxFilter(3)) start=13.0; end=28.0; step=0.5; dur=0.75 frames=int((end-start)/step) cols=2; W=700; H=260; rows=(frames+cols-1)//cols canvas=Image.new('L',(cols*W,rows*H),0); draw=ImageDraw.Draw(canvas) for i in range(frames): t=start+i*step im=render(t,t+dur,W,H-25) canvas.paste(im,((i%cols)*W,(i//cols)*H+25)) draw.text(((i%cols)*W+5,(i//cols)*H+5),f'{t:.2f}-{t+dur:.2f}',fill=180) canvas.save('/mnt/data/xy_075_montage.png') print('saved')
pythonimport numpy as np from PIL import Image, ImageFilter, ImageDraw raw=np.fromfile('/mnt/data/my_first_song.wav', dtype=np.uint8) arr=np.frombuffer(raw[44:].tobytes(), dtype='<i2').reshape(-1,2) rate=44100 Lall=arr[:,0].astype(np.int32); Rall=arr[:,1].astype(np.int32) mnx,mxx=Lall.min(),Lall.max(); mny,mxy=Rall.min(),Rall.max() def render(t0,t1,W=500,H=220): a=arr[int(t0*rate):int(t1*rate)] L=a[:,0].astype(np.int32); R=a[:,1].astype(np.int32) xs=((L-mnx)*(W-1)/(mxx-mnx)).astype(np.int32); ys=((R-mny)*(H-1)/(mxy-mny)).astype(np.int32) img=np.zeros((H,W),np.uint8) np.maximum.at(img,(H-1-ys,xs),255) return Image.fromarray(img).filter(ImageFilter.MaxFilter(3)) start=13.0; end=28.0; step=0.5; dur=0.75 frames=int((end-start)/step) cols=2; W=700; H=260; rows=(frames+cols-1)//cols canvas=Image.new('L',(cols*W,rows*H),0); draw=ImageDraw.Draw(canvas) for i in range(frames): t=start+i*step im=render(t,t+dur,W,H-25) canvas.paste(im,((i%cols)*W,(i//cols)*H+25)) draw.text(((i%cols)*W+5,(i//cols)*H+5),f'{t:.2f}-{t+dur:.2f}',fill=180) canvas.save('/mnt/data/xy_075_montage.png') print('saved') <<ImageDisplayed>>
pythonimport numpy as np from PIL import Image, ImageFilter, ImageDraw raw=np.fromfile('/mnt/data/my_first_song.wav', dtype=np.uint8) arr=np.frombuffer(raw[44:].tobytes(), dtype='<i2').reshape(-1,2) rate=44100 Lall=arr[:,0].astype(np.int32); Rall=arr[:,1].astype(np.int32) mnx,mxx=Lall.min(),Lall.max(); mny,mxy=Rall.min(),Rall.max() def render(t0,t1,W=500,H=220): a=arr[int(t0*rate):int(t1*rate)] L=a[:,0].astype(np.int32); R=a[:,1].astype(np.int32) xs=((L-mnx)*(W-1)/(mxx-mnx)).astype(np.int32); ys=((R-mny)*(H-1)/(mxy-mny)).astype(np.int32) img=np.zeros((H,W),np.uint8) np.maximum.at(img,(H-1-ys,xs),255) return Image.fromarray(img).filter(ImageFilter.MaxFilter(3)) start=13.0; end=28.0; step=0.5; dur=0.75 frames=int((end-start)/step) cols=2; W=700; H=260; rows=(frames+cols-1)//cols canvas=Image.new('L',(cols*W,rows*H),0); draw=ImageDraw.Draw(canvas) for i in range(frames): t=start+i*step im=render(t,t+dur,W,H-25) canvas.paste(im,((i%cols)*W,(i//cols)*H+25)) draw.text(((i%cols)*W+5,(i//cols)*H+5),f'{t:.2f}-{t+dur:.2f}',fill=180) canvas.save('/mnt/data/xy_075_montage.png') print('saved') <<ImageDisplayed>> <<ImageDisplayed>>
pythonimport pkgutil mods=[m.name for m in pkgutil.iter_modules() if 'sstv' in m.name.lower()] mods[:20], len(mods) ([], 0)
Đã solve được. Trick chính không phải nghe nhạc hay spectrogram thường, mà là oscilloscope / vectorscope X-Y:
(left_sample, right_sample) theo thời gian sẽ hiện hình/văn bản do “người bạn họa sĩ” vẽ bằng âm thanh.Mình đọc được flag là:
textputcCTF{dr4w1ng_w1th_s0und}
Ảnh render X-Y mình tạo để kiểm tra: xy_scope.png