섹션 스트림 분석하기 빈문서 분석




섹션 스트림은 바디스토리지 내에 존재하는 스트림이다.

기본적으로 레코드 형식이며, 압축이 되기도 한다.

이번 포스팅에서는 빈 도큐먼트를 파싱하는 과정을 담아보려고 한다.


섹션 스트림을 분석하기 위해서는 한글과 컴퓨터에서 제공하는 도큐먼트를 먼저 정독해야한다.


TagID 

SIZE 

LEVEL 

설명

 HWPTAG_PARA_HEADER

22 

 0 

문단 헤더 

 HWPTAG_PARA_TEXT

가변

 1 

text 정보

 HWPTAG_PARA_CHAR_SHAPE

가변

 1 

글자 모양 

 HWPTAG_PARA_LINE_SEG

가변

1

글자 레이아웃 

 HWPTAG_PARA_RANGE_TAG

가변

1

영역 태그 

 HWPTAG_CTRL_HEADER

 1 

 컨트롤 헤더

 HWPTAG_LIST_HEADER

 2 

 문단 리스트 헤더

 HWPTAG_PAGE_DEF 

40 

 2 

 용지 설정

 HWPTAG_FOOTNOTE_SHAPE

30 

 2 

각주/미주 모양 

 HWPTAG_PAGE_BORDER_FILL 

14 

 2 

쪽 테두리/배경 

 HWPTAG_SHAPE_COMPONENT 

 2 

개체 

 HWPTAG_TABLE 

가변

 2 

 표 개체

 ... (기타 그리기 개채)

가변

 

 

 HWPTAG_CTRL_DATA

가변

3

 

 ... (기타 레코드) 

 

 

 

(필자는 초반에 나오는 이 표를 보지 못해서 각각의 레코드들을 전부 읽어 레벨을 기록했다. -_-;)



위의 표는 Section Stream에서 본문 정보를 담고있는 레코드들을 추려서 표로 정리해본것이다.


포스팅을 하기 앞서 문단이란 무엇인지 알고 가야한다.

한글문서에서의 문단은 '엔터'로 기준을 나눌 수 있다.


첫번째줄 두번째줄이 엔터로 나뉘어서 작성이 되면 2개의 문단으로 인식한다.

한줄에 엔터없이 2번째줄까지 작성되어 내려가게 되면 그건 1개의 문단이다.


섹션의 첫 문단에는 구역 정의 레코드들이 들어가기 때문에 중간 중간의 문단 레코드들보다 필요한 사이즈가 크다. 

첫번째 문단 정보에는 ~ 레코드들이 들어간다.

그다음 문단 부터는 헤더, 텍스트, 글자모양, 글자 위치 정보의 레코드들이 하나의 문단 정보를 이루어 작성된다.



첫번째 문단

 

 두번재 문단

 

 3번째 문단

 

 header + text + shape + line + range_sahpe+ ctrl header + footnote+ border fill + etc

 

 HEADER

 TEXT

 CHAR_SHAPE

 LINE_SEG

 

 HEADER

 TEXT

 CHAR_SHAPE

 LINE_SEG

 

 +

 +

 

 

 

 

 

 

 


위에 표는 스트림에 작성되는 텍스트 헤더들을 표로 정리해본것이다.

첫번재 문단 정보에는 기본적인 텍스트( 테이블도 될수 있음) 정보들이 들어가며 추가적으로 각주/미주 정보라던지, 용지 설정 정보, 각주/미주 모양의 정보와 같은 부수적인 레코드들이 같이 붙는 형태이다.

두번째 문단 부터는 해당 문단에 작성된 텍스트 정보만 작성이 된다. 비교적 첫번째 문단에 비해 담기는 정보가 작다.





퍼시픽 림 : 업라이징

(쿠키영상 있음)


전작 1편을 보지 않은(다운받고 거의 스킵하면서 봤음)상태에서 

