Python Challenge 6의 url은 다음과 같다.

Python challenge 6 : http://www.pythonchallenge.com/pc/def/channel.html

 

now there are pairs

 

www.pythonchallenge.com

* zipline을 활용한 해결방법을 기술하고 있지 않습니다.


구성

으흠 화면에는 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

머지않아 다음과 같은 결과를 만날 수 있다.

...
91038
44221
992
8700
45100
68628
67824
46145
Collect the comments.

응? 이게 무슨 소리인지? comments를 모으라고 한다. comments가 의견/주석처럼 쓰이는 단어인데, txt 파일에 comment가 있단 말인가? 힌트는 readme.txt에 있는 hint2 answer is ininside the zip에 있다.

 

해결 아이디어

zip파일의 헤더 구조를 일단 읽고 오자

https://en.wikipedia.org/wiki/ZIP_(file_format) 

 

ZIP (file format) - Wikipedia

From Wikipedia, the free encyclopedia Jump to navigation Jump to search Family of archive file formats ZIP is an archive file format that supports lossless data compression. A ZIP file may contain one or more files or directories that may have been compres

en.wikipedia.org

이번 문제를 풀기위한 설명만을 하자면 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))

특수문자인 *, 공백(" "), 줄바꿈이 나오길래 이들은 제외하고 출력한 결과를 보고 싶었다. 다음과 같다.

EYEYONYE
OG
YGEYENGGOXEEXGEYOOGXEXOEE
NXGENNGXEXYXNOOOGGOOGYYNYX
GNN
XYEYONXEX
OX
NX
GOYNXNYXEEENGYEOOGOOO
XYGEEYXNYOOXXGNYXOOGONG
ENYEOENEY
NYEXOYGNYGOO
EYGYXGEOOEXEOGY
OONXXXEGXEXGOYEOYEYNYGGXNEOXOYXEYOOXXXXO
EEGYG

??? 아무의미가 없는 문자열이었다. 문득 스쳐 지나간 생각은

 

"순서에 맞게 풀면 ASCII Art가 나오나?"

 

였다. 아래는 위의 아이디어를 구현하기 위해 사용한 코드이다.

### 코드 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부터 해서 저장되더라 제기랄 그거 볼걸 풀었으니 귀찮게 하지 말고 넘어가자

 

다음과 같은 출력결과를 얻을 수 있었다.

찾았다

Answer Url? : http://www.pythonchallenge.com/pc/def/hockey.html 

 

어? 다음과 같은 안내문이 있다.

공기 중에 있다고 한다. 글자를 보라는 안내문에 따라 HOCKEY를 이루고 있는 글자인 oxygen이 답인 듯하다

 Answer Url : http://www.pythonchallenge.com/pc/def/oxygen.html


여담으로 이 문제를 쉽게 해결하는 방법은 모듈 zipline을 이용하는 것이다. zipline을 활용하면 필자가 수동으로 했던 헤더 분석 및 추출 등을 보다 쉽게 할 수 있다. (필자는 왜 수동으로 허튼짓을 헷냐고? 내 맘이다 절대 늦게 봐서가 아니다)

Python Challenge 5의 url은 다음과 같다.

Python challenge 5 : http://www.pythonchallenge.com/pc/def/peak.html

 

peak hell

 

www.pythonchallenge.com


구성

무슨 사진이 떡하니 있다. 이번엔 특별한 상호작용도 없는 거 같다. 사진을 다운로드하여보면 사진 이름이 peakhell.jpg 이라는 점이 특이한 상황인 거 같다. 번역은 다음과 같다.

 

발음해봐

 

ㅋㅋㅋㅋ발음했다 포스팅 재목에


해결 아이디어

peakhell을 발음하면 피-켈이다. pickle이라는 모듈을 모듈 소개시간에 소개한 적이 있는데, 모르는 사람은 읽고 오기를 추천한다.

2021.04.11 - [분류 전체보기] - [Python] - 모듈탐구 pickle - 자료구조의 저장과 불러오기

 

[Python] - 모듈탐구 pickle - 자료구조의 저장과 불러오기

컴퓨터 세계에서 자주 사용되는 json이라는 표준이다. json은 Python을 자주 사용하는 사람이라면 아는 dictionary이라는 자료구조와 유사하게 생겼는데, 광범위하게 데이터 오브젝트를 인간이 읽을

tutoreducto.tistory.com

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)

출력 결과는 다음과 같다.

