TIFF 파일 구조 분석|바이트 순서·IFD·EXIF 태그 확인 방법
TIFF(Tagged Image File Format)는 이미지 데이터와 메타데이터를 태그 기반 구조로 저장하는 파일 형식이다.
일반적으로 .tif 또는 .tiff 확장자를 사용하며, 스캔 이미지, 문서 보존, 인쇄 작업, 사진 편집, 디지털 아카이빙 등에서 활용된다.
TIFF는 PNG·JPEG·GIF처럼 하나의 고정된 압축 방식만을 사용하는 형식이 아니다. 이미지의 크기, 색상 구성, 압축 방식, 저장 위치 등을 Image File Directory(IFD) 안의 태그로 정의하는 구조를 사용한다.
특히 TIFF 파일 구조 분석에서는 파일 시작 부분의 II 또는 MM 바이트 순서 식별자, 매직 넘버 42, 첫 번째 IFD의 오프셋, 태그의 자료형과 개수, 이미지 데이터의 저장 위치를 함께 확인해야 한다.
이번 글에서는 TIFF 파일 구조 분석을 중심으로 TIFF 헤더, 리틀엔디언·빅엔디언, IFD 구조, 주요 태그, Strip 기반 이미지 데이터, Exif 메타데이터 및 파일 손상 점검 원리를 설명한다.
또한 Windows PowerShell 5.1을 이용해 TIFF 파일의 헤더와 IFD 태그를 읽고, 이미지 크기 및 데이터 경계와 SHA-256 해시값을 확인하는 방법을 살펴본다.
1. TIFF 파일 구조 분석이 필요한 이유
TIFF는 이미지 보존과 전문 이미지 처리에서 사용되는 파일 형식이다.
하지만 .tif 확장자가 붙어 있다는 사실만으로 파일의 내부 구조가 정상적이라고 판단할 수는 없다.
다음과 같은 상황에서는 TIFF 파일 구조 분석이 도움이 된다.
- TIFF 이미지가 열리지 않는 경우
- 스캔 문서의 일부 페이지가 표시되지 않는 경우
- 이미지 크기나 색상이 예상과 다른 경우
- 압축 방식 때문에 프로그램에서 읽지 못하는 경우
- 이미지 변환 후 메타데이터가 사라진 경우
- IFD 오프셋이나 태그 값이 손상되었는지 확인하려는 경우
- 파일 전송 후 원본과 동일한지 확인하려는 경우
- 다중 페이지 TIFF의 이미지 구성을 조사하려는 경우
TIFF 분석의 핵심은 헤더와 IFD를 통해 이미지 데이터를 해석하는 데 필요한 정보를 찾아내는 것이다.
다만 헤더와 태그가 정상이라고 해서 실제 이미지 픽셀을 완전히 복원할 수 있다는 의미는 아니다.
2. TIFF의 기본 개념과 특징
TIFF는 태그 기반 이미지 저장 형식이다.
파일 안에는 이미지 자체의 데이터뿐 아니라 이를 해석하는 데 필요한 정보가 포함된다.
대표적인 특징은 다음과 같다.
II또는MM바이트 순서 지원- IFD를 통한 이미지 속성 관리
- 여러 종류의 이미지 압축 방식 지원
- 흑백·그레이스케일·컬러 이미지 저장
- 다중 이미지 및 다중 페이지 구성 가능
- Exif 등 메타데이터와 연계 가능
- 이미지 데이터를 Strip 또는 Tile 단위로 저장 가능
TIFF는 구조가 유연한 대신 분석 시 고려해야 할 태그와 자료형이 많다.
따라서 파일의 처음 몇 바이트만 읽는 방식으로는 전체 이미지 구성을 확인할 수 없다.
3. TIFF 파일의 전체 구조
일반적인 Classic TIFF 파일은 다음과 같은 구조를 가진다.
TIFF File
│
├── TIFF Header
│ ├── Byte Order: II / MM
│ ├── Magic Number: 42
│ └── First IFD Offset
│
├── Image File Directory (IFD)
│ ├── Entry Count
│ ├── Tag Entry 1
│ ├── Tag Entry 2
│ ├── ...
│ └── Next IFD Offset
│
├── Tag Value Data
│
├── Image Data
│ ├── Strip / Tile 1
│ ├── Strip / Tile 2
│ └── ...
│
└── Additional IFDs (Optional)
위 그림은 개념적인 배치다.
실제 TIFF 파일에서는 IFD, 태그 값, 이미지 데이터의 물리적 저장 순서가 달라질 수 있다.
TIFF는 오프셋을 이용해 필요한 데이터의 위치를 참조하기 때문이다.
4. TIFF 파일 시그니처와 Magic Number
Classic TIFF 파일의 시작 4바이트는 바이트 순서와 매직 넘버를 나타낸다.
리틀엔디언 TIFF
49 49 2A 00
ASCII로 앞의 2바이트는 II이다.
빅엔디언 TIFF
4D 4D 00 2A
ASCII로 앞의 2바이트는 MM이다.
구조 비교
| 항목 | 리틀엔디언 | 빅엔디언 |
|---|---|---|
| Byte Order | II | MM |
| Magic Number | 2A 00 | 00 2A |
| 숫자 해석 | 낮은 바이트 우선 | 높은 바이트 우선 |
| Classic TIFF 매직 넘버 | 42 | 42 |
파일 확장자보다 실제 헤더를 확인하는 것이 중요하다.
다만 TIFF 헤더가 정상이라고 해서 모든 IFD와 이미지 데이터가 정상인 것은 아니다.
5. 리틀엔디언과 빅엔디언의 차이
엔디언(Endianness)은 여러 바이트로 이루어진 숫자를 저장하는 순서를 의미한다.
예를 들어 16비트 값 0x1234를 저장한다고 가정하자.
리틀엔디언
34 12
빅엔디언
12 34
두 바이트 배열은 서로 다르지만 동일한 숫자 값을 나타낸다.
TIFF에서는 헤더에 기록된 바이트 순서에 따라 이후의 주요 숫자 필드를 해석해야 한다.
잘못된 엔디언으로 IFD 오프셋이나 태그 값을 읽으면 비정상적으로 큰 값이 나올 수 있다.
6. Classic TIFF 헤더 구조
Classic TIFF 헤더는 8바이트로 구성된다.
| 오프셋 | 크기 | 필드 |
|---|---|---|
| 0 | 2바이트 | Byte Order |
| 2 | 2바이트 | Magic Number |
| 4 | 4바이트 | First IFD Offset |
First IFD Offset은 파일 시작 위치를 기준으로 첫 번째 IFD가 저장된 위치를 나타낸다.
예를 들어 리틀엔디언 TIFF에서 다음 바이트를 확인했다고 가정하자.
49 49 2A 00 08 00 00 00
해석 결과는 다음과 같다.
Byte Order : II
Magic Number : 42
First IFD Offset : 8
첫 번째 IFD가 파일의 8번 오프셋에서 시작한다는 의미다.
7. Image File Directory(IFD)의 역할
IFD는 TIFF 이미지의 속성을 저장하는 디렉터리 구조다.
일반적인 Classic TIFF IFD는 다음과 같이 구성된다.
+-----------------------------+
| Entry Count 2 bytes |
+-----------------------------+
| IFD Entry 1 12 bytes |
+-----------------------------+
| IFD Entry 2 12 bytes |
+-----------------------------+
| ... |
+-----------------------------+
| Next IFD Offset 4 bytes |
+-----------------------------+
주요 역할
- 이미지 너비와 높이 정의
- 비트 깊이 및 색상 구성 지정
- 압축 방식 식별
- 이미지 데이터 위치 참조
- 이미지 데이터 크기 확인
- 다음 이미지 IFD 연결
다중 페이지 TIFF에서는 여러 IFD가 연결될 수 있다.
다만 모든 TIFF 관련 메타데이터가 동일한 IFD 체인에만 저장되는 것은 아니다.
일부 확장 구조는 별도의 IFD를 참조할 수 있다.
8. IFD Entry의 12바이트 구조
Classic TIFF의 각 IFD Entry는 12바이트다.
| 필드 | 크기 | 의미 |
|---|---|---|
| Tag | 2바이트 | 태그 식별 번호 |
| Type | 2바이트 | 값의 자료형 |
| Count | 4바이트 | 값의 개수 |
| Value / Offset | 4바이트 | 값 자체 또는 값이 저장된 위치 |
태그 값의 전체 크기가 4바이트 이하라면 일반적으로 Entry 내부의 Value 필드에 직접 저장된다.
4바이트를 초과하면 해당 값이 저장된 파일 위치를 오프셋으로 참조한다.
이 구분은 TIFF 구조 분석에서 매우 중요하다.
예를 들어 Count가 3이고 Type이 SHORT라면 다음과 같이 계산한다.
SHORT Size = 2 bytes
3 × 2 = 6 bytes
6바이트는 Value 필드 4바이트보다 크므로 외부 데이터 위치를 참조해야 한다.
9. TIFF 태그의 주요 자료형
TIFF 태그에는 다양한 자료형이 사용된다.
Classic TIFF에서 자주 사용하는 자료형은 다음과 같다.
| Type ID | 이름 | 항목당 크기 |
|---|---|---|
| 1 | BYTE | 1바이트 |
| 2 | ASCII | 1바이트 |
| 3 | SHORT | 2바이트 |
| 4 | LONG | 4바이트 |
| 5 | RATIONAL | 8바이트 |
| 6 | SBYTE | 1바이트 |
| 7 | UNDEFINED | 1바이트 |
| 8 | SSHORT | 2바이트 |
| 9 | SLONG | 4바이트 |
| 10 | SRATIONAL | 8바이트 |
| 11 | FLOAT | 4바이트 |
| 12 | DOUBLE | 8바이트 |
위 표에는 기본 TIFF 및 널리 사용되는 확장 자료형이 함께 포함되어 있다.
RATIONAL은 일반적으로 두 개의 32비트 정수로 분자와 분모를 표현한다.
자료형의 크기를 잘못 계산하면 태그 값의 위치를 잘못 해석할 수 있다.
10. TIFF의 주요 이미지 태그
TIFF 이미지 분석에서 자주 확인하는 태그는 다음과 같다.
| 태그 번호 | 이름 | 의미 |
|---|---|---|
| 256 | ImageWidth | 이미지 너비 |
| 257 | ImageLength | 이미지 높이 |
| 258 | BitsPerSample | 샘플당 비트 수 |
| 259 | Compression | 압축 방식 |
| 262 | PhotometricInterpretation | 색상 해석 방식 |
| 273 | StripOffsets | Strip 데이터 위치 |
| 277 | SamplesPerPixel | 픽셀당 샘플 수 |
| 278 | RowsPerStrip | Strip당 행 수 |
| 279 | StripByteCounts | Strip 데이터 크기 |
| 282 | XResolution | 가로 해상도 |
| 283 | YResolution | 세로 해상도 |
| 296 | ResolutionUnit | 해상도 단위 |
| 305 | Software | 생성·처리 소프트웨어 |
| 306 | DateTime | 날짜·시간 |
| 324 | TileOffsets | Tile 데이터 위치 |
| 325 | TileByteCounts | Tile 데이터 크기 |
각 태그는 이미지 데이터의 해석과 위치 확인에 사용된다.
하지만 태그가 존재한다고 해서 그 값이 실제 이미지 데이터와 일치한다고 단정할 수는 없다.
11. ImageWidth·ImageLength 태그
TIFF 이미지의 가로와 세로 크기는 주로 다음 태그에 저장된다.
256 = ImageWidth
257 = ImageLength
예를 들어 다음과 같은 값이 있다고 가정하자.
ImageWidth = 1920
ImageLength = 1080
이는 이미지의 가로가 1920픽셀, 세로가 1080픽셀임을 나타낸다.
Classic TIFF에서는 이 값들이 SHORT 또는 LONG 자료형으로 저장될 수 있다.
따라서 태그를 읽을 때는 자료형과 Count를 함께 확인해야 한다.
12. Compression 태그와 압축 방식
TIFF는 여러 종류의 이미지 압축 방식을 지원한다.
압축 방식은 주로 Tag 259에 기록된다.
대표적인 값
| Compression 값 | 의미 |
|---|---|
| 1 | 비압축 |
| 2 | CCITT Modified Huffman RLE |
| 3 | CCITT Group 3 Fax |
| 4 | CCITT Group 4 Fax |
| 5 | LZW |
| 6 | 구형 JPEG 압축 |
| 7 | JPEG 압축 |
| 8 | Deflate |
| 32773 | PackBits |
위 값 중 일부는 확장 규격이나 특정 구현에서 사용된다.
TIFF에 저장된 Compression 값을 확인하면 이미지 데이터의 압축 방식을 추정할 수 있다.
하지만 압축 태그만으로 실제 압축 데이터가 정상인지 확인할 수는 없다.
또한 일부 압축 방식은 프로그램마다 지원 여부가 다를 수 있다.
13. StripOffsets와 StripByteCounts
TIFF 이미지 데이터는 Strip 단위로 저장될 수 있다.
Strip은 이미지의 여러 행을 하나의 데이터 단위로 묶은 것이다.
대표적인 태그는 다음과 같다.
273 = StripOffsets
278 = RowsPerStrip
279 = StripByteCounts
Strip 기반 저장 개념
Image
│
├── Strip 1
├── Strip 2
├── Strip 3
└── ...
StripOffsets는 각 Strip 데이터의 시작 위치를 나타낸다.
StripByteCounts는 각 Strip 데이터의 바이트 크기를 나타낸다.
예를 들어 다음과 같은 값이 있다고 가정하자.
Strip Offset = 4096
Strip Bytes = 2048
이 경우 데이터 범위는 다음과 같다.
Start = 4096
End = 4096 + 2048 = 6144
여기서 End는 마지막 바이트 다음 위치를 의미한다.
파일 크기가 6000바이트라면 해당 Strip은 파일 범위를 초과하므로 구조 이상을 의심할 수 있다.
14. Tile 기반 TIFF 구조
TIFF는 Strip 외에도 Tile 기반 저장 방식을 지원할 수 있다.
Tile은 이미지를 사각형 영역으로 나누어 저장하는 방식이다.
주요 태그
| 태그 번호 | 이름 |
|---|---|
| 322 | TileWidth |
| 323 | TileLength |
| 324 | TileOffsets |
| 325 | TileByteCounts |
Tile 기반 TIFF에서는 각 타일의 데이터 위치와 크기를 확인해야 한다.
이미지의 가로·세로 크기뿐 아니라 타일 크기와 타일 개수도 구조 검증에 영향을 준다.
Strip과 Tile은 서로 다른 이미지 데이터 배치 방식이다.
분석 대상 이미지가 어느 방식을 사용하는지 먼저 확인해야 한다.
15. TIFF와 Exif 메타데이터의 관계
Exif(Exchangeable Image File Format)는 사진과 이미지에 촬영 관련 정보를 저장하는 데 사용되는 메타데이터 구조다.
Exif는 TIFF 기반의 IFD와 태그 구조를 활용한다.
대표적인 정보
- 촬영 날짜와 시간
- 카메라 제조사와 모델
- 노출 시간
- 조리개 값
- ISO 감도
- GPS 위치 정보
- 이미지 방향 정보
JPEG 파일에서는 APP1 세그먼트 안에 Exif 데이터가 저장될 수 있다.
반면 TIFF 파일에서는 TIFF 자체의 태그와 IFD 구조를 통해 관련 정보를 표현할 수 있다.
주의사항
Exif 메타데이터는 수정하거나 삭제할 수 있다.
따라서 Exif 정보만으로 파일의 실제 촬영 시점이나 원본 여부를 확정해서는 안 된다.
16. BigTIFF와 Classic TIFF의 차이
Classic TIFF는 32비트 오프셋을 사용한다.
매우 큰 이미지 파일을 처리하기 위해 BigTIFF라는 확장 형식이 사용되기도 한다.
주요 차이
| 구분 | Classic TIFF | BigTIFF |
|---|---|---|
| 대표 매직 넘버 | 42 | 43 |
| 일반적인 헤더 크기 | 8바이트 | 16바이트 |
| 주요 오프셋 | 32비트 | 64비트 |
| IFD Entry 크기 | 12바이트 | 20바이트 |
| 대용량 파일 | 구조적 크기 제한 | 더 큰 파일 지원 |
BigTIFF는 Classic TIFF와 동일한 방식으로 모든 필드를 읽을 수 없다.
특히 IFD Entry 크기와 오프셋 필드가 다르다.
이번 PowerShell 실습은 Classic TIFF를 대상으로 하며 BigTIFF를 분석하지 않는다.
17. PowerShell 5.1로 TIFF 헤더 분석하기
이번 실습에서는 TIFF 파일의 처음 8바이트를 읽어 바이트 순서, 매직 넘버, 첫 번째 IFD 오프셋을 확인한다.
① 실습 파일
C:\FileTest\sample.tif
실제 실행 시에는 본인의 파일 경로로 변경한다.
② PowerShell 코드
$path = "C:\FileTest\sample.tif"
$stream = [System.IO.File]::OpenRead($path)
try {
$reader = New-Object System.IO.BinaryReader($stream)
if ($stream.Length -lt 8) {
throw "TIFF header is incomplete."
}
$header = $reader.ReadBytes(8)
$order = [System.Text.Encoding]::ASCII.GetString(
$header, 0, 2
)
if ($order -cne "II" -and $order -cne "MM") {
throw "Invalid TIFF byte order."
}
$little = ($order -ceq "II")
function Read-U16($bytes, $offset, $littleEndian) {
if ($littleEndian) {
return [uint16](
[int]$bytes[$offset] -bor
([int]$bytes[$offset + 1] -shl 8)
)
}
return [uint16](
([int]$bytes[$offset] -shl 8) -bor
[int]$bytes[$offset + 1]
)
}
function Read-U32($bytes, $offset, $littleEndian) {
$value = [uint64]0
if ($littleEndian) {
for ($i = 0; $i -lt 4; $i++) {
$value = $value -bor (
[uint64]$bytes[$offset + $i] -shl (8 * $i)
)
}
}
else {
for ($i = 0; $i -lt 4; $i++) {
$value = ($value -shl 8) -bor
[uint64]$bytes[$offset + $i]
}
}
return [uint32]$value
}
$magic = Read-U16 $header 2 $little
$firstIFD = Read-U32 $header 4 $little
if ($magic -ne 42) {
throw "Not a Classic TIFF file."
}
[PSCustomObject]@{
ByteOrder = $order
MagicNumber = $magic
FirstIFDOffset = $firstIFD
FileSize = $stream.Length
IFDWithinFile = (
[uint64]$firstIFD -lt [uint64]$stream.Length
)
}
}
finally {
$stream.Dispose()
}
③ 출력 예시
ByteOrder : II
MagicNumber : 42
FirstIFDOffset : 8
FileSize : 20480
IFDWithinFile : True
위 출력은 설명을 위한 예시다.
④ 코드 해석
TIFF의 바이트 순서를 확인하고, 이에 맞춰 16비트 및 32비트 숫자를 해석한다.
매직 넘버가 42인지 확인한 뒤 첫 번째 IFD의 위치를 출력한다.
⑤ 코드의 한계
이 코드는 헤더의 기본 구조만 분석한다.
첫 번째 IFD의 모든 Entry가 정상인지까지 확인하지 않으며 BigTIFF는 지원하지 않는다.
18. PowerShell 5.1로 첫 번째 IFD 태그 목록 분석하기
이번 실습에서는 첫 번째 IFD의 Entry를 읽고 태그 번호, 자료형, Count, Value 또는 Offset 필드를 출력한다.
① PowerShell 코드
$path = "C:\FileTest\sample.tif"
$stream = [System.IO.File]::OpenRead($path)
try {
$reader = New-Object System.IO.BinaryReader($stream)
if ($stream.Length -lt 8) {
throw "File too short."
}
$header = $reader.ReadBytes(8)
$order = [System.Text.Encoding]::ASCII.GetString(
$header, 0, 2
)
if ($order -cne "II" -and $order -cne "MM") {
throw "Invalid byte order."
}
$little = ($order -ceq "II")
function U16($b, $p, $le) {
if ($le) {
return [uint16](
[int]$b[$p] -bor
([int]$b[$p + 1] -shl 8)
)
}
return [uint16](
([int]$b[$p] -shl 8) -bor
[int]$b[$p + 1]
)
}
function U32($b, $p, $le) {
$v = [uint64]0
if ($le) {
for ($i = 0; $i -lt 4; $i++) {
$v = $v -bor (
[uint64]$b[$p + $i] -shl (8 * $i)
)
}
}
else {
for ($i = 0; $i -lt 4; $i++) {
$v = ($v -shl 8) -bor
[uint64]$b[$p + $i]
}
}
return [uint32]$v
}
$magic = U16 $header 2 $little
if ($magic -ne 42) {
throw "Not Classic TIFF."
}
$ifdOffset = [uint64](U32 $header 4 $little)
if (($ifdOffset + 2) -gt [uint64]$stream.Length) {
throw "IFD entry count is outside file."
}
$stream.Position = [long]$ifdOffset
$countBytes = $reader.ReadBytes(2)
$entryCount = U16 $countBytes 0 $little
$ifdEnd = $ifdOffset + 2 +
([uint64]$entryCount * 12) + 4
if ($ifdEnd -gt [uint64]$stream.Length) {
throw "IFD entries exceed file length."
}
for ($i = 0; $i -lt $entryCount; $i++) {
$entry = $reader.ReadBytes(12)
$tag = U16 $entry 0 $little
$type = U16 $entry 2 $little
$count = U32 $entry 4 $little
$valueOrOffset = U32 $entry 8 $little
[PSCustomObject]@{
Tag = $tag
Type = $type
Count = $count
ValueOrOffset = $valueOrOffset
}
}
$nextBytes = $reader.ReadBytes(4)
$nextIFD = U32 $nextBytes 0 $little
"Next IFD Offset: $nextIFD"
}
finally {
$stream.Dispose()
}
② 출력 예시
Tag Type Count ValueOrOffset
--- ---- ----- -------------
256 4 1 1920
257 4 1 1080
259 3 1 1
273 4 1 4096
279 4 1 8192
위 표는 설명을 위한 예시이며, 실제 출력은 파일의 태그 구성과 자료형에 따라 달라진다.
ValueOrOffset는 IFD Entry의 마지막 4바이트를 숫자로 읽은 원시 필드 값이다. SHORT 등 일부 자료형에서는 실제 태그 값과 다를 수 있으므로 Type, Count 및 바이트 순서를 함께 해석해야 한다.
③ 중요한 해석 주의사항
ValueOrOffset은 항상 실제 태그 값이 아니다.
태그의 Type과 Count에 따라 Entry 내부에 직접 저장된 값일 수도 있고, 별도 데이터 위치를 가리키는 오프셋일 수도 있다.
특히 SHORT 자료형의 값은 엔디언에 따라 4바이트 필드의 해석 방식에 주의해야 한다.
19. PowerShell 5.1로 TIFF 이미지 크기 태그 확인하기
이번 실습에서는 첫 번째 IFD의 ImageWidth(256)와 ImageLength(257)를 찾아 이미지 크기를 확인한다.
SHORT와 LONG 자료형의 단일 값만 처리한다.
① PowerShell 코드
$path = "C:\FileTest\sample.tif"
$stream = [System.IO.File]::OpenRead($path)
try {
$reader = New-Object System.IO.BinaryReader($stream)
if ($stream.Length -lt 8) {
throw "File too short."
}
$header = $reader.ReadBytes(8)
$order = [System.Text.Encoding]::ASCII.GetString(
$header, 0, 2
)
if ($order -cne "II" -and $order -cne "MM") {
throw "Invalid byte order."
}
$little = ($order -ceq "II")
function U16($b, $p, $le) {
if ($le) {
return [uint16](
[int]$b[$p] -bor
([int]$b[$p + 1] -shl 8)
)
}
return [uint16](
([int]$b[$p] -shl 8) -bor
[int]$b[$p + 1]
)
}
function U32($b, $p, $le) {
$v = [uint64]0
if ($le) {
for ($i = 0; $i -lt 4; $i++) {
$v = $v -bor (
[uint64]$b[$p + $i] -shl (8 * $i)
)
}
}
else {
for ($i = 0; $i -lt 4; $i++) {
$v = ($v -shl 8) -bor
[uint64]$b[$p + $i]
}
}
return [uint32]$v
}
if ((U16 $header 2 $little) -ne 42) {
throw "Not Classic TIFF."
}
$ifdOffset = [uint64](U32 $header 4 $little)
if (($ifdOffset + 2) -gt [uint64]$stream.Length) {
throw "Invalid IFD offset."
}
$stream.Position = [long]$ifdOffset
$entryCount = U16 ($reader.ReadBytes(2)) 0 $little
$ifdEnd = $ifdOffset + 2 +
([uint64]$entryCount * 12) + 4
if ($ifdEnd -gt [uint64]$stream.Length) {
throw "IFD exceeds file length."
}
$width = $null
$height = $null
for ($i = 0; $i -lt $entryCount; $i++) {
$entry = $reader.ReadBytes(12)
$tag = U16 $entry 0 $little
$type = U16 $entry 2 $little
$count = U32 $entry 4 $little
if (($tag -eq 256 -or $tag -eq 257) -and
$count -eq 1) {
$value = $null
if ($type -eq 3) {
$value = U16 $entry 8 $little
}
elseif ($type -eq 4) {
$value = U32 $entry 8 $little
}
if ($tag -eq 256) {
$width = $value
}
else {
$height = $value
}
}
}
[PSCustomObject]@{
ImageWidth = $width
ImageLength = $height
FoundBoth = (
$null -ne $width -and
$null -ne $height
)
}
}
finally {
$stream.Dispose()
}
② 출력 예시
ImageWidth : 1920
ImageLength : 1080
FoundBoth : True
위 결과는 설명을 위한 예시다.
③ 코드의 한계
첫 번째 IFD의 기본 이미지 크기 태그만 확인한다.
다중 페이지 TIFF의 모든 이미지 크기를 분석하지 않으며, 이미지 픽셀 데이터의 정상 여부도 검증하지 않는다.
20. TIFF 파일 손상 시 점검할 주요 항목
TIFF 파일이 정상적으로 열리지 않는 경우 다음 항목을 확인할 수 있다.
| 점검 항목 | 확인 목적 |
|---|---|
| II·MM | 바이트 순서 |
| Magic Number | Classic TIFF 식별 |
| First IFD Offset | 첫 번째 디렉터리 위치 |
| IFD Entry Count | 태그 항목 개수 |
| Tag Type·Count | 값의 자료형 및 크기 |
| Value·Offset | 태그 값의 위치 |
| ImageWidth·ImageLength | 이미지 크기 |
| Compression | 압축 방식 |
| StripOffsets | 이미지 데이터 시작 위치 |
| StripByteCounts | 이미지 데이터 크기 |
| TileOffsets | 타일 데이터 위치 |
| TileByteCounts | 타일 데이터 크기 |
| Next IFD Offset | 다음 이미지 디렉터리 |
| SHA-256 | 기준 파일과 동일성 |
대표적인 손상 상황
① TIFF 헤더 손상
II·MM 식별자 또는 매직 넘버가 올바르지 않으면 파일을 정상적으로 해석하지 못할 수 있다.
② IFD 오프셋 오류
IFD가 파일 범위를 벗어나면 태그를 읽을 수 없다.
③ 태그 자료형 오류
태그의 Type이나 Count가 잘못되면 값의 길이와 위치를 잘못 해석할 수 있다.
④ 이미지 데이터 경계 오류
StripOffsets와 StripByteCounts가 실제 파일 크기를 초과하면 이미지 데이터가 누락되었을 가능성이 있다.
⑤ 압축 데이터 손상
태그가 정상이어도 압축된 이미지 데이터가 손상되면 디코딩이 실패할 수 있다.
21. TIFF 구조 검증과 SHA-256 무결성 검증
TIFF 파일의 내부 구조를 확인하는 작업과 원본 파일과의 동일성을 확인하는 작업은 구분해야 한다.
구조 분석
헤더, IFD, 태그, Strip·Tile 위치 및 데이터 경계를 확인한다.
이미지 디코딩
압축 방식에 맞는 디코더를 이용해 실제 픽셀 데이터를 복원한다.
SHA-256 무결성 검증
파일 전체의 SHA-256 해시값을 계산하고 신뢰할 수 있는 기준값과 비교한다.
PowerShell 5.1 코드
$path = "C:\FileTest\sample.tif"
Get-FileHash -LiteralPath $path -Algorithm SHA256 |
Format-List Algorithm, Hash, Path
검증 방식 비교
| 구분 | 구조 분석 | 이미지 디코딩 | SHA-256 |
|---|---|---|---|
| 주요 확인 | 헤더·IFD·태그 | 실제 이미지 복원 | 파일 전체 |
| 파일 손상 탐지 | 일부 가능 | 일부 가능 | 기준값과 비교 가능 |
| 원본 동일성 | 확인 불가 | 확인 불가 | 기준값이 있으면 가능 |
| 이미지 품질 | 확인 불가 | 복원 후 별도 평가 | 확인 불가 |
| 보안상 안전성 | 보장 불가 | 보장 불가 | 단독 보장 불가 |
SHA-256 값이 신뢰할 수 있는 기준값과 일치하면 파일의 바이트 내용이 동일하다고 매우 높은 신뢰도로 판단할 수 있다.
하지만 해시값이 일치한다고 해서 TIFF 파일이 안전하거나 이미지 내용이 정확하다는 의미는 아니다.
22. 자주 묻는 질문
Q1. TIFF 파일의 Magic Number는 무엇인가요?
Classic TIFF는 매직 넘버 42를 사용한다. 리틀엔디언은 49 49 2A 00, 빅엔디언은 4D 4D 00 2A로 시작한다.
Q2. TIFF의 II와 MM은 무엇을 의미하나요?
II는 리틀엔디언, MM은 빅엔디언 바이트 순서를 나타낸다.
Q3. IFD는 무엇인가요?
Image File Directory의 약자로, 이미지 크기와 압축 방식, 데이터 위치 등 TIFF 이미지의 속성을 태그로 저장하는 디렉터리다.
Q4. TIFF는 여러 페이지를 저장할 수 있나요?
그렇다. 여러 IFD를 연결해 다중 이미지 또는 다중 페이지를 저장할 수 있다.
Q5. TIFF는 무손실 파일 형식인가요?
TIFF는 다양한 압축 방식을 담을 수 있는 컨테이너 성격의 이미지 형식이다. 무손실 또는 비압축 저장이 가능하지만 모든 TIFF가 무손실인 것은 아니다.
Q6. TIFF와 BigTIFF는 같은 구조인가요?
아니다. BigTIFF는 64비트 오프셋과 더 큰 IFD Entry를 사용하므로 별도의 해석이 필요하다.
Q7. TIFF 파일의 이미지 데이터는 어디에 저장되나요?
일반적으로 Strip 또는 Tile 단위로 저장되며 관련 태그의 오프셋과 크기를 통해 위치를 확인한다.
Q8. TIFF에도 Exif 메타데이터가 포함될 수 있나요?
그렇다. TIFF의 태그 및 IFD 기반 구조는 Exif 메타데이터와 밀접한 관련이 있다.
Q9. PowerShell만으로 TIFF 파일이 정상인지 완전히 확인할 수 있나요?
PowerShell로 헤더와 IFD 구조를 점검할 수 있지만, 모든 태그와 압축 방식의 유효성 및 실제 이미지 디코딩까지 검증하려면 전문 TIFF 처리 도구가 필요하다.
TIFF와 다른 이미지 파일 형식의 구조를 비교하고 싶다면 JPEG 파일 구조 분석과 PNG 파일 구조 분석 글도 함께 참고할 수 있다.
마무리
TIFF는 II·MM 바이트 순서와 IFD 기반 태그 구조를 사용하는 이미지 파일 형식이다.
Classic TIFF의 헤더에는 매직 넘버 42와 첫 번째 IFD 오프셋이 저장된다.
IFD에는 이미지 크기, 압축 방식, 색상 구성, Strip·Tile 데이터 위치와 같은 정보가 기록된다.
TIFF 파일 구조 분석에서는 엔디언 해석, IFD 오프셋, 태그의 Type·Count, Value·Offset 구분과 이미지 데이터 경계를 순서대로 확인하는 것이 중요하다.
그러나 이러한 구조가 정상적으로 보인다고 해서 압축 이미지 데이터가 완전히 복원되거나 파일의 원본 동일성과 안전성이 보장되는 것은 아니다.
따라서 구조 분석, 이미지 디코딩, 메타데이터 검토 및 SHA-256 무결성 검증을 목적에 맞게 구분해 활용해야 한다.
핵심 내용 3줄 요약
- TIFF는 II·MM 바이트 순서와 IFD 태그를 이용해 이미지 크기, 압축 방식 및 데이터 위치를 정의한다.
- Classic TIFF는 매직 넘버 42와 32비트 오프셋을 사용하며, BigTIFF는 별도의 확장 구조를 사용한다.
- 헤더와 태그 구조가 정상이어도 이미지 디코딩 및 원본 동일성은 별도로 검증해야 한다.
공식 및 기술 참고자료
- Adobe — TIFF Revision 6.0 Specification
https://www.adobe.io/open/standards/TIFF.html - Library of Congress — TIFF File Format
https://www.loc.gov/preservation/digital/formats/fdd/fdd000022.shtml - Library of Congress — BigTIFF File Format
https://www.loc.gov/preservation/digital/formats/fdd/fdd000328.shtml - CIPA — Exif 3.0 Specification
https://www.cipa.jp/std/documents/e/DC-008-Translation-2023-E.pdf - Microsoft Learn — File.OpenRead Method
https://learn.microsoft.com/dotnet/api/system.io.file.openread - Microsoft Learn — BinaryReader Class
https://learn.microsoft.com/dotnet/api/system.io.binaryreader - Microsoft Learn — Get-FileHash (PowerShell 5.1)
https://learn.microsoft.com/powershell/module/microsoft.powershell.utility/get-filehash?view=powershell-5.1
※ 본문의 PowerShell 코드는 Classic TIFF의 헤더와 첫 번째 IFD를 읽기 전용으로 분석하는 학습용 예시다. BigTIFF 및 모든 TIFF 압축 방식을 지원하는 완전한 디코더가 아니며, 실제 Windows PowerShell 5.1 환경에서의 실행 검증은 별도로 필요하다.
디지털 파일 검증 연구소
댓글 0
첫 댓글을 남겨보세요.