본문 바로가기

File Structure

PDF(Portable Document Format) File Structure Analysis

이번에 분석해볼 파일구조는 PDF 파일 입니다.

PDF 파일이란?

우리가 이야기 하는 PDF는 이동가능 문서 형식(Portable Document Format) 의 약자로 Adobe에서 개발한 전자 문서 형식입니다.

PDF는 컴퓨터 환경에 관계 없이 같은 표현을 하기 위함을 목적으로 개발이 되었습니다.

PDF는 2008년에 ISO 32000 오픈 포맷으로 표준화 되었습니다.

 

PDF 파일은 다른 파일 구조와 달리 HEX 데이터에서 파일을 구조를 눈으로 확인이 가능합니다.

이는 확장자를 .txt 인 텍스트 문서로 변환을 하고 나서 확인을 하면 쉽게 확인이 가능하다는 것 입니다.

PDF File Text View

알아 볼 수있는 부분은 구조적인 부분이고, 알아볼 수 없는 부분은 stream 오브젝트에 들어있는 데이터 부분입니다.

 

그렇다면 PDF 구조를 공부해야 하는 이유는?!

 

위의 질문이 공부하는 데에 있어서 가장 중요한 부분이라고 생각이 됩니다.

먼저 악성 파일을 이용하여 사용자에게 전송함에 있어서 대부분 사용자가 많이 사용하는 워드(docx)파일이나 PDF파일에 악성 스크립트를 은닉해서 전송을 하게 됩니다. 그렇기 때문에 PDF 구조를 정확히 파악해서 어떤부분에 악성 스크립트가 존재 하며 어떻게 은닉 되어 있는가를 확인 할 수 있습니다.

 

침해 사고 관련 문제나 악성코드 분석을 할때 PDF 구조를 공부해 두면 도움이 될 것 이라고 생각이 됩니다.

 

 

1. PDF를 구성하는 4가지의 구성요소

PDF 파일을 구성하는데에 있어서 4가지의 구성요소가 존재합니다.

4가지의 구성요소는 Object, File Structure, Document Structure, Content Streams 입니다.

 

1-1) Object

PDF 문서는 여러 작은 데이터 오브젝트로 구성된 모임

 

1-2) File Structure

파일 구조는 PDF 파일내에 오브젝트 들이 어떻게 저장되고 어떻게 접근되고 어떻게 업데이트 되는지 결정

 

1-3) Document Structure

문서 구조는 여러 간단한 오브젝트들이 어떻게 PDF의 각 문서를 구성하고 배치되는지 설명

 

1-4) Content Streams

콘텐츠 스트림은 PDF 문서의 외곽이나 그래프 요소들을 묘사하는 일련의 명령들을 가지고 있다.

 

위의 구성요소 4가지는 아래와 같은 구조를 가지고 있습니다.

PDF File Text View

 

2. 기본 Object의 종류

PDF는 다음과 같은 8가지의 기본적인 오브젝트 타입을 가지고 있습니다.

Basic Object Type

위의 오브젝트 타입을 하나하나 알아 보겠습니다.

 

2-1) Boolean 오브젝트

- True, False 값을 가지는 오브젝트

- 배열의 요소나 Dictionary 의 엔트리 값으로서 사용이 가능하다. 

 

2-2) Integer 오브젝트

- 아래의 2가지 종류의 Integer를 지원합니다.

- 정수형(int) : 123, +123, -123 등

- 실수형(float, double) : 12.3, +12.3, -12.3, 0.0 등

 

2-3) Strings 오브젝트

- 문자 스트링은 () 괄호 안에 쓰여집니다.

스트링은 2가지의 종류()로 나눌수 있습니다.

▩ 문자 스트링 : 

아래의 사진은 예제로 제작한 PDF 파일의 일부 오브젝트에 있는 스트링입니다.

Example String Object 1

위와 같이 (ko-KR) 처럼 문자 스트링이 적혀 있습니다.

 

- 만약 String이 길어질 경우에 아래와 같이 | (역슬래시)를 이용해서 하나의 라인을 여러 라인으로 이용할 수 있습니다.

Example String Object 2

▩ 16진수 스트링

- 16진수 스트링은 괄호 또는 <,>로 둘러싸인 16진수 아스키 문자로 이루어집니다.

Example String Object 3

- 16진수 2개가 짝을 이뤄 1바이트을 이룬다. 짝이 안맞으면 맨 마지막은 00 으로 간주한다.

예시 : <1E35B> -> 1E, 35, 80 을 의미한다.

 

2-4) Names 오브젝트

- Name 오브젝트는 하나의 심볼을 의미하며 유일한 문자열이다.

- 유일한 문자열 이기 때문에 똑같은 문자열을 가지는 2개의 오브젝트가 있을 수 없다.

- Name 오브젝트의 구분자는 /(슬래쉬)로 구분한다.

- 대소문자를 구분한다. Name 과 name 은 다른 문자열로 간주하기 때문에 서로 다른 오브젝트를 만들수 있다.