예고편에 혹해서 예매를 하게 됬는데 생각보다 재밌었다.



거대 로봇이 거대 괴물과 싸우는 ㅋㅋㅋ

유치하지만 그게 또 유지하지도 않았다.



이 영화도 짱깨의 자본이 투입되었는지라

영화의 곳곳에 중국의 흔적이 보인다.

한국씬이 있었다는데 통편집으로 그냥 자막에 '한국'이라고 한번 언급하는게 다이다.  (싸드 문제이겠지)

또 가수 UN의 김정훈? 이 거의 1초? 대사 치고 나오는데 ㅋㅋㅋ 

뭐하러 영화에 넣었을까 싶기도하고



시간나면 전편을 봐서 영화의 전반적인 내용을 알아야겠다.

그 거대한 로봇 예거가 어떤 동력으로 움직이는지도 궁금하고

카이쥬를 보내는 외계세력이 어떻게 나오는지도 궁금하기도하다.




영화가 끝나면 쿠키영상이 존재한다.

3편의 떡밥을 뿌리는 내용인데 

2편이 재밌어서 3편도 나온다면 보러갈 생각이다.















이번 포스팅에서는 한글문서가 어떤형식으로 파일구조를 갖고 있고 

어떻게 바이트단위로 박혀있는지 확인 해보는 시간을 갖기 위해 글을 써보았다.



한글 문서는 Compound File (OLE2) 구조를 갖는다.

스토리지와 스트림 형태를 띄고 있는데, 스토리지는 폴더, 스트림은 파일이라고 이해하면 쉽다.

Root 밑에는 여러개의 스토리지와 스트림이 존재한다. 

아래의 표는 hwp 파일이 갖고있는 구조를 표로 표현한것이다.



 

 이름

길이 

레코드 

압축 / 압축화 

 파일 인식 정보

FileHeader (Stream)

고정 

 

 

 문서 정보

DocInfo  (Stream)

고정 

true 

true 

본문 

BodyText (Storage)

- Section0   (Stream)

가변 

true 

true 

문서요약 

\005HwpSummaryInformation  (Stream) 

고정 


 

바이너리 데이타 

BinData  (Storage)

- BinaryData0   (Stream)

- ...   (Stream)

가변 

 

true 

미리보기 텍스트 

PrvText  (Stream)

고정 

 

 

미리보기 이미지 

PrvImage  (Stream)

가변 

 

 

문서 옵션 

DocOptioin  (Storage)

- _LnkDoc  (Stream)

- DrmLicense  (Stream)

- ...

가변 

 

 

스트립트 

Script  (Storage)

- DefaultScript  (Stream)

- JScriptVersion  (Stream)

- ...

가변 

 

 

 XML 템플릿

XML Template (Storage)

- Schema   (Stream)

- Instance  (Stream)

- ...

가변 

 

 

문서 이력 관리 

DocHistroy  (Storage)

- VersionLog0  (Stream)

- VersionLog1  (Stream)

- ...

가변 

true 

 

true 



(ssviewer로 본 한글파일 내부의 구조)

각 스토리지와 스트림의 정보는 한글과 컴퓨터에서 제공하는 문서를 참고하면 이해하기 쉽다.



간단하게 파일 인식 정보를 갖고있는 FileHeaer 스트림을 뜯어보면 아래와 같은 구조를 갖고 있다.




FileHeaer은 256바이트의 고정된 길이를 갖는 스트림이다.

사진에 첨부한 FileHeader 스트림의 Binary값을 확인해보면 

초기 32바이트에는 한글 문서 형식을 알리는 시그니처 문구가 박혀있는것을 확인할 수 있다.


그다음 4바이트 크기로 한글 문서의 버전을 확인 할 수 있다. 

바이너리 뷰어로 해당 스트림을 열어보면 5.0.3.0 버전이 찍힌걸 볼 수있다.


