[Python3] 손상된 ZIP 압축 파일 복구하기

Posted by 적분 ∫2tdt=t²+c
2018.06.24 03:00 프로그래밍

며칠 전에 서버에서 작업을 진행하다가 용량 문제 때문에 잘 안 쓰는 대량의 파일들을 zip으로 압축해둔적이 있습니다. 문제는 zip 포맷의 한계로 4GB 이상의 파일을 다룰수가 없다는 문제가 있는걸 모르고 그냥 압축을 진행한 뒤 생성된 압축파일을 검증하지도 않고 그냥 원본 파일들을 다 지워버렸던 거죠. 뒤늦게 알아차리고 zip파일 압축을 해제하려고 했으나 손상된 파일이라며 압축 해제를 계속 실패했습니다...


Archive:  archive.zip

  End-of-central-directory signature not found.  Either this file is not

  a zipfile, or it constitutes one disk of a multi-part archive.  In the

  latter case the central directory and zipfile comment will be found on

  the last disk(s) of this archive.

note:  archive.zip may be a plain executable, not an archive

unzip:  cannot find zipfile directory in one of archive.zip or

        archive.zip.zip, and cannot find archive.zip.ZIP, period.


(아 안돼...)


여러 압축 유틸을 받아서 열어보았지만, 복구되는 파일은 4개에 불과했습니다. (전체 압축한게 1만개가 넘었는데...) 도저히 자료를 그대로 날릴수가 없어서 ZIP 파일 포맷 명세를 확인해보았습니다.


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


정말 다행인게 ZIP 포맷은 오픈소스로 명세가 다 공개되어있고, 또한 압축이 파일 단위로 진행되기에 일부 손상된 파일이 있어도 나머지 파일들은 멀쩡하게 복구할 수 있다는 거였습니다.


ZIP파일의 구조를 간략하게 설명하자면, 먼저 N개의 File Entry가 차례대로 나열되고, 마지막에 Central Directory 정보가 덧붙습니다. 사실 실제 압축된 파일의 정보들은 File Entry에 들어가고, Central Directory 정보에는 기타 메타데이터 및 빠른 파일 색인을 위한 정보들이 있는것이라서 이 부분은 없어도 파일을 복구하는데에는 문제가 없습니다.


File Entry의 구조는 다음과 같습니다.

OffsetBytes

Description

04

파일헤더 시그니처 ('PK\x03\x04'의 값)

42버전
62범용 비트 플래그
82압축 방법
102마지막 수정 시간
122마지막 수정 날짜
144CRC-32
184압축된 크기
224압축되기 전 크기 (r)
262

파일 이름 길이 (n) (바이트 단위)

282기타 필드 길이 (m) (바이트 단위)
30n

파일 이름

30+nm

기타 필드

30+n+m

r

압축된 데이터 (총 r바이트 길이)

이 부분이 계속 반복되면서 총 N개의 파일을 표현하는 것이죠. 만약 정상적인 파일이라면 하나의 File Entry를 다 읽은 다음에는 바로 다음 File Entry가 등장해야합니다. 이건 파일 헤더 시그니처 값이 'PK\x03\x04'가 나오는지를 통해서 확인할 수 있습니다.


그럼 제 파일은 왜 압축 해제 유틸에서 제대로 열리지 않았을까요? 이는 앞의 4개까지는 File Entry가 제대로 나왔는데, 그 다음부터는 잘못된 값들이 덮어씌워져 파일 헤더가 제대로 나타나지 않았기 때문입니다.


PK.. ~~~~~~ PK.. ~~~~~~~~~~~ PK.. ~~~  PK.. ~~~~~

원래는 위와 같은 모양으로 데이터가 나와야하는데 중간에 잘못된 값이 덮어씌워져서


PK.. ~~~~~~ PK.. ~~~~~~~~~ABCBASBD~~  PK.. ~~~~~

PK..가 등장할것으로 예상되는 지점에 다른 값이 섞여버려 있더라구요. 이렇게 되면 두번째 파일과 세번째 파일은 사실 값이 깨졌기 때문에 복구할수가 없습니다. 그러나 네 번째 파일부터는 또 제대로 PK..라고 시그니처가 등장하기 때문에 이 지점부터는 또 복구가 가능합니다. 따라서 중간에 예상치 않은 잘못된 값이 들어왔을 경우 다음 헤더 시그니처인 PK\x03\x04의 위치를 찾아 점프하는 식으로 살릴 수 있는 모든 File Entry를 발견할 수 있습니다.


그래서 급하게 코딩을 했습니다.




코드를 실행할때 손상된 zip파일의 경로를 argument로 넘겨주면, 그 중에서 복구 가능한 File Entry만 추출하여 restored.zip으로 저장합니다. 이렇게 되면 restored.zip파일은 비록 Central Directory는 없지만 다른 압축 유틸에서 충분히 복구가 가능한 상태가 됩니다.