Example Name Object

 

2-5) Arrays 오브젝트

- 다른 언어 문법의 배열과는 달리 PDF 파일의 배열은 다양한 형태의 오브젝트 조합을 가질 수 있다.

즉, 하나의 배열안에 여러개의 오브젝트가 들어 갈 수 있다는 것을 의미한다.

- 배열은 괄호 [, ] 내의 연속된 오브젝트를 작성 가능

예시 : [123 58.54 784 0 1243 0]

 

2-6) Dictionary 오브젝트

- Dictionary 오브젝트는 엔트리로 알려진 2개의 오브젝트를 연계한 테이블을 의미한다.

- 첫번째 요소는 키(Key)을 가지고, 두번째 요소는 값(Value)을 의미합니다. 

- 첫번째 요소인 Key는 반드시 Name 오브젝트를 가지고 있어야 합니다.

- 두번째 요소인 Value는 Key와 달리 반드시 Name 오브젝트 일 필요는 없습니다.

또한 다른 어떠한 오브젝트가 와도되며, 또다른 Dictionary 오브젝트가 올수 도 있다.

- Value 값이 null일 경우에는 해당 엔트리는 없는것으로 간주합니다.

- Dictionary 오브젝트는 << , >> 사이에 Key와 Value를 쌍으로 표시합니다.

Example Dictionary Object

 

2-7) Stream 오브젝트

- String 오브젝트와 같이 연속적인 이진 데이터의 집단입니다.

- String 오브젝트는 길이 제한이 있는 반면 Stream 오브젝트는 길이 제한이 없습니다.

- 다른 오브젝트로 표현이 불가능하거나 상대적으로 크기가 큰 이미지 파일이나 페이지 구성 오브젝트(파일 내용) 은 Stream 오브젝트로 표현

- Stream 오브젝트는 stream 문자열을 시작으로 endstream 문자열 사이에 여러 바이트를 구성합니다.

 

실제로 확인해 보면 아래와 같이 stream 문자열과 endstream 사이에 바이트를 구성합니다.

 Stream Binary Data in Text File

- Dictionary 오브젝트 에서는 아래와 같이 Contains stream 이라고 적혀 있으며, /Length 라는 Name 오브젝트와 함께 바이너리의 크기가 작성 됩니다.

Example Stream Object

- 모든 Stream 오브젝트에 같이 등장하는 Dictionary 오브젝트 중에 공통적인 엔트리가 몇몇이 정해져 있는데 위의 사진에서도 나와있습니다.

/Filter 엔트리와 /Length 엔트리가 있습니다.

- /Filter 엔트리는 필터의 이름을 명시하며 이진 데이터를 해당 목적에 맞도록 해석하는 역할을 합니다.

- 중요 필터중에서 /Length 는 필수 엔트리이고, /Filter 는 선택 엔트리입니다.

 

이번에는 Stream 오브젝트에 들어있는 이진 데이터를 복구하는 방법을 알아 보겠습니다.

예제 파일로 생성한 파일의 이진 데이터를 확인해 보겠습니다.

예제 파일에서는 4번 오브젝트에서 아래와 바이너리를 Stream 을 가지고 있습니다.

Example Stream Object

바이너리를 hex값으로 확인해 보면 아래와 같습니다.

Number 4 Object Stream Data in HxD

해당 데이터를 문자열로 담아서 아래와 같은 코드를 이용해서 복호화가 가능합니다.

이때 사용된 복호화 방법은 /Filter 엔트리에 있는 /FlateDecode 입니다.

FlateDecode.py source code

위의 코드에서는 data를 줄바꿈 했지만 한줄로 입력하고 ""로 묶어 줘야 코드가 잘 작동이 됩니다.

python3 와 python2 로 했을때 결과 출력은 아래와 같습니다.

FlateDecode.py result

python2는 위의 코드가 python2를 기준으로 만들어져서 python2로 실행을 해보면 보기 편하게 출력되는 것을 알 수 있습니다.

python3는 코드로 인해서 결과값이 바이트 형식으로 출력이 됩니다.

 

이것으로 알수 있는 것은 /Filter 엔트리에 들어있는 /FlateDecode 말고도 /ASCIIHexDecode, /ASCII85Decode, /LZWDecode, /RunLengthDecode 와 같은 Decompress 데이터가 있습니다.

 

/FlateDecode 와 마찬가지로 다른 필터도 코드를 작성해서 복호화가 가능합니다

 

2-8) Indirect 오브젝트

- PDF 파일에서 레이블 된 오브젝트들을 간접 오브젝트라고 부릅니다.

- 간접 오브젝트는 오브젝트 고유의 식별자를 갖게 되며 다른 오브젝트는 이를 이용해서 해당 오브젝트를 참조할 수 있다.

- 간접 오브젝트에 쓰이는 간접 참조는 6 0 R  과 같이 숫자 2개와 R 이 붙게 되는데 이는 <OBJECT NUMBER> <GENERATE NUMBER> R 을 의미 합니다.

▩ 오브젝트 수(양수) : 

