본 게시글은 Windows 10 Pro (버전 1809) x64 에서 실행되는 Powershell을 대상으로 기록하였습니다. Windows OS 의 버전이나 사용자의 세팅 환경에 따라 다를 수 있음을 알려드립니다.
1.1 문제의 발견
서버의 원격 개발을 위해 VScode의 ssh 연결을 위해 {시작 - 실행}을 통해 Powershell을 입력하여 호출하였다.
평소 Powershell을 "CMD"와 유사하게 설정해놓고 사용하고 있다.
Windows에서는 {시작 - 실행}으로 Powershell을 실행하는 방법 이외에 다른 방법으로 호출할 수 있도록 지원한다.
설정(Windows Shortcut : Winkey + i)에서 {개인설정 - 작업표시줄}의 아래 그림에 표시된 토글 버튼의 활성화 여부에 따라 파워유저 메뉴(Windows Shortcut : Winkey + x or 시작버튼 우클릭) 에서 Powershell을 호출할 수 있다.
해당 토글의 활성화를 통해 파워유저 메뉴에서 Powershell을 실행할 수 있다.
파워유저 메뉴를 통해 실행한 Powershell과 실행을 통해 실행한 Powershell이 다른 것을 확인할 수 있었다.
같은 Powershell인데 하나는 설정이 적용되어 있고 하나는 기본 설정으로 되어 있는 것을 확인하였다.
2. Body
2.1 파워유저 메뉴의 Path
앞서 등장한 {Winkey + i}를 통해 실행할 수 있는 파워유저 메뉴는 아래의 경로에서 관리된다.
Python을 사용하여 'HWP Parser' 제작중 BinData의 내부 Stream에 대해 Decompress를 수행하던 도중 발생한 Trouble에 대해 서술한다.
2. Structure
HWP File Format에 관련하여 한컴 오피스는 홈페이지를 통해 공식 문서를 제공한다.
그 중 BinData 스토리지에는 그림이나 OLE 개체와 같이 문서에 첨부된 바이너리 데이터가 각각의 스트림으로 저장된다.
Parser 제작 과정에서 해당 한글 문서 파일의 악성 유무 판별을 위해 Decompress를 수행해야 했다.
[그림 1] BinData Area
Decompress에는 Python zlib을 활용하였다. 보편적인 zlib의 Decompress 구문은 다음과 같은 에러를 출력했다.
zlib.error: Error -3 while decompressing data: incorrect header check
[그림 2] 에러 확인
3. Trouble Shooting
문제해결을 위해 검색 도중 다음과 같은 글을 확인할 수 있었다.
글에 따르면 메모리에 저장할 수 있는 크기를 초과하는 Stream (또는 파일 입력) 크기 문제로 인해 위의 에러가 발생했을 것이라고 한다. 실제 메모리 크기를 초과한 것이 아닌 버퍼 기본 크기를 초과했기 때문이다.
이를 해결하기 위한 방법은 Stream을 버퍼링으로 처리하고 Decompress를 수행하는 방법이 존재한다. 함께 제공된 솔루션 소스코드는 다음과 같다[1].
import zlib
f_in = open('my_data.zz', 'rb')
comp_data = f_in.read()
zobj = zlib.decompressojb() # obj for decompressing data streams that won’t fit into memory at once.
data = zobj.decompress(comp_data)
위의 방법과 같이 버퍼링을 적용하고 테스트를 진행했을 때 또 다시 동일한 에러가 뜨는 것을 확인했다.
조금 더 찾아보던 도중 다음과 같은 글을 찾을 수 있었다.
[그림 3] wbit 관련 솔루션[2]
위 글에서는 wbit 옵션을 -15로 정의하면 에러가 해결된다고 제시했다. zlib 모듈 공식 홈페이지에서 제공하는 문서에 따르면 WBITS의 의미는 다음과 같다.
[표 1] MAX_WBITS의 의미[3]
The wbits argument controls the size of the history buffer (or the
“window size”) used when compressing data, and whether a header and
trailer is included in the output. It can take several ranges of values,
defaulting to 15 (MAX_WBITS):
+9 to +15: The base-two logarithm of the window size, which
therefore ranges between 512 and 32768. Larger values produce
better compression at the expense of greater memory usage. The
resulting output will include a zlib-specific header and trailer.
−9 to −15: Uses the absolute value of wbits as the
window size logarithm, while producing a raw output stream with no
header or trailing checksum.
+25 to +31 = 16 + (9 to 15): Uses the low 4 bits of the value as the
window size logarithm, while including a basic gzip header
and trailing checksum in the output.
MAX_WBIS 값은 15를 가지며 이를 -로 선언하면 -15 값으로 정의되기 때문에 에러를 해결할 수 있다.
이를 적용한 소스코드는 다음과 같다.
def bin_data(ole,bin_list):
print ()
print ('[+] BinData Information')
for content in bin_list:
if content[0] == 'BinData':
print (' - File Name : %s' %content[0]+'/'+content[1])
bin_text = ole.openstream(content[0]+'/'+content[1])
print (' - File Size : %s' %ole.get_size(content[0]+'/'+content[1]))
data2 = bin_text.read()
print (' - Hex data ~20bytes(pre-Decompress) : %s' %data2[:20])
zobj = zlib.decompressobj(-zlib.MAX_WBITS)
data3 = zobj.decompress(data2)
print (' - Hex data ~20bytes(Decompress) : %s' %data3[:20])
f = open('./'+content[1]+'_Decom.txt','wb')
f.write(data3)
f.close
print ()
Uploaded by Notion2Tistory v1.1.0