1 package magic
2
3 import (
4 "bytes"
5 "encoding/binary"
6 )
7
8 var (
9 xlsxSigFiles = []string{
10 "xl/worksheets/",
11 "xl/drawings/",
12 "xl/theme/",
13 "xl/_rels/",
14 "xl/styles.xml",
15 "xl/workbook.xml",
16 "xl/sharedStrings.xml",
17 }
18 docxSigFiles = []string{
19 "word/media/",
20 "word/_rels/document.xml.rels",
21 "word/document.xml",
22 "word/styles.xml",
23 "word/fontTable.xml",
24 "word/settings.xml",
25 "word/numbering.xml",
26 "word/header",
27 "word/footer",
28 }
29 pptxSigFiles = []string{
30 "ppt/slides/",
31 "ppt/media/",
32 "ppt/slideLayouts/",
33 "ppt/theme/",
34 "ppt/slideMasters/",
35 "ppt/tags/",
36 "ppt/notesMasters/",
37 "ppt/_rels/",
38 "ppt/handoutMasters/",
39 "ppt/notesSlides/",
40 "ppt/presentation.xml",
41 "ppt/tableStyles.xml",
42 "ppt/presProps.xml",
43 "ppt/viewProps.xml",
44 }
45 )
46
47
48 func Xlsx(raw []byte, limit uint32) bool {
49 return zipContains(raw, xlsxSigFiles...)
50 }
51
52
53 func Docx(raw []byte, limit uint32) bool {
54 return zipContains(raw, docxSigFiles...)
55 }
56
57
58 func Pptx(raw []byte, limit uint32) bool {
59 return zipContains(raw, pptxSigFiles...)
60 }
61
62
63
64
65 func Ole(raw []byte, limit uint32) bool {
66 return bytes.HasPrefix(raw, []byte{0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1})
67 }
68
69
70
71
72 func Aaf(raw []byte, limit uint32) bool {
73 if len(raw) < 31 {
74 return false
75 }
76 return bytes.HasPrefix(raw[8:], []byte{0x41, 0x41, 0x46, 0x42, 0x0D, 0x00, 0x4F, 0x4D}) &&
77 (raw[30] == 0x09 || raw[30] == 0x0C)
78 }
79
80
81
82 func Doc(raw []byte, _ uint32) bool {
83 clsids := [][]byte{
84
85 {0x06, 0x09, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46},
86
87 {0x00, 0x09, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46},
88
89 {0x07, 0x09, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46},
90 }
91
92 for _, clsid := range clsids {
93 if matchOleClsid(raw, clsid) {
94 return true
95 }
96 }
97
98 return false
99 }
100
101
102 func Ppt(raw []byte, limit uint32) bool {
103
104
105 if matchOleClsid(raw, []byte{
106 0x10, 0x8d, 0x81, 0x64, 0x9b, 0x4f, 0xcf, 0x11,
107 0x86, 0xea, 0x00, 0xaa, 0x00, 0xb9, 0x29, 0xe8,
108 }) || matchOleClsid(raw, []byte{
109 0x70, 0xae, 0x7b, 0xea, 0x3b, 0xfb, 0xcd, 0x11,
110 0xa9, 0x03, 0x00, 0xaa, 0x00, 0x51, 0x0e, 0xa3,
111 }) {
112 return true
113 }
114
115 lin := len(raw)
116 if lin < 520 {
117 return false
118 }
119 pptSubHeaders := [][]byte{
120 {0xA0, 0x46, 0x1D, 0xF0},
121 {0x00, 0x6E, 0x1E, 0xF0},
122 {0x0F, 0x00, 0xE8, 0x03},
123 }
124 for _, h := range pptSubHeaders {
125 if bytes.HasPrefix(raw[512:], h) {
126 return true
127 }
128 }
129
130 if bytes.HasPrefix(raw[512:], []byte{0xFD, 0xFF, 0xFF, 0xFF}) &&
131 raw[518] == 0x00 && raw[519] == 0x00 {
132 return true
133 }
134
135 return lin > 1152 && bytes.Contains(raw[1152:min(4096, lin)],
136 []byte("P\x00o\x00w\x00e\x00r\x00P\x00o\x00i\x00n\x00t\x00 D\x00o\x00c\x00u\x00m\x00e\x00n\x00t"))
137 }
138
139
140 func Xls(raw []byte, limit uint32) bool {
141
142
143 if matchOleClsid(raw, []byte{
144 0x10, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
145 }) || matchOleClsid(raw, []byte{
146 0x20, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
147 }) {
148 return true
149 }
150
151 lin := len(raw)
152 if lin < 520 {
153 return false
154 }
155 xlsSubHeaders := [][]byte{
156 {0x09, 0x08, 0x10, 0x00, 0x00, 0x06, 0x05, 0x00},
157 {0xFD, 0xFF, 0xFF, 0xFF, 0x10},
158 {0xFD, 0xFF, 0xFF, 0xFF, 0x1F},
159 {0xFD, 0xFF, 0xFF, 0xFF, 0x22},
160 {0xFD, 0xFF, 0xFF, 0xFF, 0x23},
161 {0xFD, 0xFF, 0xFF, 0xFF, 0x28},
162 {0xFD, 0xFF, 0xFF, 0xFF, 0x29},
163 }
164 for _, h := range xlsSubHeaders {
165 if bytes.HasPrefix(raw[512:], h) {
166 return true
167 }
168 }
169
170 return lin > 1152 && bytes.Contains(raw[1152:min(4096, lin)],
171 []byte("W\x00k\x00s\x00S\x00S\x00W\x00o\x00r\x00k\x00B\x00o\x00o\x00k"))
172 }
173
174
175 func Pub(raw []byte, limit uint32) bool {
176 return matchOleClsid(raw, []byte{
177 0x01, 0x12, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
178 0x00, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
179 })
180 }
181
182
183 func Msg(raw []byte, limit uint32) bool {
184 return matchOleClsid(raw, []byte{
185 0x0B, 0x0D, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
186 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
187 })
188 }
189
190
191
192 func Msi(raw []byte, limit uint32) bool {
193 return matchOleClsid(raw, []byte{
194 0x84, 0x10, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00,
195 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46,
196 })
197 }
198
199
200
201
202 func matchOleClsid(in []byte, clsid []byte) bool {
203
204
205
206 sectorLength := 512
207 if len(in) < sectorLength {
208 return false
209 }
210 if in[26] == 0x04 && in[27] == 0x00 {
211 sectorLength = 4096
212 }
213
214
215 firstSecID := int(binary.LittleEndian.Uint32(in[48:52]))
216
217
218 clsidOffset := sectorLength*(1+firstSecID) + 80
219
220 if len(in) <= clsidOffset+16 {
221 return false
222 }
223
224 return bytes.HasPrefix(in[clsidOffset:], clsid)
225 }
226
View as plain text