본문 바로가기
디지털 파일 검증 연구소 디지털 파일 검증 연구소

GIF 파일 구조 분석|GIF87a·GIF89a 헤더와 LZW 압축 원리

디지털 파일 검증 연구소 읽는 시간 약 39분

GIF(Graphics Interchange Format)는 정지 이미지와 간단한 애니메이션을 저장하는 데 사용되는 이미지 파일 형식이다.

일반적으로 .gif 확장자를 사용하며, GIF87a와 GIF89a라는 두 가지 주요 버전이 알려져 있다.

GIF는 PNG, JPEG, WebP와 달리 파일 헤더, 논리 화면 설명자, 색상 테이블, 이미지 설명자, 확장 블록, LZW 압축 데이터 및 종료자가 순서대로 배치되는 블록 기반 구조를 사용한다.

특히 애니메이션 GIF에서는 여러 이미지 블록과 Graphic Control Extension, Application Extension 등이 함께 사용될 수 있다.

이번 글에서는 GIF 파일 구조 분석을 중심으로 GIF87a·GIF89a 시그니처, Logical Screen Descriptor, Global Color Table, Image Descriptor, LZW 압축 데이터와 확장 블록의 역할을 설명한다.

또한 Windows PowerShell 5.1을 이용해 GIF 파일의 헤더, 이미지 크기, 색상 테이블, 블록 경계 및 종료자를 확인하는 방법을 살펴본다.


1. GIF 파일 구조 분석이 필요한 이유

GIF 파일은 웹사이트의 아이콘, 배너, 간단한 움직이는 이미지, 오래된 그래픽 자료 등에서 사용된다.

하지만 .gif 확장자가 붙어 있다고 해서 내부 구조가 항상 정상적인 것은 아니다.

다음과 같은 상황에서 GIF 파일 구조 분석이 필요할 수 있다.

  • GIF 파일이 이미지 뷰어에서 열리지 않는 경우
  • 움직이는 GIF가 첫 번째 프레임만 표시되는 경우
  • 애니메이션이 중간에 멈추는 경우
  • 색상이 비정상적으로 표시되는 경우
  • 투명 배경이 예상과 다르게 나타나는 경우
  • 파일 전송 중 데이터가 잘렸는지 확인하려는 경우
  • GIF87a와 GIF89a의 차이를 조사하려는 경우
  • 원본 파일과 복사본의 무결성을 비교하려는 경우

GIF 구조 분석은 이미지 파일의 기본 형식과 내부 블록 구성을 확인하는 과정이다.

다만 파일의 헤더와 블록 경계가 정상이라는 사실만으로 LZW 압축 데이터가 완전히 정상이라고 판단할 수는 없다.

2. GIF87a와 GIF89a의 차이

GIF의 대표적인 두 버전은 GIF87a와 GIF89a다.

구분GIF87aGIF89a
대표 시그니처GIF87aGIF89a
기본 이미지 저장지원지원
색상 테이블지원지원
Graphic Control ExtensionGIF89a 규격 기능 아님지원
투명 색상 지정기본 규격 기능 아님지원
텍스트·주석 등 확장제한적규격에 정의
애니메이션 활용제한적확장 블록과 함께 널리 사용

GIF89a는 GIF87a의 기본 구조를 확장해 그래픽 제어와 추가 정보를 표현할 수 있도록 설계되었다.

다만 GIF89a 자체가 모든 애니메이션 동작을 완전히 정의하는 것은 아니다.

특히 반복 횟수 설정에는 Netscape Application Extension과 같은 널리 사용되는 관행적 확장이 활용된다.

3. GIF 파일의 전체 구조

일반적인 GIF 파일은 다음과 같은 구조를 가진다.

GIF File
│
├── Header
│   └── GIF87a / GIF89a
│
├── Logical Screen Descriptor
│
├── Global Color Table (Optional)
│
├── Data Blocks
│   ├── Graphic Control Extension (Optional)
│   ├── Image Descriptor
│   ├── Local Color Table (Optional)
│   ├── LZW Minimum Code Size
│   ├── Image Data Sub-blocks
│   ├── Comment Extension (Optional)
│   ├── Application Extension (Optional)
│   └── Additional Images / Extensions
│
└── Trailer