[[(' ', 95)], [(' ', 14), ('#', 5), (' ', 70), ('#', 5), (' ', 1)], 
[(' ', 15), ('#', 4), (' ', 71), ('#', 4), (' ', 1)], [(' ', 15), 
('#', 4), (' ', 71), ('#', 4), (' ', 1)], [(' ', 15), ('#', 4), 
(' ', 71), ('#', 4), (' ', 1)], [(' ', 15), ('#', 4), (' ', 71), 
('#', 4), (' ', 1)], [(' ', 15), ('#', 4), (' ', 71), ('#', 4), 
(' ', 1)], [(' ', 15), ('#', 4), (' ', 71), ('#', 4), (' ', 1)],
[(' ', 15), ('#', 4), (' ', 71), ('#', 4), (' ', 1)], [(' ', 6), 
('#', 3), (' ', 6), ('#', 4), (' ', 3), ('#', 3), (' ', 9), ('#', 3), 
(' ', 7), ('#', 5), (' ', 3), ('#', 3), (' ', 4), ('#', 5), (' ', 3), 
('#', 3), (' ', 10), ('#', 3), (' ', 7), ('#', 4), (' ', 1)], [(' ', 3), 
('#', 3), (' ', 3), ('#', 2), (' ', 4), ('#', 4), (' ', 1), ('#', 7),
(' ', 5), ('#', 2), (' ', 2), ('#', 3), (' ', 6), ('#', 4), (' ', 1),
('#', 7), (' ', 3), ('#', 4), (' ', 1), ('#', 7), (' ', 5), ('#', 3), 
(' ', 2), ('#', 3), (' ', 5), ('#', 4), (' ', 1)], [(' ', 2), ('#', 3), 
(' ', 5), ('#', 3), (' ', 2), ('#', 5), (' ', 4), ('#', 4), (' ', 3),
('#', 3), (' ', 3), ('#', 4), (' ', 4), ('#', 5), (' ', 4), ('#', 4),
(' ', 2), ('#', 5), (' ', 4), ('#', 4), (' ', 3), ('#', 3), (' ', 5),
('#', 3), (' ', 3), ('#', 4), (' ', 1)], [(' ', 1), ('#', 3), (' ', 11),
('#', 4), (' ', 5), ('#', 4), (' ', 3), ('#', 3), (' ', 4), ('#', 3), 
(' ', 4), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 4), (' ', 5),
('#', 4), (' ', 2), ('#', 3), (' ', 6), ('#', 4), (' ', 2), ('#', 4),
(' ', 1)], [(' ', 1), ('#', 3), (' ', 11), ('#', 4), (' ', 5), ('#', 4),
(' ', 10), ('#', 3), (' ', 4), ('#', 4), (' ', 5), ('#', 4), (' ', 2), 
('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 3), (' ', 7), ('#', 3), 
(' ', 2), ('#', 4), (' ', 1)], [('#', 4), (' ', 11), ('#', 4), (' ', 5),
('#', 4), (' ', 5), ('#', 2), (' ', 3), ('#', 3), (' ', 4), ('#', 4), 
(' ', 5), ('#', 4), (' ', 2), ('#', 4), (' ', 5), ('#', 4), (' ', 1), 
('#', 4), (' ', 7), ('#', 3), (' ', 2), ('#', 4), (' ', 1)], [('#', 4),
(' ', 11), ('#', 4), (' ', 5), ('#', 4), (' ', 3), ('#', 10), (' ', 4),
('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 4), (' ', 5), ('#', 4), 
(' ', 1), ('#', 14), (' ', 2), ('#', 4), (' ', 1)], [('#', 4), (' ', 11),
('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 3), (' ', 4), ('#', 4),
(' ', 4), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 4), (' ', 5), 
('#', 4), (' ', 1), ('#', 4), (' ', 12), ('#', 4), (' ', 1)], [('#', 4), 
(' ', 11), ('#', 4), (' ', 5), ('#', 4), (' ', 1), ('#', 4), (' ', 5),
('#', 3), (' ', 4), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 4),
(' ', 5), ('#', 4), (' ', 1), ('#', 4), (' ', 12), ('#', 4), (' ', 1)],
[(' ', 1), ('#', 3), (' ', 11), ('#', 4), (' ', 5), ('#', 4), (' ', 1), 
('#', 4), (' ', 5), ('#', 3), (' ', 4), ('#', 4), (' ', 5), ('#', 4), 
(' ', 2), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 3), (' ', 12), 
('#', 4), (' ', 1)], [(' ', 2), ('#', 3), (' ', 6), ('#', 2), (' ', 2), 
('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 3), (' ', 4), ('#', 4),
(' ', 4), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 4), (' ', 5),
('#', 4), (' ', 3), ('#', 3), (' ', 6), ('#', 2), (' ', 3), ('#', 4), 
(' ', 1)], [(' ', 3), ('#', 3), (' ', 4), ('#', 2), (' ', 3), ('#', 4),
(' ', 5), ('#', 4), (' ', 3), ('#', 11), (' ', 3), ('#', 4), (' ', 5), 
('#', 4), (' ', 2), ('#', 4), (' ', 5), ('#', 4), (' ', 4), ('#', 3),
(' ', 4), ('#', 2), (' ', 4), ('#', 4), (' ', 1)], [(' ', 6), ('#', 3), 
(' ', 5), ('#', 6), (' ', 4), ('#', 5), (' ', 4), ('#', 2), (' ', 4),
('#', 4), (' ', 1), ('#', 6), (' ', 4), ('#', 11), (' ', 4), ('#', 5),
(' ', 6), ('#', 3), (' ', 6), ('#', 6)], [(' ', 95)]]

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("")

