Thursday, September 21, 2023

hwp structure

HWP 파일 구분


Magic

파일의 시작 부분에 0xD0CF11E0A1B11AE1로 한글 문서임을 확인한다.

Sector Shift (BBAT)

한 섹터의 크기를 나타내며, 0x09를 기본 값으로 두고 있다. 이 값은 2^n으로 계산되어 한 섹터의 크기가 512 바이트임을 뜻한다.

Small Sector Shiff (SBAT)

SBAT의 크기를 나타내며, 0x06을 기본 값으로 두고 있다. 이 값은 Sector Shift와 같이 2의 승수로 계산되며 0x06인 경우 64 바이트를 크기로 두고 있다.

Count of BBAT Depot

4바이트 기준의 BBAT의 개수를 나타내며 109개 이상일 경우, Extra BBAT를 확인하여야한다.

Root Sector ID

Root Entry의 시작 번호로, 해당 섹터의 위치 계산법은 다음과 같다. Header부분의 크기를 제외하기 위해 Header 크기를 더해주고, Root Sector ID에 BBAT 크기를 곱한다.

Root Sector ID * BBAT Size + Header

Extended BBAT ID

BBAT의 개수가 109개가 넘어, 확장된 BBAT의 섹터 ID를 나타낸다.

Extended BBAT Count

확장된 BBAT의 수를 나타내며 109개 이후에 추가된 수를 뜻한다.

BBAT Depot

109개의 Sector ID가 저장될 수 있으며, 4바이트씩 Sector ID를 가지고 있다. 위의 예시로 보면, 1개의 BBAT가 있으며 해당 Sector ID는 0x03이다. 위의 Root Sector ID에서와 같이 계산하여 Offset을 찾을 수 있다.

(BBAT Sector ID +1)* 200

Root Entry


Root Sector ID을 통해 Root Entry로 이동하면 위의 그림과 같이 볼 수 있다. Root Entry는 SBAT 크기를 기준으로 0x80(64바이트) 크기이다.

Entry Name

Entry의 시작 부분 0x40 크기는 이름이 있다. 모든 Sector에 대한 이름이 존재한다.

Name Length

Sector 이름의 길이를 2바이트로 표시한다.

Type

해당 Sector 데이터 타입을 나타낸다

  • 0: Empty

    1: Storage

    2: Stream

    3: LockBytes

    4: Property

    5: Root

ID

Entry 의 ID 정보로 앞서 보였던 연산과 같은 방식으로 진행하면 해당 Sector의 offset으로 이동할 수 있다.

Size

해당 Entry의 데이터 크기를 나타낸다.

SBAT Sector ID로 압축 데이터 찾기

  1. 연산으로 Entry 및 Stream 구하기

Header에서 Sector ID가 저장되어있는 BBAT Depot을 모아 Root Entry Chain을 만든다. 위를 예시로 보면 하늘색 상자에 하늘색 실선을 보면, BBAT에서 Sector ID가 0x03으로 나타낸다. Sector Shift인 BBAT 크기를 획득하여 ID + 1( Header) + 0x200 = 0x800 offset으로 이동한다. 만약 BBAT에서 Sector ID가 더 있다면 ID를 이어서 추가해야한다. RootEntry 에서 ID를 시작점으로 두며, ID는 4바이트씩 읽으며, 0부터 카운트 한다. 그래서 7Entry는 0x08을 가르키고, 8Entry는 0x90을 가르킨다. Chain의 끝을 나타내는 0xFFFFFFFE 가 나오면 끝이 난다. 위에서 굵은 글자로 표시된 것이 Root Entry Chain이다.


이 Chain을 기반으로 각 Storage와 Stream의 데이터를 찾을 수 있다. 먼저, 일례로 DocInfo Stream의 정보를 찾을 수 있다.

DocInfo 정보가 있는 SBAT 에서 ID를 나타내는 0x0000001F를 가지고 해당 데이터의 위치를 구할 수 있다. 1F를 31로 변환하여 8로 나누면 3의 몫과 나머지 7이 나온다. 몫의 값이 3으로 Root Entry Chain 에서 3번째(0부터) Entry 값인 10을 구하고, 나머지를 가지고 다음의 연산을 한다. 해당 연산의 값을 따라가면 압축된 데이터를 확인할 수 있다.

(몫+1)*Sector Shift + 나머지*Small Sector Shiff

  1. TagID로 데이터 영역 찾기

DocInfo를 zlib 압축해제하면 파일의 첫번째 바이트에서 HWPTAG_BEGIN 값을 구할수 있다. 한글문서파일 형식 5.0 보고서에 따르면 해당 값은 0x10으로 고정되어있는 것으로 보여진다. HWPTAG_BEGIN값을 기준으로 TagID를 찾을 수 있다.


위의 표와 같이 TagID에 따라 HWPTAG_BEGIN에 특정 정수를 더한다.


데이터 레코드의 스트림은 [TagID][Level][Size][Data] 구조를 가지고 있다. 위의 예시에서는 HWPTAG_CTRL_HEADER 의 TagID를 가지고 있으며, TagID 부터 포함하는 데이터 크기를 표현하였다. 그리고 하이퍼링크데이터는 일반적으로 [URL];[Type];[Option];[Option]; 구조이다. 뒤에 두 옵션 값에 다른 값을 대입하여도 하이퍼링크 동작에는 문제가 없다.


위의 데이터는 원 도형에 하이퍼링크를 입력한 경우로, 앞선 예시와 TagID에 차이가 있다.

그리고, 앞선 예시와는 다르게 klh% 문자열이 존재하지 않는다.


참고 자료

zlib — gzip 과 호환되는 압축 — Python 3.11.2 문서

olefile/olefile.py at 5ae06e937cd18afebfb49239e8f20b099605136f · decalage2/olefile · GitHub

OLE 파일 구조 분석 (1)

OLE 파일 구조 분석 (3)

OLE 파일 구조 분석 (2)

HWP 5.X파일의 구조.

한글문서파일형식_5.0_revision1.3.pdf


No comments:

Post a Comment

List

MobSF

MobSF는 오픈소스 모바일 앱 자동 보안 진단 프레임워크로 자동 분석 시스템 구축할 때 사용한다. 정정 및 동적 분석이 가능하며, Android, iOS, Windows에 대해 침투 테스트, 멀웨어 분석 및 보안 평가를 할 수 있다. 참고자료 필...