실제 파일에서는 이미지 블록과 확장 블록이 여러 번 등장할 수 있다.

각 이미지에는 전역 색상 테이블 또는 지역 색상 테이블이 사용될 수 있다.

GIF의 이미지 데이터는 일반적으로 LZW 방식으로 압축되어 저장된다.

4. GIF 파일 시그니처와 Magic Number

GIF 파일의 시작 6바이트에는 버전 정보를 포함하는 시그니처가 저장된다.

GIF87a

47 49 46 38 37 61

ASCII 문자열:

GIF87a

GIF89a

47 49 46 38 39 61

ASCII 문자열:

GIF89a

두 버전 모두 처음 3바이트는 GIF이다.

하지만 파일 식별 시에는 가능하면 6바이트 전체를 확인해야 한다.

확장자가 .gif라고 해도 이 시그니처가 일치하지 않으면 실제 GIF 파일이 아닐 수 있다.

5. GIF 헤더와 Logical Screen Descriptor

GIF 파일은 6바이트 헤더 다음에 7바이트 Logical Screen Descriptor가 배치된다.

기본 구조표

오프셋크기필드
06바이트GIF Signature
62바이트Logical Screen Width
82바이트Logical Screen Height
101바이트Packed Fields
111바이트Background Color Index
121바이트Pixel Aspect Ratio

가로와 세로 크기는 16비트 리틀엔디언 값이다.

예를 들어 다음과 같은 값이 있다고 가정하자.

80 02

리틀엔디언으로 해석하면 0x0280, 즉 640이다.

주의사항

Logical Screen Width와 Height는 전체 논리 화면의 크기를 나타낸다.

애니메이션을 구성하는 개별 이미지 블록의 크기와 항상 동일한 것은 아니다.

6. Packed Fields의 비트 구조

Logical Screen Descriptor의 Packed Fields에는 전역 색상 테이블의 존재 여부와 색상 관련 정보가 저장된다.

비트의미
7Global Color Table Flag
6~4Color Resolution
3Sort Flag
2~0Global Color Table Size

Global Color Table Flag

최상위 비트가 1이면 Global Color Table이 존재한다.

Color Resolution

원본 색상 표현과 관련된 정보를 나타낸다.

이 값은 실제 색상 테이블의 항목 수와 동일한 의미가 아니다.

Global Color Table Size

하위 3비트는 전역 색상 테이블의 크기를 계산하는 데 사용된다.

Number of Entries = 2 ^ (N + 1)

여기서 N은 0~7 범위의 값이다.

예를 들어 N이 7이면 256개 항목을 가진다.

7. Global Color Table 구조

GIF는 색상 테이블을 이용하는 인덱스 색상 이미지 형식이다.

Global Color Table이 존재한다면 Logical Screen Descriptor 바로 뒤에 배치된다.

각 색상 항목은 RGB 3바이트로 구성된다.

Red   1 byte
Green 1 byte
Blue  1 byte

색상 테이블 크기

Table Bytes = Number of Entries × 3

예를 들어 256개 색상 항목이 있다면 다음과 같다.

256 × 3 = 768 bytes

색상 테이블이 존재하는 경우 파일 파서는 해당 길이만큼 이동한 후 다음 블록을 읽어야 한다.

8. GIF 블록 식별자

GIF 파일의 본문에는 여러 종류의 블록이 배치된다.

대표적인 블록 식별자는 다음과 같다.

식별 바이트블록
0x2CImage Descriptor
0x21Extension Introducer
0x3BTrailer

Extension Introducer인 0x21 뒤에는 확장 종류를 나타내는 레이블이 따라온다.

주요 확장 레이블

레이블확장
0xF9Graphic Control Extension
0xFEComment Extension
0xFFApplication Extension
0x01Plain Text Extension

이러한 바이트는 올바른 블록 경계에서 해석해야 한다.

