
Go로 작성된 방어용 Office 매크로 분석기: VBA를 추출하고, 난독화를 해제하며, 완전히 시뮬레이션된 샌드박스에서 에뮬레이션하여 샘플을 실행하지 않고도 명령, PowerShell, 셸코드 및 드롭된 페이로드를 복구합니다.
MaldocTrace는 Go로 작성된 악성 Office 문서용 방어적 분석기입니다. VBA 및 Excel 4.0(XLM) 매크로를 추출하고, 난독화된 문자열을 재구성하며, 완전히 시뮬레이션된 환경에서 매크로를 에뮬레이션합니다. 출력 결과는 문서가 수행할 동작, 즉 실행할 명령, 디코딩할 페이로드, 드롭할 파일을 보고합니다. 이 모든 것은 해당 결과를 생성한 모듈, 프로시저 및 줄(또는 시트와 셀)을 가리키는 증거로 뒷받침됩니다.
매크로가 전혀 없는 문서도 다룹니다. 원격 템플릿 주입, DDE 필드, 연결 및 포함된 OLE 개체(RTF의 Equation Editor 익스플로잇 포함), 하이퍼링크된 유인 이미지, VBA 스톰핑은 정적 발견 항목으로 보고됩니다.
.doc, .xls, .ppt), OOXML (.docx, .docm, .xlsx, .xlsm, .pptm), RTF 및 원시 VBA 텍스트. MS-OVBA 압축 해제, UserForm 컨트롤 속성, 그리고 zip 폭탄 및 손상된 컨테이너에 대한 리소스 제한을 포함합니다..xls(파싱된 토큰에서 MS-XLS 함수 테이블을 사용해 디컴파일된 BIFF8 수식) 및 .xlsm의 매크로 시트, 숨겨진 시트와 매우 숨겨진 시트 포함;Auto_Open(Excel이 실행할 수 있는 모든 이름)에서 시작하는 XLM 인터프리터. GOTO, RUN, IF, WHILE/FOR/NEXT, ON.TIME 및 RETURN을 따라가며 FORMULA/FORMULA.FILL/SET.VALUE/SET.NAME을 적용하므로, 자기 복호화 매크로가 실제 코드를 재구성합니다;EXEC, CALL, REGISTER 및 별칭(URLDownloadToFileA, ShellExecuteA, WinExec, CreateProcessA)과 FOPEN/FWRITE. 안티 샌드박스 프로브(GET.WORKSPACE, GET.WINDOW, GET.CELL)는 그럴듯한 답변을 받으며 시뮬레이션된 것으로 표시됩니다;attachedTemplate, RTF \*\template);ms-msdt(Follina) 모니커 포함;QUOTE로 인코딩된 것 포함)와 Excel DDE 수식 및 링크;\objdata, 손상된 OLE1 헤더), Equation Editor 익스플로잇 탐지 포함: MTEF FONT 오버플로(CVE-2017-11882 / CVE-2018-0802) 및 MATRIX 오버플로(CVE-2018-0798), 그리고 뒤따르는 바이트 추출;Sheets("x").Range("A1"), Cells(r, c)), 문서 속성(BuiltInDocumentProperties("Comments"), 사용자 지정 속성), 문서 변수(ActiveDocument.Variables) 및 UserForm 컨트롤(TextBox.Text, Label.Caption)은 모두 에뮬레이션된 매크로에서 사용할 수 있습니다..docx에서 추출됩니다. 드로퍼는 종종 문서 본문에 페이로드를 숨기고 ActiveDocument.Paragraphs(n).Range.Text를 통해 다시 읽습니다.ByRef 매개변수, 명명된 인수, 조건부 컴파일, 루프, 배열 및 문자열 내장 함수.Chr/StrReverse/Replace/연결의 상수 폴딩. 각 값은 재구성된 방식(concatenation+Chr, StrReverse, ...)을 기록합니다.WScript.Shell, Shell, WMI Win32_Process.Create (GetObject 및 CreateObject("winmgmts:...") 모두)FileSystemObject, ADODB.Stream, XMLHTTP/WinHttpMSScriptControl.ScriptControlParagraphs, Range, Delete, SaveAs2Sheets, Worksheets, Range, Cells, Offset), XLM 엔진과 공유되며, ExecuteExcel4Macro/Application.Run을 통한 매크로 시트 호출VarPtr 포인터 포함-EncodedCommand 디코딩, URL 추출 전 'ht'+'tp' 연결의 정적 폴딩 포함MultiByteToWideChar(CP_UTF7)에 전달되는 UTF-7 셸코드 버퍼certutil -decode, 매크로가 작성한 내용으로 구축된 가상 파일 시스템에서 재현ScriptControl.AddCode에 전달된 스크립트, 명령줄이 아닌 스크립트 페이로드로 보고event-3, command-1, payload-1)와 소스 위치를 가집니다. 보조 뷰는 큰 블롭을 반복하는 대신 ID로 항목을 참조합니다.Windows, Linux, macOS(x64 및 ARM64)용 사전 빌드된 바이너리는 SHA-256 체크섬과 함께 Releases 페이지에서 제공됩니다. 각 릴리스의 노트는 CHANGELOG.md에서 가져옵니다.
Go 1.23 이상이 필요합니다.
go build -o maldocTrace ./cmd/maldoctrace
Windows의 경우:
go build -o maldocTrace.exe ./cmd/maldoctrace
maldocTrace inspect <file>
maldocTrace extract <file> --out <dir> [--force]
maldocTrace analyze <file> [--format json|text|jsonl] [--timeout 30s] [--max-steps N]
[--input-format vba] [--show-strings] [--dump-payloads DIR]
maldocTrace analyze <dir> --recursive [--format jsonl] [--workers N]
maldocTrace version
| 옵션 | 설명 |
|---|---|
--format text | 사람이 읽을 수 있는 보고서(기본값). |
--format json | 전체 버전 관리되는 JSON 보고서(스키마 1.3.0). 항상 지표, 진단, 디코딩된 문자열 및 전체 페이로드 내용을 포함합니다. |
--format jsonl | 한 줄에 하나의 JSON 보고서, --recursive를 사용한 배치 분석용. |
--show-strings | 텍스트 형식 전용. 재구성된 문자열을 중복 제거하여 나열하며, 각 모듈/프로시저/줄 발생 위치와 재구성 방법을 표시합니다. |
--dump-payloads DIR | 디코딩된 페이로드와 매크로가 드롭한 파일을 DIR/<input-sha256>/에 manifest.json과 함께 기록합니다. 파일에는 중립적인 확장자(.bin, .txt)가 부여되므로 더블클릭으로 실행될 수 없습니다. 기존 파일은 절대 덮어쓰지 않습니다. |
--input-format vba | 일반 텍스트 VBA 파일을 분석합니다. |
--timeout, --max-steps | 파일별 시간 예산 및 인터프리터 단계 예산. |
종료 코드: 0 성공(샘플이 악성인 경우에도), 1 분석 또는 내부 오류, 2 사용법 오류.
아래 네 개의 보고서는 실제 악성 문서에서 가져온 것이며 주요 복구 경로를 보여줍니다. 긴 데이터 덩어리는 …로 축약했습니다. 전체 값은 JSON 출력에 있습니다.
MultiByteToWideChar → EnumUILanguages)매크로는 바이트 배열을 UTF-7 텍스트로 채우고 MultiByteToWideChar(65000, …, VarPtr(buf(0)), …)를 사용하여 이를 RWX 메모리에 복사합니다. 그런 다음 EnumUILanguages 콜백을 통해 메모리를 실행합니다. MaldocTrace는 VarPtr을 배열로 해석하고, 버퍼를 캡처하며, UTF-7을 정적으로 디코딩합니다. 결과는 x64 셸코드(90 55 48 89 e5 = nop; push rbp; mov rbp,rsp)입니다.
$ maldocTrace analyze 394c97cc9d567e556a357f129aea03f737cbd2a1761df32146ef69d93afc73dc.doc
MaldocTrace 0.1.0 (schema 1.2.0)
File: 394c97cc9d567e556a357f129aea03f737cbd2a1761df32146ef69d93afc73dc.doc (60418 bytes)
SHA-256: 394c97cc9d567e556a357f129aea03f737cbd2a1761df32146ef69d93afc73dc
Format: ooxml
Status: partial | Macros: present
Operational: completed | Semantic coverage: partial
-- Recovered commands --
[command-1] In-memory payload via MultiByteToWideChar
Command: +VZCJSFXlgUiA7A-\x00+6AA-\x00\x00+SF3tg0gSTY1IL8LHA3I-\x00+EfYxgEgbwf//SHXKgfNvqMHg5ITk5G+p/KRp…
Receiver: kernel32.MultiByteToWideChar
Source: ThisDocument.lllIiIjLliJjJIlJ:113 (inherited=false)
Discovery: emulated_call
Execution: simulated
Resolution: resolved
-- Decoded payloads and recovered scripts --
[payload-1] UTF-7 buffer (ljLLJJLJjjjjJijI(0))
Category: binary | 1858 bytes | sha256=063d85ce18b08987b75b57c5e01d1d7e8183b54d56c4473eca415285cb28a555
Encoding: UTF-7 → UTF-16LE
Content (hex):
90554889e5554881ec80000000e8000000005d4883ed12488d4d2f48c7c27203
0000f61180311b48ffc148ffca75f381a86fe0c184e4e4e4a96fa4fca96984f4
…
Derived from: command-1
Status: decoded
-- Entry points --
ThisDocument.DoCUmeNT_OPEn (Document_Open event)
-- Modules --
ThisDocument (document/class/designer, 137195 chars)
-- Simulated operations --
#1 api_call [ThisDocument.lllIiIjLliJjJIlJ:112] -1 0 20480 12288 64
#2 api_call [ThisDocument.lllIiIjLliJjJIlJ:113] → command-1
#3 api_call [ThisDocument.lllIiIjLliJjJIlJ:114] 0 0 0
Reconstructed strings: 4395 (hidden; use --show-strings, or see decoded_strings in JSON)
certutil -decode + rundll32)매크로는 문서의 2, 4, 6, 8번째 단락에서 명령 조각을 읽습니다. 각 조각은 Left()로 정확한 길이만큼 잘립니다: C:\Users\Public\, winmgmts:Win32_Process, Rundll32, Certutil -decode. 그런 다음 매크로는 3561자 길이의 미끼 텍스트를 삭제하고, 본문의 나머지 부분인 Base64로 인코딩된 PE를 Ksh1.xls로 저장합니다. 이 파일을 certutil로 디코딩하고 그 결과를 rundll32로 로드합니다. MaldocTrace는 시뮬레이션된 문서와 가상 파일 시스템 위에서 전체 체인을 재현하여 DLL을 복구합니다.
$ maldocTrace analyze 7dbd8ecfada1d39a81a58c9468b91039.doc
MaldocTrace 0.1.0 (schema 1.2.0)
File: 7dbd8ecfada1d39a81a58c9468b91039.doc (850432 bytes)
SHA-256: dc40e48d2eb0e57cd16b1792bdccc185440f632783c7bcc87c955e1d4e88fc95
Format: cfb
Status: complete | Macros: present
Operational: completed | Semantic coverage: supported_subset