드림이는 비밀스런 이미지 파일을 자신이 공부한 알고리즘을 통해 인코딩 하였어요.
인코딩 프로그램을 분석하여 원본 이미지를 알아내주세요.
원본 파일을 구한 경우 imageviewer.py를 통해 이미지를 볼 수 있습니다.
문제 파일을 다운 받으면 prob, secretMessage.enc, imagevier.py 파일이 존재한다. prob 파일은 인코딩 프로그램이며 secretMessage.enc은 proc로 인코딩 한 결과 생성된 파일이며 원본 파일을 구한 경우 이미지를 보는 imageviewer.py로 이뤄져 있다.
파일을 인코딩하는 프로그램인 proc를 ida로 열어보면 main 함수를 볼 수 있다.
6 : secretMessage.raw 파일을 byte단위로 읽어 v3 변수에 저장
7 : byte단위로 파일을 쓰는 모드로 secretMessage.enc 파일을 열어 파일 포인터를 반환해 stream에 저장
8 : 매개변수를 secretMessage.raw의 파일 포인터와 secretMessage.enc 파일 포인터로 설정하여 sub_7FA 함수 호출
즉, secretMessage.raw 파일을 읽어서 sub_7F4 함수를 호출해 인코딩을 한 후 결과를 secretMessage.enc 파일에 저장하고 있다.
인코딩 기능을 하는 sub_7F4 함수를 분석해보자. 8~51줄까지의 코드를 분석해보면 아래와 같다.
14 : a1에서 1byte 읽어서 c에 저장
15 ~ 16 : c가 EOF(-1)면 반복문 종료
17 : c를 a2에 저장
18 : 만약 c(현재글자)가 v5(이전글자)랑 같다면 조건문 실행
23 : a1에서 1byte 읽어서 c(다음글자)에 저장
24 ~ 25 : c가 EOF(-1)면 반복문 종료
26 ~ 32 : c(다음글자)가 v5(이전글자)랑 다르면 v3를 a2에 저장하며 c를 a2에 저장하며 v5(이전글자)에 c(다음글자)를 저장 후 반복문 종료
33 ~ 38 : c(다음글자)가 v5(이전글자)랑 같다면 v3 ++로 증가시킨다. v3의 범위는 unsinged int 8이므로 0~255이므로 -1이 되면 255를 a2에 저장한 다음에 v5(이전글자)를 -1로 설정하고 반복문 종료
41 ~ 44. 이전글자랑 현재 글자랑 다르면 c(현재글자)를 v5(이전글자)에 저장한다.
이를 아래와 같이 예시를 들어서 도식화 해봤다. 즉, a1(원본파일)에 a b b b c c d d d d e 가 저장되어 있다고 치면 인코딩된 파일에는 a b b 1 c c 0 d d 2 e가 저장된다. 왜냐하면 이전글자랑 현재글자가 같으면 18줄에 의해 반복문을 한번 더 돌게 되어 있으며 c(다음글자)랑 v5(이전글자)가 다르면 v3(반복횟수)를 a2에 저장하고 c(다음글자)를 저장하지만 c(다음글자)랑 v5(이전글자)가 같으면 v3(반복횟수)를 1씩 증가시키기 때문이다.
이로써 알아낸 규칙은 원본파일에 두개 이상 반복되는 글자가 있으면 몇번 반복되는지 숫자로 적어서 인코딩된 파일을 만든다. 즉, 원본 파일 a b b b c c d d d d e 에서 b가 세번 반복 , c가 두번 반복, d가 네번 반복 하기 때문에 b b 1 / c c 0 / d d 2 / e 와 같이 표현이 된다. 뒤에 숫자는 같은 글자가 2이상 반복할 때 몇번 더 반복하는지를 나타낸다.
따라서 인코딩된 파일에서 원본 파일을 구하기 위해서는 연속으로 같은 문자가 나올 시 다른 문자가 나올 때까지 읽은 다음에 다른 문자를 int 형으로 바꿔서 같은 문자를 다른문자의 숫자만큼 반복해 원본파일에 쓴다. 예를들면, b b 2가 있으면 b b b b 를 원본에 쓴다는 것이다. 따라서 아래와 같이 코드를 작성하여서 원본파일인 raw 파일을 획득하였다.
import io
def decode(a1,a2):
prev = -1
while True :
c = a1.read(1)
if not c :
break
a2.write(c)
if prev == c :
while True :
c = a1.read(1)
if not c :
break
if c!=prev :
change = int.from_bytes(c,byteorder='little')
for i in range(change) :
print(i)
a2.write(prev)
break
else:
prev = c
def main():
with open('secretMessage.enc','rb') as a1, open('secretMessage.raw','wb') as a2:
decode(a1, a2)
if __name__ == '__main__':
main()
이를 처음에 제공된 imagevierwer.py에 secretMessage.raw 파일을 이미지로 변환하는 명령을 실행하면 아래와 같이 flag를 획득할 수 있다.