압축된 이미지 데이터 내부에서 같은 값이 발견되었다고 해서 곧바로 새로운 GIF 블록이 시작된다는 의미는 아니다.

9. Image Descriptor 구조

Image Descriptor는 이미지 데이터의 위치와 크기를 정의한다.

블록 시작 바이트는 0x2C이다.

구조표

필드크기
Image Separator1바이트
Image Left Position2바이트
Image Top Position2바이트
Image Width2바이트
Image Height2바이트
Packed Fields1바이트

총 10바이트다.

주요 역할

  • 논리 화면에서 이미지가 시작되는 위치
  • 개별 이미지의 너비와 높이
  • 지역 색상 테이블 존재 여부
  • 인터레이스 여부
  • 지역 색상 테이블 크기

애니메이션 GIF에서는 여러 Image Descriptor가 나타날 수 있다.

각 이미지 블록의 크기와 위치는 서로 다를 수 있다.

10. Local Color Table과 인터레이스

Image Descriptor의 Packed Fields에는 Local Color Table과 관련된 정보가 포함된다.

주요 비트

비트의미
7Local Color Table Flag
6Interlace Flag
5Sort Flag
4~3Reserved
2~0Local Color Table Size

지역 색상 테이블이 존재하면 해당 이미지에 적용되는 색상 목록을 저장한다.

지역 색상 테이블이 없는 경우에는 일반적으로 전역 색상 테이블을 사용한다.

인터레이스

인터레이스 GIF는 이미지 행을 특정 순서로 나누어 저장한다.

이를 통해 일부 데이터만 수신된 상태에서도 이미지가 점진적으로 표시되는 효과를 제공할 수 있다.

인터레이스 여부는 이미지 데이터의 실제 행 배치를 해석하는 데 영향을 준다.

11. GIF의 LZW 압축 원리

GIF는 이미지 픽셀의 색상 인덱스 데이터를 압축하기 위해 LZW(Lempel–Ziv–Welch) 방식을 사용한다.

LZW는 반복되는 데이터 패턴을 코드로 표현하는 사전 기반 압축 방식이다.

GIF LZW 데이터의 특징

  • 이미지 데이터는 색상 인덱스를 기반으로 한다.
  • LZW Minimum Code Size 필드가 존재한다.
  • Clear Code와 End of Information Code를 사용한다.
  • 코드 크기는 압축 진행에 따라 증가할 수 있다.
  • 압축 데이터는 여러 서브블록으로 분할되어 저장된다.

주의사항

GIF의 LZW는 단순한 일반 목적 LZW 구현과 완전히 동일하게 취급하면 안 된다.

GIF에서 정의한 코드 크기 증가 규칙, 비트 순서, Clear Code 처리와 서브블록 구조를 함께 고려해야 한다.

12. LZW Minimum Code Size

Image Descriptor와 선택적인 Local Color Table 뒤에는 LZW Minimum Code Size 1바이트가 위치한다.

이 값은 초기 코드 체계의 크기를 결정한다.

기본 개념

Minimum Code Size가 N이라면 일반적으로 다음과 같이 계산한다.

Clear Code = 2 ^ N
End Code   = (2 ^ N) + 1

초기 코드 비트 수는 일반적으로 다음과 같다.

Initial Code Width = N + 1

GIF에서 LZW Minimum Code Size는 유효한 범위와 특별한 처리 규칙을 가진다.

특히 2색 이미지에서도 최소 코드 크기로 2를 사용하는 규칙에 주의해야 한다.

13. GIF 이미지 데이터 서브블록

GIF의 압축 이미지 데이터는 서브블록(Sub-block) 구조로 저장된다.

각 서브블록은 다음과 같은 형태를 가진다.

+-----------------------+
| Block Size  1 byte    |
+-----------------------+
| Data        N bytes   |
+-----------------------+

Block Size는 0~255 범위의 값을 가진다.

실제 데이터 서브블록은 일반적으로 1~255바이트 데이터를 포함한다.

크기가 0인 서브블록은 데이터 블록의 종료를 나타낸다.

예시

03 AA BB CC
02 DD EE
00