그다음 4바이트는 파일헤더의 속성을 갖는 비트들이다. 0비트부터 11비트는 각각의 문서 속성 정보를 갖고있다.

그 이후 나머지 216바이트는 예약 바이트들로 0의 값으로 초기화되어 있는 형태를 확인할 수 있다.




한글 문서중 제일 파악하기가 쉬운게 파일헤더 스트림이다.

고정 길이를 갖고 있기에 256바이트 중에 

처음 32바이트는 시그니처, 

그다음 4바이트는 파일의 버전, 

그다음 4비트는 속성 정보,

나머지 216바이트는 예약비트로 파싱할때 비교적 수월하다.

암호화도 안되있기 때문에 그냥 가져와 바이트단위로 짜르면 한글문서의 정보를 가져올 수 있다.





다른 스트림도 이러면 좋겠지만 가변적인 길이를 갖는 스트림도 많고 압축/암호화도 걸린게 많기 때문에 

다른 스트림을 파싱하거나 렌더링할때에는 쉽게 되지는 않을꺼같다.




몇몇 스트림들은 레코드형식(헤더와 데이터의 결합)으로 구성되어있기도 하며, 

MSDN의 프로퍼티 셋 형식을 취한 스트림도 있다.




대표적인 예로 \005HwpSummaryInformation 스트림이 MSDN의 프로퍼티셋 형식을 갖고 있다.

Summary Information에 대한 자세한 설명은 MSDN을 참고

The Summary Information Property Set

The DocumentSummaryInformation and UserDefined Property Set

(한글 공식 문서의 설명)


SumamryInformation 은 MSDN의 프로퍼티셋 형식을 따른다.

해당 정보는 아래의 URL에 설명되어있다.


https://msdn.microsoft.com/ko-kr/library/windows/desktop/aa380376%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396


HwpSummaryInformation 정보를 가져오기 위해서는 바이트 단위로 짜르는것 보다는

Apache POI API에서 제공하는 프로퍼티 ID단위로 값을 가져오면 된다.

이 부분은 추 후 포스팅을 통해 값을 가져와보도록 하겠다.









마지막으로 설명하는 내용은 레코드 형식의 데이터다.


(한글의 데이터 레코드 구조)


논리적으로 연관된 데이터들을 헤더 정보와 함께 저장하는 방식을 데이터 레코드라고 한다.

레코드 구조를 가지는 스트림은 연속된 여러 개의 레코드로 구성된다. 데이터 레코드는 헤더와 데이터로 구성되며 각 헤더 정보를 활용하여 전체 논리적 구조를 생성하게 된다.

레코드의 헤더에는 데이터 확장에 대비한 정보를 가지고 있다. 따라서 이후에 ᄒᆞᆫ글의 기능이 확장되어 레코드에 데이터가 추가되는 경우에도 하위 버전의 ᄒᆞᆫ글이 상위 버전의 ᄒᆞᆫ글 문서를 읽을 수 있도록 하위 호환성이 보장된다.



Tag ID : 레코드가 나타내는 데이터의 종류를 나타내는 태그이다. Tag ID에는 10 비트가 사용되므로 0x000 - 0x3FF까지 가능하다.

0x000 - 0x00F = 일반 레코드 태그가 아닌 특별한 용도로 사용한다.

0x010 - 0x1FF = 글에 의해 내부용으로 예약된 영역(HWPTAG_BEGIN = 0x010)

0x200 - 0x3FF = 외부 어플리케이션이 사용할 수 있는 영역


Level : 대부분 하나의 오브젝트는 여러 개의 레코드로 구성되는 것이 일반적이기 때문에 하나의 레코드가 아닌 "논리적으로 연관된 연속된 레코드"라는 개념이 필요하다. 레벨은 이와 같이 연관된 레코드의 논리적인 묶음을 표현하기 위한 정보이다. 스트림을 구성하는 모든 레코드는 계층 구조로 표현할 수 있는데, 레벨은 바로 이 계층 구조에서의 depth를 나타낸다.


