JPEG 파일 구조 분석|SOI·APP·SOS·EOI 마커 확인 방법
JPEG(Joint Photographic Experts Group)는 디지털카메라 사진, 스마트폰 이미지, 웹사이트 사진 등 다양한 환경에서 사용되는 이미지 압축 기술이다.
일반적으로 .jpg 또는 .jpeg 확장자로 저장되며, 사진과 같이 색상 변화가 많은 이미지를 비교적 작은 파일 크기로 보관하는 데 적합하다.
그러나 JPEG 파일은 단순히 픽셀 데이터를 압축한 결과물만으로 구성되지 않는다.
파일의 시작을 알리는 SOI(Start of Image), 응용 프로그램 관련 정보를 저장하는 APP 마커, 이미지의 압축 설정을 정의하는 DQT·DHT·SOF, 압축 데이터의 시작을 알리는 SOS(Start of Scan), 종료를 나타내는 EOI(End of Image) 등이 일정한 규칙에 따라 배치된다.
또한 JPEG 파일에는 Exif, JFIF, ICC 색상 프로파일, XMP 등의 메타데이터가 포함될 수 있다.
이번 글에서는 JPEG 파일 구조 분석을 중심으로 주요 마커의 역할, 이미지 데이터 저장 방식, 메타데이터 식별 원리, 파일 손상 여부를 점검하는 방법을 설명한다.
Windows PowerShell을 이용해 실제 JPEG 파일의 시작 바이트와 주요 마커를 확인하는 실습도 진행한다.
1. JPEG 파일 구조 분석이 필요한 이유
JPEG 파일은 대부분의 운영체제와 이미지 프로그램에서 쉽게 열 수 있다.
하지만 파일이 열리지 않거나 이미지의 일부가 깨지는 경우에는 확장자만 확인해서 원인을 파악하기 어렵다.
다음과 같은 상황에서 JPEG 내부 구조 분석이 도움이 된다.
- JPEG 파일이 열리지 않는 경우
- 이미지 일부가 회색 또는 깨진 형태로 나타나는 경우
- 파일 전송 이후 손상이 의심되는 경우
- 확장자는 JPG지만 실제 JPEG 데이터인지 확인하려는 경우
- 이미지의 가로·세로 크기를 헤더에서 확인하려는 경우
- Exif 메타데이터의 존재 여부를 파악하려는 경우
- JPEG 파일의 시작 또는 종료 데이터가 누락되었는지 확인하려는 경우
- 원본과 수정본의 무결성을 비교하려는 경우
JPEG 구조를 이해하면 파일의 형식 오류와 이미지 데이터 손상, 메타데이터 문제를 구분하는 데 도움이 된다.
다만 JPEG 마커가 정상적으로 발견된다고 해서 이미지가 완전히 정상적으로 복원되거나 안전하다는 의미는 아니다.
2. JPEG 파일의 전체 구조
일반적인 JPEG 파일은 여러 개의 마커와 마커 세그먼트, 압축된 이미지 데이터로 구성된다.
JPEG 기본 구조
JPEG File
│
├── SOI (FF D8)
│
├── APP0 / APP1 / APP2 ...
│ ├── JFIF
│ ├── Exif / XMP
│ └── ICC Profile
│
├── DQT
│ └── Quantization Tables
│
├── SOF
│ └── Image Frame Information
│
├── DHT
│ └── Huffman Tables
│
├── DRI (Optional)
│
├── SOS
│ └── Scan Parameters
│
├── Entropy-Coded Image Data
│
└── EOI (FF D9)
위 구조는 일반적인 JPEG 파일을 이해하기 위한 개념도다.
실제 파일에서는 DQT·DHT·SOF 등의 순서와 개수가 달라질 수 있으며, Progressive JPEG는 여러 SOS와 압축 데이터 구간을 포함할 수 있다.
주요 마커의 역할
| 마커 | 코드 | 주요 역할 |
|---|---|---|
| SOI | FF D8 | 이미지 시작 |
| APP0 | FF E0 | JFIF 등 응용 정보 |
| APP1 | FF E1 | Exif·XMP 등 메타데이터 |
| APP2 | FF E2 | ICC 프로파일 등 |
| DQT | FF DB | 양자화 테이블 정의 |
| DHT | FF C4 | 허프만 테이블 정의 |
| SOF0 | FF C0 | Baseline DCT 프레임 정보 |
| SOF2 | FF C2 | Progressive DCT 프레임 정보 |
| DRI | FF DD | 재시작 간격 정의 |
| SOS | FF DA | 스캔 시작 |
| EOI | FF D9 | 이미지 종료 |
JPEG에는 위 표 이외에도 여러 마커가 존재한다.
특히 JPEG 형식은 여러 압축 프로세스를 지원하므로 모든 파일이 동일한 마커 구성을 가지는 것은 아니다.
3. JPEG 파일 시그니처와 Magic Number
JPEG 파일의 시작에는 일반적으로 다음 2바이트가 존재한다.
JPEG 시작 시그니처
FF D8
이 값은 SOI(Start of Image) 마커다.
PNG의 8바이트 시그니처와 달리 JPEG의 기본 시작 마커는 2바이트다.
대표적인 시작 바이트
JFIF 기반 JPEG 파일에서는 다음과 같은 바이트가 나타날 수 있다.
FF D8 FF E0
Exif 기반 JPEG 파일에서는 다음과 같은 바이트가 나타날 수 있다.
FF D8 FF E1
여기서 FF E0은 APP0, FF E1은 APP1 마커다.
그러나 모든 JPEG 파일이 FF D8 FF E0으로 시작하는 것은 아니다.
따라서 FF D8 뒤에 APP0이 없다는 이유만으로 JPEG 파일이 손상되었다고 판단해서는 안 된다.
4. JPEG 마커와 마커 세그먼트의 기본 구조
JPEG의 마커는 일반적으로 FF 바이트와 마커 식별 바이트로 표현된다.
하지만 모든 마커가 동일한 길이의 데이터 영역을 가지는 것은 아니다.
길이 필드가 있는 일반적인 마커 세그먼트
+----------------------+
| Marker 2 bytes |
+----------------------+
| Length 2 bytes |
+----------------------+
| Parameters Variable |
+----------------------+
Length는 빅엔디언 방식으로 저장된다.
이 길이에는 Length 필드 자체의 2바이트가 포함되지만, 앞의 Marker 2바이트는 포함되지 않는다.
따라서 길이 필드가 있는 일반 세그먼트의 전체 크기는 다음과 같다.
전체 세그먼트 크기 = 2 + Length
길이 필드가 없는 대표적인 마커
SOI와 EOI에는 일반 세그먼트처럼 Length 필드가 존재하지 않는다.
RST0~RST7 재시작 마커와 TEM 마커도 길이 필드가 없는 마커에 해당한다.
SOS 역시 길이 필드가 있는 헤더를 가지지만, 그 뒤에는 별도의 길이 필드만으로 끝을 알 수 없는 엔트로피 부호화 데이터가 이어진다.
따라서 JPEG 파일을 분석할 때는 마커별 구조 차이를 고려해야 한다.
5. SOI 마커의 구조와 역할
SOI(Start of Image)는 JPEG 이미지 데이터 스트림의 시작을 알리는 마커다.
SOI 마커
FF D8
SOI는 JPEG 파일의 형식을 식별하는 가장 기본적인 단서다.
하지만 SOI가 존재한다고 해서 나머지 마커와 압축 데이터가 정상이라는 의미는 아니다.
파일의 일부가 잘려 있거나, 잘못된 세그먼트 길이가 기록되어 있거나, 압축 데이터가 손상된 경우에도 시작 바이트는 정상일 수 있다.
따라서 SOI 확인은 JPEG 파일 검증의 첫 단계로 이해하는 것이 적절하다.
6. APP0와 JFIF 메타데이터
APP0는 응용 프로그램 관련 정보를 저장할 수 있는 마커 세그먼트다.
일반적인 JFIF(JPEG File Interchange Format) 파일에서는 APP0에 JFIF 식별 정보가 포함된다.
JFIF 식별 바이트
4A 46 49 46 00
이는 ASCII 문자열 JFIF와 종료 바이트 00에 해당한다.
JFIF APP0의 주요 필드
| 필드 | 역할 |
|---|---|
| Identifier | JFIF 식별 |
| Version | JFIF 버전 |
| Units | 픽셀 밀도 단위 |
| Xdensity | 수평 밀도 |
| Ydensity | 수직 밀도 |
| Xthumbnail | 썸네일 너비 |
| Ythumbnail | 썸네일 높이 |
| Thumbnail Data | 선택적인 썸네일 데이터 |
JFIF는 JPEG 압축 데이터의 교환을 위한 파일 형식 규칙을 제공한다.
특히 JFIF 규격에 맞는 파일에서는 SOI 다음에 JFIF APP0 세그먼트가 배치된다.
다만 JPEG 파일 전체가 반드시 JFIF 형식을 따라야 하는 것은 아니다.
7. APP1과 Exif 메타데이터
APP1은 JPEG 파일에서 Exif나 XMP 같은 메타데이터를 저장하는 데 자주 사용된다.
Exif 식별 바이트
45 78 69 66 00 00
이 값은 Exif 문자열과 두 개의 널 바이트를 나타낸다.
Exif 데이터에는 TIFF 구조를 기반으로 한 메타데이터가 포함될 수 있다.
대표적인 Exif 정보
| 항목 | 의미 |
|---|---|
| DateTimeOriginal | 원본 촬영 시각 |
| Make | 장치 제조사 |
| Model | 장치 모델 |
| Orientation | 이미지 방향 정보 |
| ExposureTime | 노출 시간 |
| FNumber | 조리개 값 |
| ISO 관련 태그 | 촬영 감도 |
| GPS 관련 태그 | 위치 관련 정보 |
모든 JPEG 파일에 Exif가 존재하는 것은 아니다.
또한 Exif에 기록된 정보는 편집 프로그램이나 별도 도구를 통해 변경할 수 있다.
따라서 Exif 정보는 파일 분석의 단서로 활용할 수 있지만, 그 자체만으로 촬영 시각이나 촬영 장치의 진위를 확정할 수는 없다.
8. APP2와 ICC 색상 프로파일
APP2는 여러 종류의 응용 데이터를 저장할 수 있다.
그중 하나가 ICC 색상 프로파일이다.
ICC 프로파일은 이미지의 색상 표현과 색 공간 해석에 사용된다.
APP2에서 확인할 수 있는 정보
- ICC 프로파일 식별 정보
- 색상 프로파일 데이터
- 여러 세그먼트로 분할된 프로파일 구성
ICC 프로파일은 용량에 따라 여러 APP2 세그먼트에 나뉘어 저장될 수 있다.
따라서 APP2가 여러 번 나타난다고 해서 곧바로 파일이 비정상적이라고 판단해서는 안 된다.
APP2 마커가 존재한다고 해서 모든 APP2가 ICC 프로파일인 것도 아니다.
9. DQT 마커와 양자화 테이블
DQT(Define Quantization Table)는 JPEG 압축 과정에서 사용하는 양자화 테이블을 정의한다.
DQT 마커
FF DB
JPEG의 일반적인 손실 압축 과정에서는 이미지 정보를 주파수 성분으로 변환한 뒤 양자화를 수행한다.
양자화는 이미지 데이터의 저장 크기를 줄이는 데 중요한 역할을 하지만, 이 과정에서 일부 정보가 손실될 수 있다.
양자화 테이블의 의미
양자화 테이블은 변환 계수에 적용할 양자화 단계를 정의한다.
이 테이블은 JPEG 압축 방식의 특성을 이해하는 중요한 정보다.
다만 양자화 테이블만 확인해서 이미지의 정확한 저장 품질이나 편집 이력을 단정할 수는 없다.
JPEG 파일에 따라 여러 개의 양자화 테이블이 정의될 수 있다.
10. DHT 마커와 허프만 테이블
DHT(Define Huffman Table)는 허프만 부호화에 사용하는 테이블을 정의한다.
DHT 마커
FF C4
허프만 부호화는 데이터에 등장하는 기호를 가변 길이 코드로 표현하는 엔트로피 부호화 방식이다.
JPEG에서는 허프만 테이블을 사용해 압축된 데이터의 비트열을 해석할 수 있다.
그러나 JPEG 표준에는 허프만 부호화 외의 엔트로피 부호화 방식도 존재한다.
따라서 모든 JPEG 파일이 반드시 DHT 마커를 포함한다고 일반화해서는 안 된다.
11. SOF 마커와 이미지 크기 정보
SOF(Start of Frame)는 JPEG 이미지의 프레임 특성을 정의한다.
대표적으로 SOF0과 SOF2가 자주 등장한다.
주요 SOF 마커
| 마커 | 의미 |
|---|---|
SOF0 (FF C0) | Baseline DCT |
SOF1 (FF C1) | Extended Sequential DCT |
SOF2 (FF C2) | Progressive DCT |
SOF 세그먼트에는 이미지 크기와 구성 요소 정보 등이 기록된다.
주요 필드
| 필드 | 의미 |
|---|---|
| Sample Precision | 샘플 정밀도 |
| Image Height | 이미지 높이 |
| Image Width | 이미지 너비 |
| Number of Components | 색상 구성 요소 수 |
| Component Parameters | 구성 요소별 샘플링·테이블 정보 |
SOF에 기록된 이미지 크기를 읽으면 전체 이미지를 디코딩하지 않고도 가로와 세로 픽셀 수를 확인할 수 있다.
다만 모든 JPEG 계열의 압축 프로세스가 SOF0 또는 SOF2만 사용하는 것은 아니다.
12. SOS 마커와 압축 이미지 데이터
SOS(Start of Scan)는 스캔의 시작을 나타내는 마커다.
SOS 마커
FF DA
SOS 세그먼트에는 스캔에 포함되는 구성 요소와 엔트로피 부호화에 필요한 일부 매개변수가 저장된다.
SOS 헤더 뒤에는 엔트로피 부호화된 이미지 데이터가 이어진다.
SOS 이후의 특징
JPEG의 압축 이미지 데이터는 일반적인 길이 필드가 있는 마커 세그먼트와 다르게 처리해야 한다.
압축 데이터에는 FF 바이트가 포함될 수 있으며, 실제 데이터의 FF 값과 마커를 구분하기 위해 바이트 스터핑(Byte Stuffing)이 사용된다.
대표적인 형태는 다음과 같다.
FF 00
이는 엔트로피 부호화 데이터 안에서 FF 데이터 바이트를 표현하기 위한 방식이다.
따라서 JPEG 파일에서 FF 바이트를 무조건 새로운 마커의 시작으로 해석해서는 안 된다.
Progressive JPEG의 특징
Progressive JPEG는 여러 스캔으로 이미지 정보를 나누어 저장할 수 있다.
이 경우 SOS가 여러 번 등장할 수 있으며, 각 스캔 사이에 추가 마커가 존재할 수도 있다.
따라서 첫 번째 SOS 뒤의 모든 데이터를 하나의 고정 길이 영역으로 간주하는 방식은 올바르지 않다.
13. EOI 마커와 JPEG 종료 구조
EOI(End of Image)는 JPEG 이미지 데이터 스트림의 종료를 나타낸다.
EOI 마커
FF D9
일반적인 단일 JPEG 파일에서는 압축 이미지 데이터가 끝난 뒤 EOI가 등장한다.
EOI 확인이 필요한 이유
- 파일이 중간에 잘렸는지 점검하는 단서가 된다.
- 압축 데이터의 종료 구조를 확인하는 데 도움이 된다.
- 파일 전송 과정에서 일부 데이터가 누락되었는지 조사할 때 활용할 수 있다.
그러나 파일 끝부분에 FF D9가 존재한다고 해서 압축 데이터가 정상이라는 의미는 아니다.
또한 EOI 뒤에 추가 데이터가 존재할 수도 있으므로 파일의 끝 2바이트만 확인하는 방식으로 모든 JPEG 파일의 구조적 정상 여부를 판단할 수는 없다.
14. JPEG와 PNG의 내부 구조 비교
JPEG와 PNG는 모두 널리 사용되는 이미지 형식이지만 내부 구조와 검증 방식이 다르다.
| 구분 | JPEG | PNG |
|---|---|---|
| 시작 식별값 | FF D8 | 89 50 4E 47 0D 0A 1A 0A |
| 기본 구조 | 마커·세그먼트·압축 데이터 | 청크 |
| 일반적인 압축 | 손실 압축 | 무손실 압축 |
| 이미지 크기 정보 | SOF 등 | IHDR |
| 메타데이터 | APP 세그먼트 등 | 메타데이터 청크 |
| 종료 식별 | EOI | IEND |
| 구조적 CRC 필드 | 일반 JPEG에 없음 | 각 청크에 존재 |
PNG는 각 청크에 CRC-32 필드가 포함되지만, 일반적인 JPEG 마커 세그먼트에는 이에 대응하는 공통 CRC 필드가 존재하지 않는다.
따라서 JPEG 파일의 무결성을 확인하려면 마커 구조 점검 외에도 파일 전체의 해시값 비교 등을 고려할 수 있다.
15. Windows PowerShell로 JPEG 시그니처 확인하기
이제 실제 JPEG 파일의 시작 바이트를 확인해 보자.
이번 실습은 파일을 수정하지 않는 읽기 전용 방식이다.
① 실습 파일 준비
다음 경로에 JPEG 파일이 있다고 가정한다.
C:\FileTest\sample.jpg
실제 실행할 때는 본인의 파일 경로로 변경한다.
② PowerShell 코드
$path = "C:\FileTest\sample.jpg"
$stream = [System.IO.File]::OpenRead($path)
try {
$buffer = New-Object byte[] 4
$count = $stream.Read($buffer, 0, 4)
if ($count -lt 2) {
throw "File is shorter than 2 bytes."
}
if ($buffer[0] -ne 0xFF -or
$buffer[1] -ne 0xD8) {
throw "Invalid JPEG SOI marker."
}
[BitConverter]::ToString($buffer, 0, $count)
}
finally {
$stream.Dispose()
}
③ 출력 결과 예시
FF-D8-FF-E0
이 결과는 JFIF 기반 JPEG 파일에서 나타날 수 있는 시작 바이트의 예시다.
Exif 기반 JPEG 파일이라면 FF-D8-FF-E1과 같은 결과가 나타날 수 있다.
④ 확인 가능한 범위
이 코드는 JPEG의 시작 SOI 마커를 확인하고 최대 4바이트를 출력한다.
SOF, SOS, EOI, 압축 데이터의 정상 여부는 확인하지 않는다.
16. PowerShell로 JPEG 주요 마커 목록 확인하기
JPEG 파일의 주요 마커를 읽으면 파일의 내부 구성을 이해하는 데 도움이 된다.
이번 실습은 SOI에서 시작해 길이 필드가 있는 세그먼트를 읽고, SOS 이후의 압축 데이터에서 바이트 스터핑과 재시작 마커를 구분하도록 구성했다.
① PowerShell 코드
$path = "C:\FileTest\sample.jpg"
$stream = [System.IO.File]::OpenRead($path)
function Read-ByteRequired {
param($InputStream)
$value = $InputStream.ReadByte()
if ($value -lt 0) {
throw "Unexpected end of file."
}
return $value
}
try {
$first = Read-ByteRequired $stream
$second = Read-ByteRequired $stream
if ($first -ne 0xFF -or $second -ne 0xD8) {
throw "Invalid JPEG SOI marker."
}
[PSCustomObject]@{
Offset = 0
Marker = "FFD8"
Name = "SOI"
Length = "-"
}
$insideScan = $false
$pendingMarker = -1
$pendingOffset = [long]0
while ($stream.Position -lt $stream.Length -or
$pendingMarker -ge 0) {
if ($pendingMarker -ge 0) {
$marker = $pendingMarker
$offset = $pendingOffset
$pendingMarker = -1
}
else {
$prefix = Read-ByteRequired $stream
if ($prefix -ne 0xFF) {
if ($insideScan) {
continue
}
throw "Expected marker prefix FF."
}
$offset = $stream.Position - 1
do {
$marker = Read-ByteRequired $stream
} while ($marker -eq 0xFF)
}
if ($marker -eq 0x00) {
if (-not $insideScan) {
throw "Unexpected stuffed byte."
}
continue
}
$code = "FF{0:X2}" -f $marker
$name = "Other"
if ($marker -eq 0xD9) {
$name = "EOI"
}
elseif ($marker -eq 0xDA) {
$name = "SOS"
}
elseif ($marker -ge 0xE0 -and $marker -le 0xEF) {
$name = "APP{0}" -f ($marker - 0xE0)
}
elseif ($marker -eq 0xDB) {
$name = "DQT"
}
elseif ($marker -eq 0xC4) {
$name = "DHT"
}
elseif ($marker -eq 0xDD) {
$name = "DRI"
}
elseif ($marker -eq 0xC0) {
$name = "SOF0"
}
elseif ($marker -eq 0xC2) {
$name = "SOF2"
}
elseif ($marker -ge 0xD0 -and $marker -le 0xD7) {
$name = "RST{0}" -f ($marker - 0xD0)
}
elseif ($marker -eq 0xFE) {
$name = "COM"
}
if ($marker -eq 0xD9) {
[PSCustomObject]@{
Offset = $offset
Marker = $code
Name = $name
Length = "-"
}
break
}
if (($marker -ge 0xD0 -and $marker -le 0xD7) -or
$marker -eq 0x01) {
[PSCustomObject]@{
Offset = $offset
Marker = $code
Name = $name
Length = "-"
}
continue
}
$high = Read-ByteRequired $stream
$low = Read-ByteRequired $stream
$length = ($high -shl 8) -bor $low
if ($length -lt 2) {
throw "Invalid marker segment length."
}
$payloadLength = $length - 2
$remaining = $stream.Length - $stream.Position
if ($payloadLength -gt $remaining) {
throw "Segment exceeds file boundary."
}
[PSCustomObject]@{
Offset = $offset
Marker = $code
Name = $name
Length = $length
}
$stream.Seek(
[long]$payloadLength,
[System.IO.SeekOrigin]::Current
) | Out-Null
if ($marker -eq 0xDA) {
$insideScan = $true
}
else {
$insideScan = $false
}
if ($insideScan) {
while ($stream.Position -lt $stream.Length) {
$value = Read-ByteRequired $stream
if ($value -ne 0xFF) {
continue
}
$candidateOffset = $stream.Position - 1
do {
$next = Read-ByteRequired $stream
} while ($next -eq 0xFF)
if ($next -eq 0x00) {
continue
}
if ($next -ge 0xD0 -and $next -le 0xD7) {
[PSCustomObject]@{
Offset = $candidateOffset
Marker = ("FF{0:X2}" -f $next)
Name = ("RST{0}" -f ($next - 0xD0))
Length = "-"
}
continue
}
$pendingMarker = $next
$pendingOffset = $candidateOffset
break
}
if ($pendingMarker -lt 0) {
throw "JPEG scan ended without a marker."
}
}
}
}
finally {
$stream.Dispose()
}
② 출력 결과 예시
Offset Marker Name Length
------ ------ ---- ------
0 FFD8 SOI -
2 FFE0 APP0 16
20 FFDB DQT 67
89 FFC0 SOF0 17
108 FFC4 DHT 31
141 FFDA SOS 12
8462 FFD9 EOI -
위 결과는 형식을 설명하기 위한 예시이며 실제 파일의 마커 순서와 오프셋, 세그먼트 길이는 다를 수 있다.
③ 이 코드로 확인할 수 있는 것
- SOI 시작 마커
- APP0·APP1 등의 응용 세그먼트
- DQT·DHT·SOF0·SOF2·SOS 마커
- 길이 필드가 있는 세그먼트의 크기
- 일반적인 스캔 데이터의 바이트 스터핑
- RST0~RST7 재시작 마커
- EOI 종료 마커의 등장 여부
④ 실습 코드의 한계
이 코드는 JPEG 구조를 살펴보기 위한 학습용 마커 탐색기다.
모든 JPEG 압축 프로세스와 확장 형식의 문법을 검증하는 완전한 디코더는 아니다.
또한 이미지 픽셀을 복원하거나 Exif 내용을 해석하지 않으며, 허프만 데이터와 양자화 테이블의 의미적 정합성을 검사하지 않는다.
따라서 출력된 마커 목록만으로 파일의 정상적인 디코딩 가능 여부를 확정해서는 안 된다.
17. PowerShell로 JPEG 이미지 크기 확인하기
이번에는 JPEG의 SOF0 또는 SOF2 세그먼트에서 이미지의 가로·세로 크기를 읽어 보자.
① PowerShell 코드
$path = "C:\FileTest\sample.jpg"
$bytes = [System.IO.File]::ReadAllBytes($path)
if ($bytes.Length -lt 4 -or
$bytes[0] -ne 0xFF -or
$bytes[1] -ne 0xD8) {
throw "Invalid JPEG SOI marker."
}
$position = 2
$found = $false
while ($position + 3 -lt $bytes.Length) {
if ($bytes[$position] -ne 0xFF) {
throw "Invalid marker boundary."
}
while ($position -lt $bytes.Length -and
$bytes[$position] -eq 0xFF) {
$position++
}
if ($position -ge $bytes.Length) {
break
}
$marker = $bytes[$position]
$position++
if ($marker -eq 0xD9 -or $marker -eq 0xDA) {
break
}
if ($marker -eq 0x01 -or
($marker -ge 0xD0 -and $marker -le 0xD7)) {
continue
}
if ($position + 1 -ge $bytes.Length) {
throw "Incomplete segment length."
}
$length = (
($bytes[$position] -shl 8) -bor
$bytes[$position + 1]
)
if ($length -lt 2 -or
$length -gt $bytes.Length - $position) {
throw "Invalid segment length."
}
if ($marker -eq 0xC0 -or $marker -eq 0xC2) {
if ($length -lt 8) {
throw "SOF segment is too short."
}
$height = (
($bytes[$position + 3] -shl 8) -bor
$bytes[$position + 4]
)
$width = (
($bytes[$position + 5] -shl 8) -bor
$bytes[$position + 6]
)
[PSCustomObject]@{
FrameType = if ($marker -eq 0xC0) {
"Baseline"
} else {
"Progressive"
}
Width = $width
Height = $height
Precision = $bytes[$position + 2]
}
$found = $true
break
}
$position += $length
}
if (-not $found) {
Write-Output "SOF0 or SOF2 was not found."
}
② 출력 예시
FrameType : Baseline
Width : 1920
Height : 1080
Precision : 8
위 결과는 예시다.
③ 결과 해석
FrameType은 SOF0 또는 SOF2에 따라 표시된다.
Width와 Height는 이미지의 픽셀 크기다.
Precision은 이미지 샘플의 정밀도를 나타낸다.
④ 주의사항
이 코드는 SOF0과 SOF2만 찾는다.
다른 SOF 마커를 사용하는 JPEG 파일에서는 크기를 찾지 못할 수 있다.
또한 파일 전체를 메모리에 읽으므로 대용량 파일이나 출처가 불분명한 파일에 사용할 때는 별도의 크기 제한이 필요하다.
18. JPEG 파일 손상 시 점검할 주요 항목
JPEG 파일에 문제가 발생하면 다음 항목을 순서대로 확인할 수 있다.
| 점검 항목 | 확인 목적 |
|---|---|
| SOI | JPEG 시작 여부 |
| APP0·APP1 | JFIF·Exif 메타데이터 존재 여부 |
| DQT | 양자화 테이블 존재 여부 |
| DHT | 허프만 테이블 존재 여부 |
| SOF | 이미지 크기와 프레임 정보 |
| SOS | 스캔 시작 구조 |
| RST | 재시작 마커 사용 여부 |
| EOI | 이미지 종료 여부 |
| SHA-256 | 기준 파일과의 바이트 동일성 비교 |
손상이 의심되는 대표적인 상황
① SOI 누락
파일이 JPEG 데이터 스트림으로 시작하지 않거나 앞부분이 손상되었을 가능성이 있다.
② 세그먼트 길이 오류
세그먼트에 기록된 길이가 파일 경계를 벗어나면 데이터가 잘렸거나 구조가 잘못되었을 수 있다.
③ SOS 이후 압축 데이터 손상
이미지가 일부만 표시되거나 색상이 깨질 수 있다.
④ EOI 누락
파일이 정상적으로 종료되지 않았거나 전송 과정에서 일부 데이터가 누락되었을 가능성이 있다.
⑤ 메타데이터 이상
일부 프로그램에서 이미지 방향이나 색상 표현이 예상과 다르게 표시될 수 있다.
다만 이러한 증상만으로 손상의 원인을 확정해서는 안 된다.
19. JPEG 구조 검증과 SHA-256 무결성 검증
JPEG 파일의 구조를 확인하는 것과 원본 파일의 동일성을 검증하는 것은 서로 다른 작업이다.
JPEG 구조 검증
마커와 세그먼트의 배치, 길이, 이미지 프레임 정보 등을 확인한다.
메타데이터 확인
APP 세그먼트에 포함된 Exif, JFIF, ICC 프로파일 등의 존재와 일부 내용을 확인한다.
SHA-256 검증
파일 전체의 바이트를 대상으로 SHA-256 해시값을 계산하고 신뢰할 수 있는 기준값과 비교한다.
검증 방식 비교
| 구분 | JPEG 구조 분석 | 메타데이터 분석 | SHA-256 |
|---|---|---|---|
| 확인 대상 | 마커와 압축 구조 | APP 데이터 등 | 파일 전체 |
| 주요 목적 | 형식 점검 | 부가 정보 확인 | 바이트 동일성 비교 |
| 원본 동일성 증명 | 불가능 | 불가능 | 신뢰할 수 있는 기준값이 있으면 가능 |
| 안전성 보장 | 불가능 | 불가능 | 단독으로는 불가능 |
JPEG 파일이 정상적으로 열리더라도 원본과 동일하다는 보장은 없다.
또한 이미지를 다시 저장하면 압축 데이터나 메타데이터가 변경되어 SHA-256 해시값이 달라질 수 있다.
20. JPEG 파일 분석 시 보안과 개인정보 주의사항
① Exif 위치 정보
일부 JPEG 파일에는 GPS 관련 메타데이터가 포함될 수 있다.
사진을 공개하기 전에 불필요한 위치 정보가 포함되어 있는지 확인하는 것이 좋다.
② 촬영 정보의 신뢰성
Exif에 기록된 촬영 날짜나 장치 모델은 변경될 수 있다.
따라서 메타데이터만으로 촬영 시점이나 장치의 진위를 확정해서는 안 된다.
③ 비정상적인 파일 구조
출처가 불분명한 JPEG 파일에는 잘못된 길이 값이나 비정상적인 데이터가 포함될 수 있다.
신뢰할 수 없는 파일을 분석할 때는 최신 보안 업데이트가 적용된 프로그램을 사용하고, 파일 크기와 리소스 사용량을 제한하는 것이 바람직하다.
④ 원본 파일 보존
무결성 검증이나 손상 분석이 목적이라면 원본을 보존하고 복사본으로 작업하는 것이 좋다.
편집 프로그램에서 다시 저장하면 JPEG 압축 데이터가 변경될 수 있다.
21. 자주 묻는 질문
Q1. JPEG 파일의 Magic Number는 무엇인가요?
일반적인 JPEG 데이터 스트림은 SOI 마커인 FF D8로 시작한다.
Q2. 모든 JPEG 파일이 FF D8 FF E0으로 시작하나요?
아니다. JFIF 기반 파일에서는 APP0가 등장할 수 있지만, Exif 기반 파일에서는 APP1이 등장하는 등 구성이 다를 수 있다.
Q3. APP1에는 항상 Exif가 들어 있나요?
아니다. APP1은 Exif뿐 아니라 XMP 등의 데이터를 저장하는 데에도 사용될 수 있다.
Q4. JPEG 파일의 이미지 크기는 어디에 기록되나요?
일반적인 JPEG에서는 SOF 세그먼트에 이미지의 가로·세로 크기와 프레임 정보가 기록된다.
Q5. SOS 마커는 항상 한 번만 등장하나요?
아니다. Progressive JPEG에서는 여러 스캔을 사용하므로 SOS가 여러 번 등장할 수 있다.
Q6. JPEG에는 PNG처럼 CRC-32가 있나요?
일반적인 JPEG 마커 세그먼트에는 PNG 청크처럼 공통 CRC-32 필드가 존재하지 않는다.
Q7. EOI 마커가 있으면 파일이 정상인가요?
반드시 그렇지는 않다. EOI가 있어도 압축 데이터나 다른 세그먼트가 손상되어 있을 수 있다.
Q8. JPEG와 JPG는 서로 다른 파일 형식인가요?
일반적으로 .jpg와 .jpeg는 같은 JPEG 이미지 형식을 나타내는 확장자로 사용된다.
마무리
JPEG 파일은 SOI, APP, DQT, DHT, SOF, SOS, EOI 등의 마커와 압축 이미지 데이터로 구성된다.
SOI는 이미지 데이터 스트림의 시작을 알리고, APP 세그먼트는 JFIF·Exif 등의 부가 정보를 저장하며, SOF는 이미지 크기와 프레임 정보를 정의한다.
SOS 이후에는 엔트로피 부호화된 이미지 데이터가 이어지고, EOI는 데이터 스트림의 종료를 나타낸다.
그러나 JPEG 파일 구조 분석은 형식과 내부 구성을 확인하는 작업이지, 이미지의 출처나 원본 동일성 또는 보안상 안전성을 보장하는 과정은 아니다.
따라서 마커 구조 확인, 메타데이터 분석, SHA-256 해시 검증은 각각의 목적에 맞게 구분하여 활용해야 한다.
핵심 내용 3줄 요약
- JPEG는
FF D8SOI 마커로 시작하며 APP·SOF·SOS 등의 마커를 통해 이미지 구조를 정의한다. - APP 세그먼트에는 JFIF·Exif·ICC 프로파일 등 다양한 메타데이터가 포함될 수 있다.
- JPEG 마커 구조가 정상이어도 압축 데이터의 정상성이나 원본 동일성이 보장되지는 않는다.
공식 및 기술 참고자료
- ITU-T — Recommendation T.81: Digital Compression and Coding of Continuous-Tone Still Images
https://www.itu.int/rec/T-REC-T.81 - W3C — JPEG File Interchange Format (JFIF), Version 1.02
https://www.w3.org/Graphics/JPEG/jfif.pdf - Library of Congress — JPEG Image Encoding Family
https://www.loc.gov/preservation/digital/formats/fdd/fdd000017.shtml - Library of Congress — JFIF, JPEG File Interchange Format, Version 1.02
https://www.loc.gov/preservation/digital/formats/fdd/fdd000018.shtml - ITU-T — Recommendation T.86: APPn Markers
https://www.itu.int/rec/T-REC-T.86 - Microsoft Learn — File.OpenRead Method
https://learn.microsoft.com/dotnet/api/system.io.file.openread - Microsoft Learn — File.ReadAllBytes Method
https://learn.microsoft.com/dotnet/api/system.io.file.readallbytes
※ 본문의 PowerShell 코드는 JPEG의 주요 마커와 일부 프레임 정보를 읽기 전용으로 확인하는 학습용 예시다. 완전한 JPEG 디코더 또는 모든 JPEG 확장 형식의 적합성 검증 도구를 대체하지 않는다.
디지털 파일 검증 연구소
댓글 0
첫 댓글을 남겨보세요.