해석하면 다음과 같다.

  • 첫 번째 서브블록: 3바이트
  • 두 번째 서브블록: 2바이트
  • 마지막 00: 서브블록 종료

이 구조를 이해해야 압축 데이터 내부의 바이트를 GIF 블록 식별자로 잘못 인식하지 않을 수 있다.

14. Graphic Control Extension

Graphic Control Extension은 GIF89a에서 이미지 표시와 관련된 제어 정보를 저장한다.

블록은 일반적으로 다음과 같이 구성된다.

21 F9 04
Packed Fields
Delay Time (2 bytes)
Transparent Color Index
00

주요 정보

필드의미
Disposal Method이전 이미지 처리 방식
User Input Flag사용자 입력 관련 플래그
Transparency Flag투명 색상 사용 여부
Delay Time표시 지연 시간
Transparent Color Index투명 색상 인덱스

Delay Time은 1/100초 단위로 기록된다.

예를 들어 값이 10이면 규격상 0.1초에 해당한다.

다만 실제 브라우저와 이미지 뷰어는 매우 짧은 지연 시간을 별도 정책에 따라 처리할 수 있다.

15. Application Extension과 애니메이션 반복

GIF89a에서는 Application Extension을 통해 응용 프로그램별 데이터를 저장할 수 있다.

대표적으로 Netscape Application Extension이 애니메이션 반복 횟수를 지정하는 데 사용된다.

일반적인 형태

21 FF 0B
NETSCAPE2.0
03 01
Loop Count (2 bytes)
00

여기서 Loop Count는 리틀엔디언 16비트 값으로 표현된다.

Netscape 반복 확장에서 0은 일반적으로 무한 반복을 의미한다.

다만 이 반복 정보는 GIF89a 핵심 규격에 정의된 모든 파일의 필수 요소가 아니다.

따라서 반복 확장이 없다고 해서 파일이 손상되었다고 판단해서는 안 된다.

16. PowerShell 5.1로 GIF 헤더와 이미지 크기 확인하기

이번 실습에서는 GIF 파일의 첫 13바이트를 읽어 버전, 논리 화면 크기, 전역 색상 테이블 존재 여부를 확인한다.

① 실습 파일

C:\FileTest\sample.gif

② PowerShell 코드

$path = "C:\FileTest\sample.gif"

$stream = [System.IO.File]::OpenRead($path)

try {
    $reader = New-Object System.IO.BinaryReader($stream)

    if ($stream.Length -lt 13) {
        throw "GIF file is shorter than 13 bytes."
    }

    $header = $reader.ReadBytes(6)
    $signature = [System.Text.Encoding]::ASCII.GetString($header)

    if ($signature -cne "GIF87a" -and
        $signature -cne "GIF89a") {
        throw "Invalid GIF signature."
    }

    $width = $reader.ReadUInt16()
    $height = $reader.ReadUInt16()
    $packed = [int]$reader.ReadByte()
    $backgroundIndex = $reader.ReadByte()
    $pixelAspect = $reader.ReadByte()

    $hasGCT = (($packed -band 0x80) -ne 0)
    $gctEntries = 0
    $gctBytes = 0

    if ($hasGCT) {
        $sizeCode = $packed -band 0x07
        $gctEntries = [int][Math]::Pow(2, $sizeCode + 1)
        $gctBytes = $gctEntries * 3
    }

    [PSCustomObject]@{
        Signature = $signature
        Width = $width
        Height = $height
        HasGlobalColorTable = $hasGCT
        GlobalColorEntries = $gctEntries
        GlobalColorBytes = $gctBytes
        BackgroundColorIndex = $backgroundIndex
        PixelAspectRatioByte = $pixelAspect
    }
}
finally {
    $stream.Dispose()
}

③ 출력 예시

Signature            : GIF89a
Width                : 640
Height               : 480
HasGlobalColorTable  : True
GlobalColorEntries   : 256
GlobalColorBytes     : 768
BackgroundColorIndex : 0
PixelAspectRatioByte : 0

위 출력은 설명을 위한 예시다.

④ 코드 해석