Size : 데이터 영역의 길이를 바이트 단위로 나타낸다. 12개의 비트가 모두 1일 때는 데이터 영역의 길이가 4095 바이트 이상인 경우로, 이때는 레코드 헤더에 연이어 길이를 나타내는 DWORD가 추가된다. , 4095 바이트 이상의 데이터일 때 레코드는 다음과 같이 표현된다.

(한글 문서파일형식에서 발췌)



대표적으로 DocInfo스트림과 Body 스토리지 내부의 Section 스트림에서 데이터 레코드형식을 취하고 있다.

TagID의 0x010에 HWP TAG 아이디를 넣음으로써 해당 레코드가 어떤 데이터를 갖고 있는지 구분할 수 있다.


현제 필자는 HwpLib 라이브러리를 통해 한글문서를 파싱, 렌더링 하는 부분을 분석중이다.

바이너리 데이터를 읽고 쓰는것이 익숙하지 않아 하루에도 머리가 여러번 쪼개지는 고통을 받고 있다.ㅋㅋ ㅜㅜ

빨리 이 과제를 마무리 짓고 싶다. ㅜㅜ




















회사 수습 연구원으로 진행하게될 과제이다.

Java로 .hwp파일을 파싱 후 렌더링하기까지가 숙제(과제)이다. 


처음에는 low레벨까지 내려가 binary형태에서 부터 파싱을 해야 하나 싶어서 걱정이 많았는데

자비로우신 팀장님께서 결과만 나오면 일단 된다고 하셨기에 갓구글님께 도움을 요청했다.


http://www.hancom.com/etc/hwpDownload.do 

(한글 문서 파일 구조 5.0 도큐먼트)


처음에 70페이지 가까이된 한글 문서 파일 구조를 던져주실때 식겁했는데

다행스럽게도 


https://github.com/neolord0/hwplib

누군가가 만들어주신 라이브러리가 존재했다.


한글과 컴퓨터(한컴)에서 만든 워드프로세서 "한글"의 파일에 대한 라이브러리입니다.

본 라이브러리는 JAVA로 구현되었으며, 한글 파일의 하부 구조인 Microsoft Compound File의 부분은 Apache-POI의 POIFS File System을 사용하여 처리합니다. 본 라이브러리는 한글과컴퓨터의 한글 문서 파일(.hwp) 공개 문서를 참고하여 개발하였습니다. 한컴에서 제공하는 문서(HWP 5.0)는 아래URL에서 받을 수 있습니다.

http://www.hancom.com/etc/hwpDownload.do?gnb0=269&gnb1=271&gnb0=101&gnb1=140



github에 올라온 프로젝트 소개글이다.

처음에 소스 분석을 하겠다고 처음부터 쭉 따라갔는데 왠 아파치 poi 라이브러리가 나오길래 막막했었다.

알고보니 아파치 poi 라이브러니는 마이크로소프트사의 워드 포맷형식을 지원하는 라이브러리였다.


여기서 생긴 의문이 

그렇다면 한글의 .hwp파일은 MicroSoft의 word 형식이랑 같다는것인가?

hwp나 word는 같은 Compound File Binary Format 이였던것이다.

한글은 XML기반으로도 파일이 제공된다고 팀장님이 알려주셨다.

CFBF, OLE라고도 불리는 이 형태는 하단의 블로그에서 자세하게 설명해준다.

http://www.reversenote.info/ole-file-format/



이제 위의 라이브러리를 통해 1차 목표인 한글파일 읽어들여

콘솔창에 띄우는 결과물을 만들어낼 생각이다.

이미 만들어져있기에 거기까진 어렵지 않지만 그냥 가져다가만 쓰면 개인과제에 의미가 없어보여서

최대한 라이브러리를 뜯어보아서 어떤형식으로 .hwp파일을 파싱하는지 공부해보려고 한다.








+ Recent posts