압축을 시도했던 원본 파일들이 약 1만개가 넘었는데, 이 방법을 통해 720개를 제외한 나머지 9천 여개의 파일들을 복구할 수 있었습니다. (십년 감수했지요) 혹시나 저와 같이 문제를 겪으시는 분들을 위해 코드를 공유하니 자유롭게 쓰시면 되겠습니다.


물론 제일 좋은건 손상된 zip파일을 만들지 않는 것이구요!


결론1: 파일을 지우기 전에 3번만 더 생각하자.

결론2: ZIP파일은 오래된 포맷이라 4GB 이상의 파일을 지원하지 않는다!



Tags
이 댓글을 비밀 댓글로
    • 2018.07.19 12:37
    비밀댓글입니다
    • python3랑 pycharm community edition 설치해서 위 코드 넣고 실행하시면 될듯합니다. 맥을 써보지는 않아서 맥에서 개발작업을 해보지는 않았지만, 맥에서도 pycharm을 많이 쓴다고 들었거든요
    • 2018.07.19 14:42
    비밀댓글입니다
    • 2018.07.19 17:24
    비밀댓글입니다
    • sys.argv[1] 부분을 복구를 원하시는 파일 경로로 바꿔넣으세요. 그 아래 43번 라인의 sys.argv[1]도 마찬가지이고요.

      w = open('restored.' + 'my.zip', 'wb')
      with open('my.zip', 'rb') as f:

      처럼요
    • 2018.08.10 19:02
    비밀댓글입니다
    • '사진'이라는 파일이 없다는 겁니다. zip 파일이면 '사진.zip'이라고 해야하지 않을까요?
    • 2018.08.12 13:01
    비밀댓글입니다
    • 2018.11.23 23:45
    비밀댓글입니다
    • 정말 안타까운 상황이네요.. 도움이 될수 있다면 도와드리고 싶지만 egg 파일은 알집이 독자적인 알고리즘을 써서 압축한거라 복구가 쉽지 않을것 같습니다. 이 포스팅에서 공유한 방법은 zip파일에만 해당하는거라서요..
    • 2019.05.10 00:22
    비밀댓글입니다
    • 분할압축 파일 손상에 대해서는 정확하게 모르겠네요. 본 포스팅의 경우는 4GB이상의 단일 zip파일 손상에 대한것이라서요.. 안타깝지만 도움이 되어드리기 어려울것 같습니다.
  1. 저도 지금 툴을 써도 안되는 상황인데
    이 코드를 어디다가 써야 실행이되나오 ㅠㅠㅠㅠ부탁드립니다 도와주세요...ㅠㅠㅠㅠㅠ
    1.python과 pycharm community edition는 깔았는데 어떻게 코드를 넣고 그걸 실행하는지잘모르겠습니다...
    2.그리고 파이썬 폴더와 같은 폴더에 두라고하셨는데
    파이썬폴더 위치가 어디있나요??
    • python을 설치하셨으면, 메모장을 여셔서 저 코드를 복사하시고 zipfixer.py 등의 이름으로 저장하시면 됩니다. (zipfixer.py를 손상된 zip파일이 있는 폴더에 같이 두시는게 편합니다.)

      그리고 해당 폴더가 열려있는 탐색기 창주소줄에
      python zipfixer.py <손상된zip파일이름>
      라고 입력하시면 해당 코드가 실행됩니다.
      • jebaluu
      • 2019.07.03 16:11

      글에서 자세히 알려주셨던 전문적인 방법이 아니라 이렇게 쉽게된다구요..?
      해봤는데 cmd창 같은거뜨고 아무일이없네요...ㅠㅠㅠㅠㅠ
      동작자체를 안하는거같은데
      음...최상위폴더여야되는건가..흐아
      막 따로 코드 실행되서 나온 압축파일이 그 폴더에 저ㅏㅈㅇ되고 그러지
      다른곳에 저장되거나 그러지않죠?
    • 음 창이 바로 꺼진다면 뭔가 에러가 난것 같습니다. 그러면
      해당 탐색기 창 주소줄에 cmd를 입력하셔서 cmd창을 켜시고, 거기서
      python zipfixer.py <손상된zip파일이름>
      라고 입력해보세요. 어떤 에러메세지가 나오는지 보실수 있을거에요
      • jebaluu
      • 2019.07.05 11:42
      구문이 올바르지않다고뜨네요...
      혹시 카카오톡 오픈채팅으로 가능하실까요??
      만약 압축파일이 복구된다면 작은 사례라도 하겠습니다 ㅠㅠㅠㅠ
    • 자세한 내용은 댓글로 주고받는것보다 메일로 소통하는게 좋을것 같습니다. bab2min@gmail.com 로 메일주시면 도와드리겠습니다.