파일 시작 6바이트에서 GIF 버전을 확인한다.

이후 16비트 리틀엔디언 값을 읽어 논리 화면의 가로와 세로 크기를 확인한다.

Packed Fields의 최상위 비트로 전역 색상 테이블의 존재 여부를 확인한다.

⑤ 코드의 한계

이 코드는 헤더와 Logical Screen Descriptor만 분석한다.

색상 테이블이 실제 파일 범위 안에 완전히 존재하는지, 이미지 데이터가 정상인지까지 검증하지 않는다.

17. PowerShell 5.1로 GIF 블록 구조 확인하기

이번 실습에서는 GIF 파일을 순서대로 읽으면서 이미지 블록, 확장 블록, 종료자를 확인한다.

압축 이미지 데이터는 직접 해제하지 않고 서브블록 크기를 이용해 건너뛴다.

① PowerShell 코드

$path = "C:\FileTest\sample.gif"

$stream = [System.IO.File]::OpenRead($path)

try {
    $reader = New-Object System.IO.BinaryReader($stream)

    if ($stream.Length -lt 13) {
        throw "File too short."
    }

    $signature = [System.Text.Encoding]::ASCII.GetString(
        $reader.ReadBytes(6)
    )

    if ($signature -cne "GIF87a" -and
        $signature -cne "GIF89a") {
        throw "Invalid GIF signature."
    }

    $null = $reader.ReadUInt16()
    $null = $reader.ReadUInt16()
    $packed = [int]$reader.ReadByte()
    $null = $reader.ReadByte()
    $null = $reader.ReadByte()

    if (($packed -band 0x80) -ne 0) {
        $entries = [int][Math]::Pow(
            2, ($packed -band 0x07) + 1
        )

        $skip = $entries * 3

        if (($stream.Position + $skip) -gt $stream.Length) {
            throw "Global color table exceeds file length."
        }

        $stream.Position += $skip
    }

    $finished = $false

    while ($stream.Position -lt $stream.Length) {

        $offset = $stream.Position
        $marker = [int]$reader.ReadByte()

        if ($marker -eq 0x3B) {
            [PSCustomObject]@{
                Offset = $offset
                Block = "Trailer"
                Detail = "End of GIF data"
            }

            $finished = $true
            break
        }

        if ($marker -eq 0x21) {
            if ($stream.Position -ge $stream.Length) {
                throw "Incomplete extension."
            }

            $label = [int]$reader.ReadByte()

            [PSCustomObject]@{
                Offset = $offset
                Block = "Extension"
                Detail = ("0x{0:X2}" -f $label)
            }

            while ($true) {
                if ($stream.Position -ge $stream.Length) {
                    throw "Incomplete extension sub-block."
                }

                $size = [int]$reader.ReadByte()

                if ($size -eq 0) {
                    break
                }

                if (($stream.Position + $size) -gt $stream.Length) {
                    throw "Extension exceeds file length."
                }

                $stream.Position += $size
            }

            continue
        }

        if ($marker -eq 0x2C) {
            if (($stream.Position + 9) -gt $stream.Length) {
                throw "Incomplete image descriptor."
            }

            $left = $reader.ReadUInt16()
            $top = $reader.ReadUInt16()
            $width = $reader.ReadUInt16()
            $height = $reader.ReadUInt16()
            $imagePacked = [int]$reader.ReadByte()

            if (($imagePacked -band 0x80) -ne 0) {
                $entries = [int][Math]::Pow(
                    2, ($imagePacked -band 0x07) + 1
                )

                $skip = $entries * 3

                if (($stream.Position + $skip) -gt $stream.Length) {
                    throw "Local color table exceeds file length."
                }

                $stream.Position += $skip
            }

            if ($stream.Position -ge $stream.Length) {
                throw "Missing LZW code size."
            }

            $lzwCodeSize = [int]$reader.ReadByte()

            [PSCustomObject]@{
                Offset = $offset
                Block = "Image"
                Detail = "$width x $height, LZW=$lzwCodeSize"
            }

            while ($true) {
                if ($stream.Position -ge $stream.Length) {
                    throw "Incomplete image data."
                }

                $size = [int]$reader.ReadByte()

                if ($size -eq 0) {
                    break
                }

                if (($stream.Position + $size) -gt $stream.Length) {
                    throw "Image data exceeds file length."
                }

                $stream.Position += $size
            }

            continue
        }

        throw ("Unknown GIF block marker: 0x{0:X2}" -f $marker)
    }

    if (-not $finished) {
        throw "GIF trailer not found."
    }
}
finally {
    $stream.Dispose()
}

