Skip to content

Commit bdccd08

Browse files
authored
Refactored message parsing code. (#3)
Also ignore useless xmlns attributes.
1 parent ed66e9b commit bdccd08

File tree

7 files changed

+233
-198
lines changed

7 files changed

+233
-198
lines changed

cmd/extract_windows.go

+2-82
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,13 @@ import (
77
"errors"
88
"fmt"
99
"os"
10-
"path/filepath"
11-
"regexp"
1210
"strings"
1311

1412
_ "github.com/mattn/go-sqlite3"
1513
"golang.org/x/sys/windows/registry"
1614
kingpin "gopkg.in/alecthomas/kingpin.v2"
1715
"www.velocidex.com/golang/binparsergen/reader"
16+
"www.velocidex.com/golang/evtx"
1817
pe "www.velocidex.com/golang/go-pe"
1918
)
2019

@@ -67,7 +66,7 @@ func walkProvider(cb func(provider string, message_table string) error) error {
6766
continue
6867
}
6968

70-
for _, message_file := range expandLocations(message_files) {
69+
for _, message_file := range evtx.ExpandLocations(message_files) {
7170
err = cb(provider_name, message_file)
7271
if err != nil {
7372
fmt.Printf("While processing %v (%v): %v\n",
@@ -82,85 +81,6 @@ func walkProvider(cb func(provider string, message_table string) error) error {
8281
return nil
8382
}
8483

85-
var system_root_re = regexp.MustCompile("(?i)%?SystemRoot%?")
86-
var windir_re = regexp.MustCompile("(?i)%windir%")
87-
var programfiles_re = regexp.MustCompile("(?i)%programfiles%")
88-
var system32_re = regexp.MustCompile(`(?i)\\System32\\`)
89-
90-
// Produce a list of possible locations the message file may be. We
91-
// process all of them because sometimes event messages are split
92-
// across multiple dlls. For example, a generic message table may
93-
// exist in C:\Windows\System32\XXX.dll but a localized message table
94-
// also exists in C:\Windows\System32\en-us\XXX.dll.mui
95-
func expandLocations(message_file string) []string {
96-
97-
// Expand environment variables in paths.
98-
replace_env_vars := func(paths []string) []string {
99-
system_root := os.Getenv("SystemRoot")
100-
windir := os.Getenv("WinDir")
101-
programfiles := os.Getenv("programfiles")
102-
programfiles_x86 := os.Getenv("ProgramFiles(x86)")
103-
104-
result := []string{}
105-
for _, path := range paths {
106-
path = system_root_re.ReplaceAllLiteralString(
107-
path, system_root)
108-
109-
path = windir_re.ReplaceAllLiteralString(
110-
path, windir)
111-
112-
if programfiles_re.FindString(path) != "" {
113-
result = append(result,
114-
programfiles_re.ReplaceAllLiteralString(
115-
path, programfiles))
116-
result = append(result,
117-
programfiles_re.ReplaceAllLiteralString(
118-
path, programfiles_x86))
119-
} else {
120-
result = append(result, path)
121-
}
122-
}
123-
return result
124-
}
125-
126-
// When paths refer to system32 the message table may instead
127-
// reside in the 32 bit version.
128-
split_system32 := func(paths []string) []string {
129-
result := []string{}
130-
for _, path := range paths {
131-
result = append(result, path)
132-
133-
// Sometimes messages are found in the 32 bit folders.
134-
if system32_re.FindString(path) != "" {
135-
result = append(result, system32_re.ReplaceAllLiteralString(
136-
path, "\\SysWow64\\"))
137-
}
138-
}
139-
140-
return result
141-
}
142-
143-
include_muis := func(paths []string) []string {
144-
result := []string{}
145-
for _, path := range paths {
146-
result = append(result, path)
147-
148-
// Sometimes messages are found in the MUI
149-
// files include those as well.
150-
dll_name := filepath.Base(path)
151-
dir_name := filepath.Dir(path)
152-
153-
result = append(result, filepath.Join(
154-
dir_name, "en-US", dll_name+".mui"))
155-
}
156-
return result
157-
}
158-
159-
// Message file values may be separated by ;
160-
return include_muis(split_system32(replace_env_vars(
161-
strings.Split(message_file, ";"))))
162-
}
163-
16484
func makeDatabase(filename string) (*sql.DB, error) {
16585
database, err := sql.Open("sqlite3", filename)
16686
if err != nil {

cmd/parse.go

+11-112
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ func (self *parsingContext) Parse() {
6262
for _, i := range records {
6363
event_map, ok := i.Event.(*ordereddict.Dict)
6464
if ok {
65-
event, ok := GetMap(event_map, "Event")
65+
event, ok := ordereddict.GetMap(event_map, "Event")
6666
if !ok {
6767
continue
6868
}
@@ -77,13 +77,18 @@ func (self *parsingContext) Parse() {
7777
}
7878

7979
func (self *parsingContext) maybeExpandMessage(event_map *ordereddict.Dict) {
80+
// If not message database is loaded just ignore it.
81+
if self.query == nil {
82+
return
83+
}
84+
8085
// Event.System.Provider.Name
81-
name, ok := GetString(event_map, "System.Provider.Name")
86+
name, ok := ordereddict.GetString(event_map, "System.Provider.Name")
8287
if !ok {
8388
return
8489
}
8590

86-
event_id, ok := GetInt(event_map, "System.EventID.Value")
91+
event_id, ok := ordereddict.GetInt(event_map, "System.EventID.Value")
8792
if !ok {
8893
return
8994
}
@@ -97,7 +102,7 @@ func (self *parsingContext) maybeExpandMessage(event_map *ordereddict.Dict) {
97102
var message string
98103
err = rows.Scan(&message)
99104
if err == nil {
100-
event_map.Set("Message", self.expandMessage(event_map, message))
105+
event_map.Set("Message", evtx.ExpandMessage(event_map, message))
101106
return
102107
}
103108
}
@@ -108,9 +113,9 @@ var expansion_re = regexp.MustCompile(`\%[0-9n]+`)
108113
func (self *parsingContext) expandMessage(event_map *ordereddict.Dict, message string) string {
109114
expansions := []string{}
110115

111-
data, pres := GetMap(event_map, "UserData.EventXML")
116+
data, pres := ordereddict.GetMap(event_map, "UserData.EventXML")
112117
if !pres {
113-
data_any, pres := GetAny(event_map, "EventData.Data")
118+
data_any, pres := ordereddict.GetAny(event_map, "EventData.Data")
114119
if !pres {
115120
return message
116121
}
@@ -152,112 +157,6 @@ func (self *parsingContext) expandMessage(event_map *ordereddict.Dict, message s
152157
})
153158
}
154159

155-
func GetString(event_map *ordereddict.Dict, members string) (string, bool) {
156-
var value interface{} = event_map
157-
var pres bool
158-
159-
for _, member := range strings.Split(members, ".") {
160-
if event_map == nil {
161-
return "", false
162-
}
163-
164-
value, pres = event_map.Get(member)
165-
if !pres {
166-
return "", false
167-
}
168-
event_map, pres = value.(*ordereddict.Dict)
169-
}
170-
171-
value_str, ok := value.(string)
172-
if ok {
173-
return value_str, true
174-
}
175-
176-
return "", false
177-
}
178-
179-
func GetMap(event_map *ordereddict.Dict, members string) (*ordereddict.Dict, bool) {
180-
var value interface{} = event_map
181-
var pres bool
182-
183-
for _, member := range strings.Split(members, ".") {
184-
if event_map == nil {
185-
return nil, false
186-
}
187-
188-
value, pres = event_map.Get(member)
189-
if !pres {
190-
return nil, false
191-
}
192-
event_map, pres = value.(*ordereddict.Dict)
193-
if !pres {
194-
return nil, false
195-
}
196-
}
197-
198-
return event_map, true
199-
}
200-
201-
func GetAny(event_map *ordereddict.Dict, members string) (interface{}, bool) {
202-
var value interface{} = event_map
203-
var pres bool
204-
205-
for _, member := range strings.Split(members, ".") {
206-
if event_map == nil {
207-
return nil, false
208-
}
209-
210-
value, pres = event_map.Get(member)
211-
if !pres {
212-
return nil, false
213-
}
214-
event_map, pres = value.(*ordereddict.Dict)
215-
}
216-
217-
return value, true
218-
}
219-
220-
func GetInt(event_map *ordereddict.Dict, members string) (int, bool) {
221-
var value interface{} = event_map
222-
var pres bool
223-
224-
for _, member := range strings.Split(members, ".") {
225-
if event_map == nil {
226-
return 0, false
227-
}
228-
229-
value, pres = event_map.Get(member)
230-
if !pres {
231-
return 0, false
232-
}
233-
event_map, pres = value.(*ordereddict.Dict)
234-
}
235-
236-
switch t := value.(type) {
237-
case int:
238-
return t, true
239-
case uint8:
240-
return int(t), true
241-
case uint16:
242-
return int(t), true
243-
case uint32:
244-
return int(t), true
245-
case uint64:
246-
return int(t), true
247-
case int8:
248-
return int(t), true
249-
case int16:
250-
return int(t), true
251-
case int32:
252-
return int(t), true
253-
case int64:
254-
return int(t), true
255-
256-
}
257-
258-
return 0, false
259-
}
260-
261160
func doParse() {
262161
ctx := NewParsingContext()
263162
ctx.Parse()

evtx.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,10 @@ func (self *TemplateNode) SetLiteral(key string, literal interface{}) {
232232
self.NestedDict = ordereddict.NewDict() //make(map[string]*TemplateNode)
233233
}
234234

235-
self.NestedDict.Set(key, &TemplateNode{Literal: literal})
235+
// Ignore useless xmlsn attributes.
236+
if key != "xmlns" {
237+
self.NestedDict.Set(key, &TemplateNode{Literal: literal})
238+
}
236239
}
237240

238241
func (self *TemplateNode) SetExpansion(key string, id, type_id uint32) {

go.mod

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
module www.velocidex.com/golang/evtx
22

33
require (
4-
github.com/Velocidex/ordereddict v0.0.0-20191103011020-3b5a5f6957d4
4+
github.com/Velocidex/ordereddict v0.0.0-20191106020901-97c468e5e403
55
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
66
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d
77
github.com/davecgh/go-spew v1.1.1
88
github.com/mattn/go-sqlite3 v1.11.0
99
github.com/pkg/errors v0.8.1
10-
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c // indirect
10+
golang.org/x/sys v0.0.0-20191029155521-f43be2a4598c
1111
gopkg.in/alecthomas/kingpin.v2 v2.2.6
12-
www.velocidex.com/golang/go-pe v0.1.1-0.20191103232346-ac12e8190bb6 // indirect
12+
www.velocidex.com/golang/binparsergen v0.1.0
13+
www.velocidex.com/golang/go-pe v0.1.1-0.20191103232346-ac12e8190bb6
1314
)
1415

1516
// replace www.velocidex.com/golang/go-pe => /home/mic/projects/go-pe/
17+
//replace github.com/Velocidex/ordereddict => /home/mic/projects/ordereddict
1618

1719
go 1.13

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
github.com/Velocidex/ordereddict v0.0.0-20191103011020-3b5a5f6957d4 h1:0sJvmFhuTT8W/qOlka2y3L1FrbRcP9ZxXMKejTGMCno=
22
github.com/Velocidex/ordereddict v0.0.0-20191103011020-3b5a5f6957d4/go.mod h1:pxJpvN5ISMtDwrdIdqnJ3ZrjIngCw+WT6gfNil6Zjvo=
3+
github.com/Velocidex/ordereddict v0.0.0-20191106020901-97c468e5e403 h1:IyE0Jbkgftu+no3dqzrQOKsISicXDk5ob6IYdXaxlS8=
4+
github.com/Velocidex/ordereddict v0.0.0-20191106020901-97c468e5e403/go.mod h1:pxJpvN5ISMtDwrdIdqnJ3ZrjIngCw+WT6gfNil6Zjvo=
35
github.com/alecthomas/repr v0.0.0-20181024024818-d37bc2a10ba1/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
46
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
57
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=

messages.go

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package evtx
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"strconv"
7+
8+
"github.com/Velocidex/ordereddict"
9+
)
10+
11+
var (
12+
expansion_re = regexp.MustCompile(`\%[0-9ntr]+`)
13+
system_root_re = regexp.MustCompile("(?i)%?SystemRoot%?")
14+
windir_re = regexp.MustCompile("(?i)%windir%")
15+
programfiles_re = regexp.MustCompile("(?i)%programfiles%")
16+
system32_re = regexp.MustCompile(`(?i)\\System32\\`)
17+
)
18+
19+
func flatten(dict *ordereddict.Dict) []interface{} {
20+
result := []interface{}{}
21+
22+
for _, k := range dict.Keys() {
23+
value, _ := dict.Get(k)
24+
25+
switch t := value.(type) {
26+
case *ordereddict.Dict:
27+
result = append(result, flatten(t)...)
28+
default:
29+
result = append(result, value)
30+
}
31+
}
32+
33+
return result
34+
}
35+
36+
func ExpandMessage(event_map *ordereddict.Dict, message string) string {
37+
data, pres := ordereddict.GetMap(event_map, "UserData")
38+
if !pres {
39+
data, pres = ordereddict.GetMap(event_map, "EventData")
40+
if !pres {
41+
return message
42+
}
43+
}
44+
expansions := flatten(data)
45+
46+
return expansion_re.ReplaceAllStringFunc(message, func(match string) string {
47+
switch match {
48+
case "%n":
49+
return "\n"
50+
case "%r":
51+
return ""
52+
case "%t":
53+
return "\t"
54+
}
55+
56+
number, _ := strconv.Atoi(match[1:])
57+
58+
// Regex expansions start at 1
59+
number -= 1
60+
if number >= 0 && number < len(expansions) {
61+
return fmt.Sprintf("%v", expansions[number])
62+
}
63+
return match
64+
})
65+
}

0 commit comments

Comments
 (0)