문제를 풀려면 encile을 복호화하여 flag.png를 복구해야 한다.
즉, flag.png를 암호화한 rot128.py 코드를 살펴보자.
*복호화하려면 암호화된 코드를 알아야 함!*
#!/usr/bin/env python3
hex_list = [(hex(i)[2:].zfill(2).upper()) for i in range(256)]
with open('flag.png', 'rb') as f:
plain_s = f.read()
plain_list = [hex(i)[2:].zfill(2).upper() for i in plain_s]
enc_list = list(range(len(plain_list)))
for i in range(len(plain_list)):
hex_b = plain_list[i]
index = hex_list.index(hex_b)
enc_list[i] = hex_list[(index + 128) % len(hex_list)]
enc_list = ''.join(enc_list)
with open('encfile', 'w', encoding='utf-8') as f:
f.write(enc_list)
#ex)
hex_list = [(hex(i)[2:].zfill(2).upper()) for i in range(256)] #결과 값 : ['00', '01', '02',... 'FF']
with open('flag.png', 'rb') as f:
plain_s = f.read()
#결과 값 : b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x07....
plain_list = [hex(i)[2:].zfill(2).upper() for i in plain_s]
#결과 값 : ['89', '50', '4E', '47', '0D', '0A', '1A', '0A', '00', '00', '00', '0D', '49', '48', '44', '52', '00', '00', '07'....]
enc_list = list(range(len(plain_list))) #결과 값 : [0, 1, 2, 3, 4, 5........]
for i in range(len(plain_list)):
hex_b = plain_list[i] #결과 값 : '89', '50', '4E', '47', '0D'....
index = hex_list.index(hex_b) #결과 값 : 137, 80, 78, 71, 13....
enc_list[i] = hex_list[(index + 128) % len(hex_list)] #결과 값 : ['09', 'D0, 'CE, C7, 8D....]
enc_list = ''.join(enc_list) #결과 값 : ['09D0CEC78D....']
with open('encfile', 'w', encoding='utf-8') as f:
f.write(enc_list) #encfile을 만들어 enc_list 값을 넣기
하나씩 코드를 살펴보도록 하겠다.
#ex)
a = [hex(0), hex(1)]
print(a)
#결과 값 : ['0x0', '0x1']
hex()함수는 정수를 입력받아 그 정수에 상응하는 16진수 문자열을 리턴한다. 문자열 앞에는 0x를 붙인다.
#ex)
a = [hex(0)[2:], hex(1)[2:]]
print(a)
#결과 값 : ['0', '1']
[2:]는 슬라이싱으로 두 번째 index부터 끝까지 값을 가져온다.
#ex)
a = [hex(0)[2:].zfill(2), hex(1)[2:].zfill(2)]
print(a)
#결과 값 : ['00', '01']
.zfill()함수는 괄호안에 있는 숫자만큼 0을 채운다.
#ex)
a = [hex(10)[2:].zfill(2), hex(10)[2:].zfill(2).upper()]
print(a)
#결과 값 : ['0a', '0A']
upper()함수는 문자를 대문자로 바꾸어준다.
hex(10)의 경우 16진수로 a이기 때문에 A가 나오게 된다.
#ex)
hex_list = [(hex(i)[2:].zfill(2).upper()) for i in range(256)]
print(hex_list)
#결과 값 : ['00', '01', '02',... 'FF']
종합하여 hex_llist를 설명하자면 for문으로 0부터 255까지 반복하고 있다.
마지막에 FF인 이유는 255는 16진수로 0xff이고, 위의 함수들로 인해 FF가 나온다.
즉, hex_list 는 위의 결과 값과 같이 ['00', '01', '02',...'FF']의 결과가 들어가 있다.
with open('flag.png', 'rb') as f:
plain_s = f.read()
#ex)
with open('flag.png', 'rb') as f:
plain_s = f.read()
print(plain_s)
#결과 값 : b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x07....
open()함수는 파일을 열고 객체를 반환하는 함수이다.
즉, 파일을 읽거나 쓸 수 있다.
open함수는 open('파일', '모드')형태이다.
open 함수의 모드는 r은 읽기, w는 쓰기 등 많이 있다.
rb는 바이너리 읽기 모드이다.
즉, 바이트 형태로 flag.png를 읽어온다는 것이다.
그 읽어온 정보를 plain_s에 저장한다.
plain_list = [hex(i)[2:].zfill(2).upper() for i in plain_s]
plain_list = [hex(i)[2:].zfill(2).upper() for i in plain_s]
print(plain_list)
#결과 값 : ['89', '50', '4E', '47', '0D', '0A', '1A', '0A', '00', '00', '00', '0D', '49', '48', '44', '52', '00', '00', '07'....]
위의 hex_list와 같은 형태이다.
plain_list에 plain_s만큼 반복하여 두 개씩 묶는다.
그럼 위와 같은 결과 값이 plain_list에 들어간다.
enc_list = list(range(len(plain_list)))
#ex)
print(len('Apple'), len('Hello World!'))
#결과 값 : 5 12
len()함수는 길이를 구하는 함수이다.
Apple의 경우 글자가 5, Hello Wrold!의 경우 12개이기 때문에 5와 12가 나온다.
enc_list = list(range(len(plain_list)))
print(enc_list)
#결과 값 : [0, 1, 2, 3, 4, 5........]
그래서 len()함수로 plain_list의 길이만큼 리스트 형식으로 enc_list에 대입한다.
그럼 enc_list에는 [0, 1, 2, 3, 4..] 리스트 형식으로 plain_list의 길이만큼 들어간다.
for i in range(len(plain_list)):
hex_b = plain_list[i]
index = hex_list.index(hex_b)
enc_list[i] = hex_list[(index + 128) % len(hex_list)]
for i in range(len(plain_list)):
hex_b = plain_list[i] #결과 값 : '89', '50', '4E', '47', '0D'....
print(hex_b)
index = hex_list.index(hex_b) #결과 값 : 137, 80, 78, 71, 13....
enc_list[i] = hex_list[(index + 128) % len(hex_list)] #결과 값 : ['09', 'D0, 'CE, C7, 8D....]
for문은 plain_list의 길이만큼 반복한다.
hex_b는 plain_list의 값들이 들어간다. 즉 plain_list와 같다라고 볼 수 있다.
hex_b의 첫 번째 값은 89이다. 이를 대입해보면
index는 hex_list의 인덱스의 값 중 89의 값을 가지고 있는 인덱스가 나오게 된다.
89를 가지고 있는 인덱스는 137이기 때문에 137이 나오게 된다.
enc_list는 137+128 % 256을 하여 값이 9가 나오게 되고, 이 값은 enc_list에 ['09']로 저장이 된다.
*이 for문 과정이 암호화하는 과정이다.*
enc_list = ''.join(enc_list) #결과 값 : ['09D0CEC78D....']
암호화를 거쳤다면 이제 원래 두 개씩 묶어 있는 것을 다 합치기 위해 join함수를 쓴다.
그러면 위와 같이 ['09, 'D0', 'CE', 'C7', '8D'....]의 리스트가 합쳐진다.
with open('encfile', 'w', encoding='utf-8') as f:
f.write(enc_list)
이제 w모드 쓰기 모드로 encfile을 만들어 암호화 된 enc_list를 encfile에 쓴다.
이제 다음과 같은 encfile이 만들어지게 된다.
hex_list = [(hex(i)[2:].zfill(2).upper()) for i in range(256)] #결과 값 : ['00', '01', '02',... 'FF']
with open('encfile', 'r') as f: #암호화된 encfile을 가져온다.
enc_s = f.read()
enc_list = [enc_s[i:i+2] for i in range(0, len(enc_s), 2)] # encfile의 길이만큼 두 개씩 묶어서 데이터를 enc_list에 저장한다.
shell = list(range(len(enc_list))) #encfile의 길이를 shell에 대입한다.
for i in range(len(enc_list)):
hex_b = enc_list[i]
index = hex_list.index(hex_b)
shell[i] = hex_list[(index + 128) % len(hex_list)]
#위에서 설명한 첫 번째 값인 89를 예로 들면
#89는 hex_list의 인덱스 값으로 나타내면 137이다. 137을 계산 하였을 때 9가 나왔다.
#이것을 복호화하는 코드는 똑같이 128를 더하고 hex_list의 길이만큼 나머지 연산을 하는 것이다.
#128+9%256은 137이다. 137은 hex_list에서 89이기 때문에 원래 값이 나오게 된다.
#따라서 암호화와 똑같은 방법으로 다시 계산하면 복호화된 값이 나온다.
shell = ''.join(shell)
byte_data = bytes.fromhex(shell) #16진수 형식으로 되어 있기 때문에 바이트형식으로 바꿔준다.
with open('flag.png', 'wb') as f: #flag.png의 원래 형태가 바이너리이기 때문에 wb를 쓴다.
f.write(byte_data)
이런 암호화 형태를 카이사르 암호라고 한다.
카이사르 암호는 각각의 알파벳을 일정한 거리만큼 밀어 글자를 치환하는 방식으로 암호화한다.