② 출력 예시

Offset Block      Detail
------ -----      ------
781    Extension  0xF9
789    Image      640 x 480, LZW=8
12050  Extension  0xF9
12058  Image      640 x 480, LZW=8
24010  Trailer    End of GIF data

위 출력은 설명을 위한 예시다.

③ 코드 해석

전역 색상 테이블이 있으면 그 길이를 건너뛴다.

이후 이미지 블록과 확장 블록을 식별하고, 서브블록 크기를 이용해 다음 블록 위치로 이동한다.

④ 코드의 한계

이 코드는 GIF 블록의 배치를 조사하는 학습용 예제다.

확장 블록의 내부 의미를 완전히 검증하지 않으며, LZW 압축 데이터의 디코딩도 수행하지 않는다.

또한 일부 비정상 파일에서는 블록의 크기 필드가 정상처럼 보이더라도 실제 이미지 복원이 실패할 수 있다.

18. PowerShell 5.1로 GIF 종료자 확인하기

GIF 파일은 일반적으로 0x3B Trailer로 끝난다.

이번 실습에서는 마지막 바이트를 읽어 종료자 존재 여부를 확인한다.

① PowerShell 코드

$path = "C:\FileTest\sample.gif"

$stream = [System.IO.File]::OpenRead($path)

try {
    if ($stream.Length -eq 0) {
        throw "Empty file."
    }

    $stream.Position = $stream.Length - 1
    $lastByte = $stream.ReadByte()

    [PSCustomObject]@{
        FileSize = $stream.Length
        LastByteHex = ("0x{0:X2}" -f $lastByte)
        HasGIFTrailer = ($lastByte -eq 0x3B)
    }
}
finally {
    $stream.Dispose()
}

② 출력 예시

FileSize      : 24011
LastByteHex   : 0x3B
HasGIFTrailer : True

위 출력은 설명을 위한 예시다.

③ 코드의 한계

마지막 바이트가 0x3B라고 해서 GIF 전체 구조가 정상인 것은 아니다.

반대로 일부 파일에는 Trailer 뒤에 추가 데이터가 존재할 수 있으므로, 마지막 바이트만 검사하는 방식은 보조 점검에 해당한다.

19. GIF 파일 손상 시 점검할 주요 항목

GIF 파일이 정상적으로 표시되지 않는다면 다음 항목을 확인할 수 있다.

점검 항목확인 목적
GIF87a·GIF89a파일 버전 및 시그니처
Logical Screen Descriptor전체 화면 크기
Global Color Table전역 색상 목록
Image Descriptor개별 이미지 위치와 크기
Local Color Table이미지별 색상 목록
LZW Minimum Code Size압축 코드 크기
Image Data Sub-blocks압축 데이터 경계
Graphic Control Extension지연 시간·투명도
Application Extension반복 횟수 등
Trailer파일 종료
SHA-256원본과 바이트 동일성

대표적인 손상 상황

① 헤더 손상

GIF 시그니처가 잘못되면 이미지 뷰어가 파일을 인식하지 못할 수 있다.

② 색상 테이블 크기 오류

색상 테이블 길이를 잘못 해석하면 다음 블록의 시작 위치가 달라진다.

③ 서브블록 누락

압축 이미지 데이터의 서브블록이 잘리면 LZW 데이터 해석에 문제가 발생할 수 있다.

④ 확장 블록 오류

Graphic Control Extension의 값이 비정상적이면 투명도나 애니메이션 표시가 예상과 다를 수 있다.

⑤ 종료자 누락

