ZIP 파일 형식이란 데이터를 압축, 보관하기 위한 파일형식이다. ZIP 파일은 하나 혹은 여러 개의 파일들을 그 크기를 줄여 압축하고 하나로 묶어 저장한다. ZIP 파일 형식에서는 다양한 종류의 압축 알고리즘의 사용이 가능하나, 2009년 현재 Deflate 알고리즘만이 가장 많이 사용되고 지원되는 압축 알고리즘이다.
파일 형식은 1989년 필 캐츠가 PKZIP에서 사용하기 위해 만들어진 것으로, Thom Henderson의 ARC 파일 압축 형식을 발전시킨 것이다. 현재도 PKZIP 형식은 PKZIP뿐만 아닌 다른 많은 유틸리티 소프트웨어에서 지원하고 있다. 마이크로소프트사는 1998년도부터 "압축 폴더"라는 이름으로 운영 체제에 포함시켜 지원하고 있으며, 애플사는 맥 오에스 텐 10.3버전부터 지원하고 있다.
ZIP 파일은 일반적으로 그 파일의 확장자로 ".zip" 혹은 ".ZIP"으로 사용하고, MIME 형식으로는 application/zip으로 표시하여 사용한다. 또한 다양한 소프트웨어에서 파일 저장 형식으로 사용되고 있으나, 그럴 경우 일반적으로 그 파일의 확장자가 다른 형태로 저장된다. 예를 들어, 자바의 경우 .jar 로, 모질라 파이어폭스의 애드온의 경우 .xpi로, 이드 소프트웨어사의 .pk3/.pk4 파일이 그러하며, 윈앰프나 윈도 미디어 플레이어의 스킨 파일들과 오픈오피스의 오픈도큐먼트 파일인 .odt와 마이크로소프트사의 Open XML 파일 형식인 .docx, 안드로이드의 애플리케이션 파일인 .apk도 그러하다.
https://ko.wikipedia.org/wiki/ZIP_(%ED%8C%8C%EC%9D%BC_%ED%8F%AC%EB%A7%B7)
집(Zip) 파일의 구조는 간략하게 위와 같이 나타낼 수 있다. 굉장히 심플한 구조를 나타내고 있으나 추가된게 많아져서 조금 복잡해졌다고 한다.
Zip 파일을 읽게되면 순서는 대략적으로 아래와 같이 나타 낼 수 있다.
① 최초 실행시 End of Central Directory로 이동한다.
② End of Central Directory에서 정보를 읽는다.
③ End of Central Directory에서 Central Directory위치로 이동한다.
④ Central Directory에서 정보를 읽는다.
⑤ End of Central Directory에서 Central Directory의 개수만큼 반복하여 End of Central Directory전까지 읽는다.
⑥ Central Directory에서 읽은 Local Header 위치로 차례대로 접근한다.
⑦ Local Header에서 정보를 읽고 해당되는 File Data를 압축해제한다.
⑧ ⑥ ~ ⑦을 Local Header의 개수 만큼 반복한다.
※ 자세히 알아보기전에 이 내용에는 Little-Endian이 중구난방인 상태이므로 Little-Endian을 알고 가는게 좋겠다.
https://ko.wikipedia.org/wiki/%EC%97%94%EB%94%94%EC%96%B8
이제 좀 더 자세히 알아보자.
① 최초 실행시 End of Central Directory로 이동한다.
- 파일이 실행이 되면 먼저 맨 마지막으로 이동하여 앞으로 차례대로 이동하면서 Signature를 읽게 된다. 그러긴 위해선 먼저 End of Central Directory의 구조를 알아보자.
임의 | Offset | Bytes | Description[25] |
1. | 0 | 4 | End of central directory signature = 0x06054b50 |
2. | 4 | 2 | The number of this disk (containing the end of central directory record) |
3. | 6 | 2 | Number of the disk on which the central directory starts |
4. | 8 | 2 | Number of central directory records on this disk |
5. | 10 | 2 | Total number of central directory records |
6. | 12 | 4 | Size of central directory (bytes) |
7. | 16 | 4 | Offset of the start of the central directory on the disk on which the central directory starts |
8. | 20 | 2 | The length of the following comment field |
9. | 22 | n | Comment |
End of Central Directory의 구조는 위와 같이 구성 되어 있다. 아래의 예제를 통해서 하나씩 설명해보겠다.
1. 0x504B0506 : End of Central Directory의 Signature로써 이 값을 가진다면 이 부분은 End of Central Directory가 된다.
2. 0x0000 : 분할 파일일 때 현재 디스크 번호이다. 최대 0xffff를 가질 수 있다.
3. 0x0000 : 분할 파일일 때 Central Directory가 시작되는 disk 번호
4. 0x0002 : 이 디스크에 있는 Central Directory records의 개수
5. 0x0002 : Central Directory records의 총 개수
6. 0x00000074 : Central Directory records의 크기
7. 0x00014495 : Directory records의 시작 offset 0x11495 = Byte 83093 (분할 압축일때는 해당 디스크 기준으로)
8. 0x0000 : 파일 Comment의 길이
9. NULL : 파일에 Comment가 없으므로 나타나지 않음.
② End of Central Directory에서 정보를 읽는다.
따라서 위의 예제의 End of Central Directory 정보는 아래와 같다.
End of Central Directory | |
Signature | '\x50\x4b\x05\x06'. |
Disk Number | 0 |
Disk # w/cd | 0 |
Disk entries | 2 |
Total entries | 2 |
Central directory size | 116Bytes |
Offset of cd wrt to starting disk | 83093Bytes |
Comment len | 0 |
ZIP file comment | NULL |
③ End of Central Directory에서 Central Directory위치로 이동한다.
- End of Central Directory에서 얻은 정보로 Central Directory 위치로 이동하게 된다.
- 정보를 읽기 위해서 먼저 Central Directory의 구조를 파악해보자.
임의 | Offset | Bytes | Description[25] |
1. | 0 | 4 | Central directory file header signature = 0x02014b50 |
2. | 4 | 2 | Version made by |
3. | 6 | 2 | Version needed to extract (minimum) |
4. | 8 | 2 | General purpose bit flag |
5. | 10 | 2 | Compression method |
6. | 12 | 2 | File last modification time |
7. | 14 | 2 | File last modification date |
8. | 16 | 4 | CRC-32 |
9. | 20 | 4 | Compressed size |
10. | 24 | 4 | Uncompressed size |
11. | 28 | 2 | File name length (n) |
12. | 30 | 2 | Extra field length (m) |
13. | 32 | 2 | File comment length (k) |
14. | 34 | 2 | Disk number where file starts |
15. | 36 | 2 | Internal file attributes |
16. | 38 | 4 | External file attributes |
17. | 42 | 4 | Relative offset of local file header. This is the number of bytes between the start of the first disk on which the file occurs, and the start of the local file header. This allows software reading the central directory to locate the position of the file inside the .ZIP file. |
18. | 46 | n | File name |
19. | 46+n | m | Extra field |
20. | 46+n+m | k | File comment |
Central Directory의 구조는 위와 같이 구성 되어 있다. 아래의 예제를 통해서 하나씩 설명해보겠다.
1. 0x504B0102 : Central Directory의 Signature이다.
2. 0x0014 : 압축 생성시 Version 정보이다. 아래의 표를 참고하여 VFAT인 것을 알 수있다.
Byte | Version | Byte | Version |
0 | MS-DOS and OS/2 (FAT/VFAT/FAT32) | 11 | MVS(OS/390-Z/OS) |
1 | Amiga | 12 | VSE |
2 | OpenVMS | 13 | Acorn Risc |
3 | UNIX | 14 | VFAT |
4 | VM/CMS | 15 | alternate MVS |
5 | Atari ST | 16 | BeOS |
6 | OS/2 H.P.F.S | 17 | Tandem |
7 | Machintosh | 18 | OS/400 |
8 | Z-System | 19 | OS/X(Darwin) |
9 | CP/M | 20-255 | unused |
10 | Windows NTFS |
|
3. 0x0014 : Version needed to extract로써 압축 해제할 때 필요한 버전이다. 여기선 0x14 = 십진법 20 = 2.0 version이 되게 된다. 버전에 따른 정보는 아래의 표를 참고한다.
Version | value | Version | value |
1.0 | Default value | 5.0 | File is encrypted using 3DES |
1.1 | File is a volume label | 5.0 | File is encrypted using original RC2 encryption |
2.0 | file is a folder(directory) | 5.0 | File is encrypted using RC4 encryption |
2.0 | file is compressed using Deflate compression | 5.1 | File is encrypted using AES encryption |
2.0 | File is encrypted using traditional PKWARE encryption | 5.1 | File is encrypted using corrected RC2 encryption** |
2.1 | File is compressed using Deflate64(tm) | 5.2 | File is encrypted using corrected RC2-64 encryption** |
2.5 | File is compressed using PKWARE DCL Implode | 6.1 | File is encrypted using non-OAEP key wrapping*** |
2.7 | File is a patch data set | 6.2 | Central directory encryption |
4.5 | File uses ZIP64 format extensions | 6.3 | File is compressed using LZMA |
4.6 | File is compressed using BZIP2 compression | 6.3 | File is compressed using PPMd+ |
5.0 | File is encrypted using DES
| 6.3 | File is encrypted using Blowfish |
|
| 6.3 | File is encrypted using Twofish |
4. 0x0000 : Flag값으로써 아래의 표를 참고하여 정보를 알 수 있다. 아래의 표를 통해 encrypted file인것을 알 수 있다.
Bit | Purpose | Bit | Purpose |
Bit 00 | encrypted file | Bit 06 | strong encryption |
Bit 01 | compression option | Bit 07 ~ 10 | unused |
Bit 02 | compression option | Bit 11 | language encoding |
Bit 03 | data descriptor | Bit 12 | reserved |
Bit 04 | enhanced deflation | Bit 13 | mask header values |
Bit 05 | compressed patched data | Bit 14 ~ 15 | reserved |
5. 0x0008 : Compressed method = 압축이 어떤 방법을 사용했는지 알 수 있다. 아래의 표를 참고하여 deflated를 사용함을 알 수 있다. 대부분의 기본 zip 파일의 압축 방법은 deflated를 따른다.
Byte | Method | Byte | Method |
0 | no compression | 10 | PKWare DCL imploded |
1 | shrunk | 11 | reserved |
2 | reduced with compression factor 1 | 12 | compressed using BZIP2 |
3 | reduced with compression factor 2 | 13 | reserved |
4 | reduced with compression factor 3 | 14 | LZMA |
5 | reduced with compression factor 4 | 15 ~ 17 | reserved |
6 | imploded | 18 | compressed using IBM TERSE |
7 | reserved | 19 | IBM LZ77 z |
8 | deflated | 98 | PPMd version l, Rev 1 |
9 | enhanced deflated |
|
|
※ Deflated 알고리즘 : DEFLATE는 기본적으로 LZ77 알고리즘을 통해 데이터를 압축한 뒤, 중복되는 내용에 대한 포인터(일치하는 내용의 위치와 길이)를 허프만 부호화를 사용하여 한 번 더 압축한다.
DEFLATE 알고리즘은 일반적으로 그 압축률에 비해 압축/해제 속도가 빠르나, 나중에 나온 압축 알고리즘에 비해서는 압축률이 다소 떨어지는 경향이 있다.
LZ77 알고리즘 = http://uzys2011.tistory.com/152
허프만 부호화 = http://wooyaggo.tistory.com/95
6. 0xBCA2 : 파일의 최종 수정시간이 된다.
① BCA2 -> A2BC : little endian이기때문에 순서를 바꿔준다.
② A 2 B C를 이진법으로 나타낸다.
A 2 B C
1010, 0010, 1011, 1100
③ 1010001010111100 :이진법으로 나타낸 것을 다 이어 붙인다.
④ (10100)01010111100 : 괄호안에 있는 값을 10진법으로 바꾼 값이 시간이 된다. (앞에서 5bits) = 20(Hour)
⑤ 10100(010101)11100 : 괄호안에 있는 값을 10진법으로 바꾼 값이 분이 된다. (이어서 6bits) = 21(Minutes)
⑥ 1010001010(111100) : 괄호안에 있는 값을 10진법으로 바꾼 값에 *2를 하면 초가 된다. (이어서 6bits) = 28 * 2 = 56(Seconds)
⑦ 20:21:56에 이 파일이 최종 수정이 되었다는 것을 알 수 있다.
Bits | Means | Bits | Means |
Bits 00~04 | seceonds divided by 2 | Bit 11~15 | hour |
Bits 05~10 | minute | Stored in standard MS-DOS format |
7. 0xDA48 : 파일의 최종 수정날짜가 된다.
① DA48 -> 48DA : little endian이기 때문에 순서를 바꿔준다.
② 4 8 D A를 이진법으로 나타낸다.
4 8 D A
0100, 1000, 1101, 1010
③ 0100100011011010 : 이진법으로 나타낸 것을 다 이어 붙인다.
④ (010010)0011011010 : 괄호안에 있는 값을 10진법으로 바꾼 값에 1980을 더하면 년도가 된다.(앞에서 6bits) = 36 + 1980 = 2016(Year)
⑤ 0100100(0110)11010 : 괄호안에 있는 값을 10진법으로 바꾼 값이 월이 된다. (이어서 4bits) = 6(Month)
⑥ 01001000110(11010) : 괄호안에 있는 값을 10진법으로 바꾼 값이 날이 된다. (이어서 5bits) = 26(Day)
⑦ 2016/6/26에 이 파일이 최종 수정이 되었다는 것을 알 수 있다.
Bits | Means | Bits | Means |
Bits 00~04 | day | Bit 09~15 | years from 1980 |
Bits 05~08 | month | Stored in standard MS-DOS format |
CRC 정보 - https://ko.wikipedia.org/wiki/%EC%88%9C%ED%99%98_%EC%A4%91%EB%B3%B5_%EA%B2%80%EC%82%AC
9. 0x0B440100 : Compressed size로 압축된 파일 크기를 의미한다.
① 0x0B440100 -> 0x0001440B : Little-Endian이기 때문에 순서를 바꿔준다.
② 82955Bytes : 0x001440B를 10진법으로 바꾸면 82955 Bytes가 나온다.
10. 0xDCAC0100 : Uncompressed size로 압축되기 전 파일 크기를 의미한다.
① 0xDCAC0100 -> 0x0001ACDC : Little-Endian이기 때문에 순서를 바꿔준다.
② 109788 Bytes : 0x0001ACDC를 10진법으로 바꾸면 109788 Bytes가 나온다.
11. 0x0008 : File name length으로 파일 이름 길이를 나타낸다.
12. 0x0008 : Extra field length으로 엑스트라 필드의 길이를 의미한다.
13. 0x0000 : File comment length으로 파일 코맨드의 길이를 의미한다.
14. 0x0000 : 이 파일이 시작되는 디스크의 수
15. 0x0001 : 파일의 내부 속성이다 아래의 표를 참고한다.
Bit | Means | Bit | Means |
Bit 0 | apparent ASCll / text file | Bit 2 | control field records precede logical records |
Bit 1 | reserved | Bit 3~16 | unused |
16. 0x00000020 : 파일의 외부 속성이다. Host-System에 영향을 받으며 Version Made by를 참고한다.
※ 14, 15, 16에 대한 설명이 부족한거 같아서 아래의 정보를 추가한다. 정확히 어떻게 해석해야하는지 감이 안온다.
17. 0x00000000 : 제일 처음의 Local Header의 Relative Offset을 나타낸다. 00000000이므로 00000000의 위치가 Local Header 1의 위치가 된다.
18. 0x313131312E4A5047 : 파일의 이름이다.
19. 18.에 이어서 0x504B0506앞까지 : Extrafield 와 Filecomment가 있다.
④ Central Directory에서 정보를 읽는다.
Central Directory | |
Signature | '\x50\x4b\x01\x02'. |
Version | 0x14 = VFAT |
Version needed | 0x14 = 20 -> 2.0 |
Flags | 00 = Encrypted file |
Compression method | 08 = deflated |
File modification time | (10100)01010111100 = 20(Hours) 10100(010101)11100 = 21(Minutes) 10100010101(11100)= 28*2=56(Seconds) = 20:21:56 |
File modification date | (0100100)011011010 = 36(Year) + 1980 = 2016 0100100(0110)11010 = 6(Month) 01001000110(11010) = 26(Day) = 2016/6/26 |
Crc-32 checksum | 0x087b1295 |
Compressed size | 0001440B = 82955Bytes |
Uncompressed size | 0001ACDC = 109788Bytes |
File name length | 0008 -> 8Bytes |
Extra field length | 0008 -> 8Bytes |
File comment length | 0000 -> 0Bytes |
Disk # start | 0000 -> 0 |
Internal attributes | Bit 1 = Reserved |
External attributes | 0x20 |
Offset of local header | 0000 |
File name | 111.jpg |
Extra field | 0x7A E5 04 00 B5 03 00 00 |
File comment |
|
⑤ End of Central Directory에서 Central Directory의 개수만큼 반복하여 End of Central Directory전까지 읽는다.
⑥ Central Directory에서 읽은 Local Header 위치로 차례대로 접근한다.
- Local Header의 정보는 Central Directory의 부분집합이 된다. 그러므로 Central Directory에 있는 정보로 Local Header의 정보를 거의 다 알 수 있다.
- 그래도 Central Directory와 구조가 약간 다르니 구조를 알아보자.
Offset | Bytes | Description[25] |
0 | 4 | Local file header signature = 0x04034b50 (read as a little-endian number) |
4 | 2 | Version needed to extract (minimum) |
6 | 2 | General purpose bit flag |
8 | 2 | Compression method |
10 | 2 | File last modification time |
12 | 2 | File last modification date |
14 | 4 | CRC-32 |
18 | 4 | Compressed size |
22 | 4 | Uncompressed size |
26 | 2 | File name length (n) |
28 | 2 | Extra field length (m) |
30 | n | File name |
30+n | m | Extra field |
Central Directory와 거의 유사하다. 단지 Version , file commend length, disk #start, internal attr, external attr, Offset of local header를 제외하고 signature만 다를 뿐 나머지는 다 똑같다.
⑦ Local Header에서 정보를 읽고 해당되는 File Data를 압축해제한다.
위의 예제 기준으로 Local Header의 정보는 아래와 같이 정리 할 수 있다.
Local Header #1 | |
Signature | '\x50\x4b\x03\x04'. |
Version | 0x14 = 20 -> 2.0 |
Flags | 00 = Encrypted file |
Compression method | 08: deflated |
File modification time | (10100)01010111100 = 20(Hours) 10100(010101)11100 = 21(Minutes) 10100010101(11100)= 28*2=56(Seconds) = 20:21:56 |
File modification date | (0100100)011011010 = 36(Year) + 1980 = 2016 0100100(0110)11010 = 6(Month) 01001000110(11010) = 26(Day) = 2016/6/26 |
Crc-32 checksum | 0x087b1295 |
Compressed size | 0001440B = 82955Bytes |
Uncompressed size | 0001ACDC = 109788Bytes |
File name length | 0008 -> 8Bytes |
Extra field length | 0008 -> 8Bytes |
File name | 111.jpg |
Extra field | 0x7A E5 04 00 B5 03 00 00 |
이러한 정보들을 이용하여 file #에 있는 파일을 해독하여 압축을 하거나 풀게된다.
⑧ ⑥ ~ ⑦을 Local Header의 개수 만큼 반복한다.
⑨ 맺으며
- Extra field가 어떤 역할을 하는지 좀 더 알아봐야 될 듯하나 영향을 크게 주는 부분인거 같지는 않다.
- 시작되는 디스크의 수나 internal attr나 external attr이 정확히 어떻게 정해지는지 잘 모르겠다.
- 마지막으로 2010년도에 반디집 만들었던 사람이 요약해둔 파일을 첨부한다. 매우 간략하게 되어있고 좀 더 신빙성 있으신 분의 글이므로 많은 참고가 될 듯 싶다.
ZIP 포맷.xlsx <http://kippler.tistory.com/91>
< 참고 자료 >
http://www.softlab365.com/wordpress/?p=478
https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html
https://en.wikipedia.org/wiki/Zip_(file_format)
'Reversing' 카테고리의 다른 글
악성코드 분석을 위한 리버스 엔지니어링 - PE 구조 (0) | 2017.08.23 |
---|---|
OllyDBG를 이용한 지뢰찾기 지뢰위치 확인 (0) | 2017.08.22 |
OllyDBG를 이용한 지뢰찾기 시간흐름 변경 (0) | 2017.08.22 |
악성코드 분석을 위한 리버스 엔지니어링 - 기본지식2 (0) | 2017.08.22 |
악성코드 분석을 위한 리버스 엔지니어링 - 기본지식 (0) | 2017.08.22 |
최근댓글