-
Notifications
You must be signed in to change notification settings - Fork 27
/
chunk.go
271 lines (250 loc) · 7.1 KB
/
chunk.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
package evtx
import (
"bytes"
"fmt"
"io"
"github.com/0xrawsec/golang-utils/datastructs"
"github.com/0xrawsec/golang-utils/encoding"
"github.com/0xrawsec/golang-utils/log"
)
// ChunkString is similare to BinXMLName
type ChunkString struct {
Name
}
// StringAt : utility function to get a ChunkString object at a given offset
// @reader : reader containing ChunkString struct @ offset
// @offset : offset at which we find the ChunkString
// return ChunkString
func StringAt(reader io.ReadSeeker, offset int64) (cs ChunkString, err error) {
// Backup offset
backup := BackupSeeker(reader)
// Offsets are relative to start of chunk
GoToSeeker(reader, offset)
err = cs.Parse(reader)
/*if err != nil {
return
}*/
GoToSeeker(reader, backup)
return
}
// ChunkStringTable definition
type ChunkStringTable map[int32]ChunkString
// TemplateTable definition
type TemplateTable map[int32]TemplateDefinitionData
/////////////////////////////// ChunkHeader ////////////////////////////////////
// ChunkHeader structure definition
type ChunkHeader struct {
Magic [8]byte
NumFirstRecLog int64
NumLastRecLog int64
FirstEventRecID int64
LastEventRecID int64
SizeHeader int32
OffsetLastRec int32
Freespace int32
CheckSum uint32
}
// Validate controls the validity of the chunk header
func (ch *ChunkHeader) Validate() error {
if string(ch.Magic[:]) != ChunkMagic {
return fmt.Errorf("Invalid chunk magic: %q", ch.Magic)
}
if ch.SizeHeader != 128 {
return fmt.Errorf("Invalid chunk header size: %d instead of 128", ch.SizeHeader)
}
if ch.OffsetLastRec >= ChunkSize {
return fmt.Errorf("Last event offset exceed size of chunk")
}
return nil
}
func (ch ChunkHeader) String() string {
return fmt.Sprintf(
"\tMagic: %s\n"+
"\tNumFirstRecLog: %d\n"+
"\tNumLastRecLog: %d\n"+
"\tNumFirstRecFile: %d\n"+
"\tNumLastRecFile: %d\n"+
"\tSizeHeader: %d\n"+
"\tOffsetLastRec: %d\n"+
"\tFreespace: %d\n"+
"\tCheckSum: 0x%08x\n",
ch.Magic,
ch.NumFirstRecLog,
ch.NumLastRecLog,
ch.FirstEventRecID,
ch.LastEventRecID,
ch.SizeHeader,
ch.OffsetLastRec,
ch.Freespace,
ch.CheckSum)
}
//////////////////////////////////// Chunk /////////////////////////////////////
// Chunk structure definition
type Chunk struct {
Offset int64
Header ChunkHeader
StringTable ChunkStringTable
TemplateTable TemplateTable
EventOffsets []int32
Data []byte
}
// NewChunk initialize and returns a new Chunk structure
// return Chunk
func NewChunk() Chunk {
return Chunk{StringTable: make(ChunkStringTable, 0), TemplateTable: make(TemplateTable, 0)}
}
// ParseChunkHeader parses a chunk header at offset
func (c *Chunk) ParseChunkHeader(reader io.ReadSeeker) {
err := encoding.Unmarshal(reader, &c.Header, Endianness)
if err != nil {
panic(err)
}
}
// Less implement datastructs.Sortable
func (c Chunk) Less(s *datastructs.Sortable) bool {
other := (*s).(Chunk)
return c.Header.NumFirstRecLog < other.Header.NumFirstRecLog
}
// ParseStringTable parses the string table located at the current offset in the
// reader and modify the chunk object
// @reader : reader object to parse string table from
func (c *Chunk) ParseStringTable(reader io.ReadSeeker) {
strOffset := int32(0)
for i := int64(0); i < sizeStringBucket*4; i += 4 {
encoding.Unmarshal(reader, &strOffset, Endianness)
if strOffset > 0 {
cs, err := StringAt(reader, int64(strOffset))
if err != nil {
if !ModeCarving {
panic(err)
}
}
c.StringTable[strOffset] = cs
}
}
return
}
// ParseTemplaTable parses the template table located at the current offset in
// the reader passed as parameter and modifies the current Chunk object
// @reader : reader object to parse string table from
func (c *Chunk) ParseTemplateTable(reader io.ReadSeeker) error {
templateDataOffset := int32(0)
for i := int32(0); i < sizeTemplateBucket*4; i = i + 4 {
//parse(buf, i, &tempOffsetTable[j])
err := encoding.Unmarshal(reader, &templateDataOffset, Endianness)
if err != nil {
// panic(err)
log.DebugDontPanic(err)
return err
}
if templateDataOffset > 0 {
backup := BackupSeeker(reader)
// We arrive in template data, we have to do some offset patching in order to get
// back to TemplateInstance token and make it easily parsable by binxml.Parse
GoToSeeker(reader, int64(templateDataOffset))
tdd := TemplateDefinitionData{}
err := tdd.Parse(reader)
if err != nil {
//panic(err)
log.DebugDontPanic(err)
return err
}
c.TemplateTable[templateDataOffset] = tdd
GoToSeeker(reader, backup)
}
}
return nil
}
// ParseEventOffsets parses the offsets at which we can find the events and
// modifies the current Chunk object
// @reader : reader object to parse event offsets from
func (c *Chunk) ParseEventOffsets(reader io.ReadSeeker) (err error) {
c.EventOffsets = make([]int32, 0)
offsetEvent := int32(BackupSeeker(reader))
c.EventOffsets = append(c.EventOffsets, offsetEvent)
for offsetEvent <= c.Header.OffsetLastRec {
eh := EventHeader{}
GoToSeeker(reader, int64(offsetEvent))
if err = encoding.Unmarshal(reader, &eh, Endianness); err != nil {
log.DebugDontPanic(err)
return err
}
// Event Header is not valid
if err = eh.Validate(); err != nil {
// we bruteforce in carving mode
if ModeCarving {
offsetEvent++
continue
}
return err
}
offsetEvent += eh.Size
c.EventOffsets = append(c.EventOffsets, offsetEvent)
}
return nil
}
// ParseEvent parses an Event from the current chunk located at the relative
// offset in c.Data, does not alter the current Chunk structure
// @offset : offset to parse the Event at
// return Event : parsed Event
func (c *Chunk) ParseEvent(offset int64) (e Event) {
if int64(c.Header.OffsetLastRec) < offset {
return
}
reader := bytes.NewReader(c.Data)
GoToSeeker(reader, offset)
e.Offset = offset
err := encoding.Unmarshal(reader, &e.Header, Endianness)
if err != nil {
panic(err)
}
/*err := encoding.Unmarshal(reader, &e.Magic, Endianness)
if err != nil {
panic(err)
}
err = encoding.Unmarshal(reader, &e.Size, Endianness)
if err != nil {
panic(err)
}
err = encoding.Unmarshal(reader, &e.ID, Endianness)
if err != nil {
panic(err)
}
err = encoding.Unmarshal(reader, &e.Timestamp, Endianness)
if err != nil {
panic(err)
}*/
return e
}
// Events returns a channel of *GoEvtxMap
// return (chan *GoEvtxMap)
func (c *Chunk) Events() (cgem chan *GoEvtxMap) {
// Unbuffered Event channel
cgem = make(chan *GoEvtxMap, len(c.EventOffsets))
go func() {
defer close(cgem)
for _, eo := range c.EventOffsets {
// for every event offset, we parsed the event at that position
event := c.ParseEvent(int64(eo))
gem, err := event.GoEvtxMap(c)
if err == nil {
cgem <- gem
}
}
}()
return
}
func (c Chunk) String() string {
templateOffsets := make([]int32, len(c.TemplateTable))
i := 0
for to, _ := range c.TemplateTable {
templateOffsets[i] = to
i++
}
return fmt.Sprintf(
"Header: %v\n"+
"StringTable: %v\n"+
"TemplateTable: %v\n"+
"EventOffsets: %v\n"+
"TemplatesOffsets (for debug): %v\n", c.Header, c.StringTable, c.TemplateTable, c.EventOffsets, templateOffsets)
}