Trailer가 없으면 파일이 완전히 기록되지 않았을 가능성을 확인해야 한다.

20. GIF 구조 검증과 SHA-256 무결성 검증

GIF 파일의 내부 구조를 확인하는 것과 원본 파일과의 바이트 동일성을 확인하는 것은 다른 작업이다.

구조 분석

헤더, 색상 테이블, 이미지 블록, 확장 블록 및 Trailer의 배치를 확인한다.

압축 데이터 검증

LZW 압축 데이터가 실제로 정상적으로 복원되는지 확인한다.

SHA-256 무결성 검증

신뢰할 수 있는 기준 해시값과 파일 전체의 SHA-256 값을 비교한다.

PowerShell 코드

$path = "C:\FileTest\sample.gif"

Get-FileHash -LiteralPath $path -Algorithm SHA256 |
    Format-List Algorithm, Hash, Path

검증 방식 비교

구분구조 분석LZW 디코딩SHA-256
주요 확인블록 및 경계이미지 압축 데이터파일 전체
파일 표시 가능성일부 단서더 직접적인 확인확인 불가
원본 동일성확인 불가확인 불가기준값이 있으면 확인
이미지 품질확인 불가복원 결과 검사 필요확인 불가
보안상 안전성보장 불가보장 불가단독 보장 불가

SHA-256 해시가 기준값과 일치하면 두 파일의 바이트 내용이 동일하다고 매우 높은 신뢰도로 판단할 수 있다.

그러나 해시값이 일치한다고 해서 GIF 파일이 안전하거나 시각적으로 올바르다는 의미는 아니다.

21. GIF 파일 분석 시 보안과 개인정보 주의사항

① 확장 블록의 부가 정보

GIF 파일에는 Comment Extension이나 Application Extension을 통해 부가 정보가 저장될 수 있다.

파일 공유 전에 불필요한 설명이나 식별 정보가 포함되어 있는지 확인할 필요가 있다.

② 비정상적인 이미지 크기

논리 화면이나 개별 이미지 크기가 과도하게 설정된 파일은 디코딩 과정에서 많은 메모리를 요구할 수 있다.

③ 잘못된 LZW 데이터

압축 데이터가 손상되거나 비정상적인 코드열을 포함하면 이미지 복원이 실패할 수 있다.

④ 원본 파일 보존

손상 분석이나 무결성 확인이 목적이라면 원본 파일을 수정하지 않고 복사본을 사용해야 한다.

GIF를 다시 저장하거나 애니메이션 프레임을 편집하면 해시값이 변경될 수 있다.

22. 자주 묻는 질문

Q1. GIF 파일의 Magic Number는 무엇인가요?

GIF87a는 47 49 46 38 37 61, GIF89a는 47 49 46 38 39 61이다.

Q2. GIF87a와 GIF89a는 무엇이 다른가요?

GIF89a는 그래픽 제어와 확장 블록을 통해 투명도, 지연 시간 등 추가 기능을 지원한다.

Q3. GIF는 최대 256색만 사용할 수 있나요?

GIF의 개별 색상 테이블은 최대 256개 항목을 가진다. 하지만 여러 이미지 블록이 각각 다른 지역 색상 테이블을 사용할 수 있으므로 파일 전체에서 사용되는 색상 종류를 단순히 256개로 제한해 설명하는 것은 정확하지 않다.

Q4. GIF 애니메이션은 어떻게 구성되나요?

여러 이미지 블록과 Graphic Control Extension 등을 이용해 프레임 표시 순서와 지연 시간을 표현한다.

Q5. GIF의 LZW 압축은 손실 압축인가요?

GIF의 LZW 자체는 무손실 압축이다. 다만 원본 이미지를 GIF의 제한된 색상 테이블로 변환하는 과정에서 색상 정보가 줄어들 수 있다.

Q6. GIF 파일에 투명도가 있나요?

GIF89a에서는 Graphic Control Extension을 통해 하나의 색상 인덱스를 투명하게 지정할 수 있다. PNG의 다단계 알파 투명도와는 방식이 다르다.

Q7. GIF 파일의 마지막 바이트는 무엇인가요?