출력 결과는 다음과 같다. 잘려서 사진으로 올린다.

찾았다.

Answer Url : http://www.pythonchallenge.com/pc/def/channel.html

Python Challenge 4의 url은 다음과 같다.

Python challenge 4 : http://www.pythonchallenge.com/pc/def/linkedlist.php

 

follow the chain

 

www.pythonchallenge.com


구성

으흠; 그림이 어떤 걸 의미하는지는 모르겠다. 관습적으로 소스 페이지를 확인해보자

그림에 a태그 href로 nothing=12345가 걸려있다. 눌러보자

오호 어떤 건지 알겠다. 재귀적으로 탐색하며 다음 nothing을 전달하다 보면 해결각이 나오나 보다 코딩 타임이다.


해결 아이디어

주석에는 urllib를 쓰라고 되어있는데 외부 모듈인 requests에 익숙해져 있는 필자는 이걸 쓰겠다. 문자열 탐색은 본문 페이지의 있는 nothing is가 고정임을 이용해서 저번 시간 사용한 정규표현식 re를 써본다.

import re
from requests import *

if __name__=="__main__" :
  url = "http://www.pythonchallenge.com/pc/def/linkedlist.php?nothing="
  nothing = "12345"

  while(True) :
    new_url = url+nothing
    res = get(new_url)
    print(res.text)
    nothing = re.findall("nothing is (\d+)",res.text, re.DOTALL)[-1]

그릏다. re.findall 모듈은 정규표현식이 ()로 둘러싸인 곳만 list화 한다. 하다 보면 오류가 난다

...
and the next nothing is 54249
and the next nothing is 29247
and the next nothing is 13115
and the next nothing is 23053
and the next nothing is 3875
and the next nothing is 16044
Yes. Divide by two and keep going.
Traceback (most recent call last):
  File "\python_challenge\4.py", line 13, in <module>
    nothing = re.findall("nothing is (\d+)",res.text, re.DOTALL)[-1]
IndexError: list index out of range

반으로 나누란다. 8022를 집어넣고 계속해보자 중간에 무슨 잘못된 안내가 있다고 하는데, 우리가 짠 코드로는 그냥 넘어갈 수 있으니, 그냥 넘어가고 계속하다 보면 다음과 같은 결과가 나온다.

...
and the next nothing is 96791
and the next nothing is 75635
and the next nothing is 52899
and the next nothing is 66831
peak.html
Traceback (most recent call last):
  File "\python_challenge\4.py", line 13, in <module>
    nothing = re.findall("nothing is (\d+)",res.text, re.DOTALL)[-1]
IndexError: list index out of range

찾았다.

Answer Url : http://www.pythonchallenge.com/pc/def/peak.html

Python Challenge 3의 url은 다음과 같다.

Python challenge 3 : http://www.pythonchallenge.com/pc/def/equality.html

 

re

 

www.pythonchallenge.com


구성

페이지 번역은 다음과 같다.

 

정확하게 세 개의 큰 보디가드를 양옆에 둔 하나의 작은 문자를 찾으시오

 

또다시 페이지 주석을 보자