- 간접 오브젝트는 보통 연속적으로 자기의 고유 숫자가 정해지지만 반드시 그런것 은 아니다.

 

▩생성 수 : 

- Generate Number 는 새로 만들어진 간접 오브젝트는 0이라는 값을 가지게 되고, 추후에 파일이 업데이트를 할때 값이 변경될 수 있다

Indirect Object

 

3. PDF File Structure

PDF 파일에는 총 4가지의 구조를 가지게 됩니다.

앞에서는 PDF 파일에서 다루는 오브젝트에 대한 내용만 다뤘다면, 이번에는 PDF 의 중요한 구조 구성요소를 알아보겠습니다.

주요 구조 구성요소는 Header, Body, Xref Table, Trailer 입니다.

PDF File Structure

 

3-1) Header

PDF 명세 버전을 알리는 한줄의 HEADER를 가지고 있습니다.

hex값이 맨처음 0x00~0x07에 작성 되어 있습니다.

PDF File Header Signature

예제 파일에서는 %PDF-1.5 라고 적혀 있는데 이는 PDF 버전을 의미하고 아래의 값에 따라서 Acrobat Version이 결정 됩니다.

PDF Version & Acrobat Version

 

3-2) Body

PDF 문서를 구성하는 오브젝트를 포함하는 BODY <- 해당 구성요소에서는 주요 데이터가 들어있습니다.

이러한 오브젝트는 문서의 내용 및 폰트, 페이지, 이미지와 같은 요소를 나타냅니다.

 

<Object Number> <GENERATE NUMBER> obj 를 띄고 있습니다.

Object No.4 Structure

 

3-3) Xref Table

간접 오브젝트에 관한 정보를 가지고 있는 전역참조테이블(CROSS-REFERENCE)을 의미 합니다.

전역 참조 테이블은 각 간접 오브젝트의 위치 정보를 가지고 있기 때문에 방대한 데이터 중에서 특정 오브젝트를 찾는 번거로움을 피하고 한번에 모든 오브젝트를 확인하고, 엑세스 하기 쉽습니다.

전역 참조 섹션은 xref 시그니쳐를 시작으로 아래로 쭉 나열되어 있습니다.

오브젝트 숫자는 0부터 진행을 합니다.

각각의 서브세션은 스페이스로 구분된 2개의 숫자로 시작이 됩니다.

첫번째 엔트리는 엔트리 오브젝트 숫자 를 의미합니다.

두번째 엔트리는 해당 서브섹션이 포함 하고 있는 엔트리의 개수를 의미합니다.

Example Xref Table

3번째 줄부터 한줄씩 실질적인 간접 오브젝트의 위치 정보를 담고 있는 엔트리가 출력이 됩니다.

각 엔트리는 정확히 20바이트 길이(end-of-line 마커를 포함)

엔트리의 종류는 총 2가지를 가지고 있습니다.

- 현재 사용중인 엔트리

- 지워져서 사용되지 않는 엔트리

Explanation Xref Table Entry

 

3-4) Trailer

전역 참조의 위치와 BODY 내 특정 오브젝트의 위치정보를 가지고 있는 트레일러(TRAILER)

PDF 파일의 트레일러는 파일 내 전역 참조 테이블과 특정 오브젝트를 빨리 찾게 해줍니다.

Example Trailer Data

trailer dictionary에 나오는 중요 엔트리는 다음과 같다.

 

/Key 엔트리 : 정수 오브젝트를 가지고있는 파일내의 모든 전역 참조 테이블내의 총엔트리의 수를 출력합니다.

/Prev 엔트리 : 정수의 오브젝트를 가지고 파일의 시작부터 바로 전 전역 참조 섹션 까지의 바이트 오프셋이다.

이 값이 없다면 한번도 업데이트가 되지 않았음을 추측할 수 있다.

/Root 엔트리 : 간접 dictionary 오브젝트 참조를 가지며 해당 PDF 문서의 catalog dictionary 이다.

/Page 엔트리 : 페이지를 나타낸다.

/Encrypt 엔트리 : DRM 기술이 적용되었거나 해당 PDF 파일에 암호가 걸려있다는 것을 의미한다.

/ObjStm 엔트리 : 다른 오브젝트에 사용될 수 있는 Object Stream을 의미 합니다.

/JS 엔트리 : JavaScript를 사용했다는 것을 의미합니다.

/OpenAction 엔트리 : 문서 또는 페이지가 오픈될 때 자동 실행되는 것을 의미한다.

/RichMedia 엔트리 : 플래시가 들어 있음을 의미한다.

 

이론적인 내용을 주로 담았기 때문에 해당 글을 보면서 직접 PDF 파일을 제작해서 구조를 보면서 공부하시면 좋을 것같습니다!

'File Structure' 카테고리의 다른 글

BEncode Structure Analysis  (0) 2021.03.23
PDF(Portable Document Format) File Structure Analysis  (0) 2020.03.02
ZIP File Structure Analysis  (4) 2020.02.12

태그