일반적인 정상 GIF 파일은 0x3B Trailer로 끝난다.

Q8. GIF 파일에도 PNG처럼 CRC-32 필드가 있나요?

GIF의 일반적인 블록 구조에는 PNG 청크와 같은 공통 CRC-32 필드가 없다.

Q9. PowerShell만으로 GIF 파일이 정상인지 확인할 수 있나요?

PowerShell로 헤더, 색상 테이블, 블록 경계 및 Trailer를 확인할 수 있다. 하지만 LZW 압축 데이터의 완전한 정상 여부를 판단하려면 실제 디코딩 검증이 필요하다.

GIF와 다른 이미지 파일 형식의 구조가 궁금하다면 PNG 파일 구조 분석과 WebP 파일 구조 분석 글도 함께 참고할 수 있다.

마무리

GIF는 GIF87a와 GIF89a 시그니처를 사용하는 블록 기반 이미지 파일 형식이다.

파일에는 Logical Screen Descriptor, 색상 테이블, 이미지 블록, 확장 블록, LZW 압축 데이터 및 Trailer가 포함될 수 있다.

GIF89a는 Graphic Control Extension과 Application Extension 등을 통해 투명도와 애니메이션 관련 정보를 표현할 수 있다.

GIF 파일 구조 분석에서는 파일 시그니처, 논리 화면 크기, 색상 테이블 길이, 이미지 블록 경계, LZW 서브블록 구조와 종료자를 순서대로 확인하는 것이 중요하다.

하지만 이러한 구조가 정상적으로 보인다고 해서 실제 이미지 데이터가 완전히 복원되거나 파일이 안전하다는 의미는 아니다.

따라서 GIF 구조 분석, 실제 LZW 디코딩, 이미지 표시 테스트 및 SHA-256 무결성 검증을 목적에 맞게 구분해야 한다.


핵심 내용 3줄 요약

  • GIF는 GIF87a·GIF89a 헤더와 Logical Screen Descriptor, 색상 테이블, 이미지 블록으로 구성되는 이미지 형식이다.
  • 이미지 데이터는 GIF 전용 LZW 방식으로 압축되며, GIF89a 확장 블록으로 투명도와 애니메이션 정보를 표현할 수 있다.
  • 블록 구조가 정상이어도 실제 이미지 복원과 원본 동일성은 별도로 검증해야 한다.

공식 및 기술 참고자료

  1. W3C — GIF89a Specification
    https://www.w3.org/Graphics/GIF/spec-gif89a.txt
  2. W3C — GIF89a Specification (HTML)
    https://www.w3.org/Graphics/GIF/spec-gif89a.html
  3. Library of Congress — GIF File Format
    https://www.loc.gov/preservation/digital/formats/fdd/fdd000133.shtml
  4. Microsoft Learn — File.OpenRead Method
    https://learn.microsoft.com/dotnet/api/system.io.file.openread
  5. Microsoft Learn — BinaryReader Class
    https://learn.microsoft.com/dotnet/api/system.io.binaryreader
  6. Microsoft Learn — Get-FileHash (PowerShell 5.1)
    https://learn.microsoft.com/powershell/module/microsoft.powershell.utility/get-filehash?view=powershell-5.1

※ PowerShell 코드는 GIF 파일의 헤더와 블록 구조를 읽기 전용으로 분석하는 학습용 예시다. 완전한 GIF 디코더를 대체하지 않으며, 실제 Windows PowerShell 5.1 환경에서의 실행 검증은 별도로 필요하다.

디지털 파일 검증 연구소

디지털 파일 검증 연구소
디지털 파일 검증 연구소는 파일 형식, 확장자, 무결성 확인, 데이터 손상 예방 및 디지털 자료 관리 방법을 다루는 정보 사이트입니다. 파일 오류의 원인과 점검 절차를 이해하기 쉽게 정리하고, 공식 문서와 검증 가능한 자료를 바탕으로 실생활에 도움이 되는 해결 방법을 안내합니다.
함께 보면 좋은 글

댓글 0

첫 댓글을 남겨보세요.