사진 하나 있다. 가운데 무슨 바코드 같이 White~Black의 그림이 있는 게 특징인 거 같다.
해결 아이디어
White~Black의 색깔은 특징적으로 (R, G, B)값이 모두 같은 (N, N, N)을 가지고 있다는 특징이 있다.
아마 일렬로 있는 저 긴 사각형의 정사각형으로 나뉜 N들의 ASCII변환값에 힌트가 있지 않을까? 우선 해당 아이디어를 코딩해보았다.
### 7.py
from PIL import Image
import numpy
if __name__=="__main__" :
with Image.open("oxygen.png","r") as img :
pix = numpy.array(img)
y = img.height//2
ans = ""
### 단위사격각형 가로 크기가 7인건 그림판으로 쟀다
for x in range(0,img.width,7) :
ans += chr(pix[y][x][1])
print(ans)
출력결과는 다음과 같다.
smart guy, you made it. the next level is [105, 110, 116, 101, 103, 114, 105, 116, 121]n\S
잘 찾은거 같으니 나온 배열을 다시 ASCII화 해보자
### 7_1.py
if __name__=="__main__" :
base = [105, 110, 116, 101, 103, 114, 105, 116, 121]
ans = ''.join([chr(x) for x in base])
print(ans)
으흠 화면에는 PayPal로 연결되는 그림 하나밖에 없다. href 걸려있는 것도 실제로 PayPal로 이동하는 링크이다. 주석을 보자
아래 주석으로 돈을 보내달라는 이야기 인데, 거짓말일 거다. 설마 풀리는 그것도 웃기는 경우니까 ㅋ 맨 윗줄에 zip이라는 주석이 있다. channel.html이라는 소스를 channel.zip으로 변경하면 압축파일을 다운로드할 수 있다.
압축파일에는 약910개정도의 텍스트 파일이 있다. 예시로 하나 열어보면 linkedlist문제 때와 같은 Next nothing is XXXX라는 문자열이 있다. 제일 아래를 보면 다음과 같이 readme가 있다.
readme.txt를 open하면 다음과 같은 안내문이 있다.
welcome to my zipped list.
hint1: start from 90052
hint2: answer is inside the zip
글쿠만 90052로 부터 시작하는 코드를 작성하자
### 코드 6.py
import re
if __name__=="__main__" :
nothing = 90052
while(True) :
try :
with open("./channel/"+str(nothing)+".txt","r") as f:
data = f.read()
nothing = re.findall("Next nothing is (\d+)",data)[-1]
print(nothing)
except Exception as e:
print(data)
break
이번 문제를 풀기위한 설명만을 하자면 ZIP 파일의 구조는 504 b0304~ (로컬 파일 헤더) + (데이터 디스크립터) + 504 b0102~ (센트럴 딕셔너리 file header)로 이루어져 있다. 모든 파일에 대해서 위와 같은 파일 형식이 들어가는데, 우리가 집중해야 되는 곳은 504b0102로 시작하는 센트럴 딕셔너리 file header이다.
이곳의 offset+len(file_name)+len(Extra_file)가 file_comment 인다. 우리 문제에서는 다행히도 comment는 한 글자로 표현되어있기 때문에, 다음과 같이 파싱 해 보았다.
### 코드 6_1.py
if __name__=="__main__" :
plz_answer = list()
avoid_list = [0,32,42]
with open("channel.zip","rb") as f:
data = f.read()
for i in range(len(data)) :
if(data[i:i+4] == b"\x50\x4b\01\02" and data[i-1] not in avoid_list) :
plz_answer.append(chr(data[i-1]))
print("".join(plz_answer))
특수문자인 *, 공백(" "), 줄바꿈이 나오길래 이들은 제외하고 출력한 결과를 보고 싶었다. 다음과 같다.
### 코드 6_2.py
import re
if __name__=="__main__" :
struct = dict()
### ZIP 파일의 센트럴 딕셔너리를 파싱하는영역
### 결과로 sturct를 만든다. struct는 확장자를 제외한 파일명을 key로 하고
### comment를 value로 하는 딕셔너리
with open("channel.zip","rb") as f:
data = f.read()
for i in range(len(data)) :
if(data[i:i+4] == b"\x50\x4b\01\02") :
number = list()
### 파일이름등에 따라 comment의 위치가 변하기 때문에 44~100으로 범위를 설정하고
### .txt를 만나면 멈추도록 해두었다.
for j in range(44,100) :
if(data[i+j] >=48 and data[i+j]<= 57) :
number.append(chr(data[i+j]))
if(data[i+j:i+j+4] == b"\x2e\x74\x78\x74") :
break
if(len(number)!=0) :
number = "".join(number)
struct[int(number)] = chr(data[i+50+len(number)])
### 생성된 struct에서 정답을 찾는다.
nothing = 90052
answer = struct[nothing]
while(True) :
try :
with open("./channel/"+str(nothing)+".txt","r") as f:
nothing = re.findall("Next nothing is (\d+)",f.read())[-1]
answer += struct[int(nothing)]
except Exception as e:
print(answer)
break
* 필자는 구현할때 offset을 정수 값으로 때려 넣었는데, 나중에 Doc을 보니까 file name와 extra file의 길이가 offset 22부터 해서 저장되더라 제기랄 그거 볼걸 풀었으니 귀찮게 하지 말고 넘어가자
pickle은 파이선전용으로 자료를 직렬화 하기 위해 만들어진 모듈이다. 소스파일에 처음 보는 html 태그가 있다. 바로 <peakhell> 이라는 태그인데, src프로퍼티에 banner.p가 있다. 아마 이 banner.p에 우리 문제의 해결 key가 들어 있을 듯하다 다운로드하여 코딩 시간이다.
import pickle
if __name__=="__main__" :
with open("banner.p","rb") as f :
ans = pickle.load(f)
print(ans)
SHIT! 진정하자. 파일 이름이 banner임을 기억하자. " "와 "#"로 이루어진 것을 보면 이는 ASCII art일 가능성이 농후하다. 그도 그럴 것이 2차원 배열로 이루어진 이 자료구조 원소들의 1번째 인덱스의 합은 전부 95로 동일하다. 같은 길이의 행이라는 것을 암시하는 것이다. 코딩 타임이다.
import pickle
if __name__=="__main__" :
with open("banner.p","rb") as f :
ans = pickle.load(f)
for i in range(len(ans)) :
for j in range(len(ans[i])) :
print(ans[i][j][0]*ans[i][j][1],end="")
print("")