또다시 많은 문자열들이 보인다. 설루션대로 문제의 풀이를 위해서는 정규표현식의 힘을 빌려야 될 거 같다. 

정규표현식이 무엇인지 모르는 사람은 아래를, Python에서 정규표현식 사용법은 그 아래를 참고하고 오도록 하자

2021.04.17 - [정보보안-이론] - 정규표현식에 대하여

 

정규표현식에 대하여

정규표현식은 여러 목적으로 사용된다. 정규표현식 자체는 어렵지 않다. 정규식(正規式)은 특정한 규칙을 가진 문자열의 집합을 표현하는 데 사용하는 형식 언어이다. < 정규표현식의 정의 : 출

tutoreducto.tistory.com

2021.04.19 - [개발/Python] - [Python] - 모듈탐구 re - [^정규표현식$?]

 

[Python] - 모듈탐구 re - [^정규표현식$?]

정규표현식을 모르고 있는 사람은 아래 포스팅을 읽고 오자 2021.04.17 - [정보보안-이론] - 정규표현식에 대하여 정규표현식에 대하여 정규표현식은 여러 목적으로 사용된다. 정규표현식 자체는

tutoreducto.tistory.com


해결 아이디어

이전문제와 동일하게 문자열은 3.dat에 저장하였으나, 개행 변환은 하지 않았다. 이번에는 개행 문자의 유무가 문제풀이에 영향이 갈 거 같았다. re모델을 통해 작성한 탐색용 정규표현식은 [A-Z]{3}[a-z]{1}[A-Z]{3}이다.

import re

if __name__=="__main__" :
  with open("3.dat","r") as f:
    data = f.read()

  answer = re.findall("[a-z][A-Z]{3}[a-z][A-Z]{3}[a-z]",data)

  for i in range(len(answer)) :
    print(answer[i][4],end="")

 

출력은 다음과 같았다.

>>> 
linkedlist

찾았다.

Answer Url : http://www.pythonchallenge.com/pc/def/linkedlist.html? 

 

힝 속았지! 페이지는 php에 있다고 한다 다음부터는 정적인 스크립트는 안쓸생각인가 보다

Answer Url : http://www.pythonchallenge.com/pc/def/linkedlist.php

 

Python Challenge 2의 url은 다음과 같다.

Python challenge 2 : http://www.pythonchallenge.com/pc/def/ocr.html

 

ocr

 

www.pythonchallenge.com


구성

페이지 번역은 다음과 같다. 힌트는 쓰지 않겠다.

 

"문자를 인식하시오. 책 안에 있을 수도 있으나, 혹시 Page Source에 있을 수도 있습니다."

 

말장난이다. Page가 장임을 이용해서 BOOK과 소속 관계를 2ke2ke하는거 같은데, Page Source안에는 다음과 같은 주석을 찾을 수 있다.

엄청많은 특수문자들이 있고, 위에 한 줄 주석으로 다음의 값 중 희귀한 값을 뽑아 보라고 한다. 코딩 타임이다.


해결 아이디어

문자열이 너무 길어 2.dat에 저장하였고, 개행 문자는 다 ""로 치환했다. 문자열이 약 10만 개 정도 되는데, 이는 순차 탐색이나, 간단한 명령어로도 반복 효율이 1초 이하로 나오기 때문에, set으로 변환을 해서 distinct 한 값으로 변환하고 이를 count 해서 가장 적게 나온 값을 출력했다.

if __name__=="__main__" :

    with open("2.dat","r") as f:
        raw_string = f.read().replace("\n","")

    raw_set = set(list(raw_string))
    raw_dict = {x:raw_string.count(x) for x in raw_set}

    new_dict = sorted(raw_dict.items(),key=(lambda x:x[1]))
    
    for i in new_dict :
        print(i[0],end="")

출력은 아래와 같았다.

>>> 
eutqilya^*&{$+!%}[_#](@)

응? 이게 무슨 글자지? 문제는 여기서 발생한다. set은 순서 없는 자료구조이다 보니, 아마 글자에서 나온 순서대로 보여준 게 아닐 거다. 코드를 조금 추가해서 eutqilya가 코드에 나온 순서로 정렬되게 바꾸자

if __name__=="__main__" :

    with open("2.dat","r") as f:
        raw_string = f.read().replace("\n","")

    raw_set = set(list(raw_string))
    raw_dict = {x:raw_string.count(x) for x in raw_set}

    new_dict = sorted(raw_dict.items(),key=(lambda x:x[1]))
    
    for i in new_dict :
        print(i[0],end="")

    target_string = "eutqilya"
    answer_list = list()
    for i in range(len(target_string)) :
        answer_list.append([target_string[i],raw_string.find(target_string[i])])

    answer_list = sorted(answer_list,key=(lambda x:x[1]))
    
    print("")
    
    for item in answer_list :
        print(item[0],end="")
    

출력은 다음과 같다.

>>> 
utylaqie^*&${+!%}[_#](@)
equality

찾았다 요놈

 

Answer Url : http://www.pythonchallenge.com/pc/def/equality.html


여담으로 이문제를 쉽게 해결하는 방법은 모듈인 collections을 활용하는 방법이다. 필자가 위에서 뻘짓을 한걸 미리 구현해 둔 모듈이다. collections을 활용한 풀이는 다음과 같다.

import collections


if __name__ == "__main__" :
    with open("2.dat","r") as f:
        res = f.read().replace("\n","")
        
    ans = collections.Counter(res)
    print(ans.most_common())

출력은 아래와 같다.

>>> 
[(')', 6186), ('@', 6157), ('(', 6154), (']', 6152), ('#', 6115), ('_', 6112), 
('[', 6108), ('}', 6105), ('%', 6104), ('!', 6079), ('+', 6066), ('$', 6046), 
('{', 6046), ('&', 6043), ('*', 6034), ('^', 6030), 
('e', 1), ('q', 1), ('u', 1), ('a', 1), ('l', 1), ('i', 1), ('t', 1), ('y', 1)]

 

Python Challenge 1의 url은 다음과 같다.

Python challenge 1 : http://www.pythonchallenge.com/pc/def/map.html

 

What about making trans?

everybody thinks twice before solving this. g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj.

www.pythonchallenge.com


구성

화면의 글자는 다음과 같다.

 

모두들 이걸 풀기 전에 한 번 더 생각하세요

 

g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj.

 

일반적인 TIP

* 힌트를 사용하세요, 보통 유용하답니다

* 당신에게 주어진 데이터를 조사하세요

* 스포일러를 조심하세요

 

으흠;; 핑크색 글자의 K를 M으로 O를 Q로, E를 G로 바꾸면 될라나? 다음과 같은 코드를 작성해 보았다.

def change(letters,src,dst) :
    return letters.replace(src,dst)

if __name__=="__main__" :
    string = "g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj."

    string = change(string,"k","m")
    string = change(string,"o","q")
    string = change(string,"e","g")

    print(string)
    
출력결과 : 
g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr ammnsrcpq ypc dmp. bmglg gr gl 
zw fylb gq glcddgagclr ylb rfyr q ufw rfgq rcvr gq qm jmlg. sqglg qrpglg.myicrpylq() 
gq pcammmclbcb. lmu ynnjw ml rfc spj.

어? 그래도 틀리다. 조금더 세밀하게 규칙을 보면 패턴을 찾을 수 있다.

맞다 눈치챈사람들도 있겠지만 이는 ROT2의 Shifting 암호이다.

문제는 우리에게 일부 항목만을 보여 주었을 뿐이다. 프로그램을 조금 수정하자 

if __name__=="__main__" :
    string = "g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle qrpgle.kyicrpylq() gq pcamkkclbcb. lmu ynnjw ml rfc spj."
    new_string = ""
    
    for i in range(len(string)) :
        ### ord('a')가 97이고, ord('z')가 122이다.
        if(ord(string[i]) >= 97 and ord(string[i]) <= 122) :
            new_string += chr(97+(ord(string[i])-95)%26)
        else :
            new_string += string[i]

    print(new_string)

두 칸씩 밀릴 수 있게 소문자 ascii 값을 2씩 더해주면서 새로운 new_string을 생성했다. 출력은 다음과 같다.

i hope you didnt translate it by hand. 
thats what computers are for. doing it in by hand is inefficient and that's why this text 
is so long. using string.maketrans() is recommended. now apply on the url.

?? 그렇다 사실 string.maketrans()라는 함수가 있다고 한다. 뭐 어떤가 이런 방법 저런 방법 있는 거지 

그런데 이제 어떻게 다음문제를 넘어갈 수 있을까?? 이번 문제의 소스는 map.html이다 map이라는 문자열을 ROT2 하면 OCR이다. 자 넘어가자

 

Answer Url : http://www.pythonchallenge.com/pc/def/ocr.html

 

+ Recent posts